code: purgatorio

Download patch

ref: 147d51bf7c6cd8798ddb26e33d857be13a66c548
parent: ef6ce918c04fc82bea759fd843cc09e273ec9f6c
author: henesy <devnull@localhost>
date: Sun Nov 4 06:33:14 EST 2018

add utils and such

diff: cannot open b/emu/DragonFly//null: file does not exist: 'b/emu/DragonFly//null' diff: cannot open b/emu/FreeBSD//null: file does not exist: 'b/emu/FreeBSD//null' diff: cannot open b/emu/Irix//null: file does not exist: 'b/emu/Irix//null' diff: cannot open b/emu/Linux//null: file does not exist: 'b/emu/Linux//null' diff: cannot open b/emu/MacOSX//null: file does not exist: 'b/emu/MacOSX//null' diff: cannot open b/emu/NetBSD//null: file does not exist: 'b/emu/NetBSD//null' diff: cannot open b/emu/Nt//null: file does not exist: 'b/emu/Nt//null' diff: cannot open b/emu/OpenBSD//null: file does not exist: 'b/emu/OpenBSD//null' diff: cannot open b/emu/Plan9//null: file does not exist: 'b/emu/Plan9//null' diff: cannot open b/emu/Solaris//null: file does not exist: 'b/emu/Solaris//null' diff: cannot open b/emu/Unixware//null: file does not exist: 'b/emu/Unixware//null' diff: cannot open b/emu/port//null: file does not exist: 'b/emu/port//null' diff: cannot open b/emu//null: file does not exist: 'b/emu//null' diff: cannot open b/limbo//null: file does not exist: 'b/limbo//null' diff: cannot open b/scripts//null: file does not exist: 'b/scripts//null' diff: cannot open b/services/httpd//null: file does not exist: 'b/services/httpd//null' diff: cannot open b/services/webget//null: file does not exist: 'b/services/webget//null' diff: cannot open b/services//null: file does not exist: 'b/services//null' diff: cannot open b/tools/db//null: file does not exist: 'b/tools/db//null' diff: cannot open b/tools/libstyx//null: file does not exist: 'b/tools/libstyx//null' diff: cannot open b/tools/odbc//null: file does not exist: 'b/tools/odbc//null' diff: cannot open b/tools/styxtest//null: file does not exist: 'b/tools/styxtest//null' diff: cannot open b/tools//null: file does not exist: 'b/tools//null' diff: cannot open b/utils/0a//null: file does not exist: 'b/utils/0a//null' diff: cannot open b/utils/0c//null: file does not exist: 'b/utils/0c//null' diff: cannot open b/utils/0l//null: file does not exist: 'b/utils/0l//null' diff: cannot open b/utils/5a//null: file does not exist: 'b/utils/5a//null' diff: cannot open b/utils/5c//null: file does not exist: 'b/utils/5c//null' diff: cannot open b/utils/5coff//null: file does not exist: 'b/utils/5coff//null' diff: cannot open b/utils/5cv//null: file does not exist: 'b/utils/5cv//null' diff: cannot open b/utils/5l//null: file does not exist: 'b/utils/5l//null' diff: cannot open b/utils/6a//null: file does not exist: 'b/utils/6a//null' diff: cannot open b/utils/6c//null: file does not exist: 'b/utils/6c//null' diff: cannot open b/utils/6l//null: file does not exist: 'b/utils/6l//null' diff: cannot open b/utils/8a//null: file does not exist: 'b/utils/8a//null' diff: cannot open b/utils/8c//null: file does not exist: 'b/utils/8c//null' diff: cannot open b/utils/8l//null: file does not exist: 'b/utils/8l//null' diff: cannot open b/utils/9c//null: file does not exist: 'b/utils/9c//null' diff: cannot open b/utils/acid//null: file does not exist: 'b/utils/acid//null' diff: cannot open b/utils/awk//null: file does not exist: 'b/utils/awk//null' diff: cannot open b/utils/c2l//null: file does not exist: 'b/utils/c2l//null' diff: cannot open b/utils/cat//null: file does not exist: 'b/utils/cat//null' diff: cannot open b/utils/cc//null: file does not exist: 'b/utils/cc//null' diff: cannot open b/utils/cp//null: file does not exist: 'b/utils/cp//null' diff: cannot open b/utils/cvbit//null: file does not exist: 'b/utils/cvbit//null' diff: cannot open b/utils/data2c//null: file does not exist: 'b/utils/data2c//null' diff: cannot open b/utils/data2s//null: file does not exist: 'b/utils/data2s//null' diff: cannot open b/utils/echo//null: file does not exist: 'b/utils/echo//null' diff: cannot open b/utils/format//null: file does not exist: 'b/utils/format//null' diff: cannot open b/utils/ftl//null: file does not exist: 'b/utils/ftl//null' diff: cannot open b/utils/iar//null: file does not exist: 'b/utils/iar//null' diff: cannot open b/utils/idea//null: file does not exist: 'b/utils/idea//null' diff: cannot open b/utils/include//null: file does not exist: 'b/utils/include//null' diff: cannot open b/utils/iyacc//null: file does not exist: 'b/utils/iyacc//null' diff: cannot open b/utils/ka//null: file does not exist: 'b/utils/ka//null' diff: cannot open b/utils/kc//null: file does not exist: 'b/utils/kc//null' diff: cannot open b/utils/kl//null: file does not exist: 'b/utils/kl//null' diff: cannot open b/utils/kprof//null: file does not exist: 'b/utils/kprof//null' diff: cannot open b/utils/ksize//null: file does not exist: 'b/utils/ksize//null' diff: cannot open b/utils/kstrip//null: file does not exist: 'b/utils/kstrip//null' diff: cannot open b/utils/ld//null: file does not exist: 'b/utils/ld//null' diff: cannot open b/utils/lib//null: file does not exist: 'b/utils/lib//null' diff: cannot open b/utils/libmach//null: file does not exist: 'b/utils/libmach//null' diff: cannot open b/utils/libregexp//null: file does not exist: 'b/utils/libregexp//null' diff: cannot open b/utils/md5sum//null: file does not exist: 'b/utils/md5sum//null' diff: cannot open b/utils/mk//null: file does not exist: 'b/utils/mk//null' diff: cannot open b/utils/mkdir//null: file does not exist: 'b/utils/mkdir//null' diff: cannot open b/utils/mkext//null: file does not exist: 'b/utils/mkext//null' diff: cannot open b/utils/mkppcimage//null: file does not exist: 'b/utils/mkppcimage//null' diff: cannot open b/utils/ms2//null: file does not exist: 'b/utils/ms2//null' diff: cannot open b/utils/mv//null: file does not exist: 'b/utils/mv//null' diff: cannot open b/utils/na//null: file does not exist: 'b/utils/na//null' diff: cannot open b/utils/ndate//null: file does not exist: 'b/utils/ndate//null' diff: cannot open b/utils/nm//null: file does not exist: 'b/utils/nm//null' diff: cannot open b/utils/ntsrv//null: file does not exist: 'b/utils/ntsrv//null' diff: cannot open b/utils/qa//null: file does not exist: 'b/utils/qa//null' diff: cannot open b/utils/qc//null: file does not exist: 'b/utils/qc//null' diff: cannot open b/utils/ql//null: file does not exist: 'b/utils/ql//null' diff: cannot open b/utils/rcsh//null: file does not exist: 'b/utils/rcsh//null' diff: cannot open b/utils/rm//null: file does not exist: 'b/utils/rm//null' diff: cannot open b/utils/sed//null: file does not exist: 'b/utils/sed//null' diff: cannot open b/utils/sqz//null: file does not exist: 'b/utils/sqz//null' diff: cannot open b/utils/srclist//null: file does not exist: 'b/utils/srclist//null' diff: cannot open b/utils/tc//null: file does not exist: 'b/utils/tc//null' diff: cannot open b/utils/test//null: file does not exist: 'b/utils/test//null' diff: cannot open b/utils/tl//null: file does not exist: 'b/utils/tl//null' diff: cannot open b/utils/tr//null: file does not exist: 'b/utils/tr//null' diff: cannot open b/utils/va//null: file does not exist: 'b/utils/va//null' diff: cannot open b/utils/vc//null: file does not exist: 'b/utils/vc//null' diff: cannot open b/utils/vl//null: file does not exist: 'b/utils/vl//null' diff: cannot open b/utils//null: file does not exist: 'b/utils//null'
--- /dev/null
+++ b/emu/DragonFly/README
@@ -1,0 +1,1 @@
+DragonFly BSD changes contributed by fgudin and extrudedaluminiu (see issue 181 at inferno-os.googlecode.com)
--- /dev/null
+++ b/emu/DragonFly/asm-386.S
@@ -1,0 +1,107 @@
+	.file	"asm-DragonFly-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+	.type	 ournewstack,@function
+	.global	executeonnewstack
+executeonnewstack:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%esi
+
+	movl	8(%ebp), %esi	/* get tos */
+	subl	$4, %esi
+	movl	16(%ebp), %eax
+	movl	%eax, (%esi)	/* stash arg on new stack */
+	subl	$4, %esi
+	movl	12(%ebp), %eax
+	movl	%eax, (%esi)	/* stash tramp on new stack */
+	mov	%esi, %esp	/* swap stacks pronto */
+	popl	%eax		/* recover the tramp address */
+	call	*%eax		/* and jump to it (ho ho) */
+
+	/* if we return here, tramp didn't do it's job */
+
+	addl	$8, %esp	/* clean up for pose value */
+
+	leal	SYS_exit, %eax
+	int	$0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ *	between the unlock and the system call, but this should
+ *	not matter since no task is waiting for the result
+ */
+
+	.type	unlockandexit,@function
+	.global	unlockandexit
+unlockandexit:
+	pushl	%ebp
+	movl	%esp, %ebp
+
+	movl	8(%ebp), %esi		/* get the key address */
+	pushl	$0			/* exit status 0 */
+	movl	$0, %eax		/* unlock the stack allocator */
+	movl	%eax, (%esi)
+	leal	SYS_exit, %eax		/* call exit */
+	int	$0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+	.type	umult,@function
+	.global	umult
+umult:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+
+	movl	8(%ebp), %eax
+	movl	12(%ebp), %ebx
+	mull	%ebx
+	movl	16(%ebp), %ebx
+	movl	%edx, (%ebx)
+
+	popl	%ebx
+	popl	%ebp
+	ret
+
+	.type	FPsave,@function
+	.global	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	FPrestore,@function
+	.global	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	getcallerpc,@function
+	.global	getcallerpc
+getcallerpc:
+	movl	4(%ebp), %eax
+	ret
+
+	.type	_tas,@function
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/DragonFly/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define 	Audio_Mic_Val		SOUND_MIXER_MIC
+#define 	Audio_Linein_Val	SOUND_MIXER_LINE
+
+#define	Audio_Speaker_Val	SOUND_MIXER_SPEAKER
+#define	Audio_Headphone_Val	SOUND_MIXER_PHONEOUT
+#define	Audio_Lineout_Val	SOUND_MIXER_VOLUME
+
+#define 	Audio_Pcm_Val		AFMT_S16_LE
+#define 	Audio_Ulaw_Val		AFMT_MU_LAW
+#define 	Audio_Alaw_Val		AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING	"/dev/dsp"
+
+enum {
+	A_Pause,
+	A_UnPause
+};
+
+enum {
+	A_In,
+	A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static	int	audio_file  = -1;	/* file in/out */
+static	int	audio_file_in  = -1;	/* copy of above when opened O_READ/O_RDWR */
+static	int	audio_file_out  = -1;	/* copy of above when opened O_WRITE/O_RDWR */
+
+static	int	audio_swap_flag = 0;	/* endian swap */
+
+static	int	audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+	int i;
+	static ushort flag = 1;
+
+	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
+	audio_info_init(&av);
+	for (i = 0; i < 32; i++)
+		mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	char ebuf[ERRMAX];
+
+	if (debug)
+		print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+	switch(omode){
+	case OREAD:
+		qlock(&inlock);
+		if(waserror()){
+			qunlock(&inlock);
+			nexterror();
+		}
+
+		if(audio_file_in >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file;
+		poperror();
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			qunlock(&inlock);
+			nexterror();
+		}
+		if(audio_file_in >= 0 || audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+	if (debug)
+		print("audio_file_open: success\nin %d out %d both %d\n",
+			audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+	USED(c);
+	USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_out < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		qunlock(&outlock);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_in < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		audio_file_out = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		close(audio_file);
+		audio_file_in = audio_file_out = audio_file = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long ba, status, chunk, total;
+	char *pva = (char *) va;
+
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(audio_file_in < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(!audio_pause_in(audio_file_in, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count) {
+		chunk = count - total;
+		osenter();
+		status = read(audio_file_in, pva + total, chunk);
+		osleave(); 
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+
+	if(total != count)
+		error(Eio);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(pva, count); 
+
+	poperror();
+	qunlock(&inlock);
+
+	return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long status = -1;
+	long ba, total, chunk, bufsz;
+
+	if (debug > 1)
+		print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+			c, va, count, offset);
+
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	if(audio_file_out < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(va, count); 
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count) {
+		chunk = min(bufsz, count - total);
+		osenter();
+		status = write(audio_file_out, va, chunk);
+		osleave();
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	return count;
+}
+
+static int
+audio_open(void)
+{
+	int fd;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		if (debug)
+			print("audio_open: failed to set blocking\n");
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	if (debug)
+		print("audio_open: blocking set\n");
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = 0;
+
+	if(! audio_set_info(fd, &av.in, A_In)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	int	fd;
+	int	ff;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if (!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if (!audio_enforce(&tmpav))
+		error(Ebadarg);
+
+	qlock(&inlock);
+	if (waserror()) {
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+		if (!audio_pause_in(audio_file_in, A_Pause))
+			error(Ebadarg);
+		if (!audio_flush(audio_file_in, A_In))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&inlock);
+
+	qlock(&outlock);
+	if (waserror()) {
+		qunlock(&outlock);
+		nexterror();
+	}
+	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+		if (!audio_pause_out(audio_file_out))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&outlock);
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+	int status;
+	osenter();
+	status = ioctl(fd, ctl, info);  /* qlock and load general stuff */
+	osleave();
+	if (status < 0)
+		print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+	return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+	int newbits, newenc;
+	
+	newbits = i->bits;
+	newenc = i->enc;
+	switch (newenc) {
+	case Audio_Alaw_Val:
+		if (newbits == 8)
+			return AFMT_A_LAW;
+		break;
+	case Audio_Ulaw_Val:
+		if (newbits == 8)
+			return AFMT_MU_LAW;
+		break;
+	case Audio_Pcm_Val:
+		if (newbits == 8)
+			return AFMT_U8;
+		else if (newbits == 16)
+			return AFMT_S16_LE;
+		break;
+	}
+	return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status;
+	int unequal_stereo = 0;
+
+	if(fd < 0)
+		return 0;
+
+	/* fmt */
+	if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+		int oldfmt, newfmt;
+		oldfmt = AFMT_QUERY;
+		if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+			return 0;
+		if (debug)
+			print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+		newfmt = choosefmt(i);
+		if (debug)
+			print("audio_set_info: new format 0x%.8lux\n", newfmt);
+		if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+			return 0;
+	}
+
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG) {
+		int channels = i->chan;
+		if (debug)
+			print("audio_set_info: new channels %d\n", channels);
+		if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+			|| channels != i->chan)
+			return 0;
+	}
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG) {
+		int speed = i->rate;
+		if (debug)
+			print("audio_set_info: new speed %d\n", speed);
+		if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+			return 0;
+	}
+
+	/* dev volume */
+	if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+		int val;
+		if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+			mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+		if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+			mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+		val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+		doioctl(fd, MIXER_WRITE(i->dev), &val);
+	}
+
+	if (i->flags & AUDIO_DEV_FLAG) {
+	}
+	
+	return 1;
+}
+
+void 
+audio_swap_endian(char *p, int n)
+{
+	int b;
+
+	while (n > 1) {
+		b = p[0];
+		p[0] = p[1];
+		p[1] = b;
+		p += 2;
+		n -= 2;
+	}
+}
+
+static int
+audio_pause_out(int fd)
+{
+	USED(fd);
+	return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+	USED(fd);
+	USED(f);
+	return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+	int x;
+	return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && 
+		(t->in.rate != 8000 || t->in.chan != 1))
+		 return 0;
+	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && 
+		(t->out.rate != 8000 || t->out.chan != 1))
+		 return 0;
+	return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/DragonFly/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgid(0, getpid());
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/DragonFly/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/cuaa0",
+        "/dev/cuaa1",
+        "/dev/cuaa2",
+        "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{115200,	B115200},
+	{230400,	B230400},
+	{-1,		-1}
+};
+
--- /dev/null
+++ b/emu/DragonFly/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/DragonFly/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/DragonFly/emu-g
@@ -1,0 +1,97 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	ip	ipif-posix ipaux
+	eia
+	mem
+
+lib
+	interp
+	math
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	void setpointer(int x, int y) { USED(x); USED(y); }
+	ulong strtochan(char *s) { USED(s); return ~0; }
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/DragonFly/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=DragonFly
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=DragonFly
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm 
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/DragonFly/os.c
@@ -1,0 +1,530 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef getwd
+#include	<signal.h>
+#include 	<sys/socket.h>
+#include	<time.h>
+#include	<sys/time.h>
+#include	<termios.h>
+#include	<sched.h>
+#include	<pwd.h>
+#include	<errno.h>
+#include	<unistd.h>
+#include	<sys/resource.h>
+
+enum
+{
+	DELETE  = 0x7F,
+	NSTACKSPERALLOC = 16,
+	X11STACK=	256*1024
+};
+char *hosttype = "DragonFly";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	Proc *p;
+	void *kstack;
+
+	lock(&procs.l);
+	p = up;
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(up->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", up->text, msg);
+
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	kstack = p->kstack;
+	free(p->prog);
+	free(p);
+	if(kstack != nil)
+		stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+	if(info)
+		print("trapBUS: signo: %d code: %d addr: %lx\n",
+		info->si_signo, info->si_code, info->si_addr);
+	else
+		print("trapBUS: no info\n"); 
+	disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+	int intwait;
+
+	USED(signo);
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+	if(intwait == 0)		/* Not posted so its a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+	USED(signo);
+	/* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+	char buf[64];
+	USED(signo);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+	disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+	struct sigaction act;
+	sigset_t mask;
+
+	memset(&act, 0 , sizeof(act));
+	sigemptyset(&initmask);
+	
+	signal(SIGPIPE, SIG_IGN);	/* prevent signal when devcmd child exits */
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+
+	act.sa_handler = trapUSR1;
+	act.sa_mask = initmask;
+	sigaction(SIGUSR1, &act, nil);
+
+	act.sa_handler = trapUSR2;
+	sigaction(SIGUSR2, &act, nil);
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR2);
+	sigaddset(&initmask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	/*
+ 	 * prevent Zombies forming when any process terminates
+	 */
+	act.sa_sigaction = 0;
+	act.sa_flags |= SA_NOCLDWAIT;
+	if(sigaction(SIGCHLD, &act, nil))
+		panic("sigaction SIGCHLD");
+
+	if(sflag == 0) {
+		act.sa_sigaction = trapBUS;
+		act.sa_flags |= SA_SIGINFO;
+		if(sigaction(SIGBUS, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapILL;
+		if(sigaction(SIGILL, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapSEGV;
+		if(sigaction(SIGSEGV, &act, nil))
+			panic("sigaction SIGSEGV");
+		act.sa_handler = trapFPE;
+		if(sigaction(SIGFPE, &act, nil))
+			panic("sigaction SIGFPE");
+		if(sigaddset(&initmask, SIGINT) == -1)
+			panic("sigaddset");
+	}
+	if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+		panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+	Proc *p;
+
+	p = arg;
+	p->pid = p->sigid = getpid();
+	sigprocmask(SIG_BLOCK, &initmask, nil);	/* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+	(*p->func)(p->arg);
+	pexit("{Tramp}", 0);
+	_exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	int pid;
+	void *tos;
+
+	p = newproc();
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(flags & KPX11){
+		p->kstack = nil;	/* never freed; also up not defined */
+		tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+	}else
+		p->kstack = stackalloc(p, &tos);
+	pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+	if(pid < 0)
+		panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+	kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	sigset_t mask;
+
+	sigprocmask(SIG_SETMASK, NULL, &mask);
+	sigdelset(&mask, SIGUSR2);
+	sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+	if(kill(p->sigid, SIGUSR2) < 0)
+		fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+	struct passwd *pwd;
+	
+	if(pwd = getpwnam("nobody")) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+libinit(char *imod)
+{
+	struct passwd *pw;
+	Proc *p;
+	void *tos;
+	char sys[64];
+
+	setsid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	if(dflag == 0)
+		termset();
+
+	setsigs();
+
+	p = newproc();
+	p->kstack = stackalloc(p, &tos);
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+ 
+	p->env->uid = getuid();
+	p->env->gid = getgid();
+
+	executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return 0;
+	if(sec0==0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct timespec time;
+
+	time.tv_sec = milsec / 1000;
+	time.tv_nsec = (milsec % 1000) * 1000000;
+	nanosleep(&time, 0);
+	return 0;
+}
+
+void
+osyield(void)
+{
+	sched_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+	Lock l;
+	void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+	*((void **)stack) = stacklist.free;
+	stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+	lock(&stacklist.l);
+	_stackfree(stack);
+	unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+	void *rv;
+	lock(&stacklist.l);
+	if (stacklist.free == 0) {
+		int x;
+		/*
+		 * obtain some more by using sbrk()
+		 */
+		void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+		if (more == 0)
+			panic("stackalloc: no more stacks");
+		/*
+		 * align to KSTACK
+		 */
+		more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+		/*
+		 * free all the new stacks onto the freelist
+		 */
+		for (x = 0; x < NSTACKSPERALLOC; x++)
+			_stackfree((char *)more + KSTACK * x);
+	}
+	rv = stacklist.free;
+	stacklist.free = *(void **)rv;
+	unlock(&stacklist.l);
+	*tos = rv + KSTACK - sizeof(void*);
+	*(Proc **)rv = p;
+	return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+	USED(a);
+	USED(n);
+	return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/asm-386.S
@@ -1,0 +1,107 @@
+	.file	"asm-FreeBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+	.type	 ournewstack,@function
+	.global	executeonnewstack
+executeonnewstack:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%esi
+
+	movl	8(%ebp), %esi	/* get tos */
+	subl	$4, %esi
+	movl	16(%ebp), %eax
+	movl	%eax, (%esi)	/* stash arg on new stack */
+	subl	$4, %esi
+	movl	12(%ebp), %eax
+	movl	%eax, (%esi)	/* stash tramp on new stack */
+	mov	%esi, %esp	/* swap stacks pronto */
+	popl	%eax		/* recover the tramp address */
+	call	*%eax		/* and jump to it (ho ho) */
+
+	/* if we return here, tramp didn't do it's job */
+
+	addl	$8, %esp	/* clean up for pose value */
+
+	leal	SYS_exit, %eax
+	int	$0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ *	between the unlock and the system call, but this should
+ *	not matter since no task is waiting for the result
+ */
+
+	.type	unlockandexit,@function
+	.global	unlockandexit
+unlockandexit:
+	pushl	%ebp
+	movl	%esp, %ebp
+
+	movl	8(%ebp), %esi		/* get the key address */
+	pushl	$0			/* exit status 0 */
+	movl	$0, %eax		/* unlock the stack allocator */
+	movl	%eax, (%esi)
+	leal	SYS_exit, %eax		/* call exit */
+	int	$0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+	.type	umult,@function
+	.global	umult
+umult:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+
+	movl	8(%ebp), %eax
+	movl	12(%ebp), %ebx
+	mull	%ebx
+	movl	16(%ebp), %ebx
+	movl	%edx, (%ebx)
+
+	popl	%ebx
+	popl	%ebp
+	ret
+
+	.type	FPsave,@function
+	.global	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	FPrestore,@function
+	.global	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	getcallerpc,@function
+	.global	getcallerpc
+getcallerpc:
+	movl	4(%ebp), %eax
+	ret
+
+	.type	_tas,@function
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/FreeBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define 	Audio_Mic_Val		SOUND_MIXER_MIC
+#define 	Audio_Linein_Val	SOUND_MIXER_LINE
+
+#define	Audio_Speaker_Val	SOUND_MIXER_SPEAKER
+#define	Audio_Headphone_Val	SOUND_MIXER_PHONEOUT
+#define	Audio_Lineout_Val	SOUND_MIXER_VOLUME
+
+#define 	Audio_Pcm_Val		AFMT_S16_LE
+#define 	Audio_Ulaw_Val		AFMT_MU_LAW
+#define 	Audio_Alaw_Val		AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING	"/dev/dsp"
+
+enum {
+	A_Pause,
+	A_UnPause
+};
+
+enum {
+	A_In,
+	A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static	int	audio_file  = -1;	/* file in/out */
+static	int	audio_file_in  = -1;	/* copy of above when opened O_READ/O_RDWR */
+static	int	audio_file_out  = -1;	/* copy of above when opened O_WRITE/O_RDWR */
+
+static	int	audio_swap_flag = 0;	/* endian swap */
+
+static	int	audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+	int i;
+	static ushort flag = 1;
+
+	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
+	audio_info_init(&av);
+	for (i = 0; i < 32; i++)
+		mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	char ebuf[ERRMAX];
+
+	if (debug)
+		print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+	switch(omode){
+	case OREAD:
+		qlock(&inlock);
+		if(waserror()){
+			qunlock(&inlock);
+			nexterror();
+		}
+
+		if(audio_file_in >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file;
+		poperror();
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			qunlock(&inlock);
+			nexterror();
+		}
+		if(audio_file_in >= 0 || audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+	if (debug)
+		print("audio_file_open: success\nin %d out %d both %d\n",
+			audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+	USED(c);
+	USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_out < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		qunlock(&outlock);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_in < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		audio_file_out = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		close(audio_file);
+		audio_file_in = audio_file_out = audio_file = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long ba, status, chunk, total;
+	char *pva = (char *) va;
+
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(audio_file_in < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(! audio_pause_in(audio_file_in, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count) {
+		chunk = count - total;
+		osenter();
+		status = read(audio_file_in, pva + total, chunk);
+		osleave(); 
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+
+	if(total != count)
+		error(Eio);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(pva, count); 
+
+	poperror();
+	qunlock(&inlock);
+
+	return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long status = -1;
+	long ba, total, chunk, bufsz;
+
+	if (debug > 1)
+		print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+			c, va, count, offset);
+
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	if(audio_file_out < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(va, count); 
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count) {
+		chunk = min(bufsz, count - total);
+		osenter();
+		status = write(audio_file_out, va, chunk);
+		osleave();
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	return count;
+}
+
+static int
+audio_open(void)
+{
+	int fd;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		if (debug)
+			print("audio_open: failed to set blocking\n");
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	if (debug)
+		print("audio_open: blocking set\n");
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = 0;
+
+	if(! audio_set_info(fd, &av.in, A_In)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	int	fd;
+	int	ff;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if (!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if (!audio_enforce(&tmpav))
+		error(Ebadarg);
+
+	qlock(&inlock);
+	if (waserror()) {
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+		if (!audio_pause_in(audio_file_in, A_Pause))
+			error(Ebadarg);
+		if (!audio_flush(audio_file_in, A_In))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&inlock);
+
+	qlock(&outlock);
+	if (waserror()) {
+		qunlock(&outlock);
+		nexterror();
+	}
+	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+		if (!audio_pause_out(audio_file_out))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&outlock);
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+	int status;
+	osenter();
+	status = ioctl(fd, ctl, info);  /* qlock and load general stuff */
+	osleave();
+	if (status < 0)
+		print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+	return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+	int newbits, newenc;
+	
+	newbits = i->bits;
+	newenc = i->enc;
+	switch (newenc) {
+	case Audio_Alaw_Val:
+		if (newbits == 8)
+			return AFMT_A_LAW;
+		break;
+	case Audio_Ulaw_Val:
+		if (newbits == 8)
+			return AFMT_MU_LAW;
+		break;
+	case Audio_Pcm_Val:
+		if (newbits == 8)
+			return AFMT_U8;
+		else if (newbits == 16)
+			return AFMT_S16_LE;
+		break;
+	}
+	return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status;
+	int unequal_stereo = 0;
+
+	if(fd < 0)
+		return 0;
+
+	/* fmt */
+	if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+		int oldfmt, newfmt;
+		oldfmt = AFMT_QUERY;
+		if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+			return 0;
+		if (debug)
+			print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+		newfmt = choosefmt(i);
+		if (debug)
+			print("audio_set_info: new format 0x%.8lux\n", newfmt);
+		if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+			return 0;
+	}
+
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG) {
+		int channels = i->chan;
+		if (debug)
+			print("audio_set_info: new channels %d\n", channels);
+		if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+			|| channels != i->chan)
+			return 0;
+	}
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG) {
+		int speed = i->rate;
+		if (debug)
+			print("audio_set_info: new speed %d\n", speed);
+		if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+			return 0;
+	}
+
+	/* dev volume */
+	if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+		int val;
+		if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+			mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+		if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+			mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+		val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+		doioctl(fd, MIXER_WRITE(i->dev), &val);
+	}
+
+	if (i->flags & AUDIO_DEV_FLAG) {
+	}
+	
+	return 1;
+}
+
+void 
+audio_swap_endian(char *p, int n)
+{
+	int b;
+
+	while (n > 1) {
+		b = p[0];
+		p[0] = p[1];
+		p[1] = b;
+		p += 2;
+		n -= 2;
+	}
+}
+
+static int
+audio_pause_out(int fd)
+{
+	USED(fd);
+	return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+	USED(fd);
+	USED(f);
+	return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+	int x;
+	return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && 
+		(t->in.rate != 8000 || t->in.chan != 1))
+		 return 0;
+	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && 
+		(t->out.rate != 8000 || t->out.chan != 1))
+		 return 0;
+	return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/FreeBSD/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgid(0, getpid());
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/FreeBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/cuaa0",
+        "/dev/cuaa1",
+        "/dev/cuaa2",
+        "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{115200,	B115200},
+	{230400,	B230400},
+	{-1,		-1}
+};
+
--- /dev/null
+++ b/emu/FreeBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/FreeBSD/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/FreeBSD/emu-g
@@ -1,0 +1,102 @@
+env
+	X11LIBS=
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	math
+
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	void setpointer(int x, int y){USED(x); USED(y);}
+	ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/FreeBSD/mkfile
@@ -1,0 +1,48 @@
+SYSTARG=FreeBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=FreeBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+X11LIBS= -lX11 -lXext
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm $X11LIBS
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/FreeBSD/mkfile-FreeBSD
@@ -1,0 +1,17 @@
+#
+#	architecture-dependent files for FreeBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+	deveia-FreeBSD.$O\
+	devip.$O\
+	ipif-posix.$O\
+	os-FreeBSD.$O\
+	win-x11.$O\
+	srv.$O\
+	lock.$O\
+	asm-FreeBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm 
--- /dev/null
+++ b/emu/FreeBSD/os.c
@@ -1,0 +1,531 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef getwd
+#include	<signal.h>
+#include 	<sys/socket.h>
+#include	<time.h>
+#include	<sys/time.h>
+#include	<termios.h>
+#include	<sched.h>
+#include	<pwd.h>
+#include	<errno.h>
+#include	<unistd.h>
+#include	<sys/resource.h>
+
+enum
+{
+	DELETE  = 0x7F,
+	NSTACKSPERALLOC = 16,
+	X11STACK=	256*1024
+};
+char *hosttype = "FreeBSD";
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	Proc *p;
+	void *kstack;
+
+	lock(&procs.l);
+	p = up;
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(up->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", up->text, msg);
+
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	kstack = p->kstack;
+	free(p->prog);
+	free(p);
+	if(kstack != nil)
+		stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+	if(info)
+		print("trapBUS: signo: %d code: %d addr: %lx\n",
+		info->si_signo, info->si_code, info->si_addr);
+	else
+		print("trapBUS: no info\n"); 
+	disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+	int intwait;
+
+	USED(signo);
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+	if(intwait == 0)		/* Not posted so its a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+	USED(signo);
+	/* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+
+	char buf[64];
+	USED(signo);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+	disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+	struct sigaction act;
+	sigset_t mask;
+
+	memset(&act, 0 , sizeof(act));
+	sigemptyset(&initmask);
+	
+	signal(SIGPIPE, SIG_IGN);	/* prevent signal when devcmd child exits */
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+
+	act.sa_handler = trapUSR1;
+	act.sa_mask = initmask;
+	sigaction(SIGUSR1, &act, nil);
+
+	act.sa_handler = trapUSR2;
+	sigaction(SIGUSR2, &act, nil);
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR2);
+	sigaddset(&initmask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	/*
+ 	 * prevent Zombies forming when any process terminates
+	 */
+	act.sa_sigaction = 0;
+	act.sa_flags |= SA_NOCLDWAIT;
+	if(sigaction(SIGCHLD, &act, nil))
+		panic("sigaction SIGCHLD");
+
+	if(sflag == 0) {
+		act.sa_sigaction = trapBUS;
+		act.sa_flags |= SA_SIGINFO;
+		if(sigaction(SIGBUS, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapILL;
+		if(sigaction(SIGILL, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapSEGV;
+		if(sigaction(SIGSEGV, &act, nil))
+			panic("sigaction SIGSEGV");
+		act.sa_handler = trapFPE;
+		if(sigaction(SIGFPE, &act, nil))
+			panic("sigaction SIGFPE");
+		if(sigaddset(&initmask, SIGINT) == -1)
+			panic("sigaddset");
+	}
+	if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+		panic("sigprocmask");
+}
+
+static int
+tramp(void *arg)
+{
+	Proc *p;
+
+	p = arg;
+	p->pid = p->sigid = getpid();
+	sigprocmask(SIG_BLOCK, &initmask, nil);	/* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+	(*p->func)(p->arg);
+	pexit("{Tramp}", 0);
+	_exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	int pid;
+	void *tos;
+
+	p = newproc();
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(flags & KPX11){
+		p->kstack = nil;	/* never freed; also up not defined */
+		tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+	}else
+		p->kstack = stackalloc(p, &tos);
+	pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+	if(pid < 0)
+		panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+	kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	sigset_t mask;
+
+	sigprocmask(SIG_SETMASK, NULL, &mask);
+	sigdelset(&mask, SIGUSR2);
+	sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+	if(kill(p->sigid, SIGUSR2) < 0)
+		fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+	struct passwd *pwd;
+	
+	if(pwd = getpwnam("nobody")) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+libinit(char *imod)
+{
+	struct passwd *pw;
+	Proc *p;
+	void *tos;
+	char sys[64];
+
+	setsid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	if(dflag == 0)
+		termset();
+
+	setsigs();
+
+	p = newproc();
+	p->kstack = stackalloc(p, &tos);
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+ 
+	p->env->uid = getuid();
+	p->env->gid = getgid();
+
+	executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return 0;
+	if(sec0==0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct timespec time;
+
+	time.tv_sec = milsec / 1000;
+	time.tv_nsec = (milsec % 1000) * 1000000;
+	nanosleep(&time, 0);
+	return 0;
+}
+
+void
+osyield(void)
+{
+	sched_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+	Lock l;
+	void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+	*((void **)stack) = stacklist.free;
+	stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+	lock(&stacklist.l);
+	_stackfree(stack);
+	unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+	void *rv;
+	lock(&stacklist.l);
+	if (stacklist.free == 0) {
+		int x;
+		/*
+		 * obtain some more by using sbrk()
+		 */
+		void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+		if (more == 0)
+			panic("stackalloc: no more stacks");
+		/*
+		 * align to KSTACK
+		 */
+		more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+		/*
+		 * free all the new stacks onto the freelist
+		 */
+		for (x = 0; x < NSTACKSPERALLOC; x++)
+			_stackfree((char *)more + KSTACK * x);
+	}
+	rv = stacklist.free;
+	stacklist.free = *(void **)rv;
+	unlock(&stacklist.l);
+	*tos = rv + KSTACK - sizeof(void*);
+	*(Proc **)rv = p;
+	return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+	USED(a);
+	USED(n);
+	return 0;
+}
--- /dev/null
+++ b/emu/Irix/NOTE
@@ -1,0 +1,1 @@
+for some strange reason, it lacks deveia!
--- /dev/null
+++ b/emu/Irix/asm-mips.s
@@ -1,0 +1,31 @@
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+LEAF(FPsave)
+	cfc1	t0, $31
+	sw	t0, 0(a0)		/* a0 is argument */
+	j	$31
+	END(FPsave)
+
+LEAF(FPrestore)
+	lw	t0, 0(a0)		/* a0 is argument */
+	ctc1	t0, $31
+	j	$31
+	END(FPrestore)
+
+
+/*
+ * lock from r4000 book
+ */
+LEAF(_tas)
+	.set 	noreorder
+1:
+	ll	v0,0(a0)		/* a0 is argument */
+	or	t1, v0, 1
+	sc	t1,0(a0)
+	beq	t1,zero,1b	
+	nop
+	j	$31			/* lock held */
+	nop
+	.set 	reorder
+	END(_tas)
--- /dev/null
+++ b/emu/Irix/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgrp();
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/Irix/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/Irix/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+#	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Irix/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=Irix
+OBJTYPE=mips
+<../../mkconfig
+SYSTARG=Irix
+OBJTYPE=mips
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lfpe -lm -lX11 -lXext
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/Irix/mkfile-Irix
@@ -1,0 +1,14 @@
+#
+#	architecture-dependent files for IRIX
+#
+
+TARGFILES=asm-Irix.$O\
+	devfs-posix.$O\
+	devip.$O\
+	ipif-posix.$O\
+	os-Irix.$O\
+	win-x11.$O\
+	srv.$O\
+	lock.$O\
+
+SYSLIBS=	-lfpe -lm -lX11
--- /dev/null
+++ b/emu/Irix/os.c
@@ -1,0 +1,475 @@
+/* Link with -lfpe. See man pages for fpc
+ * and /usr/include/sigfpe.h, sys/fpu.h.
+ */
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<time.h>
+#include	<ulocks.h>
+#include	<termios.h>
+#include 	<sigfpe.h>
+#include	<sys/prctl.h>
+#include 	<sys/fpu.h>
+#include	<sys/cachectl.h>
+#undef _POSIX_SOURCE		/* SGI incompetence */
+#include	<signal.h>
+#define _BSD_TIME
+/* for gettimeofday(), which isn't POSIX,
+ * but is fairly common
+ */
+#include 	<sys/time.h> 
+#define _POSIX_SOURCE
+#include 	<pwd.h>
+
+extern	int	rebootargc;
+extern	char**	rebootargv;
+
+int	gidnobody = -1;
+int	uidnobody = -1;
+Proc**	Xup;
+
+#define MAXSPROC 30000	/* max procs == MAXPID */
+static int	sproctbl[MAXSPROC];
+
+enum
+{
+	KSTACK	= 64*1024,
+	DELETE	= 0x7F
+};
+char *hosttype = "Irix";
+char *cputype = "mips";
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+
+	lock(&procs.l);
+	if(up->prev) 
+		up->prev->next = up->next;
+	else
+		procs.head = up->next;
+
+	if(up->next)
+		up->next->prev = up->prev;
+	else
+		procs.tail = up->prev;
+
+	sproctbl[getpid()] = -1;
+
+	unlock(&procs.l);
+
+/*	print("pexit: %s: %s\n", up->text, msg); /**/
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(up->prog);
+	free(up);
+	exit(0);
+}
+
+static void
+tramp(void *p, size_t stacksz)
+{
+	up = p;
+	up->sigid = getpid();
+	up->func(up->arg);
+	pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	int pid;
+	int id;
+	int i;
+
+	p = newproc();
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+
+	for(i = 1; i < MAXSPROC; i++) {
+		if(sproctbl[i] == -1) {
+			break;
+		}
+	}
+
+	if(i==MAXSPROC)
+		return -1;
+
+	sproctbl[i] = -i - 1; /* temporary hold of table index outside of lock */
+
+	unlock(&procs.l);
+
+	pid = sprocsp(tramp, PR_SALL, p, 0, KSTACK);
+
+	if(pid >= 0)
+		sproctbl[i] = pid;
+	else
+		sproctbl[i] = -1;
+}
+
+void
+osblock(void)
+{
+	blockproc(up->sigid);
+}
+
+void
+osready(Proc *p)
+{
+	unblockproc(p->sigid);
+}
+
+void
+trapUSR1(void)
+{
+	int intwait;
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+
+	if(intwait == 0)		/* Not posted so it's a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+void
+trapILL(void)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+void
+trapBUS(void)
+{
+	disfault(nil, "Bus error");
+}
+
+void
+trapSEGV(void)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+/*
+ * This is not a signal handler but rather a vector from real/FPcontrol-Irix.c
+ */
+void
+trapFPE(unsigned exception[5], int value[2])
+{
+	disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+	kill(p->sigid, SIGUSR1);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+
+/*	if(sproctbl[0] < 0)
+		panic("corrupt sproc tbl");
+
+	kill(sproctbl[0], SIGUSR2);
+	sginap(10000); */
+}
+
+void
+trapUSR2(void)
+{
+	int i;
+
+	for(i = MAXSPROC - 1; i > 0; i--) {
+		if(sproctbl[i] != -1) 
+			kill(sproctbl[i], SIGKILL);
+		sproctbl[i] = -1;
+	}
+
+	execvp(rebootargv[0], rebootargv);
+	panic("reboot failure");
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+getnobody(void)
+{
+	struct passwd *pwd;
+
+	pwd = getpwnam("nobody");
+	if(pwd != nil) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+	struct sigaction act;
+	struct passwd *pw;
+	int i;
+	char sys[64];
+
+	setsid();
+
+	for(i=0; i<MAXSPROC; i++)
+		sproctbl[i] = -1;
+
+	sproctbl[0] = getpid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+
+	if(dflag == 0)
+		termset();
+
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+		signal(SIGINT, cleanexit);
+	signal(SIGUSR2, trapUSR2);
+	/* For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGCLD, SIG_IGN);
+	signal(SIGPIPE, SIG_IGN);
+	memset(&act, 0 , sizeof(act));
+	act.sa_handler=trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+	if(sflag == 0) {
+		act.sa_handler=trapBUS;
+		sigaction(SIGBUS, &act, nil);
+		act.sa_handler=trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_handler=trapSEGV;
+		sigaction(SIGSEGV, &act, nil);
+	}
+
+	if(usconfig(CONF_INITUSERS, 1000) < 0)
+		panic("usconfig");
+
+	Xup = (Proc**)PRDA->usr_prda.fill;
+	up = newproc();
+
+	pw = getpwuid(getuid());
+	if(pw != nil) {
+		if (strlen(pw->pw_name) + 1 <= KNAMELEN)
+			strcpy(eve, pw->pw_name);
+		else
+			print("pw_name too long\n");
+	}
+	else
+		print("cannot getpwuid\n");
+
+	/* after setting up, since this takes locks */
+	getnobody();
+	up->env->uid = getuid();
+	up->env->gid = getgid();
+	emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		fprint(2, "keyboard read error: %s\n", strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+int
+segflush(void *a, ulong n)
+{
+	cacheflush(a, n, BCACHE);
+	return 0;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return(0);
+	if(sec0==0){
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	static int tick;
+
+	/*
+	 * Posix-conforming CLK_TCK implementations tend to call sysconf,
+	 * and we don't need the overhead.
+	 */
+	if(tick == 0)
+		tick = CLK_TCK;
+	sginap((tick*milsec)/1000);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	sginap(0);
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	nice(2);
+}
--- /dev/null
+++ b/emu/Linux/arm-tas-v5.S
@@ -1,0 +1,18 @@
+
+	.file	"arm-tas-v5.S"
+/*
+ * ulong _tas(ulong*);
+ */
+	.align	2
+	.global	_tas
+	.type	_tas, %function
+_tas:
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+	@ lr needed for prologue
+	mov	r3, #1
+	mov	r1, r0
+	swp	r0, r3, [r1]
+	bx	lr
+	.size	_tas, .-_tas
--- /dev/null
+++ b/emu/Linux/arm-tas-v7.S
@@ -1,0 +1,30 @@
+	.file	"arm-tas-v7.S"
+#ifndef ARMv7
+#define	DMB	mcr     p15, 0, r0, c7, c10, 5
+#else
+#define	DMB	dmb
+#endif
+.align 2
+.global	_tas
+.type	_tas, %function
+_tas:
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+	@ lr needed for prologue
+	DMB
+	mov	r1, r0
+	mov	r2, #0xaa
+tas1:
+	ldrex	r0, [r1]
+	cmp	r0, #0
+	bne	lockbusy
+	strex	r3, r2, [r1]
+	cmp	r3, #0
+	bne	tas1
+	DMB
+	bx	lr
+lockbusy:
+	clrex
+	bx	lr
+	.size	_tas, .-_tas
--- /dev/null
+++ b/emu/Linux/asm-386.S
@@ -1,0 +1,51 @@
+	.file	"asm-Linux-386.S"
+	.text
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+	.type	umult,@function
+	.global	umult
+umult:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+
+	movl	8(%ebp), %eax
+	movl	12(%ebp), %ebx
+	mull	%ebx
+	movl	16(%ebp), %ebx
+	movl	%edx, (%ebx)
+
+	popl	%ebx
+	popl	%ebp
+	ret
+
+	.type	FPsave,@function
+	.global	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	FPrestore,@function
+	.global	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	_tas,@function
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/Linux/asm-arm.S
@@ -1,0 +1,62 @@
+	.file	"asm-Linux-arm.S"
+	.text
+
+/*
+ * ulong umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+        .align  2
+        .global umult
+        .type   umult, %function
+umult:
+        @ args = 0, pretend = 0, frame = 12
+        @ frame_needed = 1, uses_anonymous_args = 0
+        mov     ip, sp
+        stmfd   sp!, {fp, ip, lr, pc}
+        sub     fp, ip, #4
+        sub     sp, sp, #12
+        str     r0, [fp, #-16]
+        str     r1, [fp, #-20]
+        str     r2, [fp, #-24]
+        ldr     r1, [fp, #-16]
+        ldr     r2, [fp, #-20]
+        umull   r0, r3, r1, r2
+        ldr     r1, [fp, #-24]
+        str     r3, [r1]
+        ldmea   fp, {fp, sp, pc}
+        .size   umult, .-umult
+
+/*
+ *  void		FPsave(void*);
+ */
+
+        .align  2
+        .global FPsave
+        .type   FPsave, %function
+FPsave:
+        @ args = 0, pretend = 0, frame = 4
+        @ frame_needed = 1, uses_anonymous_args = 0
+        mov     ip, sp
+        stmfd   sp!, {fp, ip, lr, pc}
+        sub     fp, ip, #4
+        sub     sp, sp, #4
+        str     r0, [fp, #-16]
+        ldmea   fp, {fp, sp, pc}
+        .size   FPsave, .-FPsave
+
+/*
+ * void		FPrestore(void*);
+ */
+        .align  2
+        .global FPrestore
+        .type   FPrestore, %function
+FPrestore:
+        @ args = 0, pretend = 0, frame = 4
+        @ frame_needed = 1, uses_anonymous_args = 0
+        mov     ip, sp
+        stmfd   sp!, {fp, ip, lr, pc}
+        sub     fp, ip, #4
+        sub     sp, sp, #4
+        str     r0, [fp, #-16]
+        ldmea   fp, {fp, sp, pc}
+        .size   FPrestore, .-FPrestore
--- /dev/null
+++ b/emu/Linux/asm-mips.S
@@ -1,0 +1,28 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+LEAF(FPsave)
+	cfc1	t0, $31
+	sw	t0, 0(a0)		/* a0 is argument */
+	j	$31
+	END(FPsave)
+
+LEAF(FPrestore)
+	lw	t0, 0(a0)		/* a0 is argument */
+	ctc1	t0, $31
+	j	$31
+	END(FPrestore)
+
+LEAF(_tas)
+	.set 	noreorder
+1:
+	ll	v0,0(a0)		/* a0 is argument */
+	or	t1, v0, 1
+	sc	t1,0(a0)
+	beq	t1,zero,1b	
+	nop
+	j	$31			/* lock held */
+	nop
+	.set 	reorder
+	END(_tas)
--- /dev/null
+++ b/emu/Linux/asm-power.S
@@ -1,0 +1,61 @@
+	.align	2
+	.global	FPsave
+FPsave:
+	stfd	%f14,0*8(%r3)
+	stfd	%f15,1*8(%r3)
+	stfd	%f16,2*8(%r3)
+	stfd	%f17,3*8(%r3)
+	stfd	%f18,4*8(%r3)
+	stfd	%f19,5*8(%r3)
+	stfd	%f20,6*8(%r3)
+	stfd	%f21,7*8(%r3)
+	stfd	%f22,8*8(%r3)
+	stfd	%f23,9*8(%r3)
+	stfd	%f24,10*8(%r3)
+	stfd	%f25,11*8(%r3)
+	stfd	%f26,12*8(%r3)
+	stfd	%f27,13*8(%r3)
+	stfd	%f28,14*8(%r3)
+	stfd	%f29,15*8(%r3)
+	stfd	%f30,16*8(%r3)
+	stfd	%f31,17*8(%r3)
+	blr
+
+	.align	2
+	.global	FPrestore
+FPrestore:
+	lfd		%f14,0*8(%r3)
+	lfd		%f15,1*8(%r3)
+	lfd		%f16,2*8(%r3)
+	lfd		%f17,3*8(%r3)
+	lfd		%f18,4*8(%r3)
+	lfd		%f19,5*8(%r3)
+	lfd		%f20,6*8(%r3)
+	lfd		%f21,7*8(%r3)
+	lfd		%f22,8*8(%r3)
+	lfd		%f23,9*8(%r3)
+	lfd		%f24,10*8(%r3)
+	lfd		%f25,11*8(%r3)
+	lfd		%f26,12*8(%r3)
+	lfd		%f27,13*8(%r3)
+	lfd		%f28,14*8(%r3)
+	lfd		%f29,15*8(%r3)
+	lfd		%f30,16*8(%r3)
+	lfd		%f31,17*8(%r3)
+	blr
+
+	.align	2
+	.global	_tas
+_tas:
+	sync
+	mr		%r4, %r3
+	addi		%r5,0,0x1	
+1:
+	lwarx	%r3, 0, %r4
+	cmpwi	%r3, 0
+	bne-	2f
+	stwcx.	%r5, 0, %r4
+	bne-	1b
+2:
+	sync
+	blr
--- /dev/null
+++ b/emu/Linux/asm-spim.S
@@ -1,0 +1,29 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+
+LEAF(FPsave)
+	cfc1	t0, $31
+	sw	t0, 0(a0)		/* a0 is argument */
+	j	$31
+	END(FPsave)
+
+LEAF(FPrestore)
+	lw	t0, 0(a0)		/* a0 is argument */
+	ctc1	t0, $31
+	j	$31
+	END(FPrestore)
+
+LEAF(_tas)
+	.set 	noreorder
+1:
+	ll	v0,0(a0)		/* a0 is argument */
+	or	t1, v0, 1
+	sc	t1,0(a0)
+	beq	t1,zero,1b	
+	nop
+	j	$31			/* lock held */
+	nop
+	.set 	reorder
+	END(_tas)
--- /dev/null
+++ b/emu/Linux/audio-oss.c
@@ -1,0 +1,441 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "audio.h"
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#define 	Audio_Mic_Val		SOUND_MIXER_MIC
+#define 	Audio_Linein_Val	SOUND_MIXER_LINE
+
+#define		Audio_Speaker_Val	SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
+#define		Audio_Headphone_Val	SOUND_MIXER_ALTPCM
+#define		Audio_Lineout_Val	SOUND_MIXER_CD
+
+#define 	Audio_Pcm_Val		AFMT_S16_LE
+#define 	Audio_Ulaw_Val		AFMT_MU_LAW
+#define 	Audio_Alaw_Val		AFMT_A_LAW
+
+#include "audio-tbls.c"
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+
+#define DEVAUDIO	"/dev/dsp"
+#define DEVMIXER	"/dev/mixer"
+
+#define DPRINT if(1)print
+
+enum {
+	A_Pause,
+	A_UnPause,
+	A_In,
+	A_Out,
+};
+
+static struct {
+	int data;	/* dsp data fd */
+	int ctl;	/* mixer fd */
+	int pause;
+	QLock lk;
+} afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
+
+static Audio_t av;
+static QLock inlock;
+static QLock outlock;
+
+static int audio_open(int);
+static int audio_pause(int, int);
+static int audio_set_info(int, Audio_d*, int);
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
+
+void 
+audio_file_init(void)
+{
+	audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	USED(c);
+	DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
+	qlock(&afd.lk);
+	if(waserror()){
+		qunlock(&afd.lk);
+		nexterror();
+	}
+	if(afd.data >= 0)
+		error(Einuse);
+	if(afd.ctl < 0){
+		afd.ctl = open(DEVMIXER, ORDWR);
+		if(afd.ctl < 0)
+			oserror();
+	}
+	afd.data = audio_open(omode);
+	if(afd.data < 0)
+		oserror();
+	poperror();
+	qunlock(&afd.lk);
+}
+
+void    
+audio_file_close(Chan *c)
+{
+	USED(c);
+	DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
+	qlock(&afd.lk);
+	if(waserror()){
+		qunlock(&afd.lk);
+		nexterror();
+	}
+	close(afd.data);
+	afd.data = -1;
+	qunlock(&afd.lk);
+	poperror();
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	long ba, status, chunk, total;
+
+	USED(c);
+	USED(offset);
+	DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(afd.data < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+		
+	if(!audio_pause(afd.data, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count){
+		chunk = count - total;
+		status = read (afd.data, va + total, chunk);
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+	
+	if(total != count)
+		error(Eio);
+
+	poperror();
+	qunlock(&inlock);
+	
+	return count;
+}
+
+long                                            
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	long status = -1;
+	long ba, total, chunk, bufsz;
+	
+	USED(c);
+	USED(offset);
+	DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+	
+	if(afd.data < 0)
+		error(Eperm);
+	
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count){
+		chunk = min(bufsz, count - total);
+		status = write(afd.data, va, chunk);
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	return count;	
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	Audio_t tmpav = av;
+	int tfd;
+
+	USED(c);
+	USED(offset);
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+	
+	DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
+	if(!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if(!canqlock(&inlock))
+		error("device busy");
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+	if(!canqlock(&outlock))
+		error("device busy");
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	/* DEVAUDIO needs to be open to issue an ioctl */
+	tfd = afd.data;
+	if(tfd < 0){
+		tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+		if(tfd < 0)
+			oserror();
+	}
+	if(waserror()){
+		if(tfd != afd.data)
+			close(tfd);
+		nexterror();
+	}
+
+	if(tmpav.in.flags & AUDIO_MOD_FLAG){
+		if(!audio_pause(tfd, A_Pause))
+			error(Ebadarg);
+		if(!audio_set_info(tfd, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+
+	poperror();
+	if(tfd != afd.data)
+		close(tfd);
+
+	tmpav.in.flags = 0;
+	av = tmpav;
+
+	poperror();
+	qunlock(&outlock);
+	poperror();
+	qunlock(&inlock);
+	return count;
+}
+
+/* Linux/OSS specific stuff */
+
+static int
+choosefmt(Audio_d *i)
+{
+	switch(i->bits){
+	case 8:
+		switch(i->enc){
+		case Audio_Alaw_Val:
+			return AFMT_A_LAW;
+		case Audio_Ulaw_Val:
+			return AFMT_MU_LAW;
+		case Audio_Pcm_Val:
+			return AFMT_U8;
+		}
+		break;
+	case 16:
+		if(i->enc == Audio_Pcm_Val)
+			return AFMT_S16_LE;
+		break;
+	}
+	return -1;
+}
+
+static int
+setvolume(int fd, int what, int left, int right)
+{
+	int can, v;
+	
+	if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+		can = ~0;
+
+	DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
+	if(!(can & (1<<what)))
+		return 0;
+	v = left | (right<<8);
+	if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
+		return 0;
+	return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status, arg;
+	int oldfmt, newfmt;
+
+	USED(d);
+	DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
+	if(fd < 0)
+		return 0;
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG){
+		arg = i->rate;
+		if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
+			return 0;
+	}
+	
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG){
+		arg = i->chan;
+		if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+			return 0;
+	}
+
+	/* precision */
+	if(i->flags & AUDIO_BITS_FLAG){
+		arg = i->bits;
+		if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
+			return 0;
+	}
+	
+	/* encoding */
+	if(i->flags & AUDIO_ENC_FLAG){
+		ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
+
+		newfmt = choosefmt(i);
+		if(newfmt < 0)
+			return 0;
+		if(newfmt != oldfmt){
+			status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
+			DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
+		}
+	}
+
+	/* dev volume */ 
+	if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
+		return setvolume(afd.ctl, i->dev, i->left, i->right);
+
+	return 1;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+audio_open(int omode)
+{
+	int fd;
+	
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	switch (omode){
+	case OREAD:
+		fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+		break;
+	case OWRITE:
+		fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
+		break;
+	case ORDWR:
+		fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
+		break;
+	}
+
+	DPRINT("audio_open %d\n", fd);
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)){
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	if(!audio_pause(fd, A_Pause)){
+		close(fd);
+		error(Eio);
+	}
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = ~0;
+
+	if(!audio_set_info(fd, &av.in, A_In)){
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+static int
+dspsync(int fd)
+{
+	return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
+		ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
+}
+
+static int
+audio_pause(int fd, int f)
+{
+	int status;
+
+//	DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
+	if(fd < 0)
+		return 0;
+	if(fd != afd.data)
+		return dspsync(fd);
+	qlock(&afd.lk);
+	if(afd.pause == f){
+		qunlock(&afd.lk);
+		return 1;
+	}
+	if(waserror()){
+		qunlock(&afd.lk);
+		nexterror();
+	}
+	status = dspsync(afd.data);
+	if(status)
+		afd.pause = f;
+	poperror();
+	qunlock(&afd.lk);
+	return status;
+}
--- /dev/null
+++ b/emu/Linux/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgrp();
+		if(nice)
+			setpriority(PRIO_PROCESS, 0, 19);
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/Linux/deveia.c
@@ -1,0 +1,44 @@
+/*
+ * Linux serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/ttyS0",
+        "/dev/ttyS1",
+        "/dev/ttyS2",
+        "/dev/ttyS3",
+        "/dev/ttyS4",
+        "/dev/ttyS5",
+        "/dev/ttyS6",
+        "/dev/ttyS7",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{115200,	B115200},
+	{230400,	B230400},
+	{460800,	B460800},
+	{-1,		-1}
+};
+
--- /dev/null
+++ b/emu/Linux/devfs.c
@@ -1,0 +1,26 @@
+#include "devfs-posix.c"
+
+#include	<linux/hdreg.h>
+#include	<linux/fs.h>
+#include	<sys/ioctl.h>
+
+static vlong
+osdisksize(int fd)
+{
+	uvlong u64;
+	long l;
+	struct hd_geometry geo;
+	
+	memset(&geo, 0, sizeof geo);
+	l = 0;
+	u64 = 0;
+#ifdef BLKGETSIZE64
+	if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
+		return u64;
+#endif
+	if(ioctl(fd, BLKGETSIZE, &l) >= 0)
+		return l*512;
+	if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
+		return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
+	return 0;
+}
--- /dev/null
+++ b/emu/Linux/emu
@@ -1,0 +1,105 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif6-posix ipaux
+	eia
+#	audio	audio-oss
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/dis
+#	/n
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Linux/emu-g
@@ -1,0 +1,101 @@
+env
+	X11LIBS=
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	ip	ipif6-posix ipaux
+	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+	math
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	void setpointer(int x, int y){USED(x); USED(y);}
+	ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Linux/emu-wrt
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+#	draw
+#	pointer
+#	snarf
+
+	ip	ipif6-posix ipaux
+#	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+#	tk
+#	freetype
+	math
+#	draw
+
+#	memlayer
+#	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+#	draw
+
+#	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+#	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	int dontcompile = 1;
+	unsigned long strtochan(char *s) {return 0;}
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Linux/mk-wrt
@@ -1,0 +1,8 @@
+#!/bin/sh
+
+OPENWRT=$HOME/OpenWrt-SDK-Linux-i686-1
+INFERNO=/usr/inferno
+
+PATH=$OPENWRT/staging_dir_mipsel/bin:$INFERNO/Linux/386/bin:$PATH
+
+mk OBJTYPE=spim CONF=emu-wrt CONFLIST=emu-wrt SYSLIBS=-lm WIN= $*
--- /dev/null
+++ b/emu/Linux/mkfile
@@ -1,0 +1,53 @@
+SYSTARG=Linux
+<../../mkconfig
+SYSTARG=Linux
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+X11LIBS= -lX11 -lXext	# can remove or override using env section in config files
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+<mkfile-$OBJTYPE	# sets $ARCHFILES
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	$ARCHFILES\
+	os.$O\
+	kproc-pthreads.$O\
+	segflush-$OBJTYPE.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= $X11LIBS -lm -lpthread
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/Linux/mkfile-arm
@@ -1,0 +1,2 @@
+ARCHFILES=\
+	arm-tas-v7.$O\
--- /dev/null
+++ b/emu/Linux/os.c
@@ -1,0 +1,304 @@
+#include	<sys/types.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sched.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<sys/time.h>
+
+#include	<stdint.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include <semaphore.h>
+
+#include	<raise.h>
+
+/* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */
+#include	<sys/syscall.h>
+#define	getpid()	syscall(SYS_getpid)
+
+enum
+{
+	DELETE	= 0x7f,
+	CTRLC	= 'C'-'@',
+	NSTACKSPERALLOC = 16,
+	X11STACK=	256*1024
+};
+char *hosttype = "Linux";
+
+typedef sem_t	Sem;
+
+extern int dflag;
+
+int	gidnobody = -1;
+int	uidnobody = -1;
+static struct 	termios tinit;
+
+static void
+sysfault(char *what, void *addr)
+{
+	char buf[64];
+
+	snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+	disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+	USED(signo);
+	USED(a);
+	sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+	return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+	USED(a);	/* ucontext_t*, could fetch pc in machine-dependent way */
+	if(isnilref(si))
+		disfault(nil, exNilref);
+	else if(signo == SIGBUS)
+		sysfault("bad address addr=", si->si_addr);	/* eg, misaligned */
+	else
+		sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+	char buf[64];
+
+	USED(signo);
+	USED(a);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+	disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+	int intwait;
+
+	USED(signo);
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+
+	if(intwait == 0)		/* Not posted so it's a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+	struct sigaction act;
+	struct passwd *pw;
+	Proc *p;
+	char sys[64];
+
+	setsid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	pw = getpwnam("nobody");
+	if(pw != nil) {
+		uidnobody = pw->pw_uid;
+		gidnobody = pw->pw_gid;
+	}
+
+	if(dflag == 0)
+		termset();
+
+	memset(&act, 0, sizeof(act));
+	act.sa_handler = trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+
+	act.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &act, nil);
+
+	/*
+	 * For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGPIPE, SIG_IGN);
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+		signal(SIGINT, cleanexit);
+
+	if(sflag == 0) {
+		act.sa_flags = SA_SIGINFO;
+		act.sa_sigaction = trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_sigaction = trapFPE;
+		sigaction(SIGFPE, &act, nil);
+		act.sa_sigaction = trapmemref;
+		sigaction(SIGBUS, &act, nil);
+		sigaction(SIGSEGV, &act, nil);
+		act.sa_flags &= ~SA_SIGINFO;
+	}
+
+	p = newproc();
+	kprocinit(p);
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+
+	p->env->uid = getuid();
+	p->env->gid = getgid();
+
+	emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		buf[0] = 'H' - '@';
+		break;
+	case CTRLC:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return 0;
+
+	if(sec0 == 0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct  timespec time;
+
+	time.tv_sec = milsec/1000;
+	time.tv_nsec= (milsec%1000)*1000000;
+	nanosleep(&time, NULL);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
--- /dev/null
+++ b/emu/Linux/segflush-386.c
@@ -1,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+int
+segflush(void *a, ulong n)
+{
+	USED(a); USED(n);
+	return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-arm.c
@@ -1,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+#define	SYS_cacheflush	__ARM_NR_cacheflush
+
+int
+segflush(void *a, ulong n)
+{
+	if(n)
+		syscall(SYS_cacheflush, a, (char*)a+n-1, 1);
+	return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-mips.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+	li	a2,BCACHE
+	li	v0,SYS_cacheflush
+	syscall
+	li	v0,0
+	j	$31
+	END(segflush)
--- /dev/null
+++ b/emu/Linux/segflush-power.c
@@ -1,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+
+/*
+ * from geoff collyer's port
+ * invalidate instruction cache and write back data cache from a to a+n-1,
+ * at least.
+ */
+int
+segflush(void *a, ulong n)
+{
+    ulong *p;
+
+    // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes.
+    // need to determine it dynamically?
+    for (p = (ulong *)((ulong)a & ~7UL); (char *)p < (char *)a + n; p++)
+        __asm__("dcbst	0,%0\n\t"	// not dcbf, which writes back, then invalidates
+            "icbi	0,%0\n\t"
+            : // no output
+            : "ar" (p)
+            );
+     __asm__("sync\n\t"
+            : // no output
+            :
+            );
+   __asm__("isync\n\t"
+            : // no output
+            :
+            );
+	return 0;
+}
--- /dev/null
+++ b/emu/Linux/segflush-spim.S
@@ -1,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+	li	a2,BCACHE
+	li	v0,SYS_cacheflush
+	syscall
+	li	v0,0
+	j	$31
+	END(segflush)
--- /dev/null
+++ b/emu/MacOSX/NOTE
@@ -1,0 +1,4 @@
+-	still interp mode only, jit not complete
+-	mkfile uses Carbon only; mkfile-x11 uses X11R6 from Apple
+-	both powerpc and x86 versions compiled from this source
+-	mkfile-g doesn't include graphics (uses emu-g not emu as configuration)
--- /dev/null
+++ b/emu/MacOSX/NOTICE
@@ -1,0 +1,3 @@
+MacOSX port Copyright © 2001-2003 Corpus Callosum Corporation,
+with portions Copyright © 2001-2003 Geoff Collyer
+MacOSX-x86 Copyright © 2006 Corpus Callosum Corporation
--- /dev/null
+++ b/emu/MacOSX/asm-386.s
@@ -1,0 +1,28 @@
+/*
+ * these are the same as on the PC (eg, Linux)
+*/
+
+	.globl	_FPsave
+_FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.globl	_FPrestore
+_FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.globl	__tas
+__tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/MacOSX/asm-power.s
@@ -1,0 +1,36 @@
+/*
+ * File: asm-power.s
+ *
+ * Copyright (c) 2003, Corpus Callosum Corporation.  All rights reserved.
+ */
+
+#include <architecture/ppc/asm_help.h>
+
+.text
+
+LEAF(_FPsave)
+	mffs	f0
+	stfd	f0,0(r3)
+	blr
+END(_FPsave)
+
+LEAF(_FPrestore)
+	lfd		f0,0(r3)
+	mtfsf 	0xff,f0
+	blr
+END(_FPrestore)
+
+LEAF(__tas)
+	sync
+	mr	r4,r3
+	addi    r5,0,0x1
+1:
+	lwarx	r3,0,r4
+	cmpwi   r3,0x0
+	bne-    2f
+	stwcx.	r5,0,r4
+	bne-    1b		/* Lost reservation, try again */
+2:
+	sync
+	blr
+END(__tas)
--- /dev/null
+++ b/emu/MacOSX/cmd.c
@@ -1,0 +1,214 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/time.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgid(0, getpid());
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/MacOSX/deveia.c
@@ -1,0 +1,123 @@
+/*
+ * Darwin serial port definitions, uses IOKit to build sysdev
+ * Loosely based on FreeBSD/deveia.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc.  All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited.  All rights reserved.
+ * Revisions Copyright © 2003 Corpus Callosum Corporation.  All rights reserved.
+*/
+
+#include <termios.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <mach/mach.h>
+
+#include <CoreFoundation/CFNumber.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+#include <sys/ioctl.h>
+#include <sys/ttycom.h>
+
+#undef nil
+
+#define B14400	14400
+#define B28800	28800
+#define B57600	57600
+#define B76800	76800
+#define B115200	115200
+#define B230400	230400
+
+extern int vflag;
+
+#define MAXDEV 16
+static char *sysdev[MAXDEV];
+
+static void _buildsysdev(void);
+#define	buildsysdev()	_buildsysdev()	/* for devfs-posix.c */
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+    {0,             B0},
+    {50,            B50},
+    {75,            B75},
+    {110,           B110},
+    {134,           B134},
+    {150,           B150},
+    {200,           B200},
+    {300,           B300},
+    {600,           B600},
+    {1200,	B1200},
+    {1800,	B1800},
+    {2400,	B2400},
+    {4800,	B4800},
+    {9600,	B9600},
+    {19200,	B19200},
+    {38400,	B38400},
+    {57600,	B57600},
+    {76800,	B76800},
+    {115200,	B115200},
+    {230400,	B230400},
+    {0,		-1}
+};
+
+static void
+_buildsysdev(void)
+{
+	mach_port_t port;
+	CFMutableDictionaryRef classesToMatch;
+	io_iterator_t serialPortIterator;
+	io_object_t serialDevice;
+	CFMutableArrayRef paths;
+	CFTypeRef path;
+	char	eiapath[MAXPATHLEN];
+	CFIndex i, o, npath;
+
+	if(IOMasterPort(MACH_PORT_NULL, &port) != KERN_SUCCESS)
+		return;
+	classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+	if(classesToMatch == NULL){
+		printf("IOServiceMatching returned a NULL dictionary.\n");
+		goto Failed;
+	}
+	CFDictionarySetValue(classesToMatch,
+		CFSTR(kIOSerialBSDTypeKey),
+		CFSTR(kIOSerialBSDAllTypes));
+
+	if(IOServiceGetMatchingServices(port, classesToMatch, &serialPortIterator) != KERN_SUCCESS)
+		goto Failed;
+
+	paths = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+	while((serialDevice = IOIteratorNext(serialPortIterator)) != 0){
+		path = IORegistryEntryCreateCFProperty(serialDevice, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+		if(path != NULL)
+			CFArrayAppendValue(paths, path);
+		IOObjectRelease(serialDevice);
+	}
+
+	npath = CFArrayGetCount(paths);
+	o = 0;
+	for(i = 0; i < npath && i < nelem(sysdev); i++){
+		if(CFStringGetCString(CFArrayGetValueAtIndex(paths, i), eiapath, sizeof(eiapath), kCFStringEncodingUTF8)){
+			sysdev[o] = strdup(eiapath);
+			if(vflag > 1)
+				print("BSD path: '%s'\n", sysdev[o]);
+			o++;
+		}
+	}
+
+	CFRelease(paths);
+	IOObjectRelease(serialPortIterator);
+
+Failed:
+	mach_port_deallocate(mach_task_self(), port);
+}
+
--- /dev/null
+++ b/emu/MacOSX/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/MacOSX/emu
@@ -1,0 +1,113 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	int	dontcompile = 1;
+	int macjit = 1;
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/MacOSX/emu-g
@@ -1,0 +1,101 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	ip	ipif-posix ipaux
+	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+	math
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	int	dontcompile = 1;
+	int macjit = 1;
+	void setpointer(int x, int y){USED(x); USED(y);}
+	ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/MacOSX/keycodes.h
@@ -1,0 +1,189 @@
+/* These are the Macintosh key scancode constants -- from Inside Macintosh */
+#define QZ_ESCAPE		0x35
+#define QZ_F1			0x7A
+#define QZ_F2			0x78
+#define QZ_F3			0x63
+#define QZ_F4			0x76
+#define QZ_F5			0x60
+#define QZ_F6			0x61
+#define QZ_F7			0x62
+#define QZ_F8			0x64
+#define QZ_F9			0x65
+#define QZ_F10			0x6D
+#define QZ_F11			0x67
+#define QZ_F12			0x6F
+#define QZ_PRINT		0x69
+#define QZ_SCROLLOCK    	0x6B
+#define QZ_PAUSE		0x71
+#define QZ_POWER		0x7F
+#define QZ_BACKQUOTE		0x32
+#define QZ_1			0x12
+#define QZ_2			0x13
+#define QZ_3			0x14
+#define QZ_4			0x15
+#define QZ_5			0x17
+#define QZ_6			0x16
+#define QZ_7			0x1A
+#define QZ_8			0x1C
+#define QZ_9			0x19
+#define QZ_0			0x1D
+#define QZ_MINUS		0x1B
+#define QZ_EQUALS		0x18
+#define QZ_BACKSPACE		0x33
+#define QZ_INSERT		0x72
+#define QZ_HOME			0x73
+#define QZ_PAGEUP		0x74
+#define QZ_NUMLOCK		0x47
+#define QZ_KP_EQUALS		0x51
+#define QZ_KP_DIVIDE		0x4B
+#define QZ_KP_MULTIPLY		0x43
+#define QZ_TAB			0x30
+#define QZ_q			0x0C
+#define QZ_w			0x0D
+#define QZ_e			0x0E
+#define QZ_r			0x0F
+#define QZ_t			0x11
+#define QZ_y			0x10
+#define QZ_u			0x20
+#define QZ_i			0x22
+#define QZ_o			0x1F
+#define QZ_p			0x23
+#define QZ_LEFTBRACKET		0x21
+#define QZ_RIGHTBRACKET		0x1E
+#define QZ_BACKSLASH		0x2A
+#define QZ_DELETE		0x75
+#define QZ_END			0x77
+#define QZ_PAGEDOWN		0x79
+#define QZ_KP7			0x59
+#define QZ_KP8			0x5B
+#define QZ_KP9			0x5C
+#define QZ_KP_MINUS		0x4E
+#define QZ_CAPSLOCK		0x39
+#define QZ_a			0x00
+#define QZ_s			0x01
+#define QZ_d			0x02
+#define QZ_f			0x03
+#define QZ_g			0x05
+#define QZ_h			0x04
+#define QZ_j			0x26
+#define QZ_k			0x28
+#define QZ_l			0x25
+#define QZ_SEMICOLON		0x29
+#define QZ_QUOTE		0x27
+#define QZ_RETURN		0x24
+#define QZ_KP4			0x56
+#define QZ_KP5			0x57
+#define QZ_KP6			0x58
+#define QZ_KP_PLUS		0x45
+#define QZ_LSHIFT		0x38
+#define QZ_z			0x06
+#define QZ_x			0x07
+#define QZ_c			0x08
+#define QZ_v			0x09
+#define QZ_b			0x0B
+#define QZ_n			0x2D
+#define QZ_m			0x2E
+#define QZ_COMMA		0x2B
+#define QZ_PERIOD		0x2F
+#define QZ_SLASH		0x2C
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RSHIFT		0x38
+#endif
+#define QZ_UP			0x7E
+#define QZ_KP1			0x53
+#define QZ_KP2			0x54
+#define QZ_KP3			0x55
+#define QZ_KP_ENTER		0x4C
+#define QZ_LCTRL		0x3B
+#define QZ_LALT			0x3A
+#define QZ_LMETA		0x37
+#define QZ_SPACE		0x31
+/* These are the same as the left versions - use left by default */
+#if 0
+#define QZ_RMETA		0x37
+#define QZ_RALT			0x3A
+#define QZ_RCTRL		0x3B
+#endif
+#define QZ_LEFT			0x7B
+#define QZ_DOWN			0x7D
+#define QZ_RIGHT		0x7C
+#define QZ_KP0			0x52
+#define QZ_KP_PERIOD		0x41
+
+/* Wierd, these keys are on my iBook under MacOS X */
+#define QZ_IBOOK_ENTER		0x34
+#define QZ_IBOOK_LEFT		0x3B
+#define QZ_IBOOK_RIGHT		0x3C
+#define QZ_IBOOK_DOWN		0x3D
+#define QZ_IBOOK_UP		0x3E
+#define KEY_ENTER 13
+#define KEY_TAB 9
+
+#define KEY_BASE 0x100
+
+/*  Function keys  */
+#define KEY_F (KEY_BASE+64)
+
+/* Control keys */
+#define KEY_CTRL (KEY_BASE)
+#define KEY_BACKSPACE (KEY_CTRL+0)
+#define KEY_DELETE (KEY_CTRL+1)
+#define KEY_INSERT (KEY_CTRL+2)
+#define KEY_HOME (KEY_CTRL+3)
+#define KEY_END (KEY_CTRL+4)
+#define KEY_PAGE_UP (KEY_CTRL+5)
+#define KEY_PAGE_DOWN (KEY_CTRL+6)
+#define KEY_ESC (KEY_CTRL+7)
+
+/* Control keys short name */
+#define KEY_BS KEY_BACKSPACE
+#define KEY_DEL KEY_DELETE
+#define KEY_INS KEY_INSERT
+#define KEY_PGUP KEY_PAGE_UP
+#define KEY_PGDOWN KEY_PAGE_DOWN
+#define KEY_PGDWN KEY_PAGE_DOWN
+
+/* Cursor movement */
+#define KEY_CRSR (KEY_BASE+16)
+#define KEY_RIGHT (KEY_CRSR+0)
+#define KEY_LEFT (KEY_CRSR+1)
+#define KEY_DOWN (KEY_CRSR+2)
+#define KEY_UP (KEY_CRSR+3)
+
+/* Multimedia keyboard/remote keys */
+#define KEY_MM_BASE (0x100+384)
+#define KEY_POWER (KEY_MM_BASE+0)
+#define KEY_MENU (KEY_MM_BASE+1)
+#define KEY_PLAY (KEY_MM_BASE+2)
+#define KEY_PAUSE (KEY_MM_BASE+3)
+#define KEY_PLAYPAUSE (KEY_MM_BASE+4)
+#define KEY_STOP (KEY_MM_BASE+5)
+#define KEY_FORWARD (KEY_MM_BASE+6)
+#define KEY_REWIND (KEY_MM_BASE+7)
+#define KEY_NEXT (KEY_MM_BASE+8)
+#define KEY_PREV (KEY_MM_BASE+9)
+#define KEY_VOLUME_UP (KEY_MM_BASE+10)
+#define KEY_VOLUME_DOWN (KEY_MM_BASE+11)
+#define KEY_MUTE (KEY_MM_BASE+12)
+
+/* Keypad keys */
+#define KEY_KEYPAD (KEY_BASE+32)
+#define KEY_KP0 (KEY_KEYPAD+0)
+#define KEY_KP1 (KEY_KEYPAD+1)
+#define KEY_KP2 (KEY_KEYPAD+2)
+#define KEY_KP3 (KEY_KEYPAD+3)
+#define KEY_KP4 (KEY_KEYPAD+4)
+#define KEY_KP5 (KEY_KEYPAD+5)
+#define KEY_KP6 (KEY_KEYPAD+6)
+#define KEY_KP7 (KEY_KEYPAD+7)
+#define KEY_KP8 (KEY_KEYPAD+8)
+#define KEY_KP9 (KEY_KEYPAD+9)
+#define KEY_KPDEC (KEY_KEYPAD+10)
+#define KEY_KPINS (KEY_KEYPAD+11)
+#define KEY_KPDEL (KEY_KEYPAD+12)
+#define KEY_KPENTER (KEY_KEYPAD+13)
+
+/* Special keys */
+#define KEY_INTERN (0x1000)
+#define KEY_CLOSE_WIN (KEY_INTERN+0)
--- /dev/null
+++ b/emu/MacOSX/mkfile
@@ -1,0 +1,56 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+	'-DOBJTYPE="'$OBJTYPE'"'\
+	-DEMU -I. -I../port\
+	-I$ROOT/$SYSTARG/$OBJTYPE/include\
+	-I$ROOT/include -I$ROOT/libinterp\
+	$CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+	-lm\
+	-framework Carbon -framework QuickTime\
+	-lpthread\
+	-framework CoreFoundation\
+	-framework IOKit\
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-g
@@ -1,0 +1,55 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu-g			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+	'-DOBJTYPE="'$OBJTYPE'"'\
+	-DEMU -I. -I../port\
+	-I$ROOT/$SYSTARG/$OBJTYPE/include\
+	-I$ROOT/include -I$ROOT/libinterp\
+	$CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+
+KERNDATE=`{$NDATE}
+
+SYSLIBS= \
+	-lm\
+	-lpthread\
+	-framework CoreFoundation\
+	-framework IOKit\
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/mkfile-x11
@@ -1,0 +1,59 @@
+SYSTARG=MacOSX
+<../../mkconfig
+SYSTARG=MacOSX
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	win-x11a.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"'\
+	'-DOBJTYPE="'$OBJTYPE'"'\
+	-DEMU -I. -I../port\
+	-I$ROOT/$SYSTARG/$OBJTYPE/include\
+	-I$ROOT/include -I$ROOT/libinterp\
+	$CTHREADFLAGS $CFLAGS $EMUOPTIONS\
+	-I/usr/X11R6/include
+
+KERNDATE=`{$NDATE}
+
+LDFLAGS=$LDFLAGS -L/usr/X11R6/lib
+
+SYSLIBS= \
+	-lm -lX11 -lXext\
+	-lpthread\
+	-framework CoreFoundation\
+	-framework IOKit\
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.c:N:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/MacOSX/os.c
@@ -1,0 +1,574 @@
+/*
+ * Loosely based on FreeBSD/os.c and Solaris/os.c
+ * Copyright © 1998, 1999 Lucent Technologies Inc.  All rights reserved.
+ * Revisions Copyright © 1999, 2000 Vita Nuova Limited.  All rights reserved.
+ * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation.  All rights reserved.
+ */
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include <raise.h>
+
+#undef _POSIX_C_SOURCE 
+#undef getwd
+
+#include	<unistd.h>
+#include        <pthread.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<signal.h>
+#include	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/time.h>
+
+#include 	<sys/socket.h>
+#include	<sched.h>
+#include	<errno.h>
+#include        <sys/ucontext.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <mach/mach_init.h>
+#include <mach/task.h>
+#include <mach/vm_map.h>
+
+#if defined(__ppc__)
+#include <architecture/ppc/cframe.h>
+#endif
+
+enum
+{
+    DELETE = 0x7F
+};
+char *hosttype = "MacOSX";
+char *cputype = OBJTYPE;
+
+typedef struct Sem Sem;
+struct Sem {
+	pthread_cond_t	c;
+	pthread_mutex_t	m;
+	int	v;
+};
+
+static pthread_key_t  prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+	return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	Proc *p;
+	Sem *sem;
+
+	USED(t);
+	USED(msg);
+
+	lock(&procs.l);
+	p = up;
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", p->text, msg);
+
+	e = p->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+		free(e->user);
+	}
+	free(p->prog);
+	sem = p->os;
+	if(sem != nil){
+		pthread_cond_destroy(&sem->c);
+		pthread_mutex_destroy(&sem->m);
+	}
+	free(p->os);
+	free(p);
+	pthread_exit(0);
+}
+
+
+
+static void
+sysfault(char *what, void *addr)
+{
+	char buf[64];
+
+	snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+	disfault(nil, buf);
+}
+
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+	USED(signo);
+	USED(a);
+	sysfault("illegal instruction pc=", si->si_addr);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+	return si != 0 && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+	USED(a);	/* ucontext_t*, could fetch pc in machine-dependent way */
+	if(isnilref(si))
+		disfault(nil, exNilref);
+	else if(signo == SIGBUS)
+		sysfault("bad address addr=", si->si_addr);	/* eg, misaligned */
+	else
+		sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+	char buf[64];
+
+	USED(signo);
+	USED(a);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+	disfault(nil, buf);
+}
+
+void
+trapUSR1(int signo)
+{
+    USED(signo);
+    
+    if(up->type != Interp)      /* Used to unblock pending I/O */
+        return;
+    if(up->intwait == 0)        /* Not posted so its a sync error */
+        disfault(nil, Eintr);	/* Should never happen */
+    
+    up->intwait = 0;		/* Clear it so the proc can continue */
+}
+
+/* from geoff collyer's port */
+void
+printILL(int sig, siginfo_t *si, void *v)
+{
+	USED(sig);
+	USED(v);
+	panic("illegal instruction with code=%d at address=%p, opcode=%#x\n",
+		si->si_code, si->si_addr, *(uchar*)si->si_addr);
+}
+
+static void
+setsigs(void)
+{
+	struct sigaction act;
+
+	memset(&act, 0 , sizeof(act));
+
+	/*
+	  * For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGPIPE, SIG_IGN);
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+
+	act.sa_handler = trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+
+	if(sflag == 0) {
+		act.sa_flags = SA_SIGINFO;
+		act.sa_sigaction = trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_sigaction = trapFPE;
+		sigaction(SIGFPE, &act, nil);
+		act.sa_sigaction = trapmemref;
+		sigaction(SIGBUS, &act, nil);
+		sigaction(SIGSEGV, &act, nil);
+		if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+			signal(SIGINT, cleanexit);
+	} else {
+		act.sa_sigaction = printILL;
+		act.sa_flags = SA_SIGINFO;
+		sigaction(SIGILL, &act, nil);
+	}
+}
+
+
+
+
+void *
+tramp(void *arg)
+{
+	Proc *p = arg;
+	p->sigid = (int)pthread_self();
+	if(pthread_setspecific(prdakey, arg)) {
+		print("set specific data failed in tramp\n");
+		pthread_exit(0);
+	}
+	p->func(p->arg);
+	pexit("{Tramp}", 0);
+	return NULL;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	pthread_t thread;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	pthread_attr_t attr;
+	Sem *sem;
+
+	p = newproc();
+	if(p == nil)
+		panic("kproc: no memory");
+	sem = malloc(sizeof(*sem));
+	if(sem == nil)
+		panic("can't allocate semaphore");
+	pthread_cond_init(&sem->c, NULL);
+	pthread_mutex_init(&sem->m, NULL);
+	sem->v = 0;
+	p->os = sem;
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	} else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(pthread_attr_init(&attr) == -1)
+		panic("pthread_attr_init failed");
+
+	pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+	pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+	if(pthread_create(&thread, &attr, tramp, p))
+		panic("thr_create failed\n");
+	pthread_attr_destroy(&attr);
+}
+
+int
+segflush(void *va, ulong len)
+{
+	kern_return_t   err;
+	vm_machine_attribute_val_t value = MATTR_VAL_ICACHE_FLUSH;
+
+	err = vm_machine_attribute( (vm_map_t)mach_task_self(),
+		(vm_address_t)va,
+		(vm_size_t)len,
+		MATTR_CACHE,
+		&value);
+	if(err != KERN_SUCCESS)
+		print("segflush: failure (%d) address %lud\n", err, va);
+	return (int)err;
+}
+
+void
+oshostintr(Proc *p)
+{
+	pthread_kill((pthread_t)p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	Sem *sem;
+
+	sem = up->os;
+	pthread_mutex_lock(&sem->m);
+	while(sem->v == 0)
+		pthread_cond_wait(&sem->c, &sem->m);
+	sem->v--;
+	pthread_mutex_unlock(&sem->m);
+}
+
+void
+osready(Proc *p)
+{
+	Sem *sem;
+
+	sem = p->os;
+	pthread_mutex_lock(&sem->m);
+	sem->v++;
+	pthread_cond_signal(&sem->c);
+	pthread_mutex_unlock(&sem->m);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON | ECHO | ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	exit(0);
+}
+
+
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+	struct passwd *pwd;
+
+	if((pwd = getpwnam("nobody"))) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+libinit(char *imod)
+{
+	struct passwd *pw;
+	Proc *p;
+	char	sys[64];
+
+	setsid();
+
+	// setup personality
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	if(dflag == 0)
+		termset();
+
+	setsigs();
+
+	if(pthread_key_create(&prdakey, NULL))
+		print("key_create failed\n");
+
+	p = newproc();
+	if(pthread_setspecific(prdakey, p))
+		panic("set specific thread data failed\n");
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+
+	up->env->uid = getuid();
+	up->env->gid = getgid();
+
+	emuinit(imod);
+}
+
+
+
+int
+readkbd(void)
+{
+	int	n;
+	char	buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long	sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t, NULL) < 0)
+		return(0);
+	if(sec0 == 0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return((t.tv_sec - sec0) * 1000 + (t.tv_usec - usec0 + 500) / 1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+int
+osmillisleep(ulong milsec)
+{
+	struct timespec time;
+	time.tv_sec = milsec / 1000;
+	time.tv_nsec = (milsec % 1000) * 1000000;
+	nanosleep(&time, nil);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	pthread_yield_np();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+//	pthread_setschedparam(pthread_t thread,  int policy, const struct sched_param *param);
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+__typeof__(sbrk(0))
+sbrk(int size)
+{
+	void *brk;
+	kern_return_t   err;
+    
+	err = vm_allocate( (vm_map_t) mach_task_self(),
+                       (vm_address_t *)&brk,
+                       size,
+                       VM_FLAGS_ANYWHERE);
+	if(err != KERN_SUCCESS)
+		brk = (void*)-1;
+	return brk;
+}
--- /dev/null
+++ b/emu/MacOSX/win.c
@@ -1,0 +1,735 @@
+// in this file, _Rect is os x Rect,
+// _Point is os x Point
+#define Point _Point
+#define Rect _Rect
+
+#include <Carbon/Carbon.h>
+//#include <QuickTime/QuickTime.h> // for full screen
+
+#undef Rect
+#undef Point
+
+#undef nil
+
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include <memdraw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include "keycodes.h"
+
+#define	Kup	Up
+#define	Kleft	Left
+#define	Kdown	Down
+#define	Kright	Right
+#define	Kalt	LAlt
+#define	Kctl	LCtrl
+#define	Kshift	LShift
+#define	Kpgup	Pgup
+#define	Kpgdown	Pgdown
+#define	Khome	Home
+#define	Kins	Ins
+#define	Kend	End
+
+#define rWindowResource  128
+
+extern	void		flushmemscreen(Rectangle);
+
+Memimage	*gscreen;
+
+static int readybit;
+static Rendez	rend;
+static int triedscreen;
+
+///
+// menu
+//
+static MenuRef windMenu;
+static MenuRef viewMenu;
+
+enum {
+	kQuitCmd = 1,
+	kFullScreenCmd = 2,
+};
+
+static WindowGroupRef winGroup = NULL;
+static WindowRef theWindow = NULL;
+static CGContextRef context;
+static CGDataProviderRef dataProviderRef;
+static CGImageRef fullScreenImage;
+static CGRect devRect;
+static CGRect bounds;
+static PasteboardRef appleclip;
+static _Rect winRect;
+
+static Boolean altPressed = false;
+static Boolean button2 = false;
+static Boolean button3 = false;
+
+static Boolean needflush = false;
+
+
+static int
+isready(void*a)
+{
+	return readybit;
+}
+
+CGContextRef QuartzContext;
+
+static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+
+static void winproc(void *a);
+static void flushproc(void *a);
+
+void
+screeninit(void)
+{
+	int fmt;
+	int dx, dy;
+	ProcessSerialNumber psn = { 0, kCurrentProcess };
+	TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+	SetFrontProcess(&psn);
+
+	fmt = XBGR32; //XRGB32;
+
+	devRect = CGDisplayBounds(CGMainDisplayID());
+//	devRect.origin.x = 0;
+//	devRect.origin.y = 0;
+//	devRect.size.width = 1024;
+//	devRect.size.height = 768;
+	dx = devRect.size.width;
+	dy = devRect.size.height;
+
+	if(1){	/* TO DO: new dev draw for changing screen size */
+		dx = Xsize;
+		dy = Ysize;
+	}
+
+	gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
+	dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata,
+						dx * dy * 4, 0);
+	fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4,
+				CGColorSpaceCreateDeviceRGB(),
+				kCGImageAlphaNoneSkipLast,
+				dataProviderRef, 0, 0, kCGRenderingIntentDefault);
+
+	kproc("osxscreen", winproc, nil, 0);
+	kproc("osxflush", flushproc, nil, 0);
+	Sleep(&rend, isready, nil);
+}
+
+void
+window_resized(void)
+{
+	GetWindowBounds(theWindow, kWindowContentRgn, &winRect);
+
+	bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top);
+}
+
+static void
+flushproc(void *a)
+{
+	for(;;) {
+		if(needflush) {
+			drawqlock();
+			needflush = false;
+			QDBeginCGContext(GetWindowPort(theWindow), &context);
+			CGContextFlush(context);
+			QDEndCGContext(GetWindowPort(theWindow), &context);
+ 			drawqunlock();
+		}
+		usleep(33333);
+	}
+}
+
+static void
+winproc(void *a)
+{
+	MenuItemIndex index;
+	int dx, dy;
+
+	winRect.left = 30;
+	winRect.top = 60;
+	dx = devRect.size.width*0.75;	/* devRect is full screen; take only most of it */
+	dy = devRect.size.height*0.75;
+	if(1){	/* TO DO */
+		dx = Xsize;
+		dy = Ysize;
+	}
+	winRect.bottom = winRect.top + dy;
+	winRect.right = winRect.left + dx;
+
+	ClearMenuBar();
+	InitCursor();
+
+	CreateStandardWindowMenu(0, &windMenu);
+	InsertMenu(windMenu, 0);
+
+	CreateNewMenu(1004, 0, &viewMenu);
+	SetMenuTitleWithCFString(viewMenu, CFSTR("View"));
+	AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0,
+			kFullScreenCmd, &index);
+	SetMenuItemCommandKey(viewMenu, index, 0, 'F');
+	AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"),
+			kMenuItemAttrDisabled,
+			kFullScreenCmd, &index);
+	InsertMenu(viewMenu, GetMenuID(windMenu));
+
+	DrawMenuBar();
+	uint32_t windowAttrs = 0
+				| kWindowCloseBoxAttribute
+				| kWindowCollapseBoxAttribute
+//				| kWindowResizableAttribute		// TO DO
+				| kWindowStandardHandlerAttribute
+//				| kWindowFullZoomAttribute		// TO DO
+		;
+
+	CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
+	CreateWindowGroup(0, &winGroup);
+	SetWindowGroup(theWindow, winGroup);
+
+	SetWindowTitleWithCFString(theWindow, CFSTR("Inferno"));
+
+	if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
+		sysfatal("pasteboard create failed");
+
+	const EventTypeSpec commands[] = {
+		{ kEventClassWindow, kEventWindowClosed },
+		{ kEventClassWindow, kEventWindowBoundsChanged },
+		{ kEventClassCommand, kEventCommandProcess }
+	};
+	const EventTypeSpec events[] = {
+		{ kEventClassKeyboard, kEventRawKeyDown },
+		{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
+		{ kEventClassKeyboard, kEventRawKeyRepeat },
+		{ kEventClassMouse, kEventMouseDown },
+		{ kEventClassMouse, kEventMouseUp },
+		{ kEventClassMouse, kEventMouseMoved },
+		{ kEventClassMouse, kEventMouseDragged },
+		{ kEventClassMouse, kEventMouseWheelMoved },
+	};
+
+ 	InstallApplicationEventHandler (
+ 								NewEventHandlerUPP (MainWindowEventHandler),
+								GetEventTypeCount(events),
+								events,
+								NULL,
+								NULL);
+	InstallWindowEventHandler (
+								theWindow,
+								NewEventHandlerUPP (MainWindowCommandHandler),
+								GetEventTypeCount(commands),
+								commands,
+								theWindow,
+								NULL);
+
+	ShowWindow(theWindow);
+	ShowMenuBar();
+	window_resized();
+	SelectWindow(theWindow);
+	// Run the event loop
+	readybit = 1;
+	Wakeup(&rend);
+	RunApplicationEventLoop();
+
+}
+
+static int
+convert_key(UInt32 key, UInt32 charcode)
+{
+	switch(key) {
+	case QZ_IBOOK_ENTER:
+	case QZ_RETURN: return '\n';
+	case QZ_ESCAPE: return 27;
+	case QZ_BACKSPACE: return '\b';
+	case QZ_LALT: return Kalt;
+	case QZ_LCTRL: return Kctl;
+	case QZ_LSHIFT: return Kshift;
+	case QZ_F1: return KF+1;
+	case QZ_F2: return KF+2;
+	case QZ_F3: return KF+3;
+	case QZ_F4: return KF+4;
+	case QZ_F5: return KF+5;
+	case QZ_F6: return KF+6;
+	case QZ_F7: return KF+7;
+	case QZ_F8: return KF+8;
+	case QZ_F9: return KF+9;
+	case QZ_F10: return KF+10;
+	case QZ_F11: return KF+11;
+	case QZ_F12: return KF+12;
+	case QZ_INSERT: return Kins;
+	case QZ_DELETE: return 0x7F;
+	case QZ_HOME: return Khome;
+	case QZ_END: return Kend;
+	case QZ_KP_PLUS: return '+';
+	case QZ_KP_MINUS: return '-';
+	case QZ_TAB: return '\t';
+	case QZ_PAGEUP: return Kpgup;
+	case QZ_PAGEDOWN: return Kpgdown;
+	case QZ_UP: return Kup;
+	case QZ_DOWN: return Kdown;
+	case QZ_LEFT: return Kleft;
+	case QZ_RIGHT: return Kright;
+	case QZ_KP_MULTIPLY: return '*';
+	case QZ_KP_DIVIDE: return '/';
+	case QZ_KP_ENTER: return '\n';
+	case QZ_KP_PERIOD: return '.';
+	case QZ_KP0: return '0';
+	case QZ_KP1: return '1';
+	case QZ_KP2: return '2';
+	case QZ_KP3: return '3';
+	case QZ_KP4: return '4';
+	case QZ_KP5: return '5';
+	case QZ_KP6: return '6';
+	case QZ_KP7: return '7';
+	case QZ_KP8: return '8';
+	case QZ_KP9: return '9';
+	default: return charcode;
+	}
+}
+
+void
+sendbuttons(int b, int x, int y)
+{
+	mousetrack(b, x, y, 0);
+}
+
+static Ptr fullScreenRestore;
+static int amFullScreen = 0;
+static WindowRef oldWindow = NULL;
+
+static void
+leave_full_screen(void)
+{
+	if(amFullScreen){
+		EndFullScreen(fullScreenRestore, 0);
+		theWindow = oldWindow;
+		ShowWindow(theWindow);
+		amFullScreen = 0;
+		window_resized();
+		Rectangle rect =  { { 0, 0 }, { bounds.size.width, bounds.size.height} };
+		drawqlock();
+ 		flushmemscreen(rect);
+ 		drawqunlock();
+	}
+}
+
+static void
+full_screen(void)
+{
+	if(!amFullScreen){
+		oldWindow = theWindow;
+		HideWindow(theWindow);
+		BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0);
+		amFullScreen = 1;
+		window_resized();
+		Rectangle rect =  { { 0, 0 },
+ 							{ bounds.size.width,
+ 							  bounds.size.height} };
+		drawqlock();
+ 		flushmemscreen(rect);
+ 		drawqunlock();
+	}
+}
+
+static OSStatus
+MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+	OSStatus result = noErr;
+	result = CallNextEventHandler(nextHandler, event);
+	UInt32 class = GetEventClass (event);
+	UInt32 kind = GetEventKind (event);
+	static uint32_t mousebuttons = 0; // bitmask of buttons currently down
+	static uint32_t mouseX = 0;
+	static uint32_t mouseY = 0;
+
+	if(class == kEventClassKeyboard) {
+		char macCharCodes;
+		UInt32 macKeyCode;
+		UInt32 macKeyModifiers;
+
+		GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,
+							NULL, sizeof(macCharCodes), NULL, &macCharCodes);
+		GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
+							sizeof(macKeyCode), NULL, &macKeyCode);
+		GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
+							sizeof(macKeyModifiers), NULL, &macKeyModifiers);
+        switch(kind) {
+		case kEventRawKeyModifiersChanged:
+			if (macKeyModifiers == (controlKey | optionKey)) leave_full_screen();
+
+			switch(macKeyModifiers & (optionKey | cmdKey)) {
+			case (optionKey | cmdKey):
+				/* due to chording we need to handle the case when both
+				 * modifier keys are pressed at the same time.
+				 * currently it's only 2-3 snarf and the 3-2 noop
+				 */
+				altPressed = true;
+				if(mousebuttons & 1 || mousebuttons & 2 || mousebuttons & 4) {
+					mousebuttons |= 2;	/* set button 2 */
+					mousebuttons |= 4;	/* set button 3 */
+					button2 = true;
+					button3 = true;
+					sendbuttons(mousebuttons, mouseX, mouseY);
+				}
+				break;
+			case optionKey:
+				altPressed = true;
+				if(mousebuttons & 1 || mousebuttons & 4) {
+					mousebuttons |= 2;	/* set button 2 */
+					button2 = true;
+					sendbuttons(mousebuttons, mouseX, mouseY);
+				}
+				break;
+			case cmdKey:
+				if(mousebuttons & 1 || mousebuttons & 2) {
+					mousebuttons |= 4;	/* set button 3 */
+					button3 = true;
+					sendbuttons(mousebuttons, mouseX, mouseY);
+				}else
+					gkbdputc(gkbdq, Latin);
+				break;
+			case 0:
+			default:
+				if(button2 || button3) {
+					if(button2) {
+						mousebuttons &= ~2;	/* clear button 2 */
+						button2 = false;
+						altPressed = false;
+					}
+					if(button3) {
+						mousebuttons &= ~4;	/* clear button 3 */
+						button3 = false;
+					}
+					sendbuttons(mousebuttons, mouseX, mouseY);
+				}
+				if(altPressed) {
+					gkbdputc(gkbdq, Kalt);
+					altPressed = false;
+				}
+				break;
+			}
+			break;
+		case kEventRawKeyDown:
+		case kEventRawKeyRepeat:
+			if(macKeyModifiers != cmdKey) {
+				int key;
+				key = convert_key(macKeyCode, macCharCodes);
+				if(key != -1)
+					gkbdputc(gkbdq, key);
+			}else
+				result = eventNotHandledErr;
+			break;
+		default:
+			break;
+		}
+	}
+	else if(class == kEventClassMouse) {
+		_Point mousePos;
+
+		GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
+							0, sizeof mousePos, 0, &mousePos);
+		
+		switch(kind) {
+		case kEventMouseWheelMoved:
+		{
+		    int32_t wheeldelta;
+			GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32,
+								0,sizeof(wheeldelta), 0, &wheeldelta);
+			mouseX = mousePos.h - winRect.left;
+			mouseY = mousePos.v - winRect.top;
+			sendbuttons(wheeldelta>0 ? 8 : 16, mouseX, mouseY);
+			break;
+		}
+		case kEventMouseUp:
+		case kEventMouseDown:
+		{
+			uint32_t buttons;
+			uint32_t modifiers;
+			uint32_t clkcnt;
+
+			GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
+								0, sizeof(modifiers), 0, &modifiers);
+			GetEventParameter(event, kEventParamMouseChord, typeUInt32,
+								0, sizeof buttons, 0, &buttons);
+			GetEventParameter(event, kEventParamClickCount, typeUInt32,
+								0, sizeof(clkcnt), 0, &clkcnt);
+			
+			/* simulate other buttons via alt/apple key. like x11 */
+			if(modifiers & optionKey) {
+				mousebuttons = ((buttons & 1) ? 2 : 0);
+				altPressed = false;
+			} else if(modifiers & cmdKey)
+				mousebuttons = ((buttons & 1) ? 4 : 0);
+			else
+				mousebuttons = (buttons & 1);
+
+			mousebuttons |= ((buttons & 2)<<1);
+			mousebuttons |= ((buttons & 4)>>1);
+			if(clkcnt > 1)
+				mousebuttons |= 1<<8;
+
+		} /* Fallthrough */
+		case kEventMouseMoved:
+		case kEventMouseDragged:
+			mouseX = mousePos.h - winRect.left;
+			mouseY = mousePos.v - winRect.top;
+			sendbuttons(mousebuttons, mouseX, mouseY);
+			break;
+		default:
+			result = eventNotHandledErr;
+			break;
+		}
+	}
+	return result;
+}
+
+
+//default window command handler (from menus)
+static OSStatus
+MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+	OSStatus result = noErr;
+	UInt32 class = GetEventClass (event);
+	UInt32 kind = GetEventKind (event);
+
+	result = CallNextEventHandler(nextHandler, event);
+
+	if(class == kEventClassCommand) {
+		HICommand theHICommand;
+		GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+							NULL, sizeof(HICommand), NULL, &theHICommand);
+
+		switch(theHICommand.commandID) {
+		case kHICommandQuit:
+			cleanexit(0);
+			break;
+
+		case kFullScreenCmd:
+			full_screen();
+			break;
+
+		default:
+			result = eventNotHandledErr;
+			break;
+		}
+	} else if(class == kEventClassWindow) {
+		WindowRef     window;
+		_Rect          rectPort = {0,0,0,0};
+
+		GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
+							NULL, sizeof(WindowRef), NULL, &window);
+
+		if(window)
+			GetPortBounds(GetWindowPort(window), &rectPort);
+
+		switch(kind) {
+		case kEventWindowClosed:
+			theWindow = NULL;
+			cleanexit(0); // only one window
+			break;
+
+		//resize window
+		case kEventWindowBoundsChanged:
+			window_resized();
+			Rectangle rect =  { { 0, 0 },
+ 									{ bounds.size.width,
+ 									  bounds.size.height} };
+			drawqlock();
+ 			flushmemscreen(rect);
+ 			drawqunlock();
+			break;
+
+		default:
+			result = eventNotHandledErr;
+			break;
+		}
+	}
+
+	return result;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	CGRect rbounds;
+
+	// sanity check.  Trips from the initial "terminal"
+	if (r.max.x < r.min.x || r.max.y < r.min.y)
+		return;
+
+	rbounds.size.width = r.max.x - r.min.x;
+	rbounds.size.height = r.max.y - r.min.y;
+	rbounds.origin.x = r.min.x;
+	rbounds.origin.y = r.min.y;
+
+	if(rbounds.size.width <= 0 || rbounds.size.height <= 0)
+		return;
+		
+	QDBeginCGContext(GetWindowPort(theWindow), &context);
+	
+	// The sub-image is relative to our whole screen image.
+	CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds);
+	
+	// Drawing the sub-image is relative to the window.
+	rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height;
+	CGContextDrawImage(context, rbounds, subimg);
+	CGImageRelease(subimg);
+	QDEndCGContext(GetWindowPort(theWindow), &context);
+
+	needflush = true;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{	
+	if(!triedscreen) {
+		triedscreen = 1;
+		screeninit();	/* TO DO: call this elsewhere? */
+	}
+	*r = gscreen->r;
+	*chan = gscreen->chan;
+	*depth = gscreen->depth;
+	*width = gscreen->width;
+	*softscreen = 1;
+
+	return gscreen->data->bdata;
+}
+
+// PAL - no palette handling.  Don't intend to either.
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+
+// PAL: Certainly wrong to return a grayscale.
+	 *r = i;
+	 *g = i;
+	 *b = i;
+}
+
+void
+setcolor(ulong index, ulong r, ulong g, ulong b)
+{
+	USED(index); USED(r); USED(g); USED(b);
+}
+
+enum{
+	SnarfSize=	100*1024
+};
+
+static char snarf[3*SnarfSize+1];
+static Rune rsnarf[SnarfSize+1];
+
+char*
+clipread(void)
+{
+	CFDataRef cfdata;
+	OSStatus err = noErr;
+	ItemCount nitems;
+	int i;
+	char *s;
+
+	if(appleclip == NULL)
+		return nil;
+	// Wow.  This is ridiculously complicated.
+	PasteboardSynchronize(appleclip);
+	if((err = PasteboardGetItemCount(appleclip, &nitems)) != noErr) {
+		fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err);
+		return 0;
+	}
+
+	// Yes, based at 1.  Silly API.
+	for(i = 1; i <= nitems; i++) {
+		PasteboardItemID itemID;
+		CFArrayRef flavorTypeArray;
+		CFIndex flavorCount;
+
+		if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){
+			fprint(2, "Can't get pasteboard item identifier: %d\n", err);
+			return 0;
+		}
+
+		if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){
+			fprint(2, "Can't copy pasteboard item flavors: %d\n", err);
+			return 0;
+		}
+
+		flavorCount = CFArrayGetCount(flavorTypeArray);
+		CFIndex flavorIndex;
+		for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){
+			CFStringRef flavorType;
+			flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+			if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){
+				if((err = PasteboardCopyItemFlavorData(appleclip, itemID,
+					CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){
+					fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err);
+					return 0;
+				}
+				CFIndex length = CFDataGetLength(cfdata);
+				if (length > sizeof rsnarf) length = sizeof rsnarf;
+				CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf);
+				snprint(snarf, sizeof snarf, "%.*S", length/sizeof(Rune), rsnarf);
+				for(s = snarf; *s; s++)
+					if(*s == '\r')
+						*s = '\n';
+				CFRelease(cfdata);
+				return strdup(snarf);
+			}
+		}
+	}
+	return 0;
+}
+
+int
+clipwrite(char *snarf)
+{
+	CFDataRef cfdata;
+	PasteboardSyncFlags flags;
+
+	if(appleclip == NULL)
+		return 0;
+	runeseprint(rsnarf, rsnarf+nelem(rsnarf), "%s", snarf);
+	if(PasteboardClear(appleclip) != noErr){
+		fprint(2, "apple pasteboard clear failed\n");
+		return 0;
+	}
+	flags = PasteboardSynchronize(appleclip);
+	if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
+		fprint(2, "apple pasteboard cannot assert ownership\n");
+		return 0;
+	}
+	cfdata = CFDataCreate(kCFAllocatorDefault, (uchar*)rsnarf, runestrlen(rsnarf)*2);
+	if(cfdata == nil){
+		fprint(2, "apple pasteboard cfdatacreate failed\n");
+		return 0;
+	}
+	if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
+		CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
+		fprint(2, "apple pasteboard putitem failed\n");
+		CFRelease(cfdata);
+		return 0;
+	}
+	CFRelease(cfdata);
+	return 1;
+}
+
+void
+setpointer(int x, int y)
+{
+	CGPoint pnt;
+
+	pnt.x = x + winRect.left;
+	pnt.y = y + winRect.top;
+	CGWarpMouseCursorPosition(pnt);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	USED(c);
+	/* removed, pending extensive change for newer MacOS X */
+}
--- /dev/null
+++ b/emu/NOTICE
@@ -1,0 +1,32 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+	Portions Copyright © 1997-1999 Vita Nuova Limited
+	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+	Portions Copyright © 2005 Russ Cox, MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/emu/NetBSD/asm-386.S
@@ -1,0 +1,101 @@
+	.file	"asm-NetBSD-386.S"
+#include <sys/syscall.h>
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+	.type	executeonnewstack,@function
+	.global	executeonnewstack
+executeonnewstack:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%esi
+
+	movl	8(%ebp), %esi	/* get tos */
+	subl	$4, %esi
+	movl	16(%ebp), %eax
+	movl	%eax, (%esi)	/* stash arg on new stack */
+	subl	$4, %esi
+	movl	12(%ebp), %eax
+	movl	%eax, (%esi)	/* stash tramp on new stack */
+	mov	%esi, %esp	/* swap stacks pronto */
+	popl	%eax		/* recover the tramp address */
+	call	*%eax		/* and jump to it (ho ho) */
+
+	/* if we return here, tramp didn't do its job */
+
+	addl	$8, %esp	/* clean up for pose value */
+
+	leal	SYS_exit, %eax
+	int	$0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ *	between the unlock and the system call, but this should
+ *	not matter since no task is waiting for the result
+ */
+
+	.type	unlockandexit,@function
+	.global	unlockandexit
+unlockandexit:
+	pushl	%ebp
+	movl	%esp, %ebp
+
+	movl	8(%ebp), %esi		/* get the key address */
+	pushl	$0			/* exit status 0 */
+	movl	$0, %eax		/* unlock the stack allocator */
+	movl	%eax, (%esi)
+	leal	SYS_exit, %eax		/* call exit */
+	int	$0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+	.type	umult,@function
+	.global	umult
+umult:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+
+	movl	8(%ebp), %eax
+	movl	12(%ebp), %ebx
+	mull	%ebx
+	movl	16(%ebp), %ebx
+	movl	%edx, (%ebx)
+
+	popl	%ebx
+	popl	%ebp
+	ret
+
+	.type	FPsave,@function
+	.global	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	FPrestore,@function
+	.global	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	_tas,@function
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/NetBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/soundcard.h>
+
+#define 	Audio_Mic_Val		SOUND_MIXER_MIC
+#define 	Audio_Linein_Val	SOUND_MIXER_LINE
+
+#define	Audio_Speaker_Val	SOUND_MIXER_SPEAKER
+#define	Audio_Headphone_Val	SOUND_MIXER_PHONEOUT
+#define	Audio_Lineout_Val	SOUND_MIXER_VOLUME
+
+#define 	Audio_Pcm_Val		AFMT_S16_LE
+#define 	Audio_Ulaw_Val		AFMT_MU_LAW
+#define 	Audio_Alaw_Val		AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING	"/dev/dsp"
+
+enum {
+	A_Pause,
+	A_UnPause
+};
+
+enum {
+	A_In,
+	A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static	int	audio_file  = -1;	/* file in/out */
+static	int	audio_file_in  = -1;	/* copy of above when opened O_READ/O_RDWR */
+static	int	audio_file_out  = -1;	/* copy of above when opened O_WRITE/O_RDWR */
+
+static	int	audio_swap_flag = 0;	/* endian swap */
+
+static	int	audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+	int i;
+	static ushort flag = 1;
+
+	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
+	audio_info_init(&av);
+	for (i = 0; i < 32; i++)
+		mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	char ebuf[ERRMAX];
+
+	if (debug)
+		print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+	switch(omode){
+	case OREAD:
+		qlock(&inlock);
+		if(waserror()){
+			qunlock(&inlock);
+			nexterror();
+		}
+
+		if(audio_file_in >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file;
+		poperror();
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			qunlock(&inlock);
+			nexterror();
+		}
+		if(audio_file_in >= 0 || audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+	if (debug)
+		print("audio_file_open: success\nin %d out %d both %d\n",
+			audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+	USED(c);
+	USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_out < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		qunlock(&outlock);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_in < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		audio_file_out = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		close(audio_file);
+		audio_file_in = audio_file_out = audio_file = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long ba, status, chunk, total;
+	char *pva = (char *) va;
+
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(audio_file_in < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(! audio_pause_in(audio_file_in, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count) {
+		chunk = count - total;
+		osenter();
+		status = read(audio_file_in, pva + total, chunk);
+		osleave(); 
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+
+	if(total != count)
+		error(Eio);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(pva, count); 
+
+	poperror();
+	qunlock(&inlock);
+
+	return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long status = -1;
+	long ba, total, chunk, bufsz;
+
+	if (debug > 1)
+		print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+			c, va, count, offset);
+
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	if(audio_file_out < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(va, count); 
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count) {
+		chunk = min(bufsz, count - total);
+		osenter();
+		status = write(audio_file_out, va, chunk);
+		osleave();
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	return count;
+}
+
+static int
+audio_open(void)
+{
+	int fd;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		if (debug)
+			print("audio_open: failed to set blocking\n");
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	if (debug)
+		print("audio_open: blocking set\n");
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = 0;
+
+	if(! audio_set_info(fd, &av.in, A_In)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	int	fd;
+	int	ff;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if (!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if (!audio_enforce(&tmpav))
+		error(Ebadarg);
+
+	qlock(&inlock);
+	if (waserror()) {
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+		if (!audio_pause_in(audio_file_in, A_Pause))
+			error(Ebadarg);
+		if (!audio_flush(audio_file_in, A_In))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&inlock);
+
+	qlock(&outlock);
+	if (waserror()) {
+		qunlock(&outlock);
+		nexterror();
+	}
+	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+		if (!audio_pause_out(audio_file_out))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&outlock);
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+	int status;
+	osenter();
+	status = ioctl(fd, ctl, info);  /* qlock and load general stuff */
+	osleave();
+	if (status < 0)
+		print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+	return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+	int newbits, newenc;
+	
+	newbits = i->bits;
+	newenc = i->enc;
+	switch (newenc) {
+	case Audio_Alaw_Val:
+		if (newbits == 8)
+			return AFMT_A_LAW;
+		break;
+	case Audio_Ulaw_Val:
+		if (newbits == 8)
+			return AFMT_MU_LAW;
+		break;
+	case Audio_Pcm_Val:
+		if (newbits == 8)
+			return AFMT_U8;
+		else if (newbits == 16)
+			return AFMT_S16_LE;
+		break;
+	}
+	return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status;
+	int unequal_stereo = 0;
+
+	if(fd < 0)
+		return 0;
+
+	/* fmt */
+	if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+		int oldfmt, newfmt;
+		oldfmt = AFMT_QUERY;
+		if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+			return 0;
+		if (debug)
+			print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+		newfmt = choosefmt(i);
+		if (debug)
+			print("audio_set_info: new format 0x%.8lux\n", newfmt);
+		if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+			return 0;
+	}
+
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG) {
+		int channels = i->chan;
+		if (debug)
+			print("audio_set_info: new channels %d\n", channels);
+		if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+			|| channels != i->chan)
+			return 0;
+	}
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG) {
+		int speed = i->rate;
+		if (debug)
+			print("audio_set_info: new speed %d\n", speed);
+		if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+			return 0;
+	}
+
+	/* dev volume */
+	if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+		int val;
+		if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+			mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+		if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+			mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+		val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+		doioctl(fd, MIXER_WRITE(i->dev), &val);
+	}
+
+	if (i->flags & AUDIO_DEV_FLAG) {
+	}
+	
+	return 1;
+}
+
+void 
+audio_swap_endian(char *p, int n)
+{
+	int b;
+
+	while (n > 1) {
+		b = p[0];
+		p[0] = p[1];
+		p[1] = b;
+		p += 2;
+		n -= 2;
+	}
+}
+
+static int
+audio_pause_out(int fd)
+{
+	USED(fd);
+	return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+	USED(fd);
+	USED(f);
+	return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+	int x;
+	return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && 
+		(t->in.rate != 8000 || t->in.chan != 1))
+		 return 0;
+	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && 
+		(t->out.rate != 8000 || t->out.chan != 1))
+		 return 0;
+	return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/NetBSD/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgid(0, getpid());
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/NetBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * NetBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/dty00",
+        "/dev/dty01",
+        "/dev/dty02",
+        "/dev/dty03",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{115200,	B115200},
+	{230400,	B230400},
+	{-1,		-1}
+};
+
--- /dev/null
+++ b/emu/NetBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/NetBSD/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/NetBSD/emu-g
@@ -1,0 +1,102 @@
+env
+	X11LIBS=
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	math
+
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+	void setpointer(int x, int y){USED(x); USED(y);}
+	ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/NetBSD/emu.c
@@ -1,0 +1,88 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+
+#include "emu.root.h"
+
+ulong ndevs = 29;
+
+extern Dev rootdevtab;
+extern Dev consdevtab;
+extern Dev envdevtab;
+extern Dev mntdevtab;
+extern Dev pipedevtab;
+extern Dev progdevtab;
+extern Dev profdevtab;
+extern Dev srvdevtab;
+extern Dev dupdevtab;
+extern Dev ssldevtab;
+extern Dev capdevtab;
+extern Dev fsdevtab;
+extern Dev cmddevtab;
+extern Dev indirdevtab;
+extern Dev drawdevtab;
+extern Dev pointerdevtab;
+extern Dev snarfdevtab;
+extern Dev ipdevtab;
+extern Dev eiadevtab;
+extern Dev audiodevtab;
+extern Dev memdevtab;
+Dev* devtab[]={
+	&rootdevtab,
+	&consdevtab,
+	&envdevtab,
+	&mntdevtab,
+	&pipedevtab,
+	&progdevtab,
+	&profdevtab,
+	&srvdevtab,
+	&dupdevtab,
+	&ssldevtab,
+	&capdevtab,
+	&fsdevtab,
+	&cmddevtab,
+	&indirdevtab,
+	&drawdevtab,
+	&pointerdevtab,
+	&snarfdevtab,
+	&ipdevtab,
+	&eiadevtab,
+	&audiodevtab,
+	&memdevtab,
+	nil,
+	nil,
+	nil,
+	nil,
+	nil,
+	nil,
+	nil,
+	nil,
+	nil,
+};
+
+void links(void){
+}
+
+extern void sysmodinit(void);
+extern void drawmodinit(void);
+extern void tkmodinit(void);
+extern void mathmodinit(void);
+extern void srvmodinit(void);
+extern void keyringmodinit(void);
+extern void loadermodinit(void);
+extern void freetypemodinit(void);
+void modinit(void){
+	sysmodinit();
+	drawmodinit();
+	tkmodinit();
+	mathmodinit();
+	srvmodinit();
+	keyringmodinit();
+	loadermodinit();
+	freetypemodinit();
+}
+
+char* conffile = "emu";
+ulong kerndate = KERNDATE;
--- /dev/null
+++ b/emu/NetBSD/mkfile
@@ -1,0 +1,49 @@
+SYSTARG=NetBSD
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=NetBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+# can remove or override X11LIBS using env section in config files
+X11LIBS= -L/usr/X11R7/lib -R/usr/X11R7/lib -lXext -lX11
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= ${X11LIBS} -lossaudio -lm
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/NetBSD/os.c
@@ -1,0 +1,537 @@
+#include	<sys/types.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sched.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<sys/time.h>
+#include	<errno.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+/* For dynamic linking init/fini code that needs malloc */
+void (*coherence)(void) = nofence;
+
+
+enum
+{
+	DELETE	= 0x7f,
+	CTRLC	= 'C'-'@',
+	NSTACKSPERALLOC = 16,
+	X11STACK=	256*1024
+};
+char *hosttype = "NetBSD";
+
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg);
+void unlockandexit(ulong *key);
+
+extern int dflag;
+
+int	gidnobody = -1;
+int	uidnobody = -1;
+static struct 	termios tinit;
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	void *kstack;
+
+	lock(&procs.l);
+	if(up->prev)
+		up->prev->next = up->next;
+	else
+		procs.head = up->next;
+
+	if(up->next)
+		up->next->prev = up->prev;
+	else
+		procs.tail = up->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", up->text, msg);
+
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	kstack = up->kstack;
+	free(up->prog);
+	free(up);
+	if(kstack != nil)
+		stackfreeandexit(kstack);
+}
+
+int
+tramp(void *arg)
+{
+	Proc *p;
+	p = arg;
+	p->pid = p->sigid = getpid();
+	(*p->func)(p->arg);
+	pexit("{Tramp}", 0);
+	return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	int pid;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	void *tos;
+
+	p = newproc();
+	if(0)
+		print("start %s:%.8lx\n", name, p);
+	if(p == nil) {
+		print("kproc(%s): no memory", name);
+		panic("kproc: no memory");
+	}
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	if(flags & KPX11){
+		p->kstack = nil;	/* never freed; also up not defined */
+		tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+	}else
+		p->kstack = stackalloc(p, &tos);
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if (__clone(tramp, tos, /*CLONE_PTRACE|*/CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0) {
+		fprint(2, "emu: clone failed: %s\n", strerror(errno));
+		panic("kproc: clone failed");
+	}
+}
+
+/*
+ * TO DO:
+ * To get pc on trap, use sigaction instead of signal and
+ * examine its siginfo structure
+ */
+
+/*
+static void
+diserr(char *s, int pc)
+{
+	char buf[ERRMAX];
+
+	snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc);
+	disfault(nil, buf);
+}
+*/
+
+static void
+trapILL(int signo)
+{
+	USED(signo);
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+trapBUS(int signo)
+{
+	USED(signo);
+	disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(int signo)
+{
+	USED(signo);
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+	char buf[64];
+	USED(signo);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+	disfault(nil, buf);
+}
+
+static void
+trapUSR1(int signo)
+{
+	int intwait;
+
+	USED(signo);
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+
+	if(intwait == 0)		/* Not posted so it's a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+	kill(p->sigid, SIGUSR1);
+}
+
+static void
+trapUSR2(int signo)
+{
+	USED(signo);
+	/* we've done our work of interrupting sigsuspend */
+}
+
+void
+osblock(void)
+{
+	sigset_t mask;
+
+	sigprocmask(SIG_SETMASK, NULL, &mask);
+	sigdelset(&mask, SIGUSR2);
+	sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+	if(kill(p->sigid, SIGUSR2) < 0)
+		fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+	struct termios t;
+	struct sigaction act;
+	sigset_t mask;
+	struct passwd *pw;
+	Proc *p;
+	void *tos;
+	char sys[64];
+
+	setsid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+
+	pw = getpwnam("nobody");
+	if(pw != nil) {
+		uidnobody = pw->pw_uid;
+		gidnobody = pw->pw_gid;
+	}
+
+	if(dflag == 0)
+		termset();
+
+	memset(&act, 0 , sizeof(act));
+	act.sa_handler = trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	memset(&act, 0 , sizeof(act));
+	act.sa_handler = trapUSR2;
+	sigaction(SIGUSR2, &act, nil);
+
+	act.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &act, nil);
+
+	/*
+	 * For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGPIPE, SIG_IGN);
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+		signal(SIGINT, cleanexit);
+
+	if(sflag == 0) {
+		act.sa_handler = trapBUS;
+		sigaction(SIGBUS, &act, nil);
+		act.sa_handler = trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_handler = trapSEGV;
+		sigaction(SIGSEGV, &act, nil);
+		act.sa_handler = trapFPE;
+		sigaction(SIGFPE, &act, nil);
+	}
+
+	p = newproc();
+	p->kstack = stackalloc(p, &tos);
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+
+	p->env->uid = getuid();
+	p->env->gid = getgid();
+
+	executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		buf[0] = 'H' - '@';
+		break;
+	case CTRLC:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return 0;
+
+	if(sec0 == 0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct  timespec time;
+
+	time.tv_sec = milsec/1000;
+	time.tv_nsec= (milsec%1000)*1000000;
+	nanosleep(&time, NULL);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	sched_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+	Lock l;
+	void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+	*((void **)stack) = stacklist.free;
+	stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+	lock(&stacklist.l);
+	_stackfree(stack);
+	unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+	void *rv;
+	lock(&stacklist.l);
+	if (stacklist.free == 0) {
+		int x;
+		/*
+		 * obtain some more by using sbrk()
+		 */
+		void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+		if (more == 0)
+			panic("stackalloc: no more stacks");
+		/*
+		 * align to KSTACK
+		 */
+		more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+		/*
+		 * free all the new stacks onto the freelist
+		 */
+		for (x = 0; x < NSTACKSPERALLOC; x++)
+			_stackfree((char *)more + KSTACK * x);
+	}
+	rv = stacklist.free;
+	stacklist.free = *(void **)rv;
+	unlock(&stacklist.l);
+	*tos = rv + KSTACK - sizeof(void *);
+	*(Proc **)rv = p;
+	return rv;
+}
+
+int
+segflush(void *a, ulong n)
+{
+	USED(a);
+	USED(n);
+	return 0;
+}
--- /dev/null
+++ b/emu/Nt/audio.c
@@ -1,0 +1,810 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <mmsystem.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#define 	Audio_Mic_Val		0
+#define 	Audio_Linein_Val	-1
+
+#define	Audio_Speaker_Val	0
+#define	Audio_Headphone_Val	-1
+#define	Audio_Lineout_Val	-1
+
+#define 	Audio_Pcm_Val		WAVE_FORMAT_PCM
+#define 	Audio_Ulaw_Val		(WAVE_FORMAT_PCM+1)
+#define 	Audio_Alaw_Val		(WAVE_FORMAT_PCM+2)
+
+#define 	Audio_Max_Queue		8
+
+#define BUFLEN		1000
+
+#define INISOPEN	0x00000002 // the microphone is open
+#define OUTISOPEN	0x00000004 // the speaker is open
+#define INPUTISGOING	0x00000020 // microphone is being recorded/read
+
+#include "audio.h"
+#include "audio-tbls.c"
+
+static int debug = 0;
+
+/* TO DO: sensible expression of double-buffering */
+#define  Ping 0
+#define  Pong 1
+
+static HWAVEIN audio_file_in;
+static HWAVEOUT audio_file_out;
+
+static long out_buf_count;
+
+typedef struct _awin {
+	WAVEHDR hdr;
+	long	sz;
+	char*	ptr;
+	char	data[Audio_Max_Buf];
+} AWin;
+
+static AWin audio_ping;
+static AWin audio_pong;
+
+static long paddle = Ping;
+static int ping_is_filling;
+static int pong_is_filling;
+
+static long audio_flags = 0;
+static int audio_init = 0;
+
+static QLock flag_lock;
+
+static Audio_t av;
+
+static HANDLE outlock;
+static HANDLE inlock;
+
+static int audio_open_in(HWAVEIN*, Audio_d*);
+static int audio_open_out(HWAVEOUT*, Audio_d*);
+static void audio_close_in(void);
+static void audio_close_out(void);
+static void CALLBACK waveInProc(HWAVEIN, UINT, DWORD, DWORD, DWORD);
+static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD);
+
+#define AUDIOIN  0
+#define AUDIOOUT  1
+
+/* 
+* Error routines
+*/
+static int
+audioerror(unsigned int code, int in_out, char *msg)
+{
+	char errorText[MAXERRORLENGTH];
+
+	if (code != MMSYSERR_NOERROR) {
+		switch(in_out) {
+		case AUDIOIN:
+			waveInGetErrorText(code, errorText, sizeof(errorText));
+			//print("ERROR -- %s: %s\n", msg, errorText);
+			return(-1);
+		case AUDIOOUT:
+			waveOutGetErrorText(code, errorText, sizeof(errorText));
+			//print("ERROR -- %s: %s\n", msg, errorText);
+			return(-1);
+		default:
+			print("%s: Unknown device\n", msg);
+		}
+	}
+	//print("TRACE %s\n", msg);
+	return 0;
+}
+
+void
+audio_file_init(void)
+{
+	audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	int in_is_open = 0;
+
+	switch(omode){
+	case OREAD:
+		qlock(&flag_lock);
+
+		if(waserror()) {
+			qunlock(&flag_lock);
+			nexterror();
+		}
+
+		if(audio_flags & INISOPEN)
+			error(Einuse);
+
+		inlock = CreateMutex(NULL, FALSE, NULL);
+		if(inlock == NULL)
+			error(Einuse);
+
+		if(!audio_open_in(&audio_file_in, &av.in) ) {
+			CloseHandle(inlock);
+			error(Ebadarg);
+		}
+
+		ping_is_filling = 0;
+		pong_is_filling = 0;
+		paddle = Ping;
+		audio_flags |= INISOPEN;
+
+		poperror();
+		qunlock(&flag_lock);
+		break;
+	case OWRITE:
+		qlock(&flag_lock);
+		if(waserror()){
+			qunlock(&flag_lock);
+			nexterror();
+		}
+
+		if(audio_flags & OUTISOPEN)
+			error(Einuse);
+
+		outlock = CreateMutex(NULL, FALSE, NULL);
+		if(outlock == NULL)
+			error(Einuse);
+
+		if(!audio_open_out(&audio_file_out, &av.out) ) {
+			CloseHandle(outlock);
+			error(Ebadarg);
+		}
+
+		out_buf_count = 0;
+		audio_flags |= OUTISOPEN;
+
+		poperror();
+		qunlock(&flag_lock);
+		break;
+	case ORDWR:
+		qlock(&flag_lock);
+		if(waserror()){
+			qunlock(&flag_lock);
+			if(in_is_open)
+				audio_close_in();
+			nexterror();
+		}
+
+		if((audio_flags & INISOPEN) || (audio_flags & OUTISOPEN))
+			error(Einuse);
+
+		if(!audio_open_in(&audio_file_in, &av.in) )
+			error(Ebadarg);
+
+		in_is_open = 1;
+
+		if(!audio_open_out(&audio_file_out, &av.out))  {
+			CloseHandle(outlock);
+			error(Ebadarg);
+		}
+
+		inlock = CreateMutex(NULL, FALSE, NULL);
+		if(inlock == NULL)
+			error(Einuse);
+
+		outlock = CreateMutex(NULL, FALSE, NULL);
+		if(outlock == NULL) {
+			CloseHandle(inlock);
+			error(Einuse);
+		}
+
+		audio_flags |= INISOPEN;
+		audio_flags |= OUTISOPEN;
+		ping_is_filling = 0;
+		pong_is_filling = 0;
+		paddle = Ping;
+		out_buf_count = 0;
+
+		poperror();
+		qunlock(&flag_lock);
+		break;
+	default:
+		error(Egreg);
+	}
+}
+
+static int
+audio_open_in(HWAVEIN* h, Audio_d* d)
+{
+	HWAVEIN th;
+	WAVEFORMATEX format; 
+
+	format.wFormatTag = d->enc;
+	format.nChannels = d->chan;
+	format.nSamplesPerSec = d->rate;
+	format.wBitsPerSample = d->bits;
+	format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+	format.nAvgBytesPerSec = 
+		format.nSamplesPerSec * format.nBlockAlign;
+	format.cbSize = 0;
+
+	if (audioerror(
+		waveInOpen(&th, WAVE_MAPPER, &format, (DWORD)waveInProc, 0, CALLBACK_FUNCTION), 
+		AUDIOIN,
+		"cannot open microphone/line-in") == 0) {
+		*h = th;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+audio_open_out(HWAVEOUT* h, Audio_d* d)
+{
+	unsigned int code;
+	HWAVEOUT th;
+	WAVEFORMATEX format; 
+
+	format.wFormatTag = d->enc;
+	format.nChannels = d->chan;
+	format.nSamplesPerSec = d->rate;
+	format.wBitsPerSample = d->bits;
+	format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+	format.nAvgBytesPerSec = 
+		format.nSamplesPerSec * format.nBlockAlign;
+	format.cbSize = 0;
+
+	code = waveOutOpen(&th, WAVE_MAPPER, &format, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
+
+	if (audioerror(code, AUDIOOUT, "cannot open speaker/line-out") == 0) {
+		out_buf_count = 0;
+		*h = th;
+		return 1;
+	}
+
+	return 0;
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&flag_lock);
+		audio_close_in();
+		audio_flags &= ~(INISOPEN|INPUTISGOING);
+		CloseHandle(inlock);
+		qunlock(&flag_lock);
+		break;
+	case OWRITE:
+		qlock(&flag_lock);
+		audio_close_out();
+		audio_flags &= ~OUTISOPEN;
+		CloseHandle(outlock);
+		qunlock(&flag_lock);
+		break;
+	case ORDWR:
+		qlock(&flag_lock);
+		audio_close_in();
+		audio_close_out();
+
+		audio_flags &= ~(INISOPEN|INPUTISGOING|OUTISOPEN);
+
+		CloseHandle(outlock);
+		CloseHandle(inlock);
+		qunlock(&flag_lock);
+		break;
+	}
+}
+
+static void
+audio_close_in()
+{
+	audioerror(waveInStop(audio_file_in), AUDIOIN, "audio_close_in Stop");
+	audioerror(waveInReset(audio_file_in), AUDIOIN, "audio_close_in Reset");
+
+	audioerror(waveInUnprepareHeader(audio_file_in, &audio_ping.hdr, 
+			sizeof(WAVEHDR)), AUDIOIN, "in un prepare ping header");
+	audio_ping.sz = 0;
+	audio_ping.ptr = &audio_ping.data[0];
+	audioerror(waveInUnprepareHeader(audio_file_in, &audio_pong.hdr, 
+			sizeof(WAVEHDR)), AUDIOIN, "in un prepare pong header");
+	audio_pong.sz = 0;
+	audio_pong.ptr = &audio_pong.data[0];
+
+	audioerror(waveInClose(audio_file_in), AUDIOIN, "in close");
+
+}
+
+static void
+audio_close_out()
+{
+Again:
+	WaitForSingleObject(outlock, INFINITE);
+	while(out_buf_count > 0) {
+		ReleaseMutex(outlock);
+		sleep(0);
+		goto Again;
+	}
+	ReleaseMutex(outlock);
+
+	audioerror(waveOutReset(audio_file_out), AUDIOOUT, "close wave out reset");
+	audioerror(waveOutClose(audio_file_out), AUDIOOUT, "closing out device");
+}
+
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	MMRESULT status;
+	long len = av.in.buf * Audio_Max_Buf / Audio_Max_Val;
+	char *v = (char *) va;
+	char *p;
+	long ba, n, chunk, total;
+
+
+ 	qlock(&flag_lock);
+	WaitForSingleObject(inlock, INFINITE);
+
+	if(waserror()) {
+		audioerror(waveInStop(audio_file_in), AUDIOIN, 
+			"audio_file_read Stop 1");
+		audioerror(waveInReset(audio_file_in), AUDIOIN, 
+			"audio_file_read Reset 1");
+		audioerror(waveInUnprepareHeader(audio_file_in, 
+			&audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, 
+			"in unprepare ping");
+		audioerror(waveInUnprepareHeader(audio_file_in, 
+			&audio_pong.hdr, sizeof(WAVEHDR)), 
+			AUDIOIN, "in unprepare pong");
+
+		audio_ping.sz = 0;
+		audio_ping.ptr = &audio_ping.data[0];
+		audio_pong.sz = 0;
+		audio_pong.ptr = &audio_pong.data[0];
+
+		ping_is_filling = pong_is_filling = 0;
+		paddle = Ping;
+
+		qunlock(&flag_lock);
+		ReleaseMutex(inlock);
+
+		nexterror();
+	}
+
+	if(!(audio_flags & INISOPEN))
+		error(Eperm);
+
+	/* check for block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(len < 1 || count % ba)
+		error(Ebadarg);
+
+	if(!(audio_flags & INPUTISGOING)) {
+		if(audioerror(waveInStart(audio_file_in), AUDIOIN, 
+			"in start") == -1)
+				error(Eio);
+
+		audio_ping.sz = 0;
+		audio_ping.ptr = &audio_ping.data[0];
+		audio_ping.hdr.lpData = audio_ping.ptr;
+		audio_ping.hdr.dwBufferLength = len;  
+		audio_ping.hdr.dwUser = Ping;
+		audio_ping.hdr.dwFlags = 0;
+
+		status = waveInPrepareHeader(audio_file_in, &audio_ping.hdr, 
+			sizeof(WAVEHDR));
+
+		if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+			error(Eio);
+
+		audio_pong.sz = 0;
+		audio_pong.ptr = &audio_pong.data[0];
+		audio_pong.hdr.lpData = audio_pong.ptr;
+		audio_pong.hdr.dwBufferLength = len;  
+		audio_pong.hdr.dwUser = Pong;
+		audio_pong.hdr.dwFlags = 0;
+
+		status = waveInPrepareHeader(audio_file_in, &audio_pong.hdr, 
+			sizeof(WAVEHDR));
+
+		if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+			error(Eio);
+
+		status = waveInAddBuffer(audio_file_in, &audio_ping.hdr, 
+			sizeof(WAVEHDR));
+		if (audioerror(status, AUDIOIN, "file_read Add Buffer")== -1){
+			waveInUnprepareHeader(audio_file_in, &audio_ping.hdr, 
+				sizeof(WAVEHDR));
+			audio_ping.sz = 0;
+			audio_ping.ptr = &audio_ping.data[0];
+			error(Eio);
+		}
+
+		ping_is_filling = 1;
+		pong_is_filling = 0;
+		paddle = Ping;
+		audio_flags |= INPUTISGOING;
+	}
+	poperror();
+	ReleaseMutex(inlock);
+
+	total = 0;
+
+Draining:
+
+	WaitForSingleObject(inlock, INFINITE);
+	if(waserror()) {
+		audioerror(waveInStop(audio_file_in), AUDIOIN, 
+			"audio_file_read Stop 2");
+		audioerror(waveInReset(audio_file_in), AUDIOIN, 
+			"audio_file_read Reset 2");
+		audioerror(waveInUnprepareHeader(audio_file_in, 
+			&audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, 
+			"in unprepare ping");
+		audioerror(waveInUnprepareHeader(audio_file_in, 
+			&audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN, 
+			"in unprepare pong");
+
+		audio_ping.sz = 0;
+		audio_ping.ptr = &audio_ping.data[0];
+		audio_pong.sz = 0;
+		audio_pong.ptr = &audio_pong.data[0];
+
+		audio_flags &= ~INPUTISGOING;
+
+		ReleaseMutex(inlock);
+		qunlock(&flag_lock);
+		nexterror();
+	}
+
+	while((total < count) && ((audio_ping.sz > 0) || (audio_pong.sz > 0))) {
+		n  = paddle == Ping ? audio_ping.sz : audio_pong.sz;
+		p  = paddle == Ping ? audio_ping.ptr : audio_pong.ptr;
+
+		chunk = min(n, count - total);
+
+		memmove(v+total, p , chunk);
+
+		total += chunk;
+
+		if(paddle == Ping) {
+			if(!pong_is_filling) {
+
+				if(audioerror(waveInAddBuffer(audio_file_in,
+						&audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN, 
+						"draining ping calling add buffer pong") == -1)
+						error(Eio);
+
+				pong_is_filling = 1;
+			}
+
+			audio_ping.sz -= chunk;
+			if(audio_ping.sz > 0) {
+				audio_ping.ptr += chunk;
+			} else {
+				audio_ping.ptr = &audio_ping.data[0];
+				ping_is_filling = 0;
+				paddle = Pong;
+			}
+		} else {
+			if(!ping_is_filling) {
+
+				if(audioerror(waveInAddBuffer(audio_file_in,
+						&audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, 
+						"draining pong calling add buffer ping") == -1)
+						error(Eio);
+
+				ping_is_filling = 1;
+			}
+
+			audio_pong.sz -= chunk;
+			if(audio_pong.sz > 0) {
+				audio_pong.ptr += chunk;
+			} else {
+				audio_pong.ptr = &audio_pong.data[0];
+				pong_is_filling = 0;
+				paddle = Ping;
+			}
+		}
+	}
+
+	poperror();
+
+	ReleaseMutex(inlock);
+
+	if(total == count) {
+		qunlock(&flag_lock);
+		return count;
+	}
+
+Filling:
+	WaitForSingleObject(inlock, INFINITE);
+	while((audio_ping.sz < 1) && (audio_pong.sz < 1)) {
+		ReleaseMutex(inlock);
+		sleep(0);
+		goto Filling;	
+	}
+	ReleaseMutex(inlock);
+
+	goto Draining;
+}
+
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	MMRESULT status;
+	WAVEHDR *hHdr = (WAVEHDR *) NULL;
+	char *hData = NULL;
+	char *p = (char *) va;
+	long ba;
+	long bufsz;
+	long chunk;
+	long total;
+
+	qlock(&flag_lock);
+	if(waserror()){
+		qunlock(&flag_lock);
+		nexterror();
+	}
+
+	if(!(audio_flags & OUTISOPEN))
+		error(Eperm);
+
+	/* check for block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz < 1)
+		error(Ebadarg);
+
+	total = 0;
+
+	while(total < count) {
+
+Again:
+	chunk = min(bufsz, count - total);
+
+Drain:
+	WaitForSingleObject(outlock, INFINITE);
+	while(out_buf_count > bufsz) {
+		ReleaseMutex(outlock);
+		sleep(0);
+		goto Drain;
+	}
+
+	if(out_buf_count == 0)
+		audioerror(waveOutReset(audio_file_out), AUDIOOUT, "wave out reset");
+	ReleaseMutex(outlock);
+
+	/* 
+	 * allocate and lock the memory for the wave header 
+	 * and data blocks 
+	 */
+	hHdr = (WAVEHDR *) malloc(sizeof(WAVEHDR));
+	if (!hHdr)
+		error(Enomem);
+
+	hData = malloc(chunk);
+	if (!hData) {
+		free(hHdr);
+		error(Enomem);
+	}
+
+	/*
+	 * initialize the wave header struct
+	 */
+
+	/*
+	 * copy user data into write Q 
+	 */
+	memmove(hData, p+total, chunk);  
+
+	hHdr->lpData = hData;
+	hHdr->dwBufferLength = chunk; 
+	hHdr->dwBytesRecorded = 0; 
+	hHdr->dwUser = chunk;
+	hHdr->dwFlags = 0;
+	hHdr->dwLoops = 0;
+	hHdr->lpNext = 0;
+	hHdr->reserved = 0;
+
+	status = waveOutPrepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+	if (audioerror(status, AUDIOOUT, "out prepare header") == -1) {
+		free(hHdr);
+		free(hData);
+		error(Eio);
+	}
+
+	status =
+	waveOutWrite(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+	if (audioerror(status, AUDIOOUT, "out write data") == -1) {
+		waveOutUnprepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+		free(hHdr);
+		free(hData);
+		error(Eio);
+	}
+
+	WaitForSingleObject(outlock, INFINITE);
+	out_buf_count += chunk;
+	ReleaseMutex(outlock);
+
+	total += chunk;
+
+	}
+
+	poperror();
+	qunlock(&flag_lock);
+	osmillisleep(1);	/* hack to get around thread scheduler */
+
+	return count;
+}
+
+void CALLBACK
+waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+	LPWAVEHDR hHdr;
+	long count;
+
+	switch(uMsg) {
+	case WIM_OPEN:
+		break;
+	case WIM_CLOSE:
+		break;
+	case WIM_DATA:
+		hHdr = (LPWAVEHDR)dwParam1;
+		if(hHdr != NULL) {
+			count = hHdr->dwBytesRecorded;
+			if(count > 0) {
+				WaitForSingleObject(inlock, INFINITE);
+				if(hHdr->dwUser == Ping) 
+					audio_ping.sz = count;
+				else
+					audio_pong.sz = count;
+				ReleaseMutex(inlock);
+			}
+		}
+		break;
+	}
+	return;
+}
+
+
+void CALLBACK
+waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwOutstance, DWORD dwParam1, DWORD dwParam2)
+{
+	LPWAVEHDR hHdr;
+
+	switch(uMsg) {
+	case WOM_DONE:
+		hHdr = (LPWAVEHDR)dwParam1;
+		if(hHdr != NULL) {
+		WaitForSingleObject(outlock, INFINITE);
+		out_buf_count -= hHdr->dwUser;
+		ReleaseMutex(outlock);
+		audioerror(
+			waveOutUnprepareHeader(
+			audio_file_out, hHdr, sizeof(WAVEHDR)),
+			AUDIOOUT, "out un prepare header");
+		if(hHdr->lpData != NULL) 
+			free(hHdr->lpData);
+		free(hHdr);
+		}
+		break;
+	case WOM_CLOSE:
+		WaitForSingleObject(outlock, INFINITE);
+		out_buf_count = 0;
+		ReleaseMutex(outlock);
+		break;
+	case WOM_OPEN:
+		break;
+	}
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	WAVEFORMATEX format;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if(!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if((tmpav.in.enc != Audio_Pcm_Val) || (tmpav.out.enc != Audio_Pcm_Val))
+		error(Ebadarg);
+
+	if(tmpav.in.flags & AUDIO_MOD_FLAG) {
+		format.wFormatTag = tmpav.in.enc;
+		format.wBitsPerSample = tmpav.in.bits;
+		format.nChannels = tmpav.in.chan;
+		format.nSamplesPerSec = tmpav.in.rate;
+		format.nBlockAlign = 
+			(tmpav.in.chan * tmpav.in.bits) / Bits_Per_Byte;
+		format.nAvgBytesPerSec = 
+			format.nSamplesPerSec * format.nBlockAlign;
+		format.cbSize = 0;
+
+		if(audioerror(
+			waveInOpen(NULL, WAVE_MAPPER, &format, 0, 0,  WAVE_FORMAT_QUERY),
+			AUDIOIN, "cannot open microphone/line-in to test parameters") == -1)
+				error(Ebadarg);
+
+		qlock(&flag_lock);
+
+		if(waserror()){
+			qunlock(&flag_lock);
+			nexterror();
+		}
+
+		if(audio_flags & INISOPEN) {
+			audio_close_in();
+			audio_flags &= ~INISOPEN;
+			audio_flags &= ~INPUTISGOING;
+			if(!audio_open_in(&audio_file_in, &tmpav.in))
+				error(Eio);
+			audio_flags |= INISOPEN;
+		}
+		poperror();
+		qunlock(&flag_lock);
+	}
+
+	if(tmpav.out.flags & AUDIO_MOD_FLAG) {
+
+		format.wFormatTag = tmpav.out.enc;
+		format.wBitsPerSample = tmpav.out.bits;
+		format.nChannels = tmpav.out.chan;
+		format.nSamplesPerSec = tmpav.out.rate;
+		format.nBlockAlign = 
+			(tmpav.out.chan * tmpav.out.bits) / Bits_Per_Byte;
+		format.nAvgBytesPerSec = 
+			format.nSamplesPerSec * format.nBlockAlign;
+		format.cbSize = 0;
+
+		if (audioerror(waveOutOpen(NULL, WAVE_MAPPER, 
+			&format,
+			0, 0, WAVE_FORMAT_QUERY), 
+			AUDIOOUT, "cannot open output to test parameters") == -1)
+				error(Ebadarg);
+
+		qlock(&flag_lock);
+		if(waserror()){
+			qunlock(&flag_lock);
+			nexterror();
+		}
+		if(audio_flags & OUTISOPEN) {
+			audio_close_out(); 
+
+			audio_flags &= ~OUTISOPEN;
+			if(!audio_open_out(&audio_file_out, &tmpav.out)) {
+				error(Eio);
+				return -1;
+			}
+			audio_flags |= OUTISOPEN;
+		}
+		poperror();
+		qunlock(&flag_lock);
+	}
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/Nt/cmd.c
@@ -1,0 +1,243 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include	<windows.h>
+#include <winbase.h>
+#include	<winsock.h>
+#undef Unknown
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+extern int	nth2fd(HANDLE);
+extern wchar_t	*widen(char*);
+
+/*
+ * thanks to rcsh for these.
+ *
+ * windows quoting rules - I think
+ * Words are separated by space or tab
+ * Words containing a space or tab can be quoted using "
+ * 2N backslashes + " ==> N backslashes and end quote
+ * 2N+1 backslashes + " ==> N backslashes + literal "
+ * N backslashes not followed by " ==> N backslashes
+ */
+static char *
+dblquote(char *cmd, char *s)
+{
+	int nb;
+	char *p;
+
+	for(p=s; *p; p++)
+		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
+			break;
+
+	if(*p == 0){				/* easy case */
+		strcpy(cmd, s);
+		return cmd+(p-s);
+	}
+
+	*cmd++ = '"';
+	for(;;) {
+		for(nb=0; *s=='\\'; nb++)
+			*cmd++ = *s++;
+
+		if(*s == 0) {			/* trailing backslashes -> 2N */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			break;
+		}
+
+		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			*cmd++ = '\\';		/* escape the quote */
+		}
+		*cmd++ = *s++;
+	}
+
+	*cmd++ = '"';
+	*cmd = 0;
+
+	return cmd;
+}
+
+static char *
+ntquotedcmd(char **argv)
+{
+	int i, n;
+	char *cmd, *p;
+
+		/* conservatively calculate length of command;
+		 * backslash expansion can cause growth in dblquote().
+		 */
+	for(i=0,n=0; argv[i]; i++)
+		n += 2*strlen(argv[i]);
+	n++;
+	
+	cmd = malloc(n);
+	if(cmd == nil)
+		return nil;
+	for(i=0,p=cmd; argv[i]; i++) {
+		p = dblquote(p, argv[i]);
+		*p++ = ' ';
+	}
+	if(p != cmd)
+		p--;
+	*p = 0;
+
+	return cmd;
+}
+
+static HANDLE
+exporthandle(HANDLE h, int close)
+{
+	HANDLE cp, dh;
+	DWORD flags = DUPLICATE_SAME_ACCESS;
+	if (close)
+		flags |= DUPLICATE_CLOSE_SOURCE;
+	cp = GetCurrentProcess();
+	if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))
+		return nil;
+	return dh;
+}
+
+/* TO DO: check that oserrstr will have the right text on error */
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	STARTUPINFO si;
+	SECURITY_ATTRIBUTES sec;
+	HANDLE rh, wh, eh, srh, swh, seh;
+	PROCESS_INFORMATION pinfo;
+	char *cmd;
+	wchar_t *wcmd, *wdir;
+	int prio;
+
+	wdir = nil;
+	if(dir != nil)
+		wdir = widen(dir);
+
+	cmd = ntquotedcmd(args);
+	if(cmd == nil)
+		error(Enomem);
+
+	wcmd = widen(cmd);
+	sec.nLength = sizeof(sec);
+	sec.lpSecurityDescriptor = 0;
+	sec.bInheritHandle = 0;
+	rh = wh = eh = srh = swh = seh = nil;
+	if(!CreatePipe(&rh, &swh, &sec, 0))
+		goto Error;
+	if(!CreatePipe(&srh, &wh, &sec, 0))
+		goto Error;
+	if(!CreatePipe(&seh, &eh, &sec, 0))
+		goto Error;
+	rh = exporthandle(rh, 1);
+	if(rh == nil)
+		goto Error;
+	wh = exporthandle(wh, 1);
+	if(wh == nil)
+		goto Error;
+	eh = exporthandle(eh, 1);
+	if(eh == nil)
+		goto Error;
+
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+	si.wShowWindow = SW_SHOW;
+	si.hStdInput = rh;
+	si.hStdOutput = wh;
+	si.hStdError = eh;
+
+	prio = 0;
+	if(nice){
+		prio = IDLE_PRIORITY_CLASS;
+		if(nice > 1)
+			prio |= CREATE_SUSPENDED;
+	}
+
+	/* default of nil for wpath seems to be what we want; nil for env exports our current one */
+	if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,
+	   CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,
+	   0 /*env*/, wdir, &si, &pinfo)){
+		//print("can't create process '%Q' %d\n", wcmd, GetLastError());
+		goto Error;
+	}
+
+	fd[0] = nth2fd(swh);
+	fd[1] = nth2fd(srh);
+	fd[2] = nth2fd(seh);
+	if(fd[1] == 1 || fd[2] == 2)
+		panic("invalid mapping of handle to fd");
+	CloseHandle(si.hStdInput);
+	CloseHandle(si.hStdOutput);
+	CloseHandle(si.hStdError);
+
+	if(prio & CREATE_SUSPENDED){
+		if(nice > 1)
+			SetThreadPriority(pinfo.hThread,
+				nice>3? THREAD_PRIORITY_IDLE:
+				nice>2? THREAD_PRIORITY_LOWEST:
+				THREAD_PRIORITY_BELOW_NORMAL);
+		ResumeThread(pinfo.hThread);
+	}
+	CloseHandle(pinfo.hThread);
+	/* don't close process handle */
+	free(cmd);
+	free(wcmd);
+	free(wdir);
+	return pinfo.hProcess;
+
+Error:
+	if(rh)
+		CloseHandle(rh);
+	if(wh)
+		CloseHandle(wh);
+	if(eh)
+		CloseHandle(eh);
+	if(srh)
+		CloseHandle(srh);
+	if(swh)
+		CloseHandle(swh);
+	if(seh)
+		CloseHandle(seh);
+	free(cmd);
+	free(wcmd);
+	free(wdir);
+	return nil;
+}
+
+int
+oscmdwait(void *v, char *buf, int n)
+{
+	int status;
+	HANDLE proc = (HANDLE)v;
+
+	/* need not worry about being interrupted */
+	if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)
+		return -1;
+	if(!GetExitCodeProcess(proc, &status))
+		status = 1;
+	if(status)
+		n = snprint(buf, n, "0 0 0 0 'status %d'", status);
+	else
+		n = snprint(buf, n, "0 0 0 0 ''");
+	return n;
+
+}
+
+int
+oscmdkill(void *v)
+{
+	if(TerminateProcess((HANDLE)v, 666) == FALSE)
+		return -1;
+	return 0;
+}
+
+void
+oscmdfree(void *v)
+{
+	CloseHandle((HANDLE)v);
+}
--- /dev/null
+++ b/emu/Nt/devarch.c
@@ -1,0 +1,442 @@
+/*
+ *  platform-specific interface
+ */
+
+#define Unknown win_Unknown
+#define UNICODE
+#include	<windows.h>
+#include <winbase.h>
+#include	<winsock.h>
+#undef Unknown
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"r16.h"
+
+enum{
+	Qdir,
+	Qarchctl,
+	Qcputype,
+	Qregquery,
+	Qhostmem
+};
+
+static
+Dirtab archtab[]={
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"archctl",	{Qarchctl, 0},	0,	0444,
+	"cputype",	{Qcputype},	0,	0444,
+	"regquery",	{Qregquery}, 0,	0666,
+	"hostmem",	{Qhostmem},	0,	0444,
+};
+
+typedef struct Value Value;
+struct Value {
+	int	type;
+	int	size;
+	union {
+		ulong	w;
+		vlong	q;
+		char	data[1];	/* utf-8 */
+	};
+};
+
+typedef struct Regroot Regroot;
+struct Regroot {
+	char*	name;
+	HKEY	root;
+};
+
+static Regroot roots[] = {
+	{"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
+	{"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+	{"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
+	{"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+	{"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+	{"HKEY_USERS", HKEY_USERS},
+};
+
+static struct {
+	ulong	mhz;
+	int ncpu;
+	char	cpu[64];
+} arch;
+
+static	QLock	reglock;
+
+static	Value*	getregistry(HKEY, Rune16*, Rune16*);
+static int nprocs(void);
+
+static void
+archinit(void)
+{
+	Value *v;
+	char *p;
+
+	v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString");
+	if(v != nil){
+		snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+		if((p = strrchr(arch.cpu, ' ')) != nil)
+			for(; p >= arch.cpu && *p == ' '; p--)
+				*p = '\0';
+		free(v);
+	}else{
+		v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"VendorIdentifier");
+		if(v != nil){
+			snprint(arch.cpu, sizeof(arch.cpu), "%s", v->data);
+			free(v);
+		}else
+			snprint(arch.cpu, sizeof(arch.cpu), "unknown");
+	}
+	v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"~MHz");
+	if(v != nil){
+		arch.mhz = v->w;
+		free(v);
+	}
+	arch.ncpu = nprocs();
+}
+
+static int
+nprocs(void)
+{
+	int n;
+	char *p;
+	Rune16 *r;
+	Value *v;
+	n = 0;
+	for(;;){
+		p = smprint("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", n);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		r = widen(p);
+		free(p);
+		v = getregistry(HKEY_LOCAL_MACHINE, r, L"~MHz");
+		free(r);
+		if(v == nil)
+			break;
+		free(v);
+		n++;
+	}
+	return n;
+}
+
+static Chan*
+archattach(char* spec)
+{
+	return devattach('a', spec);
+}
+
+static Walkqid*
+archwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, archtab, nelem(archtab), devgen);
+}
+
+static int
+archstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, archtab, nelem(archtab), devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+	return devopen(c, omode, archtab, nelem(archtab), devgen);
+}
+
+static void
+archclose(Chan* c)
+{
+	if((ulong)c->qid.path == Qregquery && c->aux != nil)
+		free(c->aux);
+}
+
+static long
+archread(Chan* c, void* a, long n, vlong offset)
+{
+	char *p;
+	Value *v;
+	int i, l;
+	MEMORYSTATUS mem;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, archtab, nelem(archtab), devgen);
+	case Qarchctl:
+	case Qcputype:
+		l = 0;
+		if((ulong)c->qid.path == Qcputype)
+			l = 4;
+		p = smalloc(READSTR);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		snprint(p, READSTR, "cpu %q %lud %d\n", arch.cpu, arch.mhz, arch.ncpu);
+		n = readstr(offset, a, n, p+l);
+		poperror();
+		free(p);
+		break;
+	case Qregquery:
+		v = c->aux;
+		if(v == nil)
+			return 0;
+		p = smalloc(READSTR);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		switch(v->type){
+		case REG_NONE:
+			n = readstr(offset, a, n, "nil");
+			break;
+		case REG_DWORD:
+			snprint(p, READSTR, "int %ld", v->w);
+			n = readstr(offset, a, n, p);
+			break;
+#ifdef REG_QWORD
+		case REG_QWORD:
+			snprint(p, READSTR, "int %lld", v->q);
+			n = readstr(offset, a, n, p);
+			break;
+#endif
+		case REG_SZ:
+		case REG_EXPAND_SZ:
+			if(v->data[0])
+				snprint(p, READSTR, "str %q", v->data);
+			n = readstr(offset, a, n, p);
+			break;
+		case REG_MULTI_SZ:
+			l = snprint(p, READSTR, "str");
+			for(i=0;;){
+				l += snprint(p+l, READSTR-l, " %q", v->data+i);
+				while(v->data[i++] != 0){
+					/* skip */
+				}
+				if(v->data[i] == 0)
+					break;	/* final terminator */
+			}
+			n = readstr(offset, a, n, p);
+			break;
+		case REG_BINARY:
+			l = n;
+			n = readstr(offset, a, l, "bin");
+			if(n >= 3){
+				offset -= 3;
+				if(offset+l > v->size)
+					l = v->size - offset;
+				memmove((char*)a+n, v->data+offset, l);
+				n += l;
+			}
+			break;
+		default:
+			error("unknown registry type");
+			n=0;
+			break;
+		}
+		poperror();
+		free(p);
+		c->aux = nil;
+		free(v);
+		break;
+	case Qhostmem:
+		mem.dwLength = sizeof(mem);
+		GlobalMemoryStatus(&mem);	/* GlobalMemoryStatusEx isn't on NT */
+		p = smalloc(READSTR);
+		if(waserror()){
+			free(p);
+			nexterror();
+		}
+		snprint(p, READSTR, "load %ld\nphys %lud %lud\nvirt %lud %lud\nswap %lud %lud\n",
+			mem.dwMemoryLoad,
+			mem.dwAvailPhys, mem.dwTotalPhys, mem.dwAvailVirtual, mem.dwTotalVirtual,
+			mem.dwAvailPageFile, mem.dwTotalPageFile);
+		n = readstr(offset, a, n, p);
+		poperror();
+		free(p);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+archwrite(Chan* c, void* a, long n, vlong offset)
+{
+	Value *v;
+	int i;
+	Cmdbuf *cb;
+	Rune16 *key, *item;
+
+	if((ulong)c->qid.path != Qregquery)
+		error(Eperm);
+	USED(offset);
+	if(c->aux != nil){
+		free(c->aux);
+		c->aux = nil;
+	}
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	if(cb->nf < 3)
+		error(Ebadctl);
+	for(i=0; i<nelem(roots); i++)
+		if(strcmp(cb->f[0], roots[i].name) == 0)
+			break;
+	if(i >= nelem(roots))
+		errorf("unknown root: %s", cb->f[0]);
+	key = widen(cb->f[1]);
+	if(waserror()){
+		free(key);
+		nexterror();
+	}
+	item = widen(cb->f[2]);
+	if(waserror()){
+		free(item);
+		nexterror();
+	}
+	v = getregistry(roots[i].root, key, item);
+	if(v == nil)
+		error(up->env->errstr);
+	c->aux = v;
+	poperror();
+	free(item);
+	poperror();
+	free(key);
+	poperror();
+	free(cb);
+	return n;
+}
+
+Dev archdevtab = {
+	'a',
+	"arch",
+
+	archinit,
+	archattach,
+	archwalk,
+	archstat,
+	archopen,
+	devcreate,
+	archclose,
+	archread,
+	devbread,
+	archwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static void
+regerr(int rc)
+{
+	Rune16 err[64];
+	char emsg[sizeof(err)*UTFmax+1];
+
+	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+		0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		err, sizeof(err), 0);
+	runes16toutf(emsg, err, nelem(err));
+	error(emsg);
+}
+
+static Value*
+getregistry(HKEY root, Rune16 *keyname, Rune16 *name)
+{
+	long res;
+	HKEY key;
+	DWORD dtype, n;
+	int i, l, nb;
+	void* vp;
+	char *p;
+	Value *val;
+	Rune16 *rb;
+
+	qlock(&reglock);
+	if(waserror()){
+		qunlock(&reglock);
+		return nil;
+	}
+	res = RegOpenKey(root, keyname, &key);
+	if(res != ERROR_SUCCESS)
+		regerr(res);
+	if(waserror()){
+		RegCloseKey(key);
+		nexterror();
+	}
+	n = 0;
+	res = RegQueryValueEx(key, name, NULL, &dtype, NULL, &n);
+	if(res != ERROR_SUCCESS)
+		regerr(res);
+	nb = n;
+	if(dtype == REG_SZ || dtype == REG_EXPAND_SZ || dtype == REG_MULTI_SZ){
+		nb = n*UTFmax + 1;
+		rb = smalloc((n+2)*sizeof(Rune16));
+		memset(rb, 0, (n+2)*sizeof(Rune16));
+	}else
+		rb = nil;
+	if(waserror()){
+		free(rb);
+		nexterror();
+	}
+	val = smalloc(sizeof(Value)+nb);
+	if(waserror()){
+		free(val);
+		nexterror();
+	}
+	val->type = dtype;
+	val->size = n;
+	switch(dtype){
+	case REG_DWORD:
+		vp = &val->w;
+		break;
+#ifdef REG_QWORD
+	case REG_QWORD:
+		vp = &val->q;
+		break;
+#endif
+	case REG_SZ:
+	case REG_EXPAND_SZ:
+	case REG_MULTI_SZ:
+		vp = rb;
+		break;
+	case REG_BINARY:
+	case REG_NONE:
+		vp = val->data;
+		break;
+	default:
+		errorf("unsupported registry type: %d", dtype);
+		return nil;	/* for compiler */
+	}
+	res = RegQueryValueEx(key, name, NULL, NULL, vp, &n);
+	if(res != ERROR_SUCCESS)
+		regerr(res);
+	poperror();
+	if(rb != nil){
+		if(dtype == REG_MULTI_SZ){
+			p = val->data;
+			for(i=0;;){
+				l = runes16len(rb+i);
+				runes16toutf(p, rb+i, l);
+				i += l+1;
+				if(rb[i] == 0)
+					break;
+				p += strlen(p)+1;
+			}
+		}else
+			runes16toutf(val->data, rb, n);
+		free(rb);
+	}
+	poperror();
+	poperror();
+	RegCloseKey(key);
+	poperror();
+	qunlock(&reglock);
+	return val;
+}
--- /dev/null
+++ b/emu/Nt/deveia.c
@@ -1,0 +1,672 @@
+/*
+ *  Windows serial driver
+ *
+ * to do:
+ *	scan the registry for serial ports?
+ */
+
+#define Unknown win_Unknown
+#include	<windows.h>
+#undef Unknown
+#undef	Sleep
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<fcntl.h>
+#include	<stdio.h>
+#include	<lm.h>
+#include	<direct.h>
+
+// local fcts
+static void openport(int);
+static void wrctl(int, char*);
+static long rdstat(int, void*, long, ulong );
+
+enum
+{
+	Devchar = 't',
+
+	Ndataqid = 1,
+	Nctlqid,
+	Nstatqid,
+	Nqid = 3,		/* number of QIDs */
+
+	Maxctl = 128,
+
+	// in/out buffer sizes for comm port (NT requires an even number)
+	// set it to x* the max styx message rounded up to the
+	// nearest 4 byte value
+	CommBufSize = ((((8192+128)*2)+3) & ~3)
+};
+
+/*
+ *  Macros to manage QIDs
+ */
+#define NETTYPE(x)	((x)&0x0F)
+#define NETID(x)	((x)>>4)
+#define NETQID(i,t)	(((i)<<4)|(t))
+
+static Dirtab *eiadir;
+static int ndir;
+
+typedef struct Eia Eia;
+struct Eia {
+	Ref	r;
+	HANDLE      comfh;          //handle to open port
+	int		restore;       //flag to restore prev. states 
+	DCB		dcb;           //win32 device control block used for restore
+	int		id;            //index to host port name in sysdev
+};
+
+// the same timeouts are used for all ports
+// currently there is no Inferno interface to
+// change the timeouts.
+static COMMTIMEOUTS  timeouts;  
+                   
+// std win32 serial port names are COM1..COM4
+// however there can be more and they can be
+// named anything. we should be more flexible
+// pehaps adding a ctl command to allow you to
+// access any win32 comm port
+static char* sysdev[] = {
+	"COM1:",
+	"COM2:",
+	"COM3:",
+	"COM4:",
+	"COM5:",
+	"COM6:",
+	"COM7:",
+	"COM8:",
+	NULL
+};
+    
+static Eia *eia;
+
+typedef struct OptTable OptTable;
+struct OptTable {
+	char   *str;
+	DWORD  flag;
+};
+
+#define BAD ((DWORD)-1)
+
+// valid bit-per-byte sizes
+static OptTable size[] = {
+	{"5",	5},
+	{"6",	6},
+	{"7",	7},
+	{"8",	8},
+	{NULL,  BAD}
+};
+
+// valid stop bits
+static OptTable stopbits[] = {
+	{"1",    ONESTOPBIT},
+	{"1.5",  ONE5STOPBITS},
+	{"2",    TWOSTOPBITS},
+	{NULL,   BAD}
+};
+
+// valid parity settings
+static OptTable parity[] = {
+	{"o",    ODDPARITY},
+	{"e",    EVENPARITY},
+	{"s",    SPACEPARITY},
+	{"m",    MARKPARITY},
+	{"n",    NOPARITY},
+	{NULL,   NOPARITY}
+};
+
+
+static char *
+ftos(OptTable *tbl, DWORD flag)
+{
+	while(tbl->str && tbl->flag != flag)
+		tbl++;
+	if(tbl->str == 0)
+		return "unknown";
+	return tbl->str;
+}
+
+static DWORD
+stof(OptTable *tbl, char *str)
+{
+	while(tbl->str && strcmp(tbl->str, str) != 0)
+		tbl++;
+	return tbl->flag;
+}
+
+static void
+eiainit(void)
+{
+	int     i,x;
+	byte    ports;   //bitmask of active host ports
+	int     nports;  //number of active host ports
+	int     max;     //number of highest port
+	Dirtab *dp;
+
+	// setup the timeouts; choose carefully
+	timeouts.ReadIntervalTimeout = 2;
+	timeouts.ReadTotalTimeoutMultiplier = 0;
+	timeouts.ReadTotalTimeoutConstant = 200;
+	timeouts.WriteTotalTimeoutMultiplier = 0;
+	timeouts.WriteTotalTimeoutConstant = 400;
+
+	// check to see which ports exist by trying to open them
+	// keep results in a bitmask
+	ports = nports = max = 0;
+	for(i=0; (sysdev[i] != NULL) && (i<8); i++) {
+		HANDLE comfh = CreateFile(sysdev[i], 0, 0, NULL,	/* no security attrs */
+			OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+		if(comfh != INVALID_HANDLE_VALUE) {
+			ports |= 1<<i;   
+			CloseHandle(comfh);
+			nports++;
+			max = i;
+		}
+	}
+
+	if(nports == 0)
+		return;  //no ports
+
+	// allocate directory table and eia structure
+	// for each active port.
+	ndir = Nqid*nports+1;
+	dp = eiadir = malloc(ndir*sizeof(Dirtab));
+	if(dp == 0)
+		panic("eiainit");
+	eia = malloc(nports*sizeof(Eia));
+	if(eia == 0) {
+		free(dp);
+		panic("eiainit");
+	}
+
+	// fill in the directory table and initialize
+	// the eia structure.  skip inactive ports.
+	sprint(dp->name, ".");
+	dp->qid.path = 0;
+	dp->qid.type = QTDIR;
+	dp->perm = DMDIR|0555;
+	dp++;
+	x = 0;  // index in eia[]
+	for(i = 0; i <= max; i++) {
+		if( (ports & (1<<i)) == 0)
+			continue;  //port 'i' is not active
+		sprint(dp->name, "eia%d", i);
+		dp->qid.path = NETQID(x, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "eia%dctl", i);
+		dp->qid.path = NETQID(x, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "eia%dstatus", i);
+		dp->qid.path = NETQID(x, Nstatqid);
+		dp->perm = 0660;
+		dp++;
+		// init the eia structure
+		eia[x].restore = 0;
+		eia[x].id = i;
+		x++;
+	}
+}
+
+static Chan*
+eiaattach(char *spec)
+{
+	if(eiadir == nil)
+		error(Enodev);
+
+	return devattach(Devchar, spec);
+}
+
+static Walkqid*
+eiawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
+}
+
+static int
+eiastat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, eiadir, ndir, devgen);
+}
+
+static Chan*
+eiaopen(Chan *c, int mode)
+{
+	int port = NETID(c->qid.path);
+
+	c = devopen(c, mode, eiadir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Nctlqid:
+	case Ndataqid:
+	case Nstatqid:
+		if(incref(&eia[port].r) != 1)
+			break;
+		if(waserror()) {
+			decref(&eia[port].r);
+			nexterror();
+		}
+		openport(port);
+		poperror();
+		break;
+	}
+	return c;
+}
+
+static void
+eiaclose(Chan *c)
+{
+	int port = NETID(c->qid.path);
+
+	if((c->flag & COPEN) == 0)
+		return;
+
+	switch(NETTYPE(c->qid.path)) {
+	case Nctlqid:
+	case Ndataqid:
+	case Nstatqid:
+		if(decref(&eia[port].r) == 0) {
+			osenter();
+			CloseHandle(eia[port].comfh);
+			osleave();
+		}
+		break;
+	}
+
+}
+
+static long
+eiaread(Chan *c, void *buf, long n, vlong offset)
+{
+	DWORD cnt;
+	int port = NETID(c->qid.path);
+	BOOL good;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, eiadir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Ndataqid:
+		cnt = 0;
+		// if ReadFile timeouts and cnt==0 then just re-read
+		// this will give osleave() a chance to detect an
+		// interruption (i.e. killprog)
+		while(cnt==0) {
+  			osenter(); 
+			good = ReadFile(eia[port].comfh, buf, n, &cnt, NULL);
+			SleepEx(0,FALSE);  //allow another thread access to port
+			osleave();
+			if(!good)
+				oserror();
+		}
+		return cnt;
+	case Nctlqid:
+		return readnum(offset, buf, n, eia[port].id, NUMSIZE);
+	case Nstatqid:
+		return rdstat(port, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static long
+eiawrite(Chan *c, void *buf, long n, vlong offset)
+{
+	DWORD cnt;
+	char cmd[Maxctl];
+	int port = NETID(c->qid.path);
+	BOOL good;
+	uchar *data;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Ndataqid:
+		cnt = 0;
+		data = (uchar*)buf;
+		// if WriteFile times out (i.e. return true; cnt<n) then
+		// allow osleave() to check for an interrupt otherwise try
+		// to send the unsent data.
+		while(n>0) {
+	  		osenter(); 
+			good = WriteFile(eia[port].comfh, data, n, &cnt, NULL);
+			osleave(); 
+			if(!good)
+				oserror();
+			data += cnt;
+			n -= cnt;
+		}
+		return (data-(uchar*)buf);
+	case Nctlqid:
+		if(n >= sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		wrctl(port, cmd);
+		return n;
+	}
+	return 0;
+}
+
+static int
+eiawstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	int i;
+
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	if(NETTYPE(c->qid.path) == Nstatqid)
+		error(Eperm);
+
+	n = convM2D(dp, n, &d, nil);
+	i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
+	if(d.mode != ~0UL)
+		eiadir[i+1].perm = d.mode&0666;
+	return n;
+}
+
+Dev eiadevtab = {
+        Devchar,
+        "eia",
+
+        eiainit,
+        eiaattach,
+        eiawalk,
+        eiastat,
+        eiaopen,
+        devcreate,
+        eiaclose,
+        eiaread,
+        devbread,
+        eiawrite,
+        devbwrite,
+        devremove,
+        eiawstat
+};
+
+
+//
+// local functions
+//
+
+/*
+ * open the indicated comm port and then set 
+ * the default settings for the port.
+ */
+static void
+openport(int port)
+{
+	Eia* p = &eia[port];
+
+	// open the port
+	p->comfh = CreateFile(sysdev[p->id], 
+		GENERIC_READ|GENERIC_WRITE,     //open underlying port for rd/wr
+		0,	                            //comm port can't be shared
+		NULL,	                        //no security attrs
+		OPEN_EXISTING,                  //a must for comm port
+		FILE_ATTRIBUTE_NORMAL,          //nonoverlapped io
+		NULL);                          //another must for comm port
+
+	if(p->comfh == INVALID_HANDLE_VALUE)
+		oserror();
+	if(waserror()){
+		CloseHandle(p->comfh);
+		p->comfh = INVALID_HANDLE_VALUE;
+		nexterror();
+	}
+
+	// setup in/out buffers (NT requires an even number)
+	if(!SetupComm(p->comfh, CommBufSize, CommBufSize))
+		oserror();
+
+	// either use existing settings or set defaults
+	if(!p->restore) {
+		// set default settings
+		if(!GetCommState(p->comfh, &p->dcb))
+			oserror();
+		p->dcb.BaudRate = 9600;
+		p->dcb.ByteSize = 8;
+		p->dcb.fParity = 0;
+		p->dcb.Parity = NOPARITY;
+		p->dcb.StopBits = ONESTOPBIT;
+		p->dcb.fInX = 0;  //default to xoff
+		p->dcb.fOutX = 0;  
+		p->dcb.fAbortOnError = 1; //read/write abort on err
+	}
+
+	// set state and timeouts
+	if(!SetCommState(p->comfh, &p->dcb) ||
+	   !SetCommTimeouts(p->comfh, &timeouts))
+		oserror();
+	poperror();
+}
+
+/*
+ * Obtain status information on the com port.
+ */
+static long
+rdstat(int port, void *buf, long n, ulong offset)
+{
+	HANDLE comfh = eia[port].comfh;
+	char str[Maxctl];
+	char *s;
+	DCB dcb;
+	DWORD modemstatus;
+	DWORD porterr;
+	COMSTAT  portstat;
+	int frame, overrun, i;
+
+	// valid line control ids
+	static enum {
+		L_CTS, L_DSR, L_RING, L_DCD, L_DTR, L_RTS, L_MAX
+	};
+	int status[L_MAX];
+
+	// line control strings (should match above id's)
+	static char* lines[] = {
+		"cts", "dsr", "ring", "dcd", "dtr",	"rts", NULL
+	};
+
+
+	// get any error conditions; also clears error flag
+	// and enables io
+	if(!ClearCommError(comfh, &porterr, &portstat))
+		oserror();
+
+	// get comm port state
+	if(!GetCommState(comfh, &dcb))
+		oserror();
+
+	// get modem line information
+	if(!GetCommModemStatus(comfh, &modemstatus))
+		oserror();
+
+	// now set our local flags
+	status[L_CTS] = MS_CTS_ON & modemstatus;
+	status[L_DSR] = MS_DSR_ON & modemstatus;
+	status[L_RING] = MS_RING_ON & modemstatus;
+	status[L_DCD] = MS_RLSD_ON & modemstatus;
+	status[L_DTR] = FALSE;  //?? cand this work: dcb.fDtrControl;
+	status[L_RTS] = FALSE;  //??   dcb.fRtsControl;
+	frame = porterr & CE_FRAME;
+	overrun = porterr & CE_OVERRUN;
+
+	/* TO DO: mimic native eia driver's first line */
+
+	s = seprint(str, str+sizeof(str), "opens %d ferr %d oerr %d baud %d", 
+		    eia[port].r.ref-1, 
+			frame, 
+			overrun,
+		    dcb.BaudRate);
+
+	// add line settings
+	for(i=0; i < L_MAX; i++) 
+		if(status[i])
+			s = seprint(s, str+sizeof(str), " %s", lines[i]);
+	seprint(s, str+sizeof(str), "\n");
+	return readstr(offset, buf, n, str);
+}
+
+//
+// write on ctl file. modify the settings for
+// the underlying port.
+//
+static void
+wrctl(int port, char *cmd)
+{
+	DCB dcb;
+	int nf, n,  i;
+	char *f[16];
+	HANDLE comfh = eia[port].comfh;
+	DWORD  flag, opt;
+	BOOL   rslt;
+	int chg;
+
+	// get the current settings for the port
+	if(!GetCommState(comfh, &dcb))
+		oserror();
+
+	chg = 0;
+	nf = tokenize(cmd, f, nelem(f));
+	for(i = 0; i < nf; i++){
+		if(strcmp(f[i], "break") == 0){
+			if(!SetCommBreak(comfh))
+				oserror();
+			SleepEx((DWORD)300, FALSE);
+			if(!ClearCommBreak(comfh))
+				oserror();
+			continue;
+		}
+
+		n = atoi(f[i]+1);
+		switch(*f[i]) {
+		case 'B':
+		case 'b':	// set the baud rate
+			if(n < 110)
+				error(Ebadarg);
+			dcb.BaudRate = n;
+			chg = 1;
+			break;
+		case 'C':
+		case 'c':
+			/* dcd */
+			break;
+		case 'D':
+		case 'd':  // set DTR
+			opt = n ? SETDTR : CLRDTR;
+			if(!EscapeCommFunction(comfh, opt))
+				oserror();
+			break;
+		case 'E':
+		case 'e':
+			/* dsr */
+			break;
+		case 'F':
+		case 'f':	// flush any untransmitted data
+			if(!PurgeComm(comfh, PURGE_TXCLEAR)) 
+				oserror();
+			break;
+		case 'H':
+		case 'h':
+			/* hangup */
+			/* TO DO: close handle */
+			break;
+		case 'I':
+		case 'i':
+			/* fifo: nothing to do */
+			break;
+		case 'K':
+		case 'k':
+			/* send a break */
+			if(!SetCommBreak(comfh))
+				oserror();
+			SleepEx((DWORD)300, FALSE);
+			if(!ClearCommBreak(comfh))
+				oserror();
+			break;
+		case 'L':
+		case 'l':	// set bits per byte 
+			flag = stof(size, f[0]+1);
+			if(flag == BAD)
+				error(Ebadarg);
+			dcb.ByteSize = (BYTE)flag;
+			chg = 1;
+			break;
+		case 'M':
+		case 'm':	// set CTS (modem control)
+			dcb.fOutxCtsFlow = (n!=0);
+			chg = 1;
+			break;
+		case 'N':
+		case 'n':
+			/* don't block on output */
+			break;
+		case 'P':
+		case 'p':	// set parity -- even or odd
+			flag = stof(parity, f[0]+1);
+			if(flag==BAD)
+				error(Ebadarg);
+			dcb.Parity = (BYTE)flag;
+			chg = 1;
+			break;
+		case 'Q':
+		case 'q':
+			/* set i/o queue limits */
+			break;
+		case 'R':
+		case 'r':	// set RTS
+			opt = n ? SETRTS : CLRRTS;
+			if(!EscapeCommFunction(comfh, opt))
+				oserror();
+			break;
+		case 'S':
+		case 's':	// set stop bits -- valid: 1 or 2 (win32 allows 1.5??)
+			flag = stof(stopbits, f[0]+1);
+			if(flag==BAD)
+				error(Ebadarg);
+			dcb.StopBits = flag;
+			chg = 1;
+			break;
+		case 'T':
+		case 't':
+			break;
+		case 'W':
+		case 'w':
+			/* set uart timer */
+			break;
+		case 'X':
+		case 'x':	// xon/xoff
+			opt = n ? SETXON : SETXOFF;
+			if(!EscapeCommFunction(comfh, opt))
+				oserror();
+			break;
+		default:
+			/* ignore */
+			break;
+		}
+	}
+
+	if(!chg)
+		return;
+	// make the changes on the underlying port, but flush
+	// outgoing chars down the port before
+	osenter();
+	rslt = FlushFileBuffers(comfh);
+	if(rslt)
+		rslt = SetCommState(comfh, &dcb);
+	osleave();
+	if(!rslt)
+		oserror();
+	eia[port].restore = 1;
+	eia[port].dcb = dcb;
+}
--- /dev/null
+++ b/emu/Nt/devfs.c
@@ -1,0 +1,2375 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include	<windows.h>
+#include	<winbase.h>
+#undef Unknown
+#undef	Sleep
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"r16.h"
+#include	<lm.h>
+
+/* TODO: try using / in place of \ in path names */
+
+#ifndef SID_MAX_SUB_AUTHORITIES
+#define	SID_MAX_SUB_AUTHORITIES	15
+#endif
+
+enum
+{
+	MAX_SID		= sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
+	ACL_ROCK	= sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID),
+	SD_ROCK		= SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK,
+	MAXCOMP		= 128,
+};
+
+typedef struct	User	User;
+typedef struct  Gmem	Gmem;
+typedef	struct	Stat	Stat;
+typedef	struct Fsinfo	Fsinfo;
+typedef	WIN32_FIND_DATA	Fsdir;
+
+#ifndef INVALID_SET_FILE_POINTER
+#define	INVALID_SET_FILE_POINTER	((DWORD)-1)
+#endif
+
+struct Fsinfo
+{
+	int	uid;
+	int	gid;
+	int	mode;
+	int	fd;
+	vlong	offset;
+	QLock	oq;
+	char*	spec;
+	Rune16*	srv;
+	Cname*	name;	/* Windows' idea of the file name */
+	ushort	usesec;
+	ushort	checksec;
+	Fsdir*	de;	/* non-nil for saved entry from last dirread at offset */
+};
+#define	FS(c)	((Fsinfo*)(c)->aux)
+
+/*
+ * info about a user or group
+ * there are two ways to specify a user:
+ *	by sid, a unique identifier
+ *	by user and domain names
+ * this structure is used to convert between the two,
+ * as well as figure out which groups a users belongs to.
+ * the user information never gets thrown away,
+ * but the group information gets refreshed with each setid.
+ */
+struct User
+{
+	QLock	lk;		/* locks the gotgroup and group fields */
+	SID	*sid;
+	Rune16	*name;
+	Rune16	*dom;
+	int	type;		/* the type of sid, ie SidTypeUser, SidTypeAlias, ... */
+	int	gotgroup;	/* tried to add group */
+	Gmem	*group;		/* global and local groups to which this user or group belongs. */
+	User	*next;
+};
+
+struct Gmem
+{
+	User	*user;
+	Gmem	*next;
+};
+
+/*
+ * intermediate stat information
+ */
+struct Stat
+{
+	User	*owner;
+	User	*group;
+	ulong	mode;
+};
+
+/*
+ * some "well-known" sids
+ */
+static	SID	*creatorowner;
+static	SID	*creatorgroup;
+static	SID	*everyone;
+static	SID	*ntignore;
+static	SID	*ntroot;	/* user who is supposed to run emu as a server */
+
+/*
+ * all users we ever see end up in this table
+ * users are never deleted, but we should update
+ * group information for users sometime
+ */
+static struct
+{
+	QLock	lk;
+	User	*u;
+}users;
+
+/*
+ * conversion from inferno permission modes to nt access masks
+ * is this good enough?  this is what nt sets, except for NOMODE
+ */
+#define	NOMODE	(READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define	RMODE	(READ_CONTROL|SYNCHRONIZE\
+		|FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define	XMODE	(READ_CONTROL|SYNCHRONIZE\
+		|FILE_EXECUTE|FILE_READ_ATTRIBUTES)
+#define	WMODE	(DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
+		|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
+		|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)
+
+static	int
+modetomask[] =
+{
+	NOMODE,
+	XMODE,
+	WMODE,
+	WMODE|XMODE,
+	RMODE,
+	RMODE|XMODE,
+	RMODE|WMODE,
+	RMODE|WMODE|XMODE,
+};
+
+extern	DWORD	PlatformId;
+	char    rootdir[MAXROOT] = "\\inferno";
+	Rune16	rootname[] = L"inferno-server";
+static	Qid	rootqid;
+static	User	*fsnone;
+static	User	*fsuser;
+static	Rune16	*ntsrv;
+static	int	usesec;
+static	int	checksec;
+static	int	isserver;
+static	int	file_share_delete;
+static	uchar	isntfrog[256];
+
+static	void		fsremove(Chan*);
+
+	wchar_t	*widen(char *s);
+	char		*narrowen(wchar_t *ws);
+	int		widebytes(wchar_t *ws);
+
+static char Etoolong[] = "file name too long";
+
+extern	int		nth2fd(HANDLE);
+extern	HANDLE		ntfd2h(int);
+static	int		cnisroot(Cname*);
+static	int		fsisroot(Chan*);
+static	int		okelem(char*, int);
+static	int		fsexist(char*, Qid*);
+static	char*	fspath(Cname*, char*, char*, char*);
+static	Cname*	fswalkpath(Cname*, char*, int);
+static	char*	fslastelem(Cname*);
+static	long		fsdirread(Chan*, uchar*, int, vlong);
+static	ulong		fsqidpath(char*);
+static	int		fsomode(int);
+static	int		fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);
+static 	int		fsdirsize(WIN32_FIND_DATA*, char*, Chan*);
+static	void		fssettime(char*, long, long);
+static	long		unixtime(FILETIME);
+static	FILETIME	wintime(ulong);
+static	void		secinit(void);
+static	int		secstat(Dir*, char*, Rune16*);
+static	int		secsize(char*, Rune16*);
+static	void		seccheck(char*, ulong, Rune16*);
+static	int		sechasperm(char*, ulong, Rune16*);
+static	SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
+static	int		secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune16*);
+static	int		secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune16*);
+static	SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
+static	SID		*dupsid(SID*);
+static	int		ismembersid(Rune16*, User*, SID*);
+static	int		ismember(User*, User*);
+static	User		*sidtouser(Rune16*, SID*);
+static	User		*domnametouser(Rune16*, Rune16*, Rune16*);
+static	User		*nametouser(Rune16*, Rune16*);
+static	User		*unametouser(Rune16*, char*);
+static	void		addgroups(User*, int);
+static	User		*mkuser(SID*, int, Rune16*, Rune16*);
+static	Rune16		*domsrv(Rune16 *, Rune16[MAX_PATH]);
+static	Rune16		*filesrv(char*);
+static	int		fsacls(char*);
+static	User		*secuser(void);
+
+
+int
+winfilematch(char *path, WIN32_FIND_DATA *data)
+{
+	char *p;
+	wchar_t *wpath;
+	int r;
+
+	p = path+strlen(path);
+	while(p > path && p[-1] != '\\')
+		--p;
+	wpath = widen(p);
+	r = (data->cFileName[0] == '.' && runes16len(data->cFileName) == 1)
+			|| runes16cmp(data->cFileName, wpath) == 0;
+	free(wpath);
+	return r;
+}
+
+int
+winfileclash(char *path)
+{
+	HANDLE h;
+	WIN32_FIND_DATA data;
+	wchar_t *wpath;
+
+	wpath = widen(path);
+	h = FindFirstFile(wpath, &data);
+	free(wpath);
+	if (h != INVALID_HANDLE_VALUE) {
+		FindClose(h);
+		return !winfilematch(path, &data);
+	}
+	return 0;
+}
+
+
+/*
+ * this gets called to set up the environment when we switch users
+ */
+void
+setid(char *name, int owner)
+{
+	User *u;
+
+	if(owner && !iseve())
+		return;
+
+	kstrdup(&up->env->user, name);
+
+	if(!usesec)
+		return;
+
+	u = unametouser(ntsrv, up->env->user);
+	if(u == nil)
+		u = fsnone;
+	else {
+		qlock(&u->lk);
+		addgroups(u, 1);
+		qunlock(&u->lk);
+	}
+	if(u == nil)
+		panic("setid: user nil\n");
+
+	up->env->ui = u;
+}
+
+static void
+fsfree(Chan *c)
+{
+	cnameclose(FS(c)->name);
+	if(FS(c)->de != nil)
+		free(FS(c)->de);
+	free(FS(c));
+}
+
+void
+fsinit(void)
+{
+	int n, isvol;
+	ulong attr;
+	char *p, tmp[MAXROOT];
+	wchar_t *wp, *wpath, *last;
+	wchar_t wrootdir[MAXROOT];
+
+	isntfrog['/'] = 1;
+	isntfrog['\\'] = 1;
+	isntfrog[':'] = 1;
+	isntfrog['*'] = 1;
+	isntfrog['?'] = 1;
+	isntfrog['"'] = 1;
+	isntfrog['<'] = 1;
+	isntfrog['>'] = 1;
+
+	/*
+	 * vet the root
+	 */
+	strcpy(tmp, rootdir);
+	for(p = tmp; *p; p++)
+		if(*p == '/')
+			*p = '\\';
+	if(tmp[0] != 0 && tmp[1] == ':') {
+		if(tmp[2] == 0) {
+			tmp[2] = '\\';
+			tmp[3] = 0;
+		}
+		else if(tmp[2] != '\\') {
+			/* don't allow c:foo - only c:\foo */
+			panic("illegal root pathX");
+		}
+	}
+	wrootdir[0] = '\0';
+	wpath = widen(tmp);
+	for(wp = wpath; *wp; wp++) {
+		if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':'))
+			panic("illegal root path");
+	}
+	n = GetFullPathName(wpath, MAXROOT, wrootdir, &last);
+	free(wpath);	
+	runes16toutf(rootdir, wrootdir, MAXROOT);
+	if(n >= MAXROOT || n == 0)
+		panic("illegal root path");
+
+	/* get rid of trailing \ */
+	while(rootdir[n-1] == '\\') {
+		if(n <= 2) {
+			panic("illegal root path");
+		}
+		rootdir[--n] = '\0';
+	}
+
+	isvol = 0;
+	if(rootdir[1] == ':' && rootdir[2] == '\0')
+		isvol = 1;
+	else if(rootdir[0] == '\\' && rootdir[1] == '\\') {
+		p = strchr(&rootdir[2], '\\');
+		if(p == nil)
+			panic("inferno root can't be a server");
+		isvol = strchr(p+1, '\\') == nil;
+	}
+
+	if(strchr(rootdir, '\\') == nil)
+		strcat(rootdir, "\\.");
+	attr = GetFileAttributes(wrootdir);
+	if(attr == 0xFFFFFFFF)
+		panic("root path '%s' does not exist", narrowen(wrootdir));
+	rootqid.path = fsqidpath(rootdir);
+	if(attr & FILE_ATTRIBUTE_DIRECTORY)
+		rootqid.type |= QTDIR;
+	rootdir[n] = '\0';
+
+	rootqid.vers = time(0);
+
+	/*
+	 * set up for nt file security checking
+	 */
+	ntsrv = filesrv(rootdir);
+	usesec = PlatformId == VER_PLATFORM_WIN32_NT; 	/* true for NT and 2000 */
+	if(usesec){
+		file_share_delete = FILE_SHARE_DELETE;	/* sensible handling of shared files by delete and rename */
+		secinit();
+		if(!fsacls(rootdir))
+			usesec = 0;
+	}
+	checksec = usesec && isserver;
+}
+
+Chan*
+fsattach(char *spec)
+{
+	Chan *c;
+	static int devno;
+	static Lock l;
+	char *drive = (char *)spec;
+
+	if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0'))
+		error(Ebadspec);
+
+	c = devattach('U', spec);
+	lock(&l);
+	c->dev = devno++;
+	unlock(&l);
+	c->qid = rootqid;
+	c->aux = smalloc(sizeof(Fsinfo));
+	FS(c)->srv = ntsrv;
+	if(!emptystr(spec)) {
+		char *s = smalloc(strlen(spec)+1);
+		strcpy(s, spec);
+		FS(c)->spec = s;
+		FS(c)->srv = filesrv(spec);
+		if(usesec)
+			FS(c)->usesec = fsacls(spec);
+		FS(c)->checksec = FS(c)->usesec && isserver;
+		c->qid.path = fsqidpath(spec);
+		c->qid.type = QTDIR;
+		c->qid.vers = 0;
+	}else{
+		FS(c)->usesec = usesec;
+		FS(c)->checksec = checksec;
+	}
+	FS(c)->name = newcname("/");
+	return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int j, alloc;
+	Walkqid *wq;
+	char path[MAX_PATH], *p;
+	Cname *ph;
+	Cname *current, *next;
+
+	if(nname > 0)
+		isdir(c);
+
+	alloc = 0;
+	current = nil;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone != nil)
+			cclose(wq->clone);
+		cnameclose(current);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;
+		alloc = 1;
+	}
+	wq->clone = nc;
+	current = FS(c)->name;
+	if(current != nil)
+		incref(&current->r);
+	for(j = 0; j < nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			break;
+		}
+		if(!okelem(name[j], 0)){
+			if(j == 0)
+				error(Efilename);
+			break;
+		}
+		p = fspath(current, name[j], path, FS(c)->spec);
+		if(FS(c)->checksec) {
+			*p = '\0';
+			if(!sechasperm(path, XMODE, FS(c)->srv)){
+				if(j == 0)
+					error(Eperm);
+				break;
+			}
+			*p = '\\';
+		}
+
+		if(strcmp(name[j], "..") == 0) {
+			if(fsisroot(c))
+				nc->qid = rootqid;
+			else{
+				ph = fswalkpath(current, "..", 1);
+				if(cnisroot(ph)){
+					nc->qid = rootqid;
+					current = ph;
+					if(current != nil)
+						incref(&current->r);
+				}
+				else {
+					fspath(ph, 0, path, FS(c)->spec);
+					if(!fsexist(path, &nc->qid)){
+						cnameclose(ph);
+						if(j == 0)
+							error(Enonexist);
+						break;
+					}
+				}
+				next = fswalkpath(current, name[j], 1);
+				cnameclose(current);
+				current = next;
+				cnameclose(ph);
+			}
+		}
+		else{
+			if(!fsexist(path, &nc->qid)){
+				if(j == 0)
+					error(Enonexist);
+				break;
+			}
+			next = fswalkpath(current, name[j], 1);
+			cnameclose(current);
+			current = next;
+		}
+		wq->qid[wq->nqid++] = nc->qid;
+	}
+	poperror();
+	if(wq->nqid < nname){
+		cnameclose(current);
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else if(wq->clone){
+		nc->aux = smalloc(sizeof(Fsinfo));
+		nc->type = c->type;
+		FS(nc)->spec = FS(c)->spec;
+		FS(nc)->srv = FS(c)->srv;
+		FS(nc)->name = current;
+		FS(nc)->usesec = FS(c)->usesec;
+		FS(nc)->checksec = FS(c)->checksec;
+	}
+	return wq;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+	HANDLE h;
+	int m, isdir, aflag, cflag;
+	char path[MAX_PATH];
+	wchar_t *wpath;
+
+	isdir = c->qid.type & QTDIR;
+	if(isdir && mode != OREAD)
+		error(Eperm);
+	fspath(FS(c)->name, 0, path, FS(c)->spec);
+
+	if(FS(c)->checksec) {
+		switch(mode & (OTRUNC|3)) {
+		case OREAD:
+			seccheck(path, RMODE, FS(c)->srv);
+			break;
+		case OWRITE:
+		case OWRITE|OTRUNC:
+			seccheck(path, WMODE, FS(c)->srv);
+			break;
+		case ORDWR:
+		case ORDWR|OTRUNC:
+		case OREAD|OTRUNC:
+			seccheck(path, RMODE|WMODE, FS(c)->srv);
+			break;
+		case OEXEC:
+			seccheck(path, XMODE, FS(c)->srv);
+			break;
+		default:
+			error(Ebadarg);
+		}
+	}
+
+	c->mode = openmode(mode);
+	if(isdir)
+		FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+	else {
+		m = fsomode(mode & 3);
+		cflag = OPEN_EXISTING;
+		if(mode & OTRUNC)
+			cflag = TRUNCATE_EXISTING;
+		aflag = FILE_FLAG_RANDOM_ACCESS;
+		if(mode & ORCLOSE)
+			aflag |= FILE_FLAG_DELETE_ON_CLOSE;
+		if (winfileclash(path))
+			error(Eexist);
+		wpath = widen(path);
+		h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0);
+		free(wpath);
+		if(h == INVALID_HANDLE_VALUE)
+			oserror();
+		FS(c)->fd = nth2fd(h);
+	}
+
+	c->offset = 0;
+	FS(c)->offset = 0;
+	c->flag |= COPEN;
+	return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+	Stat st;
+	HANDLE h;
+	int m, aflag;
+	SECURITY_ATTRIBUTES sa;
+	SECURITY_DESCRIPTOR *sd;
+	BY_HANDLE_FILE_INFORMATION hi;
+	char *p, path[MAX_PATH], sdrock[SD_ROCK];
+	wchar_t *wpath;
+	ACL *acl;
+
+	if(!okelem(name, 1))
+		error(Efilename);
+
+	m = fsomode(mode & 3);
+	p = fspath(FS(c)->name, name, path, FS(c)->spec);
+	acl = (ACL*)smalloc(ACL_ROCK);
+	sd = nil;
+	if(FS(c)->usesec) {
+		*p = '\0';
+		sd = secsd(path, sdrock);
+		*p = '\\';
+		if(sd == nil){
+			free(acl);
+			oserror();
+		}
+		if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv)
+		|| !secsdstat(sd, &st, FS(c)->srv)){
+			if(sd != (void*)sdrock)
+				free(sd);
+			free(acl);
+			error(Eperm);
+		}
+		if(sd != (void*)sdrock)
+			free(sd);
+		if(perm & DMDIR)
+			st.mode = (perm & ~0777) | (st.mode & perm & 0777);
+		else
+			st.mode = (perm & ~0666) | (st.mode & perm & 0666);
+		st.owner = up->env->ui;
+		if(!isserver)
+			st.owner = fsuser;
+		sd = secmksd(sdrock, &st, acl, perm & DMDIR);
+		if(sd == nil){
+			free(acl);
+			oserror();
+		}
+	}
+	sa.nLength = sizeof(sa);
+	sa.lpSecurityDescriptor = sd;
+	sa.bInheritHandle = 0;
+
+	if(perm & DMDIR) {
+		if(mode != OREAD) {
+			free(acl);
+			error(Eisdir);
+		}
+		wpath = widen(path);
+		if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) {
+			free(wpath);
+			free(acl);
+			oserror();
+		}
+		free(wpath);
+		FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+	}
+	else {
+		aflag = 0;
+		if(mode & ORCLOSE)
+			aflag = FILE_FLAG_DELETE_ON_CLOSE;
+		if (winfileclash(path))
+			error(Eexist);
+		wpath = widen(path);
+		h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0);
+		free(wpath);
+		if(h == INVALID_HANDLE_VALUE) {
+			free(acl);
+			oserror();
+		}
+		FS(c)->fd = nth2fd(h);
+		c->qid.path = fsqidpath(path);
+		c->qid.type = 0;
+		c->qid.vers = 0;
+		if(GetFileInformationByHandle(h, &hi))
+			c->qid.vers = unixtime(hi.ftLastWriteTime);
+	}
+
+	c->mode = openmode(mode);
+	c->offset = 0;
+	FS(c)->offset = 0;
+	c->flag |= COPEN;
+	FS(c)->name = fswalkpath(FS(c)->name, name, 0);
+	free(acl);
+}
+
+void
+fsclose(Chan *c)
+{
+	HANDLE h;
+
+	if(c->flag & COPEN){
+		h = ntfd2h(FS(c)->fd);
+		if(h != INVALID_HANDLE_VALUE){
+			if(c->qid.type & QTDIR)
+				FindClose(h);
+			else
+				CloseHandle(h);
+		}
+	}
+	if(c->flag & CRCLOSE){
+		if(!waserror()){
+			fsremove(c);
+			poperror();
+		}
+		return;
+	}
+	fsfree(c);
+}
+
+/*
+ * 64-bit seeks, using SetFilePointer because SetFilePointerEx
+ * is not supported by NT
+ */
+static void
+fslseek(HANDLE h, vlong offset)
+{
+	LONG hi;
+
+	if(offset <= 0x7fffffff){
+		if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+			oserror();
+	}else{
+		hi = offset>>32;
+		if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
+		   GetLastError() != NO_ERROR)
+			oserror();
+	}
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+	DWORD n2;
+	HANDLE h;
+
+	qlock(&FS(c)->oq);
+	if(waserror()){
+		qunlock(&FS(c)->oq);
+		nexterror();
+	}
+	if(c->qid.type & QTDIR) {
+		n2 = fsdirread(c, va, n, offset);
+	}
+	else {
+		h = ntfd2h(FS(c)->fd);
+		if(FS(c)->offset != offset){
+			fslseek(h, offset);
+			FS(c)->offset = offset;
+		}
+		if(!ReadFile(h, va, n, &n2, NULL))
+			oserror();
+		FS(c)->offset += n2;
+	}
+	qunlock(&FS(c)->oq);
+	poperror();
+	return n2;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+	DWORD n2;
+	HANDLE h;
+
+	qlock(&FS(c)->oq);
+	if(waserror()){
+		qunlock(&FS(c)->oq);
+		nexterror();
+	}
+	h = ntfd2h(FS(c)->fd);
+	if(FS(c)->offset != offset){
+		fslseek(h, offset);
+		FS(c)->offset = offset;
+	}
+	if(!WriteFile(h, va, n, &n2, NULL))
+		oserror();
+	FS(c)->offset += n2;
+	qunlock(&FS(c)->oq);
+	poperror();
+	return n2;
+}
+
+int
+fsstat(Chan *c, uchar *buf, int n)
+{
+	WIN32_FIND_DATA data;
+	char path[MAX_PATH];
+	wchar_t *wpath;
+
+	/*
+	 * have to fake up a data for volumes like
+	 * c: and \\server\share since you can't FindFirstFile them
+	 */
+	if(fsisroot(c)){
+		strcpy(path, rootdir);
+		if(strchr(path, '\\') == nil)
+			strcat(path, "\\.");
+		wpath = widen(path);
+		data.dwFileAttributes = GetFileAttributes(wpath);
+		free(wpath);
+		if(data.dwFileAttributes == 0xffffffff)
+			oserror();
+		data.ftCreationTime =
+		data.ftLastAccessTime =
+		data.ftLastWriteTime = wintime(time(0));
+		data.nFileSizeHigh = 0;
+		data.nFileSizeLow = 0;
+		utftorunes16(data.cFileName, ".", MAX_PATH);
+	} else {
+		HANDLE h = INVALID_HANDLE_VALUE;
+
+		fspath(FS(c)->name, 0, path, FS(c)->spec);
+		if (c->flag & COPEN)
+			h = ntfd2h(FS(c)->fd);
+
+		if (h != INVALID_HANDLE_VALUE) {
+			BY_HANDLE_FILE_INFORMATION fi;
+			if (c->mode & OWRITE)
+				FlushFileBuffers(h);
+			if (!GetFileInformationByHandle(h, &fi))
+				oserror();
+			data.dwFileAttributes = fi.dwFileAttributes;
+			data.ftCreationTime = fi.ftCreationTime;
+			data.ftLastAccessTime = fi.ftLastAccessTime;
+			data.ftLastWriteTime = fi.ftLastWriteTime;;
+			data.nFileSizeHigh = fi.nFileSizeHigh;
+			data.nFileSizeLow = fi.nFileSizeLow;
+		} else {
+			wpath = widen(path);
+			h = FindFirstFile(wpath, &data);
+			free(wpath);
+			if(h == INVALID_HANDLE_VALUE)
+				oserror();
+			if (!winfilematch(path, &data)) {
+				FindClose(h);
+				error(Enonexist);
+			}
+			FindClose(h);
+		}
+		utftorunes16(data.cFileName, fslastelem(FS(c)->name), MAX_PATH);
+	}
+
+	return fsdirset(buf, n, &data, path, c, 0);
+}
+
+int
+fswstat(Chan *c, uchar *buf, int n)
+{
+	int wsd;
+	Dir dir;
+	Stat st;
+	Cname * volatile ph;
+	HANDLE h;
+	ulong attr;
+	User *ou, *gu;
+	WIN32_FIND_DATA data;
+	SECURITY_DESCRIPTOR *sd;
+	char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256];
+	wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH];
+	wchar_t *wpath;
+	int nmatch;
+
+	n = convM2D(buf, n, &dir, strs);
+	if(n == 0)
+		error(Eshortstat);
+
+	last = fspath(FS(c)->name, 0, path, FS(c)->spec);
+	utftorunes16(wspath, path, MAX_PATH);
+
+	if(fsisroot(c)){
+		if(dir.atime != ~0)
+			data.ftLastAccessTime = wintime(dir.atime);
+		if(dir.mtime != ~0)
+			data.ftLastWriteTime = wintime(dir.mtime);
+		utftorunes16(data.cFileName, ".", MAX_PATH);
+	}else{
+		h = FindFirstFile(wspath, &data);
+		if(h == INVALID_HANDLE_VALUE)
+			oserror();
+		if (!winfilematch(path, &data)) {
+			FindClose(h);
+			error(Enonexist);
+		}
+		FindClose(h);
+	}
+
+	wsd = 0;
+	ou = nil;
+	gu = nil;
+	if(FS(c)->usesec) {
+		if(FS(c)->checksec && up->env->ui == fsnone)
+			error(Eperm);
+
+		/*
+		 * find new owner and group
+		 */
+		if(!emptystr(dir.uid)){
+			ou = unametouser(FS(c)->srv, dir.uid);
+			if(ou == nil)
+				oserror();
+		}
+		if(!emptystr(dir.gid)){
+			gu = unametouser(FS(c)->srv, dir.gid);
+			if(gu == nil){
+				if(strcmp(dir.gid, "unknown") != 0
+				&& strcmp(dir.gid, "deleted") != 0)
+					oserror();
+				gu = ou;
+			}
+		}
+
+		/*
+		 * find old stat info
+		 */
+		sd = secsd(path, sdrock);
+		if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){
+			if(sd != nil && sd != (void*)sdrock)
+				free(sd);
+			oserror();
+		}
+		if(sd != (void*)sdrock)
+			free(sd);
+
+		/*
+		 * permission rules:
+		 * if none, can't do anything
+		 * chown => no way
+		 * chgrp => current owner or group, and in new group
+		 * mode/time => owner or in either group
+		 * rename => write in parent
+		 */
+		if(ou == nil)
+			ou = st.owner;
+		if(FS(c)->checksec && st.owner != ou)
+			error(Eperm);
+
+		if(gu == nil)
+			gu = st.group;
+		if(st.group != gu){
+			if(FS(c)->checksec
+			&&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu)
+			|| !ismember(up->env->ui, st.group)))
+				error(Eperm);
+			wsd = 1;
+		}
+
+		if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+		|| dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime
+		|| dir.mode != ~0 && st.mode != dir.mode){
+			if(FS(c)->checksec
+			&& !ismember(up->env->ui, ou)
+			&& !ismember(up->env->ui, gu)
+			&& !ismember(up->env->ui, st.group))
+				error(Eperm);
+			if(dir.mode != ~0 && st.mode != dir.mode)
+				wsd = 1;
+		}
+	}
+	wpath = widen(dir.name);
+	nmatch = runes16cmp(wpath, data.cFileName);
+	free(wpath);
+	if(!emptystr(dir.name) && nmatch != 0){
+		if(!okelem(dir.name, 1))
+			error(Efilename);
+		ph = fswalkpath(FS(c)->name, "..", 1);
+		if(waserror()){
+			cnameclose(ph);
+			nexterror();
+		}
+		ph = fswalkpath(ph, dir.name, 0);
+		fspath(ph, 0, newpath, FS(c)->spec);
+		utftorunes16(wsnewpath, newpath, MAX_PATH);
+		if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath))
+			error("file already exists");
+		if(fsisroot(c))
+			error(Eperm);
+		if(FS(c)->checksec){
+			*last = '\0';
+			seccheck(path, WMODE, FS(c)->srv);
+			*last = '\\';
+		}
+		poperror();
+		cnameclose(ph);
+	}
+
+	if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
+	|| dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime)
+		fssettime(path, dir.atime, dir.mtime);
+
+	attr = data.dwFileAttributes;
+	if(dir.mode & 0222)
+		attr &= ~FILE_ATTRIBUTE_READONLY;
+	else
+		attr |= FILE_ATTRIBUTE_READONLY;
+	if(!fsisroot(c)
+	&& attr != data.dwFileAttributes
+	&& (attr & FILE_ATTRIBUTE_READONLY))
+		SetFileAttributes(wspath, attr);
+	if(FS(c)->usesec && wsd){
+		ACL *acl = (ACL *) smalloc(ACL_ROCK);
+		st.owner = ou;
+		st.group = gu;
+		if(dir.mode != ~0)
+			st.mode = dir.mode;
+		sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+		if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){
+			free(acl);
+			oserror();
+		}
+		free(acl);
+	}
+
+	if(!fsisroot(c)
+	&& attr != data.dwFileAttributes
+	&& !(attr & FILE_ATTRIBUTE_READONLY))
+		SetFileAttributes(wspath, attr);
+
+	/* do last so path is valid throughout */
+	wpath = widen(dir.name);
+	nmatch = runes16cmp(wpath, data.cFileName);
+	free(wpath);
+	if(!emptystr(dir.name) && nmatch != 0) {
+		ph = fswalkpath(FS(c)->name, "..", 1);
+		if(waserror()){
+			cnameclose(ph);
+			nexterror();
+		}
+		ph = fswalkpath(ph, dir.name, 0);
+		fspath(ph, 0, newpath, FS(c)->spec);
+		utftorunes16(wsnewpath, newpath, MAX_PATH);
+		/*
+		 * can't rename if it is open: if this process has it open, close it temporarily.
+		 */
+		if(!file_share_delete && c->flag & COPEN){
+			h = ntfd2h(FS(c)->fd);
+			if(h != INVALID_HANDLE_VALUE)
+				CloseHandle(h);	/* woe betide it if ORCLOSE */
+			FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
+		}
+		if(!MoveFile(wspath, wsnewpath)) {
+			oserror();
+		} else if(!file_share_delete && c->flag & COPEN) {
+			int	aflag;
+			SECURITY_ATTRIBUTES sa;
+			
+			/* The move succeeded, so open new file to maintain handle */
+			sa.nLength = sizeof(sa);
+			sa.lpSecurityDescriptor = sd;
+			sa.bInheritHandle = 0;
+			if(c->flag & CRCLOSE)
+				aflag = FILE_FLAG_DELETE_ON_CLOSE;
+			h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0);
+			if(h == INVALID_HANDLE_VALUE)
+				oserror();
+			FS(c)->fd = nth2fd(h);
+		}
+		cnameclose(FS(c)->name);
+		poperror();
+		FS(c)->name = ph;
+	}
+	return n;
+}
+
+static void
+fsremove(Chan *c)
+{
+	int n;
+	char *p, path[MAX_PATH];
+	wchar_t wspath[MAX_PATH];
+
+	if(waserror()){
+		fsfree(c);
+		nexterror();
+	}
+	if(fsisroot(c))
+		error(Eperm);
+	p = fspath(FS(c)->name, 0, path, FS(c)->spec);
+	utftorunes16(wspath, path, MAX_PATH);
+	if(FS(c)->checksec){
+		*p = '\0';
+		seccheck(path, WMODE, FS(c)->srv);
+		*p = '\\';
+	}
+	if(c->qid.type & QTDIR)
+		n = RemoveDirectory(wspath);
+	else
+		n = DeleteFile(wspath);
+	if (!n) {
+		ulong attr, mode;
+		SECURITY_DESCRIPTOR *sd = nil;
+		char sdrock[SD_ROCK];
+		Stat st;
+		int secok;
+		attr = GetFileAttributes(wspath);
+		if(attr != 0xFFFFFFFF) {
+			if (FS(c)->usesec) {
+				sd = secsd(path, sdrock);
+				secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv);
+				if (secok) {
+					ACL *acl = (ACL *) smalloc(ACL_ROCK);
+					mode = st.mode;
+					st.mode |= 0660;
+					sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+					if(sd != nil) {
+						SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+					}
+					free(acl);
+					if(sd != nil && sd != (void*)sdrock)
+						free(sd);
+					sd = nil;
+				}
+			}
+			SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL);
+			if(c->qid.type & QTDIR)
+				n = RemoveDirectory(wspath);
+			else
+				n = DeleteFile(wspath);
+			if (!n) {
+				if (FS(c)->usesec && secok) {
+					ACL *acl = (ACL *) smalloc(ACL_ROCK);
+					st.mode =  mode;
+					sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
+					if(sd != nil) {
+						SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
+					}
+					free(acl);
+				}
+				SetFileAttributes(wspath, attr);
+				if(sd != nil && sd != (void*)sdrock)
+					free(sd);
+			}
+		}
+	}
+	if(!n)
+		oserror();
+	poperror();
+	fsfree(c);
+}
+
+/*
+ * check elem for illegal characters /\:*?"<>
+ * ... and relatives are also disallowed,
+ * since they specify grandparents, which we
+ * are not prepared to handle
+ */
+static int
+okelem(char *elem, int nodots)
+{
+	int c, dots;
+
+	dots = 0;
+	while((c = *(uchar*)elem) != 0){
+		if(isntfrog[c])
+			return 0;
+		if(c == '.' && dots >= 0)
+			dots++;
+		else
+			dots = -1;
+		elem++;
+	}
+	if(nodots)
+		return dots <= 0;
+	return dots <= 2;
+}
+
+static int
+cnisroot(Cname *c)
+{
+	return strcmp(c->s, "/") == 0;
+}
+
+static int
+fsisroot(Chan *c)
+{
+	return strcmp(FS(c)->name->s, "/") == 0;
+}
+
+static char*
+fspath(Cname *c, char *ext, char *path, char *spec)
+{
+	char *p, *last, *rootd;
+	int extlen = 0;
+
+	rootd = spec != nil ? spec : rootdir;
+	if(ext)
+		extlen = strlen(ext) + 1;
+	if(strlen(rootd) + extlen >= MAX_PATH)
+		error(Etoolong);
+	strcpy(path, rootd);
+	if(cnisroot(c)){
+		if(ext) {
+			strcat(path, "\\");
+			strcat(path, ext);
+		}
+	}else{
+		if(*c->s != '/') {
+			if(strlen(path) + 1 >= MAX_PATH)
+				error(Etoolong);
+			strcat(path, "\\");
+		}
+		if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH)
+			error(Etoolong);
+		strcat(path, c->s);
+		if(ext){
+			strcat(path, "\\");
+			strcat(path, ext);
+		}
+	}
+	last = path;
+	for(p = path; *p != '\0'; p++){
+		if(*p == '/' || *p == '\\'){
+			*p = '\\';
+			last = p;
+		}
+	}
+	return last;
+}
+
+extern void cleancname(Cname*);
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+	if(dup)
+		c = newcname(c->s);
+	c = addelem(c, name);
+	if(isdotdot(name))
+		cleancname(c);
+	return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+	char *p;
+
+	p = c->s + c->len;
+	while(p > c->s && p[-1] != '/')
+		p--;
+	return p;
+}
+
+static int
+fsdirbadentry(WIN32_FIND_DATA *data)
+{
+	wchar_t *s;
+
+	s = data->cFileName;
+	if(s[0] == 0)
+		return 1;
+	if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
+		return 1;
+
+	return 0;
+}
+
+static Fsdir*
+fsdirent(Chan *c, char *path, Fsdir *data)
+{
+	wchar_t *wpath;
+	HANDLE h;
+
+	h = ntfd2h(FS(c)->fd);
+	if(data == nil)
+		data = smalloc(sizeof(*data));
+	if(FS(c)->offset == 0){
+		if(h != INVALID_HANDLE_VALUE)
+			FindClose(h);
+		wpath = widen(path);
+		h = FindFirstFile(wpath, data);
+		free(wpath);
+		FS(c)->fd = nth2fd(h);
+		if(h == INVALID_HANDLE_VALUE){
+			free(data);
+			return nil;
+		}
+		if(!fsdirbadentry(data))
+			return data;
+	}
+	do{
+		if(!FindNextFile(h, data)){
+			free(data);
+			return nil;
+		}
+	}while(fsdirbadentry(data));
+	return data;
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+	int i, r;
+	char path[MAX_PATH], *p;
+	Fsdir *de;
+	vlong o;
+
+	if(count == 0 || offset < 0)
+		return 0;
+	p = fspath(FS(c)->name, "*.*", path, FS(c)->spec);
+	p++;
+	de = nil;
+	if(FS(c)->offset != offset){
+		de = FS(c)->de;
+		if(FS(c)->de != nil){
+			free(FS(c)->de);
+			FS(c)->de = nil;
+		}
+		FS(c)->offset = 0;
+		for(o = 0; o < offset;){
+			de = fsdirent(c, path, de);
+			if(de == nil){
+				FS(c)->offset = o;
+				return 0;
+			}
+			runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+			path[MAX_PATH-1] = '\0';
+			o += fsdirsize(de, path, c);
+		}
+		FS(c)->offset = offset;
+	}
+	for(i = 0; i < count;){
+		if(FS(c)->de != nil){	/* left over from previous read at offset */
+			de = FS(c)->de;
+			FS(c)->de = nil;
+		}else{
+			de = fsdirent(c, path, de);
+			if(de == nil)
+				break;
+		}
+		runes16toutf(p, de->cFileName, &path[MAX_PATH]-p);
+		path[MAX_PATH-1] = '\0';
+		r = fsdirset(va+i, count-i, de, path, c, 1);
+		if(r <= 0){
+			/* won't fit; save for next read at this offset */
+			FS(c)->de = de;
+			break;
+		}
+		i += r;
+		FS(c)->offset += r;
+	}
+	return i;
+}
+
+static ulong
+fsqidpath(char *p)
+{
+	ulong h;
+	int c;
+
+	h = 0;
+	while(*p != '\0'){
+		/* force case insensitive file names */
+		c = *p++;
+		if(c >= 'A' && c <= 'Z')
+			c += 'a'-'A';
+		h = h * 19 ^ c;
+	}
+	return h;
+}
+
+/* TO DO: substitute fixed, made-up (unlikely) names for these */
+static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil };
+
+static int
+devfile(char *p)
+{
+	char *s, *t, *u, **ss;
+
+	if((u = strrchr(p, '\\')) != nil)
+		u++;
+	else if((u = strrchr(p, '/')) != nil)
+		u++;
+	else
+		u = p;
+	for(ss = devf; *ss != nil; ss++){
+		for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++)
+			if(*s != *t && *s != *t+'a'-'A')
+				break;
+		if(*s == '\0' && (*t == '\0' || *t == '.'))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * there are other ways to figure out
+ * the attributes and times for a file.
+ * perhaps they are faster
+ */
+static int
+fsexist(char *p, Qid *q)
+{
+	HANDLE h;
+	WIN32_FIND_DATA data;
+	wchar_t *wpath;
+
+	if(devfile(p))
+		return 0;
+	wpath = widen(p);
+	h = FindFirstFile(wpath, &data);
+	free(wpath);
+	if(h == INVALID_HANDLE_VALUE)
+		return 0;
+			if (!winfilematch(p, &data)) {
+				FindClose(h);
+				return 0;
+			}
+	FindClose(h);
+
+	q->path = fsqidpath(p);
+	q->type = 0;
+
+	if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		q->type |= QTDIR;
+
+	q->vers = unixtime(data.ftLastWriteTime);
+
+	return 1;
+}
+
+static int
+fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
+{
+	Dir dir;
+	static char neveryone[] = "Everyone";
+
+	dir.name = narrowen(data->cFileName);
+	dir.muid = nil;
+	dir.qid.path = fsqidpath(path);
+	dir.qid.vers = 0;
+	dir.qid.type = 0;
+	dir.mode = 0;
+	dir.atime = unixtime(data->ftLastAccessTime);
+	dir.mtime = unixtime(data->ftLastWriteTime);
+	dir.qid.vers = dir.mtime;
+	dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32));
+	dir.type = 'U';
+	dir.dev = c->dev;
+
+	if(!FS(c)->usesec){
+		/* no NT security so make something up */
+		dir.uid = neveryone;
+		dir.gid = neveryone;
+		dir.mode = 0777;
+	}else if(!secstat(&dir, path, FS(c)->srv))
+		oserror();
+
+	if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+		dir.mode &= ~0222;
+	if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
+		dir.qid.type |= QTDIR;
+		dir.mode |= DMDIR;
+		dir.length = 0;
+	}
+
+	if(isdir && sizeD2M(&dir) > n)
+		n = -1;
+	else 
+		n = convD2M(&dir, edir, n);
+	if(dir.uid != neveryone)
+		free(dir.uid);
+	if(dir.gid != neveryone)
+		free(dir.gid);
+	free(dir.name);
+	return n;
+}
+
+static int
+fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c)
+{
+	int i, n;
+
+	n = widebytes(data->cFileName);
+	if(!FS(c)->usesec)
+		n += 8+8;
+	else{
+		i = secsize(path, FS(c)->srv);
+		if(i < 0)
+			oserror();
+		n += i;
+	}
+	return STATFIXLEN+n;
+}
+
+static void
+fssettime(char *path, long at, long mt)
+{
+	HANDLE h;
+	FILETIME atime, mtime;
+	wchar_t *wpath;
+
+	wpath = widen(path);
+	h = CreateFile(wpath, GENERIC_WRITE,
+		0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+	free(wpath);
+	if(h == INVALID_HANDLE_VALUE)
+		return;
+	mtime = wintime(mt);
+	atime = wintime(at);
+	if(!SetFileTime(h, 0, &atime, &mtime)){
+		CloseHandle(h);
+		oserror();
+	}
+	CloseHandle(h);
+}
+
+static int
+fsomode(int m)
+{
+	switch(m & 0x3) {
+	case OREAD:
+	case OEXEC:
+		return GENERIC_READ;
+	case OWRITE:
+		return GENERIC_WRITE;
+	case ORDWR:
+		return GENERIC_READ|GENERIC_WRITE;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+static long
+unixtime(FILETIME ft)
+{
+	vlong t;
+
+	t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
+	t -= (vlong)10000000*134774*24*60*60;
+
+	return (long)(t/10000000);
+}
+
+static FILETIME
+wintime(ulong t)
+{
+	FILETIME ft;
+	vlong vt;
+
+	vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;
+
+	ft.dwLowDateTime = vt;
+	ft.dwHighDateTime = vt>>32;
+
+	return ft;
+}
+
+/*
+ * the sec routines manage file permissions for nt.
+ * nt files have an associated security descriptor,
+ * which has in it an owner, a group,
+ * and a discretionary acces control list, or acl,
+ * which specifies the permissions for the file.
+ *
+ * the strategy for mapping between inferno owner,
+ * group, other, and mode and nt file security is:
+ *
+ *	inferno owner == nt file owner
+ *	inferno other == nt Everyone
+ *	inferno group == first non-owner,
+ *			non-Everyone user given in the acl,
+ *			or the owner if there is no such user.
+ * we examine the entire acl when check for permissions,
+ * but only report a subset.
+ *
+ * when we write an acl, we also give all permissions to
+ * the special user rootname, who is supposed to run emu in server mode.
+ */
+static void
+secinit(void)
+{
+	HANDLE token;
+	TOKEN_PRIVILEGES *priv;
+	char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
+	SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
+	SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
+	SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;
+
+	if(!AllocateAndInitializeSid(&id, 1,
+		SECURITY_CREATOR_OWNER_RID,
+		1, 2, 3, 4, 5, 6, 7, &creatorowner)
+	|| !AllocateAndInitializeSid(&id, 1,
+		SECURITY_CREATOR_GROUP_RID,
+		1, 2, 3, 4, 5, 6, 7, &creatorgroup)
+	|| !AllocateAndInitializeSid(&wid, 1,
+		SECURITY_WORLD_RID,
+		1, 2, 3, 4, 5, 6, 7, &everyone)
+	|| !AllocateAndInitializeSid(&ntid, 1,
+		0,
+		1, 2, 3, 4, 5, 6, 7, &ntignore))
+		panic("can't initialize well-known sids");
+
+	fsnone = sidtouser(ntsrv, everyone);
+	if(fsnone == nil)
+		panic("can't make none user");
+
+	/*
+	 * see if we are running as the emu server user
+	 * if so, set up SE_RESTORE_NAME privilege,
+	 * which allows setting the owner field in a security descriptor.
+	 * other interesting privileges are SE_TAKE_OWNERSHIP_NAME,
+	 * which enables changing the ownership of a file to yourself
+	 * regardless of the permissions on the file, SE_BACKUP_NAME,
+	 * which enables reading any files regardless of permission,
+	 * and SE_CHANGE_NOTIFY_NAME, which enables walking through
+	 * directories without X permission.
+	 * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing
+	 * and reading any file data, regardless of permission,
+	 * if the file is opened with FILE_BACKUP_SEMANTICS.
+	 */
+	isserver = 0;
+	fsuser = secuser();
+	if(fsuser == nil)
+		fsuser = fsnone;
+	else if(runes16cmp(fsuser->name, rootname) == 0
+	     && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){
+		priv = (TOKEN_PRIVILEGES*)privrock;
+		priv->PrivilegeCount = 1;
+		priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+		if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid)
+		&& AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
+			isserver = 1;
+		CloseHandle(token);
+	}
+}
+
+/*
+ * get the User for the executing process
+ */
+static User*
+secuser(void)
+{
+	DWORD need;
+	HANDLE token;
+	TOKEN_USER *tu;
+	char turock[sizeof(TOKEN_USER) + MAX_SID];
+
+	if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+		return nil;
+
+	tu = (TOKEN_USER*)turock;
+	if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){
+		CloseHandle(token);
+		return nil;
+	}
+	CloseHandle(token);
+	return sidtouser(nil, tu->User.Sid);
+}
+
+static int
+secstat(Dir *dir, char *file, Rune16 *srv)
+{
+	int ok, n;
+	Stat st;
+	char sdrock[SD_ROCK];
+	SECURITY_DESCRIPTOR *sd;
+
+	sd = secsd(file, sdrock);
+	if(sd == nil){
+		int e = GetLastError();
+		if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){
+			dir->uid = strdup("unknown");
+			dir->gid = strdup("unknown");
+			if(dir->uid == nil || dir->gid == nil){
+				free(dir->uid);
+				error(Enomem);	/* will change to use kstrdup */
+			}
+			dir->mode = 0;
+			return 1;
+		}
+		return 0;
+	}
+	ok = secsdstat(sd, &st, srv);
+	if(sd != (void*)sdrock)
+		free(sd);
+	if(ok){
+		dir->mode = st.mode;
+		n = rune16nlen(st.owner->name, runes16len(st.owner->name));
+		dir->uid = smalloc(n+1);
+		runes16toutf(dir->uid, st.owner->name, n+1);
+		n = rune16nlen(st.group->name, runes16len(st.group->name));
+		dir->gid = smalloc(n+1);
+		runes16toutf(dir->gid, st.group->name, n+1);
+	}
+	return ok;
+}
+
+static int
+secsize(char *file, Rune16 *srv)
+{
+	int ok;
+	Stat st;
+	char sdrock[SD_ROCK];
+	SECURITY_DESCRIPTOR *sd;
+
+	sd = secsd(file, sdrock);
+	if(sd == nil){
+		int e = GetLastError();
+		if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION)
+			return 7+7;
+		return -1;
+	}
+	ok = secsdstat(sd, &st, srv);
+	if(sd != (void*)sdrock)
+		free(sd);
+	if(ok)
+		return rune16nlen(st.owner->name, runes16len(st.owner->name))+rune16nlen(st.group->name, runes16len(st.group->name));
+	return -1;
+}
+
+/*
+ * verify that u had access to file
+ */
+static void
+seccheck(char *file, ulong access, Rune16 *srv)
+{
+	if(!sechasperm(file, access, srv))
+		error(Eperm);
+}
+
+static int
+sechasperm(char *file, ulong access, Rune16 *srv)
+{
+	int ok;
+	char sdrock[SD_ROCK];
+	SECURITY_DESCRIPTOR *sd;
+
+	/*
+	 * only really needs dacl info
+	 */
+	sd = secsd(file, sdrock);
+	if(sd == nil)
+		return 0;
+	ok = secsdhasperm(sd, access, srv);
+	if(sd != (void*)sdrock)
+		free(sd);
+	return ok;
+}
+
+static SECURITY_DESCRIPTOR*
+secsd(char *file, char sdrock[SD_ROCK])
+{
+	DWORD need;
+	SECURITY_DESCRIPTOR *sd;
+	char *path, pathrock[6];
+	wchar_t *wpath;
+
+	path = file;
+	if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){
+		path = pathrock;
+		strcpy(path, "?:\\.");
+		path[0] = file[0];
+	}
+	sd = (SECURITY_DESCRIPTOR*)sdrock;
+	need = 0;
+	wpath = widen(path);
+	if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) {
+		free(wpath);
+		return sd;
+	}
+	 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+		free(wpath);
+		return nil;
+	}
+	sd = malloc(need);
+	if(sd == nil) {
+		free(wpath);
+		error(Enomem);
+	}
+	if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) {
+		free(wpath);
+		return sd;
+	}
+	free(wpath);
+	free(sd);
+	return nil;
+}
+
+static int
+secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune16 *srv)
+{
+	ACL *acl;
+	BOOL hasacl, b;
+	ACE_HEADER *aceh;
+	User *owner, *group;
+	SID *sid, *osid, *gsid;
+	ACCESS_ALLOWED_ACE *ace;
+	int i, allow, deny, *p, m;
+	ACL_SIZE_INFORMATION size;
+
+	st->mode = 0;
+
+	osid = nil;
+	gsid = nil;
+	if(!GetSecurityDescriptorOwner(sd, &osid, &b)
+	|| !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+		return 0;
+
+	if(acl == 0)
+		size.AceCount = 0;
+	else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+		return 0;
+
+	/*
+	 * first pass through acl finds group
+	 */
+	for(i = 0; i < size.AceCount; i++){
+		if(!GetAce(acl, i, &aceh))
+			continue;
+		if(aceh->AceFlags & INHERIT_ONLY_ACE)
+			continue;
+
+		if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
+		&& aceh->AceType != ACCESS_DENIED_ACE_TYPE)
+			continue;
+
+		ace = (ACCESS_ALLOWED_ACE*)aceh;
+		sid = (SID*)&ace->SidStart;
+		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+			continue;
+
+		if(EqualSid(sid, everyone))
+			;
+		else if(EqualSid(sid, osid))
+			;
+		else if(EqualPrefixSid(sid, ntignore))
+			continue;		/* boring nt accounts */
+		else{
+			gsid = sid;
+			break;
+		}
+	}
+	if(gsid == nil)
+		gsid = osid;
+
+	owner = sidtouser(srv, osid);
+	if(owner == nil)
+		return 0;
+	group = sidtouser(srv, gsid);
+	if(group == nil)
+		return 0;
+
+	/* no acl means full access */
+	allow = 0;
+	if(acl == 0)
+		allow = 0777;
+	deny = 0;
+	for(i = 0; i < size.AceCount; i++){
+		if(!GetAce(acl, i, &aceh))
+			continue;
+		if(aceh->AceFlags & INHERIT_ONLY_ACE)
+			continue;
+
+		if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+			p = &allow;
+		else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+			p = &deny;
+		else
+			continue;
+
+		ace = (ACCESS_ALLOWED_ACE*)aceh;
+		sid = (SID*)&ace->SidStart;
+		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+			continue;
+
+		m = 0;
+		if(ace->Mask & FILE_EXECUTE)
+			m |= 1;
+		if(ace->Mask & FILE_WRITE_DATA)
+			m |= 2;
+		if(ace->Mask & FILE_READ_DATA)
+			m |= 4;
+
+		if(ismembersid(srv, owner, sid))
+			*p |= (m << 6) & ~(allow|deny) & 0700;
+		if(ismembersid(srv, group, sid))
+			*p |= (m << 3) & ~(allow|deny) & 0070;
+		if(EqualSid(everyone, sid))
+			*p |= m & ~(allow|deny) & 0007;
+	}
+
+	st->mode = allow & ~deny;
+	st->owner = owner;
+	st->group = group;
+	return 1;
+}
+
+static int
+secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune16 *srv)
+{
+	User *u;
+	ACL *acl;
+	BOOL hasacl, b;
+	ACE_HEADER *aceh;
+	SID *sid, *osid, *gsid;
+	int i, allow, deny, *p, m;
+	ACCESS_ALLOWED_ACE *ace;
+	ACL_SIZE_INFORMATION size;
+
+	u = up->env->ui;
+	allow = 0;
+	deny = 0;
+	osid = nil;
+	gsid = nil;
+	if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
+		return 0;
+
+	/* no acl means full access */
+	if(acl == 0)
+		return 1;
+	if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
+		return 0;
+	for(i = 0; i < size.AceCount; i++){
+		if(!GetAce(acl, i, &aceh))
+			continue;
+		if(aceh->AceFlags & INHERIT_ONLY_ACE)
+			continue;
+
+		if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
+			p = &allow;
+		else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
+			p = &deny;
+		else
+			continue;
+
+		ace = (ACCESS_ALLOWED_ACE*)aceh;
+		sid = (SID*)&ace->SidStart;
+		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
+			continue;
+
+		m = ace->Mask;
+
+		if(ismembersid(srv, u, sid))
+			*p |= m & ~(allow|deny);
+	}
+
+	allow &= ~deny;
+
+	return (allow & access) == access;
+}
+
+static SECURITY_DESCRIPTOR*
+secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir)
+{
+	int m;
+
+	ulong mode;
+	ACE_HEADER *aceh;
+	SECURITY_DESCRIPTOR *sd;
+
+	sd = (SECURITY_DESCRIPTOR*)sdrock;
+	if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION))
+		return nil;
+
+	mode = st->mode;
+	if(st->owner == st->group){
+		mode |= (mode >> 3) & 0070;
+		mode |= (mode << 3) & 0700;
+	}
+
+
+	m = modetomask[(mode>>6) & 7];
+	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid))
+		return nil;
+
+	if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner))
+		return nil;
+
+	m = modetomask[(mode>>3) & 7];
+	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid))
+		return nil;
+
+	m = modetomask[(mode>>0) & 7];
+	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone))
+		return nil;
+
+	if(isdir){
+		/* hack to add inherit flags */
+		if(!GetAce(dacl, 1, &aceh))
+			return nil;
+		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+		if(!GetAce(dacl, 2, &aceh))
+			return nil;
+		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+		if(!GetAce(dacl, 3, &aceh))
+			return nil;
+		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+	}
+
+	/*
+	 * allow server user to access any file
+	 */
+	if(isserver){
+		if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid))
+			return nil;
+		if(isdir){
+			if(!GetAce(dacl, 4, &aceh))
+				return nil;
+			aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
+		}
+	}
+
+	if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
+		return nil;
+	if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0))
+		return nil;
+//	if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0))
+//		return nil;
+	return sd;
+}
+
+/*
+ * the user manipulation routines
+ * just make it easier to deal with user identities
+ */
+static User*
+sidtouser(Rune16 *srv, SID *s)
+{
+	SID_NAME_USE type;
+	Rune16 aname[100], dname[100];
+	DWORD naname, ndname;
+	User *u;
+
+	qlock(&users.lk);
+	for(u = users.u; u != 0; u = u->next)
+		if(EqualSid(s, u->sid))
+			break;
+	qunlock(&users.lk);
+
+	if(u != 0)
+		return u;
+
+	naname = sizeof(aname);
+	ndname = sizeof(dname);
+
+	if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type))
+		return mkuser(s, SidTypeUnknown, L"unknown", L"unknown");
+	return mkuser(s, type, aname, dname);
+}
+
+static User*
+domnametouser(Rune16 *srv, Rune16 *name, Rune16 *dom)
+{
+	User *u;
+
+	qlock(&users.lk);
+	for(u = users.u; u != 0; u = u->next)
+		if(runes16cmp(name, u->name) == 0 && runes16cmp(dom, u->dom) == 0)
+			break;
+	qunlock(&users.lk);
+	if(u == 0)
+		u = nametouser(srv, name);
+	return u;
+}
+
+static User*
+nametouser(Rune16 *srv, Rune16 *name)
+{
+	char sidrock[MAX_SID];
+	SID *sid;
+	SID_NAME_USE type;
+	Rune16 dom[MAX_PATH];
+	DWORD nsid, ndom;
+
+	sid = (SID*)sidrock;
+	nsid = sizeof(sidrock);
+	ndom = sizeof(dom);
+	if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type))
+		return nil;
+
+	return mkuser(sid, type, name, dom);
+}
+
+/*
+ * this mapping could be cached
+ */
+static User*
+unametouser(Rune16 *srv, char *name)
+{
+	Rune16 rname[MAX_PATH];
+
+	utftorunes16(rname, name, MAX_PATH);
+	return nametouser(srv, rname);
+}
+
+/*
+ * make a user structure and add it to the global cache.
+ */
+static User*
+mkuser(SID *sid, int type, Rune16 *name, Rune16 *dom)
+{
+	User *u;
+
+	qlock(&users.lk);
+	for(u = users.u; u != 0; u = u->next){
+		if(EqualSid(sid, u->sid)){
+			qunlock(&users.lk);
+			return u;
+		}
+	}
+
+	switch(type) {
+	default:
+		break;
+	case SidTypeDeletedAccount:
+		name = L"deleted";
+		break;
+	case SidTypeInvalid:
+		name = L"invalid";
+		break;
+	case SidTypeUnknown:
+		name = L"unknown";
+		break;
+	}
+
+	u = malloc(sizeof(User));
+	if(u == nil){
+		qunlock(&users.lk);
+		return 0;
+	}
+	u->next = nil;
+	u->group = nil;
+	u->sid = dupsid(sid);
+	u->type = type;
+	u->name = nil;
+	if(name != nil)
+		u->name = runes16dup(name);
+	u->dom = nil;
+	if(dom != nil)
+		u->dom = runes16dup(dom);
+
+	u->next = users.u;
+	users.u = u;
+
+	qunlock(&users.lk);
+	return u;
+}
+
+/*
+ * check if u is a member of gsid,
+ * which might be a group.
+ */
+static int
+ismembersid(Rune16 *srv, User *u, SID *gsid)
+{
+	User *g;
+
+	if(EqualSid(u->sid, gsid))
+		return 1;
+
+	g = sidtouser(srv, gsid);
+	if(g == 0)
+		return 0;
+	return ismember(u, g);
+}
+
+static int
+ismember(User *u, User *g)
+{
+	Gmem *grps;
+
+	if(EqualSid(u->sid, g->sid))
+		return 1;
+
+	if(EqualSid(g->sid, everyone))
+		return 1;
+
+	qlock(&u->lk);
+	addgroups(u, 0);
+	for(grps = u->group; grps != 0; grps = grps->next){
+		if(EqualSid(grps->user->sid, g->sid)){
+			qunlock(&u->lk);
+			return 1;
+		}
+	}
+	qunlock(&u->lk);
+	return 0;
+}
+
+/*
+ * find out what groups a user belongs to.
+ * if force, throw out the old info and do it again.
+ *
+ * note that a global group is also know as a group,
+ * and a local group is also know as an alias.
+ * global groups can only contain users.
+ * local groups can contain global groups or users.
+ * this code finds all global groups to which a user belongs,
+ * and all the local groups to which the user or a global group
+ * containing the user belongs.
+ */
+static void
+addgroups(User *u, int force)
+{
+	LOCALGROUP_USERS_INFO_0 *loc;
+	GROUP_USERS_INFO_0 *grp;
+	DWORD i, n, rem;
+	User *gu;
+	Gmem *g, *next;
+	Rune16 *srv, srvrock[MAX_PATH];
+
+	if(force){
+		u->gotgroup = 0;
+		for(g = u->group; g != nil; g = next){
+			next = g->next;
+			free(g);
+		}
+		u->group = nil;
+	}
+	if(u->gotgroup)
+		return;
+	u->gotgroup = 1;
+
+	n = 0;
+	srv = domsrv(u->dom, srvrock);
+	i = NetUserGetGroups(srv, u->name, 0,
+		(BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem);
+	if(i == NERR_Success || i == ERROR_MORE_DATA){
+		for(i = 0; i < n; i++){
+			gu = domnametouser(srv, grp[i].grui0_name, u->dom);
+			if(gu == 0)
+				continue;
+			g = malloc(sizeof(Gmem));
+			if(g == nil)
+				error(Enomem);
+			g->user = gu;
+			g->next = u->group;
+			u->group = g;
+		}
+		NetApiBufferFree(grp);
+	}
+
+	n = 0;
+	i = NetUserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
+		(BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem);
+	if(i == NERR_Success || i == ERROR_MORE_DATA){
+		for(i = 0; i < n; i++){
+			gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
+			if(gu == NULL)
+				continue;
+			g = malloc(sizeof(Gmem));
+			if(g == nil)
+				error(Enomem);
+			g->user = gu;
+			g->next = u->group;
+			u->group = g;
+		}
+		NetApiBufferFree(loc);
+	}
+}
+
+static SID*
+dupsid(SID *sid)
+{
+	SID *nsid;
+	int n;
+
+	n = GetLengthSid(sid);
+	nsid = malloc(n);
+	if(nsid == nil || !CopySid(n, nsid, sid))
+		panic("can't copy sid");
+	return nsid;
+}
+
+/*
+ * return the name of the server machine for file
+ */
+static Rune16*
+filesrv(char *file)
+{
+	int n;
+	Rune16 *srv;
+	char *p, uni[MAX_PATH], mfile[MAX_PATH];
+	wchar_t vol[3];
+
+	strcpy(mfile, file);
+	/* assume file is a fully qualified name - X: or \\server */
+	if(file[1] == ':') {
+		vol[0] = file[0];
+		vol[1] = file[1];
+		vol[2] = 0;
+		if(GetDriveType(vol) != DRIVE_REMOTE)
+			return 0;
+		n = sizeof(uni);
+		if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
+			return nil;
+		runes16toutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH);
+		file = mfile;
+	}
+	file += 2;
+	p = strchr(file, '\\');
+	if(p == 0)
+		n = strlen(file);
+	else
+		n = p - file;
+	if(n >= MAX_PATH)
+		n = MAX_PATH-1;
+
+	memmove(uni, file, n);
+	uni[n] = '\0';
+
+	srv = malloc((n + 1) * sizeof(Rune16));
+	if(srv == nil)
+		panic("filesrv: no memory");
+	utftorunes16(srv, uni, n+1);
+	return srv;
+}
+
+/*
+ * does the file system support acls?
+ */
+static int
+fsacls(char *file)
+{
+	char *p;
+	DWORD flags;
+	char path[MAX_PATH];
+	wchar_t wpath[MAX_PATH];
+
+	/* assume file is a fully qualified name - X: or \\server */
+	if(file[1] == ':') {
+		path[0] = file[0];
+		path[1] = file[1];
+		path[2] = '\\';
+		path[3] = 0;
+	} else {
+		strcpy(path, file);
+		p = strchr(path+2, '\\');
+		if(p == 0)
+			return 0;
+		p = strchr(p+1, '\\');
+		if(p == 0)
+			strcat(path, "\\");
+		else
+			p[1] = 0;
+	}
+	utftorunes16(wpath, path, MAX_PATH);
+	if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0))
+		return 0;
+
+	return flags & FS_PERSISTENT_ACLS;
+}
+
+/*
+ * given a domain, find out the server to ask about its users.
+ * we just ask the local machine to do the translation,
+ * so it might fail sometimes.  in those cases, we don't
+ * trust the domain anyway, and vice versa, so it's not
+ * clear what benifit we would gain by getting the answer "right".
+ */
+static Rune16*
+domsrv(Rune16 *dom, Rune16 srv[MAX_PATH])
+{
+	Rune16 *psrv;
+	int n, r;
+
+	if(dom[0] == 0)
+		return nil;
+
+	r = NetGetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
+	if(r == NERR_Success) {
+		n = runes16len(psrv);
+		if(n >= MAX_PATH)
+			n = MAX_PATH-1;
+		memmove(srv, psrv, n*sizeof(Rune16));
+		srv[n] = 0;
+		NetApiBufferFree(psrv);
+		return srv;
+	}
+
+	return nil;
+}
+
+Dev fsdevtab = {
+	'U',
+	"fs",
+
+	fsinit,
+	fsattach,
+	fswalk,
+	fsstat,
+	fsopen,
+	fscreate,
+	fsclose,
+	fsread,
+	devbread,
+	fswrite,
+	devbwrite,
+	fsremove,
+	fswstat
+};
--- /dev/null
+++ b/emu/Nt/emu
@@ -1,0 +1,112 @@
+env
+	LDFLAGS=	-subsystem:console -entry:WinMainCRTStartup $LDFLAGS
+
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win
+
+	ip	ipif ipaux
+	eia
+	audio	audio
+	mem
+	arch
+	pointer
+	snarf
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	9
+
+link
+
+mod
+	sys
+	draw
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Nt/fp.c
@@ -1,0 +1,102 @@
+typedef	unsigned long	ulong;
+typedef	unsigned int	uint;
+typedef	unsigned short	ushort;
+typedef	unsigned char	uchar;
+typedef	signed char	schar;
+
+#include <float.h>
+#include "mathi.h"
+
+#define	NANEXP	(2047<<20)
+#define	NANMASK	(2047<<20)
+#define	NANSIGN	(1<<31)
+
+int	isInf(double, int);
+
+double
+NaN(void)
+{
+	union
+	{
+		double	d;
+		long	x[2];
+	} a;
+
+	a.x[1] = NANEXP;
+	a.x[0] = 1;
+	return a.d;
+}
+
+int
+isNaN(double d)
+{
+	union
+	{
+		double	d;
+		long	x[2];
+	} a;
+
+	a.d = d;
+	if((a.x[1] & NANMASK) != NANEXP)
+		return 0;
+	return !isInf(d, 0);
+}
+
+double
+Inf(int sign)
+{
+	union
+	{
+		double	d;
+		long	x[2];
+	} a;
+
+	a.x[1] = NANEXP;
+	a.x[0] = 0;
+	if(sign < 0)
+		a.x[1] |= NANSIGN;
+	return a.d;
+}
+
+int
+isInf(double d, int sign)
+{
+	union
+	{
+		double	d;
+		long	x[2];
+	} a;
+
+	a.d = d;
+	if(a.x[0] != 0)
+		return 0;
+	if(a.x[1] == NANEXP)
+		return sign >= 0;
+	if(a.x[1] == (NANEXP|NANSIGN))
+		return sign <= 0;
+	return 0;
+}
+
+ulong
+getfcr(void)
+{
+	return getFPcontrol();
+}
+
+void
+setfcr(ulong m)
+{
+	FPcontrol(m, ~0);
+}
+
+ulong
+getfsr(void)
+{
+	return getFPstatus();
+}
+
+void
+setfsr(ulong m)
+{
+	FPstatus(m, ~0);
+}
--- /dev/null
+++ b/emu/Nt/ie
@@ -1,0 +1,109 @@
+env
+	LDFLAGS=	-subsystem:windows $LDFLAGS
+	OSX=	ie-os
+
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	indir
+
+	draw	ie-win
+
+	ip	ipif ipaux
+	eia
+	audio	audio
+	mem
+	pointer
+#	arch
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	9
+
+link
+
+mod
+	sys
+	draw
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/root /
+	/root/dev	/
+	/root/fd	/
+	/root/prog	/
+	/root/prof	/
+	/root/net	/
+	/root/net.alt	/
+	/root/chan	/
+	/root/nvfs	/
+	/root/env	/
+	/root/dis	/
+	/root/dis/lib	/
+	/root/dis/install	/
+	/root/dis/install/archfs.dis	/dis/install/archfs.dis
+	/root/dis/install/arch.dis	/dis/install/arch.dis
+	/root/dis/gunzip.dis	/dis/gunzip.dis
+	/root/dis/lib/bufio.dis	/dis/lib/bufio.dis
+	/root/dis/lib/inflate.dis	/dis/lib/inflate.dis
+	/root/dis/lib/string.dis	/dis/lib/string.dis
+	/root/dis/lib/daytime.dis	/dis/lib/daytime.dis
+	/root/dis/lib/arg.dis	/dis/lib/arg.dis
+	/root/dis/lib/styx.dis	/dis/lib/styx.dis
--- /dev/null
+++ b/emu/Nt/ie-os.c
@@ -1,0 +1,791 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include	<windows.h>
+#include <winbase.h>
+#include	<winsock.h>
+#undef Unknown
+#include	<excpt.h>
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"r16.h"
+#include	"ieplugin.h"
+
+extern int	SYS_SLEEP = 2;
+extern int SOCK_SELECT = 3;
+#define	MAXSLEEPERS	1500
+
+extern Plugin*	plugin;
+extern void	newiop();
+extern int		sendiop();
+
+DWORD		PlatformId;
+static char*	path;
+static HANDLE	kbdh = INVALID_HANDLE_VALUE;
+static HANDLE	conh = INVALID_HANDLE_VALUE;
+static int		sleepers;
+
+static ulong erendezvous(void*, ulong);
+
+__declspec(thread)       Proc    *up;
+
+HANDLE	ntfd2h(int);
+int	nth2fd(HANDLE);
+char *hosttype = "Nt";
+
+static void
+pfree(Proc *p)
+{
+	Osenv *e;
+
+	lock(&procs.l);
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	e = p->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(e->user);
+	free(p->prog);
+	free(p);
+}
+
+void
+osblock(void)
+{
+	erendezvous(up, 0);
+}
+
+void
+osready(Proc *p)
+{
+	erendezvous(p, 0);
+}
+
+
+void
+pexit(char *msg, int t)
+{
+	pfree(up);
+	ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+	EXCEPTION_POINTERS ep;
+	ep.ExceptionRecord = rec;
+	ep.ContextRecord = context;
+	TrapHandler(&ep);
+	return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+	// install our own exception handler
+	// replacing all others installed on this thread
+	DWORD handler = (DWORD)Exhandler;
+	_asm {
+		mov eax,handler
+		push eax
+		mov eax,-1
+		push eax
+		mov fs:[0],esp
+	}
+		
+	up = p;
+	up->func(up->arg);
+	pexit("", 0);
+	// should never get here but tidy up anyway
+	_asm {
+		mov fs:[0],-1
+		add esp, 8
+	}
+	return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	DWORD h;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+
+	p = newproc();
+	if(p == nil)
+		panic("out of kernel processes");
+		
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->ui = up->env->ui;
+	kstrdup(&p->env->user, up->env->user);
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+	if(p->pid <= 0)
+		panic("ran out of kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+	if (p->syscall == SOCK_SELECT)
+		return;
+	p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+	if(p->syscall == SYS_SLEEP) {
+		QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+	}
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+	DWORD r;
+	char buf[1];
+
+	if(ReadFile(plugin->conin, buf, sizeof(buf), &r, 0) == FALSE)
+		panic("keyboard fail");
+	if (r == 0)
+		panic("keyboard EOF");
+
+	if(buf[0] == '\r')
+		buf[0] = '\n';
+	return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+	newiop();
+	IOP.op = Iquit;
+	sendiop();
+	ExitProcess(x);
+}
+
+struct ecodes {
+	DWORD	code;
+	char*	name;
+} ecodes[] = {
+	EXCEPTION_ACCESS_VIOLATION,		"Segmentation violation",
+	EXCEPTION_DATATYPE_MISALIGNMENT,	"Data Alignment",
+	EXCEPTION_BREAKPOINT,                	"Breakpoint",
+	EXCEPTION_SINGLE_STEP,               	"SingleStep",
+	EXCEPTION_ARRAY_BOUNDS_EXCEEDED,	"Array Bounds Check",
+	EXCEPTION_FLT_DENORMAL_OPERAND,		"Denormalized Float",
+	EXCEPTION_FLT_DIVIDE_BY_ZERO,		"Floating Point Divide by Zero",
+	EXCEPTION_FLT_INEXACT_RESULT,		"Inexact Floating Point",
+	EXCEPTION_FLT_INVALID_OPERATION,	"Invalid Floating Operation",
+	EXCEPTION_FLT_OVERFLOW,			"Floating Point Result Overflow",
+	EXCEPTION_FLT_STACK_CHECK,		"Floating Point Stack Check",
+	EXCEPTION_FLT_UNDERFLOW,		"Floating Point Result Underflow",
+	EXCEPTION_INT_DIVIDE_BY_ZERO,		"Divide by Zero",
+	EXCEPTION_INT_OVERFLOW,			"Integer Overflow",
+	EXCEPTION_PRIV_INSTRUCTION,		"Privileged Instruction",
+	EXCEPTION_IN_PAGE_ERROR,		"Page-in Error",
+	EXCEPTION_ILLEGAL_INSTRUCTION,		"Illegal Instruction",
+	EXCEPTION_NONCONTINUABLE_EXCEPTION,	"Non-Continuable Exception",
+	EXCEPTION_STACK_OVERFLOW,		"Stack Overflow",
+	EXCEPTION_INVALID_DISPOSITION,		"Invalid Disposition",
+	EXCEPTION_GUARD_PAGE,			"Guard Page Violation",
+	0,					nil
+};
+
+void
+dodisfault(void)
+{
+	disfault(nil, up->env->errstr);
+}
+
+typedef struct Ereg Ereg;
+struct Ereg {
+	Ereg *prev;
+	FARPROC handler;
+};
+
+void
+dumpex()
+{
+	Ereg *er;
+	int i;
+	_asm { mov eax,fs:[0] };
+	_asm { mov [er],eax };
+
+	i = 0;
+	while ((unsigned)er != ~0) {
+		print("handler %ux\n", er->handler);
+		i++;
+	er = er->prev;
+	}
+	print("EXCEPTION CHAIN LENGTH = %d\n", i);
+}
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+	int i;
+	char *name;
+	DWORD code;
+	// WORD pc;
+	char buf[ERRMAX];
+
+	code = ureg->ExceptionRecord->ExceptionCode;
+	// pc = ureg->ContextRecord->Eip;
+
+	name = nil;
+	for(i = 0; i < nelem(ecodes); i++) {
+		if(ecodes[i].code == code) {
+			name = ecodes[i].name;
+			break;
+		}
+	}
+
+	if(name == nil) {
+		snprint(buf, sizeof(buf), "Unrecognized Machine Trap (%.8lux)\n", code);
+		name = buf;
+	}
+/*
+	if(pc != 0) {
+		snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+		name = buf;
+	}
+*/
+	/* YUCK! */
+	strncpy(up->env->errstr, name, ERRMAX);
+	switch (code) {
+	case EXCEPTION_FLT_DENORMAL_OPERAND:
+	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+	case EXCEPTION_FLT_INEXACT_RESULT:
+	case EXCEPTION_FLT_INVALID_OPERATION:
+	case EXCEPTION_FLT_OVERFLOW:
+	case EXCEPTION_FLT_STACK_CHECK:
+	case EXCEPTION_FLT_UNDERFLOW:
+		/* clear exception flags and register stack */
+		_asm { fnclex };
+		ureg->ContextRecord->FloatSave.StatusWord = 0x0000;
+		ureg->ContextRecord->FloatSave.TagWord = 0xffff;
+	}
+	ureg->ContextRecord->Eip = (DWORD)dodisfault;
+	return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static	int	rebootok = 0;	/* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+	if(rebootok){
+		execvp(file, argv);
+		panic("reboot failure");
+	}else
+		error("reboot option not supported on this system");
+}
+
+void
+libinit(char *imod)
+{
+	WSADATA wasdat;
+//	DWORD lasterror, namelen;
+	OSVERSIONINFO os;
+//	char sys[64];
+
+	os.dwOSVersionInfoSize = sizeof(os);
+	if(!GetVersionEx(&os))
+		panic("can't get os version");
+	PlatformId = os.dwPlatformId;
+	if (PlatformId == VER_PLATFORM_WIN32_NT) {	/* true for NT and 2000 */
+		rebootok = 1;
+	} else {
+		rebootok = 0;
+	}
+
+	if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+		panic("invalid handle value or size");
+
+	if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+		panic("no winsock.dll");
+
+//	gethostname(sys, sizeof(sys));
+//	kstrdup(&ossysname, sys);
+	kstrdup(&ossysname, "plugin");
+
+//	if(sflag == 0)
+//		SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+	path = getenv("PATH");
+	if(path == nil)
+		path = ".";
+
+	up = newproc();
+	if(up == nil)
+		panic("cannot create kernel process");
+
+	kstrdup(&eve, "system");
+	emuinit(imod);
+}
+
+enum
+{
+	NHLOG	= 7,
+	NHASH	= (1<<NHLOG)
+};
+
+typedef struct Tag Tag;
+struct Tag
+{
+	void*	tag;
+	ulong	val;
+	HANDLE	pid;
+	Tag*	next;
+};
+
+static	Tag*	ht[NHASH];
+static	Tag*	ft;
+static	Lock	hlock;
+static	int	nsema;
+
+ulong
+erendezvous(void *tag, ulong value)
+{
+	int h;
+	ulong rval;
+	Tag *t, **l, *f;
+
+
+	h = (ulong)tag & (NHASH-1);
+
+	lock(&hlock);
+	l = &ht[h];
+	for(t = ht[h]; t; t = t->next) {
+		if(t->tag == tag) {
+			rval = t->val;
+			t->val = value;
+			t->tag = 0;
+			unlock(&hlock);
+			if(SetEvent(t->pid) == FALSE)
+				panic("Release failed\n");
+			return rval;		
+		}
+	}
+
+	t = ft;
+	if(t == 0) {
+		t = malloc(sizeof(Tag));
+		if(t == nil)
+			panic("rendezvous: no memory");
+		t->pid = CreateEvent(0, 0, 0, 0);
+	}
+	else
+		ft = t->next;
+
+	t->tag = tag;
+	t->val = value;
+	t->next = *l;
+	*l = t;
+	unlock(&hlock);
+
+	if(WaitForSingleObject(t->pid, INFINITE) != WAIT_OBJECT_0)
+		panic("WaitForSingleObject failed\n");
+
+	lock(&hlock);
+	rval = t->val;
+	for(f = *l; f; f = f->next) {
+		if(f == t) {
+			*l = f->next;
+			break;
+		}
+		l = &f->next;
+	}
+	t->next = ft;
+	ft = t;
+	unlock(&hlock);
+
+	return rval;
+}
+
+void
+FPsave(void *fptr)
+{
+	_asm {
+		mov	eax, fptr
+		fstenv	[eax]
+	}
+}
+
+void
+FPrestore(void *fptr)
+{
+	_asm {
+		mov	eax, fptr
+		fldenv	[eax]
+	}
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+	ulong lo, hi;
+
+	_asm {
+		mov	eax, a
+		mov	ecx, b
+		MUL	ecx
+		mov	lo, eax
+		mov	hi, edx
+	}
+	*high = hi;
+	return lo;
+}
+
+int
+close(int fd)
+{
+	if(fd != -1)
+		CloseHandle(ntfd2h(fd));
+	return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+	if(!ReadFile(ntfd2h(fd), buf, n, &n, NULL))
+		return -1;
+	return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+	if(fd == 1 || fd == 2){
+		int w;
+		if (plugin->conout == NULL)
+			return n;
+		if (!WriteFile(plugin->conout, buf, n, &w, NULL) || n != w)
+			abort();
+		return n;
+	}
+	if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+		return -1;
+	return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+	return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+	return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+	Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+	void *brk;
+
+	brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 	
+	if(brk == 0)
+		return (void*)-1;
+
+	return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+	ulong cpc;
+	_asm {
+		mov eax, dword ptr [ebp]
+		mov eax, dword ptr [eax+4]
+		mov dword ptr cpc, eax
+	}
+	return cpc;
+}
+
+/*
+ulong
+getpc(void *arg, ulong *narg)
+{
+	ulong *a = arg, *fp, pc;
+
+	if(a == nil){
+		*narg = 0;
+		return 0;
+	}
+	fp = a-2;
+	pc = fp[1];
+	fp = *(ulong**)fp;
+	if(fp == nil)
+		*narg = 0;
+	else
+		*narg = (ulong)(fp+2);
+	return pc;
+}
+*/
+	
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ *  days per month plus days/year
+ */
+static	int	dmsize[] =
+{
+	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static	int	ldmsize[] =
+{
+	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ *  return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+	/* a leap year is a multiple of 4, excluding centuries
+	 * that are not multiples of 400 */
+	if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+		return ldmsize;
+	else
+		return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+	long secs;
+	int i, *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < tm->wYear; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(tm->wYear);
+	for(i = 1; i < tm->wMonth; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	/*
+	 * secs in last month
+	 */
+	secs += (tm->wDay-1) * SEC2DAY;
+
+	/*
+	 * hours, minutes, seconds
+	 */
+	secs += tm->wHour * SEC2HOUR;
+	secs += tm->wMinute * SEC2MIN;
+	secs += tm->wSecond;
+
+	return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+	SYSTEMTIME tm;
+	vlong secs;
+
+	GetSystemTime(&tm);
+	secs = tm2sec(&tm);
+	return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+	return osusectime()*1000;	/* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	up->syscall = 1;
+	SleepEx(milsec, FALSE);
+	up->syscall = 0;
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	if (sleepers > MAXSLEEPERS)
+		return -1;
+	sleepers++;
+	up->syscall = SYS_SLEEP;
+	SleepEx(milsec, TRUE);
+	up->syscall = 0;
+	sleepers--;
+	return 0;
+}
+
+void
+osyield(void)
+{	
+	sleep(0);
+}
+
+void
+ospause(void)
+{
+      for(;;)
+              sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+	panic("open");
+	return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+	panic("creat");
+	return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+	panic("stat");
+	return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+	panic("chown");
+	return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+	panic("chmod");
+	return -1;
+}
+
+void
+link(char *path, char *next)
+{
+	panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/Nt/ie-win.c
@@ -1,0 +1,224 @@
+#define Unknown win_Unknown
+#include	<windows.h>
+#undef Unknown
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include	"keyboard.h"
+#include	"cursor.h"
+#include	"ieplugin.h"
+
+/*
+ * image channel descriptors - copied from draw.h as it clashes with windows.h on many things
+ */
+enum {
+	CRed = 0,
+	CGreen,
+	CBlue,
+	CGrey,
+	CAlpha,
+	CMap,
+	CIgnore,
+	NChan,
+};
+
+#define __DC(type, nbits)	((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b)	__DC(a,b)
+#define CHAN2(a,b,c,d)	(CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f)	(CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h)	(CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+	GREY1	= CHAN1(CGrey, 1),
+	GREY2	= CHAN1(CGrey, 2),
+	GREY4	= CHAN1(CGrey, 4),
+	GREY8	= CHAN1(CGrey, 8),
+	CMAP8	= CHAN1(CMap, 8),
+	RGB15	= CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+	RGB16	= CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+	RGB24	= CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+	RGBA32	= CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+	ARGB32	= CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8),	/* stupid VGAs */
+	XRGB32  = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+};
+
+extern ulong displaychan;
+
+extern void drawend(void);
+
+extern	int	chantodepth(ulong);
+extern	int	main(int argc, char **argv);
+static	void	dprint(char*, ...);
+static	DWORD WINAPI	winproc(LPVOID);
+
+static	HINSTANCE	inst;
+static	HINSTANCE	previnst;
+static	int		attached;
+static	ulong	*data;
+
+extern	DWORD	PlatformId;
+char*	gkscanid = "emu_win32vk";
+
+extern int cflag;
+Plugin *plugin = NULL;
+
+DWORD WINAPI
+pluginproc(LPVOID p)
+{
+	int x, y, b;
+
+	for (;;) {
+		WaitForSingleObject(plugin->dopop, INFINITE);
+		switch (POP.op) {
+		case Pgfxkey:
+			if(gkbdq != nil)
+				gkbdputc(gkbdq, POP.u.key);
+			break;
+		case Pmouse:
+			x = POP.u.m.x;
+			y = POP.u.m.y;
+			b = POP.u.m.b;
+			mousetrack(b, x, y, 0);
+			break;
+		}
+		SetEvent(plugin->popdone);
+	}
+}
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+	HANDLE sharedmem;
+	uint pid = _getpid();
+	char iname[16];
+	inst = winst;
+	previnst = wprevinst;
+	sprint(iname, "%uX", pid);
+	sharedmem = OpenFileMapping(FILE_MAP_WRITE, FALSE, iname);
+	if (sharedmem != NULL)
+		plugin = MapViewOfFile(sharedmem, FILE_MAP_WRITE, 0, 0, 0);
+	if (plugin != NULL) {
+		DWORD tid;
+		int i;
+		Xsize = plugin->Xsize;
+		Ysize = plugin->Ysize;
+		displaychan = plugin->cdesc;
+		cflag = plugin->cflag;
+		for (i = 0; i < PI_NCLOSE; i++)
+			CloseHandle(plugin->closehandles[i]);
+		CreateThread(0, 0, pluginproc, 0, 0, &tid);
+	
+		/* cmdline passed into WinMain does not contain name of executable.
+		 * The globals __argc and __argv to include this info - like UNIX
+		 */
+		main(__argc, __argv);
+		UnmapViewOfFile(plugin);
+		plugin = NULL;
+	}
+	if (sharedmem != NULL)
+		CloseHandle(sharedmem);
+	return 0;
+}
+
+static Lock ioplock;
+
+void
+newiop()
+{
+	lock(&ioplock);
+}
+
+int
+sendiop()
+{
+	int val;
+	SetEvent(plugin->doiop);
+	WaitForSingleObject(plugin->iopdone, INFINITE);
+	val = plugin->iop.val;
+	unlock(&ioplock);
+	return val;
+}
+
+void
+dprint(char *fmt, ...)
+{
+	va_list arg;
+	char buf[128];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+	va_end(arg);
+	OutputDebugString("inferno: ");
+	OutputDebugString(buf);
+}
+
+uchar*
+attachscreen(IRectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+	int k;
+
+	if (!attached) {
+		newiop();
+		IOP.op = Iattachscr;
+		if (sendiop() != 0)
+			return nil;
+		data = plugin->screen;
+		attached = 1;
+	}
+	r->min.x = 0;
+	r->min.y = 0;
+	r->max.x = Xsize;
+	r->max.y = Ysize;
+
+	if(displaychan == 0)
+		displaychan = CMAP8;
+	*chan = displaychan;
+
+	k = chantodepth(displaychan);
+	*d = k;
+	*width = (Xsize/4)*(k/8);
+	*softscreen = 1;
+	return (uchar*)data;
+}
+
+void
+flushmemscreen(IRectangle r)
+{
+	if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+		return;
+	newiop();
+	IOP.op = Iflushscr;
+	IOP.u.r = r;
+	sendiop();
+}
+
+void
+setpointer(int x, int y)
+{
+	USED(x); USED(y);
+	// TODO
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	USED(c);
+	// TODO
+}
+
+char*
+clipread(void)
+{
+	return nil;
+}
+
+int
+clipwrite(char *p)
+{
+	USED(p);
+	return -1;
+}
--- /dev/null
+++ b/emu/Nt/ieplugin.h
@@ -1,0 +1,75 @@
+typedef struct Pop Pop;
+typedef struct Iop Iop;
+typedef struct IPoint IPoint;
+typedef struct IRectangle IRectangle;
+typedef struct Plugin Plugin;
+
+enum {
+	// messages from Plugin to Inferno
+	Pgfxkey,
+	Pmouse,
+
+	// message from Inferno to Plugin
+	Iattachscr,
+	Iflushscr,
+	Isetcur,
+	Idrawcur,
+	Iquit,
+};
+
+struct Pop {
+	int	op;
+	union {
+		int key;			// Pgfxkey
+		struct {
+			int	x;
+			int	y;
+			int	b;
+			int	modify;
+		} m;				// Pmouse
+	} u;
+};
+
+struct IPoint
+{
+	LONG	x;
+	LONG	y;
+};
+
+struct IRectangle
+{
+	IPoint	min;
+	IPoint	max;
+};
+
+struct Iop {
+	int	op;
+	int	val;
+	union {
+		IRectangle	r;		// Iflushscr
+		// need additional support for Isetcur & Idrawcur
+	} u;
+};
+#define PI_NCLOSE	2
+
+struct Plugin {
+	LONG sz;				// size of this data struct (including screen mem)
+	HANDLE	conin;		// console input (from plugin) - never NULL
+	HANDLE	conout;		// console output (to plugin) - can be NULL
+	HANDLE	datain;		// #C data file for initialisation (HACK!)
+	HANDLE	dopop;		// new Pop available
+	HANDLE	popdone;		// acknowledgement of Pop
+	HANDLE	doiop;		// new Iop available
+	HANDLE	iopdone;		// acknowledgement of Iop
+	HANDLE	closehandles[PI_NCLOSE];
+	Pop pop;
+	Iop iop;
+	int Xsize;				// screen dimensions
+	int Ysize;
+	ULONG cdesc;			// display chans descriptor
+	int cflag;
+	ULONG screen[1];
+};
+
+#define IOP	(plugin->iop)
+#define POP	(plugin->pop)
--- /dev/null
+++ b/emu/Nt/ipif.c
@@ -1,0 +1,409 @@
+#define Unknown win_Unknown
+#include        <windows.h>
+#include        <winbase.h>
+#include        <sys/types.h>
+#include        <winsock.h>
+#undef Unknown
+#include        "dat.h"
+#include        "fns.h"
+#include        "ip.h"
+#include        "error.h"
+
+typedef int socklen_t;	/* Windows is leading edge as always */
+
+
+extern int SOCK_SELECT;
+
+char Enotv4[] = "address not IPv4";
+
+static void
+ipw6(uchar *a, ulong w)
+{
+	memmove(a, v4prefix, IPv4off);
+	memmove(a+IPv4off, &w, IPv4addrlen);
+}
+
+int
+so_socket(int type)
+{
+	int fd, one;
+
+	switch(type) {
+	default:
+		error("bad protocol type");
+	case S_TCP:
+		type = SOCK_STREAM;
+		break;
+	case S_UDP:
+		type = SOCK_DGRAM;
+		break;
+	}
+	fd = socket(AF_INET, type, 0);
+	if(fd < 0)
+		oserror();
+	if(type == SOCK_DGRAM){
+		one = 1;
+		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+	}else{
+		one = 1;
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+	}
+	return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+	uchar *h = hdr;
+
+
+	osenter();
+	if(hdr == 0)
+		r = send(sock, va, len, 0);
+	else {
+		memset(&sa, 0, sizeof(sa));
+		sin = (struct sockaddr_in*)&sa;
+		sin->sin_family = AF_INET;
+		switch(hdrlen){
+		case OUdphdrlenv4:
+			memmove(&sin->sin_addr, h,  4);
+			memmove(&sin->sin_port, h+8, 2);
+			break;
+		case OUdphdrlen:
+			v6tov4((uchar*)&sin->sin_addr, h);
+			memmove(&sin->sin_port, h+2*IPaddrlen, 2);	/* rport */
+			break;
+		default:
+			v6tov4((uchar*)&sin->sin_addr, h);
+			memmove(&sin->sin_port, h+3*IPaddrlen, 2);
+			break;
+		}
+		r = sendto(sock, va, len, 0, &sa, sizeof(sa));
+	}
+	osleave();
+	return r;
+}
+
+static int
+doselect(int sock)
+{
+	fd_set	waitr;
+	struct timeval seltime;
+
+	up->syscall = SOCK_SELECT;
+	FD_ZERO(&waitr);
+	FD_SET(sock, &waitr);
+	for(;;){
+		int nfds;
+		fd_set in, exc;
+
+		in = waitr;
+		exc = waitr;
+		seltime.tv_sec = 1;
+		seltime.tv_usec = 0L;
+		nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+		if(up->intwait) {
+			up->intwait = 0;
+			return -1;
+		}
+		if(nfds < 0) {
+			print("select error\n");
+			return 0;
+		}
+		if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+			return 0;
+		}
+	}
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	socklen_t l;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+	uchar h[Udphdrlen];
+
+	osenter();
+	if(doselect(sock) < 0) {
+		osleave();
+		return -1;
+	}
+	if(hdr == 0)
+		r = recv(sock, va, len, 0);
+	else {
+		sin = (struct sockaddr_in*)&sa;
+		l = sizeof(sa);
+		r = recvfrom(sock, va, len, 0, &sa, &l);
+		if(r >= 0) {
+			memset(h, 0, sizeof(h));
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				memmove(h, &sin->sin_addr, IPv4addrlen);
+				memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
+				break;
+			case OUdphdrlen:
+				v4tov6(h, (uchar*)&sin->sin_addr);
+				memmove(h+2*IPaddrlen, &sin->sin_port, 2);
+				break;
+			default:
+				v4tov6(h, (uchar*)&sin->sin_addr);
+				memmove(h+3*IPaddrlen, &sin->sin_port, 2);
+				break;
+			}
+
+			/* alas there's no way to get the local addr/port correctly.  Pretend. */
+			memset(&sa, 0, sizeof(sa));
+			getsockname(sock, &sa, &l);
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
+				memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
+				break;
+			case OUdphdrlen:
+				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+				memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
+				break;
+			default:
+				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+				v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr);	/* ifcaddr */
+				memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
+				break;
+			}
+			memmove(hdr, h, hdrlen);
+		}
+	}
+	osleave();
+	return r;
+}
+
+void
+so_close(int sock)
+{
+	closesocket(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+	int r;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	if(!isv4(raddr))
+		error(Enotv4);
+
+	memset(&sa, 0, sizeof(sa));
+	sin = (struct sockaddr_in*)&sa;
+	sin->sin_family = AF_INET;
+	hnputs(&sin->sin_port, rport);
+	memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
+
+	osenter();
+	r = connect(fd, &sa, sizeof(sa));
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+	socklen_t len;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	len = sizeof(sa);
+	if(getsockname(fd, &sa, &len) < 0)
+		oserror();
+
+	sin = (struct sockaddr_in*)&sa;
+	if(sin->sin_family != AF_INET || len != sizeof(*sin))
+		error(Enotv4);
+
+	ipw6(laddr, sin->sin_addr.s_addr);
+	*lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+	int r;
+
+	osenter();
+	r = listen(fd, 256);
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+	int nfd;
+	socklen_t len;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	sin = (struct sockaddr_in*)&sa;
+
+	len = sizeof(sa);
+	osenter();
+	if(doselect(fd) < 0) {
+		osleave();
+		return -1;
+	}
+	nfd = accept(fd, &sa, &len);
+	osleave();
+	if(nfd < 0)
+		oserror();
+
+	if(sin->sin_family != AF_INET || len != sizeof(*sin))
+		error(Enotv4);
+
+	ipw6(raddr, sin->sin_addr.s_addr);
+	*rport = nhgets(&sin->sin_port);
+	return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+	int i, one;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	sin = (struct sockaddr_in*)&sa;
+
+	one = 1;
+//	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+//		oserrstr(up->genbuf, sizeof(up->genbuf));
+//		print("setsockopt: %s", err);
+//	}
+
+	if(su) {
+		for(i = 600; i < 1024; i++) {
+			memset(&sa, 0, sizeof(sa));
+			sin->sin_family = AF_INET;
+			memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+			hnputs(&sin->sin_port, i);
+
+			if(bind(fd, &sa, sizeof(sa)) >= 0)	
+				return;
+		}
+		oserror();
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sin->sin_family = AF_INET;
+	memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+	hnputs(&sin->sin_port, port);
+
+	if(bind(fd, &sa, sizeof(sa)) < 0)
+		oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+	int i;
+	char buf[32];
+	uchar *p;
+	struct hostent *hp;
+
+	hp = gethostbyname(host);
+	if(hp == 0)
+		return 0;
+
+	for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+		p = (uchar*)hp->h_addr_list[i];
+		sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+		hostv[i] = strdup(buf);
+		if(hostv[i] == 0)
+			break;
+	}
+	return i;
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+	int i;
+	struct hostent *hp;
+	unsigned long straddr;
+
+	straddr = inet_addr(addr);
+	if(straddr == -1)
+		return 0;
+
+	hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
+	if(hp == 0)
+		return 0;
+
+	hostv[0] = strdup(hp->h_name);
+	if(hostv[0] == 0)
+		return 0;
+	for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
+		hostv[i] = strdup(hp->h_aliases[i-1]);
+		if(hostv[i] == 0)
+			break;
+	}
+	return i;
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+	ushort p;
+	struct servent *s;
+
+	s = getservbyname(service, net);
+	if(s == 0)
+		return -1;
+	p = s->s_port;
+	sprint(port, "%d", nhgets(&p));	
+	return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+	int r;
+	static struct linger l = {1, 0};
+
+	osenter();
+	if(nolinger)
+		setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+	r = closesocket(fd);
+	osleave();
+	return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+	error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+	USED(restricted);
+	USED(port);
+	/* Windows requires bound sockets, even on port 0 */
+	return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+	int on;
+
+	USED(ms);
+	on = 1;
+	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/ipif6.c
@@ -1,0 +1,413 @@
+#define Unknown win_Unknown
+
+/* vista (0x0600) was the first to support IPPROTO_IPV6 */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+
+#include	<winsock2.h>
+#include	<ws2def.h>
+#include	<ws2tcpip.h>
+#include	<wspiapi.h>
+#undef Unknown
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"ip.h"
+#include	"error.h"
+
+extern int SOCK_SELECT;
+
+int
+so_socket(int type)
+{
+	int fd, one;
+	int v6only;
+
+	switch(type) {
+	default:
+		error("bad protocol type");
+	case S_TCP:
+		type = SOCK_STREAM;
+		break;
+	case S_UDP:
+		type = SOCK_DGRAM;
+		break;
+	}
+
+	fd = socket(AF_INET6, type, 0);
+	if(fd < 0)
+		oserror();
+
+	/* OpenBSD has v6only fixed to 1, so the following will fail */
+	v6only = 0;
+	if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, sizeof v6only) < 0)
+		oserror();
+
+	if(type == SOCK_DGRAM){
+		one = 1;
+		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+	}else{
+		one = 1;
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+	}
+	return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+	char *h = hdr;
+
+	osenter();
+	if(hdr == 0)
+		r = send(sock, va, len, 0);
+	else {
+		memset(&sa, 0, sizeof(sa));
+		sin6 = (struct sockaddr_in6*)&sa;
+		sin6->sin6_family = AF_INET6;
+		switch(hdrlen){
+		case OUdphdrlenv4:
+			v4tov6((uchar*)&sin6->sin6_addr, h);
+			memmove(&sin6->sin6_port, h+8, 2);
+			break;
+		case OUdphdrlen:
+			memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+			memmove(&sin6->sin6_port, h+2*IPaddrlen, 2);	/* rport */
+			break;
+		default:
+			memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+			memmove(&sin6->sin6_port, h+3*IPaddrlen, 2);
+			break;
+		}
+		r = sendto(sock, va, len, 0, (struct sockaddr*)sin6, sizeof(*sin6));
+	}
+	osleave();
+	return r;
+}
+
+static int
+doselect(int sock)
+{
+	fd_set	waitr;
+	struct timeval seltime;
+
+	up->syscall = SOCK_SELECT;
+	FD_ZERO(&waitr);
+	FD_SET(sock, &waitr);
+	for(;;){
+		int nfds;
+		fd_set in, exc;
+
+		in = waitr;
+		exc = waitr;
+		seltime.tv_sec = 1;
+		seltime.tv_usec = 0L;
+		nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime);
+		if(up->intwait) {
+			up->intwait = 0;
+			return -1;
+		}
+		if(nfds < 0) {
+			print("select error\n");
+			return 0;
+		}
+		if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){
+			return 0;
+		}
+	}
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r, l;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+	char h[Udphdrlen];
+
+	osenter();
+	if(doselect(sock) < 0) {
+		osleave();
+		return -1;
+	}
+	if(hdr == 0)
+		r = recv(sock, va, len, 0);
+	else {
+		sin6 = (struct sockaddr_in6*)&sa;
+		l = sizeof(sa);
+		r = recvfrom(sock, va, len, 0, (struct sockaddr*)&sa, &l);
+		if(r >= 0) {
+			memset(h, 0, sizeof h);
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				if(v6tov4(h, (uchar*)&sin6->sin6_addr) < 0) {
+					osleave();
+					error("OUdphdrlenv4 with IPv6 address");
+				}
+				memmove(h+2*IPv4addrlen, &sin6->sin6_port, 2);
+				break;
+			case OUdphdrlen:
+				memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen, &sin6->sin6_port, 2);
+				break;
+			default:
+				memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+3*IPaddrlen, &sin6->sin6_port, 2);
+				break;
+			}
+
+			/* alas there's no way to get the local addr/port correctly.  Pretend. */
+			memset(&sa, 0, sizeof sa);
+			l = sizeof(sa);
+			getsockname(sock, (struct sockaddr*)&sa, &l);
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				/*
+				 * we get v6Unspecified/noaddr if local address cannot be determined.
+				 * that's reasonable for ipv4 too.
+				 */
+				if(ipcmp(v6Unspecified, (uchar*)&sin6->sin6_addr) != 0
+				&& v6tov4(h+IPv4addrlen, (uchar*)&sin6->sin6_addr) < 0) {
+					osleave();
+					error("OUdphdrlenv4 with IPv6 address");
+				}
+				memmove(h+2*IPv4addrlen+2, &sin6->sin6_port, 2);
+				break;
+			case OUdphdrlen:
+				memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen+2, &sin6->sin6_port, 2);
+				break;
+			default:
+				memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);	/* ifcaddr */
+				memmove(h+3*IPaddrlen+2, &sin6->sin6_port, 2);
+				break;
+			}
+			memmove(hdr, h, hdrlen);
+		}
+	}
+	osleave();
+	return r;
+}
+
+void
+so_close(int sock)
+{
+	closesocket(sock);
+}
+
+void
+so_connect(int fd, unsigned char *raddr, unsigned short rport)
+{
+	int r;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	memset(&sa, 0, sizeof(sa));
+	sin6 = (struct sockaddr_in6*)&sa;
+	sin6->sin6_family = AF_INET6;
+	hnputs(&sin6->sin6_port, rport);
+	memmove((uchar*)&sin6->sin6_addr, raddr, IPaddrlen);
+
+	osenter();
+	r = connect(fd, (struct sockaddr*)sin6, sizeof(*sin6));
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+void
+so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
+{
+	int len;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	len = sizeof(*sin6);
+	if(getsockname(fd, (struct sockaddr*)&sa, &len) < 0)
+		oserror();
+
+	sin6 = (struct sockaddr_in6*)&sa;
+	if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+		error("not AF_INET6");
+
+	memmove(laddr, &sin6->sin6_addr, IPaddrlen);
+	*lport = nhgets(&sin6->sin6_port);
+}
+
+void
+so_listen(int fd)
+{
+	int r;
+
+	osenter();
+	r = listen(fd, 256);
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+int
+so_accept(int fd, unsigned char *raddr, unsigned short *rport)
+{
+	int nfd, len;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6*)&sa;
+
+	len = sizeof(*sin6);
+	osenter();
+	if(doselect(fd) < 0) {
+		osleave();
+		return -1;
+	}
+	nfd = accept(fd, (struct sockaddr*)&sa, &len);
+	osleave();
+	if(nfd < 0)
+		oserror();
+
+	if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+		error("not AF_INET6");
+
+	memmove(raddr, &sin6->sin6_addr, IPaddrlen);
+	*rport = nhgets(&sin6->sin6_port);
+	return nfd;
+}
+
+void
+so_bind(int fd, int su, unsigned char *addr, unsigned short port)
+{
+	int i, one;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6*)&sa;
+
+	one = 1;
+	if(0 && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+		oserrstr(up->genbuf, sizeof(up->genbuf));
+		print("setsockopt: %s", up->genbuf);
+	}
+
+	if(su) {
+		for(i = 600; i < 1024; i++) {
+			memset(&sa, 0, sizeof(sa));
+			sin6->sin6_family = AF_INET6;
+			memmove(&sin6->sin6_addr, addr, IPaddrlen);
+			hnputs(&sin6->sin6_port, i);
+
+			if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) >= 0)	
+				return;
+		}
+		oserror();
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sin6->sin6_family = AF_INET6;
+	memmove(&sin6->sin6_addr, addr, IPaddrlen);
+	hnputs(&sin6->sin6_port, port);
+
+	if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) < 0)
+		oserror();
+}
+
+static int
+resolve(char *name, char **hostv, int n, int isnumeric)
+{
+	int i;
+	struct addrinfo *res0, *r;
+	char buf[5*8];
+	uchar addr[IPaddrlen];
+	struct addrinfo hints;
+
+	memset(&hints, 0, sizeof hints);
+	hints.ai_flags = isnumeric ? AI_NUMERICHOST : 0;
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if(getaddrinfo(name, nil, &hints, &res0) < 0)
+		return 0;
+	i = 0;
+	for(r = res0; r != nil && i < n; r = r->ai_next) {
+		if(r->ai_family == AF_INET)
+			v4tov6(addr, (uchar*)&((struct sockaddr_in*)r->ai_addr)->sin_addr);
+		else if(r->ai_family == AF_INET6)
+			memmove(addr, &((struct sockaddr_in6*)r->ai_addr)->sin6_addr, IPaddrlen);
+		else
+			continue;
+
+		snprint(buf, sizeof buf, "%I", addr);
+		hostv[i++] = strdup(buf);
+	}
+
+	freeaddrinfo(res0);
+	return i;
+}
+
+int
+so_gethostbyname(char *host, char **hostv, int n)
+{
+	return resolve(host, hostv, n, 0);
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+	return resolve(addr, hostv, n, 1);
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+	ushort p;
+	struct servent *s;
+
+	s = getservbyname(service, net);
+	if(s == 0)
+		return -1;
+	p = s->s_port;
+	sprint(port, "%d", nhgets(&p));	
+	return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+	int r;
+	static struct linger l = {1, 0};
+
+	osenter();
+	if(nolinger)
+		setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+	r = closesocket(fd);
+	osleave();
+	return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+	error("arp not implemented");
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+	USED(restricted);
+	USED(port);
+	/* Windows requires bound sockets, even on port 0 */
+	return 1;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+	int on;
+
+	USED(ms);
+	on = 1;
+	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+}
--- /dev/null
+++ b/emu/Nt/mkfile
@@ -1,0 +1,51 @@
+SYSTARG=Nt
+OBJTYPE=386
+#uncomment following line for full Microsoft debug symbols
+#LDEBUG=-debug -debugtype:cv -pdb:none
+<../../mkconfig
+SYSTARG=Nt
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu ie
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+OSX=os
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	$OSX.$O\
+	$CONF.root.$O\
+	lock.$O\
+	fp.$O\
+	r16.$O\
+	vlrt.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+	r16.h\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS=	$SYSLIBS netapi32.lib wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib
+KERNDATE=`{$NDATE}
+
+default:V:	i$CONF.exe
+
+<../port/portmkfile
+
+i$CONF.exe:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -out:$target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: i$CONF.exe
+	cp i$CONF.exe $INSTALLDIR/$CONF.exe
--- /dev/null
+++ b/emu/Nt/nt.rc
@@ -1,0 +1,1 @@
+100 ICON inferno.ico
--- /dev/null
+++ b/emu/Nt/os.c
@@ -1,0 +1,715 @@
+#define Unknown win_Unknown
+#define UNICODE
+#include	<windows.h>
+#include <winbase.h>
+#include	<winsock.h>
+#undef Unknown
+#include	<excpt.h>
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include	"r16.h"
+
+int	SYS_SLEEP = 2;
+int SOCK_SELECT = 3;
+#define	MAXSLEEPERS	1500
+
+extern	int	cflag;
+
+DWORD	PlatformId;
+DWORD	consolestate;
+static	char*	path;
+static	HANDLE	kbdh = INVALID_HANDLE_VALUE;
+static	HANDLE	conh = INVALID_HANDLE_VALUE;
+static	HANDLE	errh = INVALID_HANDLE_VALUE;
+static	int	donetermset = 0;
+static	int sleepers = 0;
+
+
+__declspec(thread)       Proc    *up;
+
+HANDLE	ntfd2h(int);
+int	nth2fd(HANDLE);
+void	termrestore(void);
+char *hosttype = "Nt";
+char *cputype = "386";
+void	(*coherence)(void) = nofence;
+
+static void
+pfree(Proc *p)
+{
+	Osenv *e;
+
+	lock(&procs.l);
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	e = p->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(e->user);
+	free(p->prog);
+	CloseHandle((HANDLE)p->os);
+	free(p);
+}
+
+void
+osblock(void)
+{
+	if(WaitForSingleObject((HANDLE)up->os, INFINITE) != WAIT_OBJECT_0)
+		panic("osblock failed");
+}
+
+void
+osready(Proc *p)
+{
+	if(SetEvent((HANDLE)p->os) == FALSE)
+		panic("osready failed");
+}
+
+void
+pexit(char *msg, int t)
+{
+	pfree(up);
+	ExitThread(0);
+}
+
+LONG TrapHandler(LPEXCEPTION_POINTERS ureg);
+
+__cdecl
+Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon)
+{
+	EXCEPTION_POINTERS ep;
+	ep.ExceptionRecord = rec;
+	ep.ContextRecord = context;
+	TrapHandler(&ep);
+	return ExceptionContinueExecution;
+}
+
+DWORD WINAPI
+tramp(LPVOID p)
+{
+	up = p;
+	up->func(up->arg);
+	pexit("", 0);
+	/* not reached */
+	for(;;)
+		panic("tramp");
+	return 0;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	DWORD h;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+
+	p = newproc();
+	if(p == nil)
+		panic("out of kernel processes");
+	p->os = CreateEvent(NULL, FALSE, FALSE, NULL);
+	if(p->os == NULL)
+		panic("can't allocate os event");
+		
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->ui = up->env->ui;
+	kstrdup(&p->env->user, up->env->user);
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h);
+	if(p->pid <= 0)
+		panic("ran out of  kernel processes");
+}
+
+#if(_WIN32_WINNT >= 0x0400)
+void APIENTRY sleepintr(DWORD param)
+{
+}
+#endif
+
+void
+oshostintr(Proc *p)
+{
+	if (p->syscall == SOCK_SELECT)
+		return;
+	p->intwait = 0;
+#if(_WIN32_WINNT >= 0x0400)
+	if(p->syscall == SYS_SLEEP) {
+		QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid);
+	}
+#endif
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	longjmp(env, val);
+}
+
+int
+readkbd(void)
+{
+	DWORD r;
+	char buf[1];
+
+	if(ReadFile(kbdh, buf, sizeof(buf), &r, 0) == FALSE)
+		panic("keyboard fail");
+	if (r == 0)
+		panic("keyboard EOF");
+
+	if (buf[0] == 0x03) {
+		// INTR (CTRL+C)
+		termrestore();
+		ExitProcess(0);
+	}
+	if(buf[0] == '\r')
+		buf[0] = '\n';
+	return buf[0];
+}
+
+void
+cleanexit(int x)
+{
+	sleep(2);		/* give user a chance to see message */
+	termrestore();
+	ExitProcess(x);
+}
+
+struct ecodes {
+	DWORD	code;
+	char*	name;
+} ecodes[] = {
+	EXCEPTION_ACCESS_VIOLATION,		"segmentation violation",
+	EXCEPTION_DATATYPE_MISALIGNMENT,	"data alignment",
+	EXCEPTION_BREAKPOINT,                	"breakpoint",
+	EXCEPTION_SINGLE_STEP,               	"single step",
+	EXCEPTION_ARRAY_BOUNDS_EXCEEDED,	"array bounds check",
+	EXCEPTION_FLT_DENORMAL_OPERAND,		"denormalized float",
+	EXCEPTION_FLT_DIVIDE_BY_ZERO,		"floating point divide by zero",
+	EXCEPTION_FLT_INEXACT_RESULT,		"inexact floating point",
+	EXCEPTION_FLT_INVALID_OPERATION,	"invalid floating operation",
+	EXCEPTION_FLT_OVERFLOW,			"floating point result overflow",
+	EXCEPTION_FLT_STACK_CHECK,		"floating point stack check",
+	EXCEPTION_FLT_UNDERFLOW,		"floating point result underflow",
+	EXCEPTION_INT_DIVIDE_BY_ZERO,		"divide by zero",
+	EXCEPTION_INT_OVERFLOW,			"integer overflow",
+	EXCEPTION_PRIV_INSTRUCTION,		"privileged instruction",
+	EXCEPTION_IN_PAGE_ERROR,		"page-in error",
+	EXCEPTION_ILLEGAL_INSTRUCTION,		"illegal instruction",
+	EXCEPTION_NONCONTINUABLE_EXCEPTION,	"non-continuable exception",
+	EXCEPTION_STACK_OVERFLOW,		"stack overflow",
+	EXCEPTION_INVALID_DISPOSITION,		"invalid disposition",
+	EXCEPTION_GUARD_PAGE,			"guard page violation",
+	0,					nil
+};
+
+LONG
+TrapHandler(LPEXCEPTION_POINTERS ureg)
+{
+	int i;
+	char *name;
+	DWORD code;
+	// WORD pc;
+	char buf[ERRMAX];
+
+	code = ureg->ExceptionRecord->ExceptionCode;
+	// pc = ureg->ContextRecord->Eip;
+
+	name = nil;
+	for(i = 0; i < nelem(ecodes); i++) {
+		if(ecodes[i].code == code) {
+			name = ecodes[i].name;
+			break;
+		}
+	}
+
+	if(name == nil) {
+		snprint(buf, sizeof(buf), "unknown trap type (%#.8lux)\n", code);
+		name = buf;
+	}
+/*
+	if(pc != 0) {
+		snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc);
+		name = buf;
+	}
+*/
+	switch (code) {
+	case EXCEPTION_FLT_DENORMAL_OPERAND:
+	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+	case EXCEPTION_FLT_INEXACT_RESULT:
+	case EXCEPTION_FLT_INVALID_OPERATION:
+	case EXCEPTION_FLT_OVERFLOW:
+	case EXCEPTION_FLT_STACK_CHECK:
+	case EXCEPTION_FLT_UNDERFLOW:
+		/* clear exception flags and ensure safe empty state */
+		_asm { fnclex };
+		_asm { fninit };
+	}
+	disfault(nil, name);
+	/* not reached */
+	return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+static void
+termset(void)
+{
+	DWORD flag;
+
+	if(donetermset)
+		return;
+	donetermset = 1;
+	conh = GetStdHandle(STD_OUTPUT_HANDLE);
+	kbdh = GetStdHandle(STD_INPUT_HANDLE);
+	errh = GetStdHandle(STD_ERROR_HANDLE);
+	if(errh == INVALID_HANDLE_VALUE)
+		errh = conh;
+
+	// The following will fail if kbdh not from console (e.g. a pipe)
+	// in which case we don't care
+	GetConsoleMode(kbdh, &consolestate);
+	flag = consolestate;
+	flag = flag & ~(ENABLE_PROCESSED_INPUT|ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
+	SetConsoleMode(kbdh, flag);
+}
+
+void
+termrestore(void)
+{
+	if(kbdh != INVALID_HANDLE_VALUE)
+		SetConsoleMode(kbdh, consolestate);
+}
+
+static	int	rebootok = 0;	/* is shutdown -r supported? */
+
+void
+osreboot(char *file, char **argv)
+{
+	if(rebootok){
+		termrestore();
+		execvp(file, argv);
+		panic("reboot failure");
+	}
+}
+
+void
+libinit(char *imod)
+{
+	WSADATA wasdat;
+	DWORD lasterror, namelen;
+	OSVERSIONINFO os;
+	char sys[64], uname[64];
+	wchar_t wuname[64];
+	char *uns;
+
+	os.dwOSVersionInfoSize = sizeof(os);
+	if(!GetVersionEx(&os))
+		panic("can't get os version");
+	PlatformId = os.dwPlatformId;
+	if (PlatformId == VER_PLATFORM_WIN32_NT) {	/* true for NT and 2000 */
+		rebootok = 1;
+	} else {
+		rebootok = 0;
+	}
+	termset();
+
+	if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int))
+		panic("invalid handle value or size");
+
+	if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0)
+		panic("no winsock.dll");
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	if(sflag == 0)
+		SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler);
+
+	path = getenv("PATH");
+	if(path == nil)
+		path = ".";
+
+	up = newproc();
+	if(up == nil)
+		panic("cannot create kernel process");
+
+	strcpy(uname, "inferno");
+	namelen = sizeof(wuname);
+	if(GetUserName(wuname, &namelen) != TRUE) {
+		lasterror = GetLastError();	
+		if(PlatformId == VER_PLATFORM_WIN32_NT || lasterror != ERROR_NOT_LOGGED_ON)
+			print("cannot GetUserName: %d\n", lasterror);
+	}else{
+		uns = narrowen(wuname);
+		snprint(uname, sizeof(uname), "%s", uns);
+		free(uns);
+	}
+	kstrdup(&eve, uname);
+
+	emuinit(imod);
+}
+
+void
+FPsave(void *fptr)
+{
+	_asm {
+		mov	eax, fptr
+		fstenv	[eax]
+	}
+}
+
+void
+FPrestore(void *fptr)
+{
+	_asm {
+		mov	eax, fptr
+		fldenv	[eax]
+	}
+}
+
+ulong
+umult(ulong a, ulong b, ulong *high)
+{
+	ulong lo, hi;
+
+	_asm {
+		mov	eax, a
+		mov	ecx, b
+		MUL	ecx
+		mov	lo, eax
+		mov	hi, edx
+	}
+	*high = hi;
+	return lo;
+}
+
+int
+close(int fd)
+{
+	if(fd == -1)
+		return 0;
+	CloseHandle(ntfd2h(fd));
+	return 0;
+}
+
+int
+read(int fd, void *buf, uint n)
+{
+	HANDLE h;
+
+	if(fd == 0)
+		h = kbdh;
+	else
+		h = ntfd2h(fd);
+	if(h == INVALID_HANDLE_VALUE)
+		return -1;
+	if(!ReadFile(h, buf, n, &n, NULL))
+		return -1;
+	return n;
+}
+
+int
+write(int fd, void *buf, uint n)
+{
+	HANDLE h;
+
+	if(fd == 1 || fd == 2){
+		if(!donetermset)
+			termset();
+		if(fd == 1)
+			h = conh;
+		else
+			h = errh;
+		if(h == INVALID_HANDLE_VALUE)
+			return -1;
+		if(!WriteFile(h, buf, n, &n, NULL))
+			return -1;
+		return n;
+	}
+	if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL))
+		return -1;
+	return n;
+}
+
+/*
+ * map handles and fds.
+ * this code assumes sizeof(HANDLE) == sizeof(int),
+ * that INVALID_HANDLE_VALUE is -1, and assumes
+ * that all tests of invalid fds check only for -1, not < 0
+ */
+int
+nth2fd(HANDLE h)
+{
+	return (int)h;
+}
+
+HANDLE
+ntfd2h(int fd)
+{
+	return (HANDLE)fd;
+}
+
+void
+oslopri(void)
+{
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+}
+
+/* Resolve system header name conflict */
+#undef Sleep
+void
+sleep(int secs)
+{
+	Sleep(secs*1000);
+}
+
+void*
+sbrk(int size)
+{
+	void *brk;
+
+	brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 	
+	if(brk == 0)
+		return (void*)-1;
+
+	return brk;
+}
+
+ulong
+getcallerpc(void *arg)
+{
+	ulong cpc;
+	_asm {
+		mov eax, dword ptr [ebp]
+		mov eax, dword ptr [eax+4]
+		mov dword ptr cpc, eax
+	}
+	return cpc;
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	return GetTickCount();
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ *  days per month plus days/year
+ */
+static	int	dmsize[] =
+{
+	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static	int	ldmsize[] =
+{
+	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ *  return the days/month for the given year
+ */
+static int*
+yrsize(int yr)
+{
+	/* a leap year is a multiple of 4, excluding centuries
+	 * that are not multiples of 400 */
+	if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) )
+		return ldmsize;
+	else
+		return dmsize;
+}
+
+static long
+tm2sec(SYSTEMTIME *tm)
+{
+	long secs;
+	int i, *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < tm->wYear; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(tm->wYear);
+	for(i = 1; i < tm->wMonth; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	/*
+	 * secs in last month
+	 */
+	secs += (tm->wDay-1) * SEC2DAY;
+
+	/*
+	 * hours, minutes, seconds
+	 */
+	secs += tm->wHour * SEC2HOUR;
+	secs += tm->wMinute * SEC2MIN;
+	secs += tm->wSecond;
+
+	return secs;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+	SYSTEMTIME tm;
+	vlong secs;
+
+	GetSystemTime(&tm);
+	secs = tm2sec(&tm);
+	return secs * 1000000 + tm.wMilliseconds * 1000;
+}
+
+vlong
+osnsec(void)
+{
+	return osusectime()*1000;	/* TO DO better */
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	SleepEx(milsec, FALSE);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	if (sleepers > MAXSLEEPERS)
+		return -1;
+	sleepers++;
+	up->syscall = SYS_SLEEP;
+	SleepEx(milsec, TRUE);
+	up->syscall = 0;
+	sleepers--;
+	return 0;
+}
+
+void
+osyield(void)
+{	
+	SwitchToThread();
+}
+
+void
+ospause(void)
+{
+      for(;;)
+              sleep(1000000);
+}
+
+/*
+ * these should never be called, and are included
+ * as stubs since we are linking against a library which defines them
+ */
+int
+open(const char *path, int how, ...)
+{
+	panic("open");
+	return -1;
+}
+
+int
+creat(const char *path, int how)
+{
+	panic("creat");
+	return -1;
+}
+
+int
+stat(const char *path, struct stat *sp)
+{
+	panic("stat");
+	return -1;
+}
+
+int
+chown(const char *path, int uid, int gid)
+{
+	panic("chown");
+	return -1;
+}
+
+int
+chmod(const char *path, int mode)
+{
+	panic("chmod");
+	return -1;
+}
+
+void
+link(char *path, char *next)
+{
+	panic("link");
+}
+
+int
+segflush(void *a, ulong n)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/Nt/r16.c
@@ -1,0 +1,186 @@
+#define UNICODE
+#define Unknown win_Unknown
+#include	<windows.h>
+#include	<winbase.h>
+#undef Unknown
+#undef	Sleep
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"r16.h"
+
+enum
+{
+	Bits10 = 0x03ff,		/* 0011 1111 1111 */
+
+	R16self = 0x10000,
+
+	HSurrogateMin = 0xd800,
+	HSurrogateMax = 0xdbff,
+	LSurrogateMin = 0xdc00,
+	LSurrogateMax = 0xdfff,
+};
+
+Rune16*
+runes16dup(Rune16 *r)
+{
+	int n;
+	Rune16 *s;
+
+	n = runes16len(r) + 1;
+	s = malloc(n * sizeof(Rune16));
+	if(s == nil)
+		error(Enomem);
+	memmove(s, r, n * sizeof(Rune16));
+	return s;
+}
+
+int
+runes16len(Rune16 *r)
+{
+	int n;
+
+	n = 0;
+	while(*r++ != 0)
+		n++;
+	return n;
+}
+
+char*
+runes16toutf(char *p, Rune16 *r, int nc)
+{
+	char *op, *ep;
+	int n;
+	Rune c, lc;
+
+	op = p;
+	ep = p + nc;
+	while(c = *r++) {
+		if(c > Runemax)
+			c = Runeerror;
+		if(c >= LSurrogateMin && c <= LSurrogateMax)
+			c = Runeerror;
+		if(c >= HSurrogateMin && c<= HSurrogateMax){
+			lc = *r++;
+			if(lc >= LSurrogateMin || lc <= LSurrogateMax)
+				c = (c&Bits10)<<10 | (lc&Bits10) + R16self;
+			else
+				c = Runeerror;
+		}
+		n = runelen(c);
+		if(p + n >= ep)
+			break;
+		p += runetochar(p, &c);
+	}
+	*p = '\0';
+	return op;
+}
+
+int
+rune16nlen(Rune16 *r, int nrune)
+{
+	int nb;
+	Rune c;
+
+	nb = 0;
+	while(nrune--) {
+		c = *r++;
+		if(c < R16self)
+			nb += runelen(c);
+		else {
+			c -= R16self;
+			nb += runelen(HSurrogateMin | (c>>10));
+			nb += runelen(LSurrogateMin | (c&Bits10));
+		}
+	}
+	return nb;
+}
+
+Rune16*
+utftorunes16(Rune16 *r, char *p, int nc)
+{
+	Rune16 *or, *er;
+	Rune rc;
+
+	or = r;
+	er = r + nc;
+	while(*p != '\0' && r + 1 < er){
+		p += chartorune(&rc, p);
+		if(rc < R16self){
+			*r++ = rc;
+			continue;
+		}
+		if(rc > Runemax || er-r < 2){
+			*r++ = Runeerror;
+			continue;
+		}
+		rc -= R16self;
+		*r++ = HSurrogateMin | (rc>>10);
+		*r++ = LSurrogateMin | (rc&Bits10);
+	}
+	*r = '\0';
+	return or;
+}
+
+int
+runes16cmp(Rune16 *s1, Rune16 *s2)
+{
+	Rune16 r1, r2;
+
+	for(;;) {
+		r1 = *s1++;
+		r2 = *s2++;
+		if(r1 != r2) {
+			if(r1 > r2)
+				return 1;
+			return -1;
+		}
+		if(r1 == 0)
+			return 0;
+	}
+}
+
+wchar_t *
+widen(char *s)
+{
+	int n;
+	wchar_t *ws;
+
+	n = utflen(s) + 1;
+	ws = smalloc(n*sizeof(wchar_t));
+	utftorunes16(ws, s, n);
+	return ws;
+}
+
+
+char *
+narrowen(wchar_t *ws)
+{
+	char *s;
+	int n;
+
+	n = widebytes(ws);
+	s = smalloc(n);
+	runes16toutf(s, ws, n);
+	return s;
+}
+
+
+int
+widebytes(wchar_t *ws)
+{
+	int n = 0;
+	wchar_t c;
+
+	while (*ws){
+		c = *ws++;
+		if(c < R16self)
+			n += runelen(c);
+		else {
+			c -= R16self;
+			n += runelen(HSurrogateMin | (c>>10));
+			n += runelen(LSurrogateMin | (c&Bits10));
+		}
+	}
+	return n+1;
+}
--- /dev/null
+++ b/emu/Nt/r16.h
@@ -1,0 +1,11 @@
+typedef unsigned short Rune16;
+
+wchar_t	*widen(char *s);
+char		*narrowen(wchar_t *ws);
+int		widebytes(wchar_t *ws);
+int		runes16len(Rune16*);
+int		rune16nlen(Rune16*, int);
+Rune16*	runes16dup(Rune16*);
+Rune16*	utftorunes16(Rune16*, char*, int);
+char*	runes16toutf(char*, Rune16*, int);
+int		runes16cmp(Rune16*, Rune16*);
--- /dev/null
+++ b/emu/Nt/vlrt.c
@@ -1,0 +1,751 @@
+#include "dat.h"
+
+/*
+ * typedef	unsigned long	ulong;
+ * typedef	unsigned int	uint;
+ * typedef	unsigned short	ushort;
+ * typedef	unsigned char	uchar;
+ * typedef	signed char	schar;
+*/
+
+#define	SIGN(n)	(1UL<<(n-1))
+
+typedef	struct	Vlong	Vlong;
+struct	Vlong
+{
+	ulong	lo;
+	ulong	hi;
+};
+
+void	abort(void);
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+	ulong lo, hi;
+
+	lo = a.lo + b.lo;
+	hi = a.hi + b.hi;
+	if(lo < a.lo)
+		hi++;
+	r->lo = lo;
+	r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+	ulong lo, hi;
+
+	lo = a.lo - b.lo;
+	hi = a.hi - b.hi;
+	if(lo > a.lo)
+		hi--;
+	r->lo = lo;
+	r->hi = hi;
+}
+
+void
+_mulv(Vlong *r, Vlong a, Vlong b)
+{
+	ulong ahi, alo, chi, clo, x;
+	int i;
+
+	ahi = a.hi;
+	alo = a.lo;
+	chi = 0;
+	clo = 0;
+
+	x = b.lo;
+	for(i=0; i<32; i++) {
+		if(x & 1) {
+			chi += ahi;
+			clo += alo;
+			if(clo < alo)
+				chi++;
+		}
+		ahi <<= 1;
+		if(alo & SIGN(32))
+			ahi += 1;
+		alo <<= 1;
+		x >>= 1;
+	}
+
+	/*
+	 * same, but
+	 *	alo is known to be 0
+	 *	can stop when x == 0
+	 */
+	for(x=b.hi; x; x>>=1) {
+		if(x & 1)
+			chi += ahi;
+		ahi <<= 1;
+	}
+
+	r->hi = chi;
+	r->lo = clo;
+}
+
+void
+_d2v(Vlong *y, double d)
+{
+	Vlong x;
+	ulong xhi, xlo, ylo, yhi;
+	int sh;
+
+	*(double*)&x = d;
+
+	xhi = (x.hi & 0xfffff) | 0x100000;
+	xlo = x.lo;
+	sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+	ylo = 0;
+	yhi = 0;
+	if(sh >= 0) {
+		/* v = (hi||lo) >> sh */
+		if(sh < 32) {
+			if(sh == 0) {
+				ylo = xlo;
+				yhi = xhi;
+			} else {
+				ylo = (xlo >> sh) | (xhi << (32-sh));
+				yhi = xhi >> sh;
+			}
+		} else {
+			if(sh == 32) {
+				ylo = xhi;
+			} else
+			if(sh < 64) {
+				ylo = xhi >> (sh-32);
+			}
+		}
+	} else {
+		/* v = (hi||lo) << -sh */
+		sh = -sh;
+		if(sh <= 10) {
+			ylo = xlo << sh;
+			yhi = (xhi << sh) | (xlo >> (32-sh));
+		} else {
+			/* overflow */
+			yhi = d;	/* causes something awful */
+		}
+	}
+	if(x.hi & SIGN(32)) {
+		if(ylo != 0) {
+			ylo = -ylo;
+			yhi = ~yhi;
+		} else
+			yhi = -yhi;
+	}
+
+	y->hi = yhi;
+	y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+	_d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+	if(x.hi & SIGN(32)) {
+		if(x.lo) {
+			x.lo = -x.lo;
+			x.hi = ~x.hi;
+		} else
+			x.hi = -x.hi;
+		return -((long)x.hi*4294967296. + x.lo);
+	}
+	return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+	return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+	ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+	int i;
+
+	numhi = num.hi;
+	numlo = num.lo;
+	denhi = den.hi;
+	denlo = den.lo;
+
+	/*
+	 * get a divide by zero
+	 */
+	if(denlo==0 && denhi==0) {
+		numlo = numlo / denlo;
+	}
+
+	/*
+	 * set up the divisor and find the number of iterations needed
+	 */
+	if(numhi >= SIGN(32)) {
+		quohi = SIGN(32);
+		quolo = 0;
+	} else {
+		quohi = numhi;
+		quolo = numlo;
+	}
+	i = 0;
+	while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+		denhi = (denhi<<1) | (denlo>>31);
+		denlo <<= 1;
+		i++;
+	}
+
+	quohi = 0;
+	quolo = 0;
+	for(; i >= 0; i--) {
+		quohi = (quohi<<1) | (quolo>>31);
+		quolo <<= 1;
+		if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+			t = numlo;
+			numlo -= denlo;
+			if(numlo > t)
+				numhi--;
+			numhi -= denhi;
+			quolo |= 1;
+		}
+		denlo = (denlo>>1) | (denhi<<31);
+		denhi >>= 1;
+	}
+
+	if(q) {
+		q->lo = quolo;
+		q->hi = quohi;
+	}
+	if(r) {
+		r->lo = numlo;
+		r->hi = numhi;
+	}
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+	if(n.hi == 0 && d.hi == 0) {
+		q->hi = 0;
+		q->lo = n.lo / d.lo;
+		return;
+	}
+	dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+	if(n.hi == 0 && d.hi == 0) {
+		r->hi = 0;
+		r->lo = n.lo % d.lo;
+		return;
+	}
+	dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+	if(v->lo == 0) {
+		v->hi = -v->hi;
+		return;
+	}
+	v->lo = -v->lo;
+	v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+	long nneg, dneg;
+
+	if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+		q->lo = (long)n.lo / (long)d.lo;
+		q->hi = ((long)q->lo) >> 31;
+		return;
+	}
+	nneg = n.hi >> 31;
+	if(nneg)
+		vneg(&n);
+	dneg = d.hi >> 31;
+	if(dneg)
+		vneg(&d);
+	dodiv(n, d, q, 0);
+	if(nneg != dneg)
+		vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+	long nneg, dneg;
+
+	if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+		r->lo = (long)n.lo % (long)d.lo;
+		r->hi = ((long)r->lo) >> 31;
+		return;
+	}
+	nneg = n.hi >> 31;
+	if(nneg)
+		vneg(&n);
+	dneg = d.hi >> 31;
+	if(dneg)
+		vneg(&d);
+	dodiv(n, d, 0, r);
+	if(nneg)
+		vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+	long t;
+
+	t = a.hi;
+	if(b >= 32) {
+		r->hi = t>>31;
+		if(b >= 64) {
+			/* this is illegal re C standard */
+			r->lo = t>>31;
+			return;
+		}
+		r->lo = t >> (b-32);
+		return;
+	}
+	if(b <= 0) {
+		r->hi = t;
+		r->lo = a.lo;
+		return;
+	}
+	r->hi = t >> b;
+	r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+	ulong t;
+
+	t = a.hi;
+	if(b >= 32) {
+		r->hi = 0;
+		if(b >= 64) {
+			/* this is illegal re C standard */
+			r->lo = 0;
+			return;
+		}
+		r->lo = t >> (b-32);
+		return;
+	}
+	if(b <= 0) {
+		r->hi = t;
+		r->lo = a.lo;
+		return;
+	}
+	r->hi = t >> b;
+	r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+	ulong t;
+
+	t = a.lo;
+	if(b >= 32) {
+		r->lo = 0;
+		if(b >= 64) {
+			/* this is illegal re C standard */
+			r->hi = 0;
+			return;
+		}
+		r->hi = t << (b-32);
+		return;
+	}
+	if(b <= 0) {
+		r->lo = t;
+		r->hi = a.hi;
+		return;
+	}
+	r->lo = t << b;
+	r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+	r->hi = a.hi & b.hi;
+	r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+	r->hi = a.hi | b.hi;
+	r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+	r->hi = a.hi ^ b.hi;
+	r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+	l->hi = r->hi;
+	l->lo = r->lo;
+	r->lo++;
+	if(r->lo == 0)
+		r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+	l->hi = r->hi;
+	l->lo = r->lo;
+	if(r->lo == 0)
+		r->hi--;
+	r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+	r->lo++;
+	if(r->lo == 0)
+		r->hi++;
+	l->hi = r->hi;
+	l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+	if(r->lo == 0)
+		r->hi--;
+	r->lo--;
+	l->hi = r->hi;
+	l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+	Vlong t, u;
+
+	u.lo = 0;
+	u.hi = 0;
+	switch(type) {
+	default:
+		abort();
+		break;
+
+	case 1:	/* schar */
+		t.lo = *(schar*)lv;
+		t.hi = t.lo >> 31;
+		fn(&u, t, rv);
+		*(schar*)lv = u.lo;
+		break;
+
+	case 2:	/* uchar */
+		t.lo = *(uchar*)lv;
+		t.hi = 0;
+		fn(&u, t, rv);
+		*(uchar*)lv = u.lo;
+		break;
+
+	case 3:	/* short */
+		t.lo = *(short*)lv;
+		t.hi = t.lo >> 31;
+		fn(&u, t, rv);
+		*(short*)lv = u.lo;
+		break;
+
+	case 4:	/* ushort */
+		t.lo = *(ushort*)lv;
+		t.hi = 0;
+		fn(&u, t, rv);
+		*(ushort*)lv = u.lo;
+		break;
+
+	case 9:	/* int */
+		t.lo = *(int*)lv;
+		t.hi = t.lo >> 31;
+		fn(&u, t, rv);
+		*(int*)lv = u.lo;
+		break;
+
+	case 10:	/* uint */
+		t.lo = *(uint*)lv;
+		t.hi = 0;
+		fn(&u, t, rv);
+		*(uint*)lv = u.lo;
+		break;
+
+	case 5:	/* long */
+		t.lo = *(long*)lv;
+		t.hi = t.lo >> 31;
+		fn(&u, t, rv);
+		*(long*)lv = u.lo;
+		break;
+
+	case 6:	/* ulong */
+		t.lo = *(ulong*)lv;
+		t.hi = 0;
+		fn(&u, t, rv);
+		*(ulong*)lv = u.lo;
+		break;
+
+	case 7:	/* vlong */
+	case 8:	/* uvlong */
+		fn(&u, *(Vlong*)lv, rv);
+		*(Vlong*)lv = u;
+		break;
+	}
+	*ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+	long t;
+
+	t = (ulong)p;
+	ret->lo = t;
+	ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+	long t;
+
+	t = sl;
+	ret->lo = t;
+	ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+	long t;
+
+	t = ul;
+	ret->lo = t;
+	ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+	long t;
+
+	t = si;
+	ret->lo = t;
+	ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+	long t;
+
+	t = ui;
+	ret->lo = t;
+	ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+	long t;
+
+	t = (sh << 16) >> 16;
+	ret->lo = t;
+	ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+	long t;
+
+	t = ul & 0xffff;
+	ret->lo = t;
+	ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+	long t;
+
+	t = (uc << 24) >> 24;
+	ret->lo = t;
+	ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+	long t;
+
+	t = ul & 0xff;
+	ret->lo = t;
+	ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+	long t;
+
+	t = rv.lo & 0xff;
+	return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+	return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+	long t;
+
+	t = rv.lo & 0xffff;
+	return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+	return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+	return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+	return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+	return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+	return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+	return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+	return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+	return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+	return (long)lv.hi < (long)rv.hi || 
+		(lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+	return (long)lv.hi < (long)rv.hi || 
+		(lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+	return (long)lv.hi > (long)rv.hi || 
+		(lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+	return (long)lv.hi > (long)rv.hi || 
+		(lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+	return lv.hi < rv.hi || 
+		(lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+	return lv.hi < rv.hi || 
+		(lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+	return lv.hi > rv.hi || 
+		(lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+	return lv.hi > rv.hi || 
+		(lv.hi == rv.hi && lv.lo >= rv.lo);
+}
--- /dev/null
+++ b/emu/Nt/win.c
@@ -1,0 +1,798 @@
+#define Unknown WUnknown
+#define Colormap	WColormap
+#define Cursor		WCursor
+#define Display		WDisplay
+#define Drawable	WDrawable
+#define Font		WFont
+#define GC		WGC
+#define Point		WPoint
+#define Rectangle	WRectangle
+#define Screen		WScreen
+#define Visual		WVisual
+#define Window		WWindow
+
+#include	<windows.h>
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef XDrawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+#undef Unknown
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<draw.h>
+#include	"keyboard.h"
+#include	"cursor.h"
+#include	"r16.h"
+
+extern ulong displaychan;
+
+extern	int	bytesperline(Rectangle, int);
+extern	int	main(int argc, char **argv);
+static	void	dprint(char*, ...);
+static	DWORD WINAPI	winproc(LPVOID);
+
+static	HINSTANCE	inst;
+static	HINSTANCE	previnst;
+static	int		cmdshow;
+static	HWND		window;
+static	HDC		screen;
+static	HPALETTE	palette;
+static	int		maxxsize;
+static	int		maxysize;
+static	int		attached;
+static	int		isunicode = 1;
+static	HCURSOR		hcursor;
+
+char	*argv0 = "inferno";
+static	ulong	*data;
+
+extern	DWORD	PlatformId;
+char*	gkscanid = "emu_win32vk";
+
+int WINAPI
+WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
+{
+	inst = winst;
+	previnst = wprevinst;
+	cmdshow = wcmdshow;
+
+	/* cmdline passed into WinMain does not contain name of executable.
+	 * The globals __argc and __argv to include this info - like UNIX
+	 */
+	main(__argc, __argv);
+	return 0;
+}
+
+static void
+dprint(char *fmt, ...)
+{
+	va_list arg;
+	char buf[128];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
+	va_end(arg);
+	OutputDebugString("inferno: ");
+	OutputDebugString(buf);
+}
+
+static void
+graphicscmap(PALETTEENTRY *pal)
+{
+	int r, g, b, cr, cg, cb, v, p;
+	int num, den;
+	int i, j;
+	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+			den=r;
+			if(g>den) den=g;
+			if(b>den) den=b;
+			if(den==0)	/* divide check -- pick grey shades */
+				cr=cg=cb=v*17;
+			else{
+				num=17*(4*den+v);
+				cr=r*num/den;
+				cg=g*num/den;
+				cb=b*num/den;
+			}
+			p = i+(j&15);
+			pal[p].peRed = cr*0x01010101;
+			pal[p].peGreen = cg*0x01010101;
+			pal[p].peBlue = cb*0x01010101;
+			pal[p].peFlags = 0;
+		}
+	}
+}
+
+static void
+graphicsgmap(PALETTEENTRY *pal, int d)
+{
+	int i, j, s, m, p;
+
+	s = 8-d;
+	m = 1;
+	while(--d >= 0)
+		m *= 2;
+	m = 255/(m-1);
+	for(i=0; i < 256; i++){
+		j = (i>>s)*m;
+		p = 255-i;
+		pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101;
+		pal[p].peFlags = 0;
+	}
+}
+
+static ulong
+autochan(void)
+{
+	HDC dc;
+	int bpp;
+
+	dc = GetDC(NULL);
+	if (dc == NULL)
+		return CMAP8;
+
+	bpp = GetDeviceCaps(dc, BITSPIXEL);
+	if (bpp < 15)
+		return CMAP8;
+	if (bpp < 24)
+		return RGB15;
+	if (bpp < 32)
+		return RGB24;
+	return XRGB32;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+	int i, k;
+	ulong c;
+	DWORD h;
+	RECT bs;
+	RGBQUAD *rgb;
+	HBITMAP bits;
+	BITMAPINFO *bmi;
+	LOGPALETTE *logpal;
+	PALETTEENTRY *pal;
+	int bsh, bsw, sx, sy;
+
+	if(attached)
+		goto Return;
+
+	/* Compute bodersizes */
+	memset(&bs, 0, sizeof(bs));
+	AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0);
+	bsw = bs.right - bs.left;
+	bsh = bs.bottom - bs.top;
+	sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw;
+	Xsize -= Xsize % 4;	/* Round down */
+	if(Xsize > sx)
+		Xsize = sx;
+	sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20;
+	if(Ysize > sy)
+		Ysize = sy;
+
+	logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY));
+	if(logpal == nil)
+		return nil;
+	logpal->palVersion = 0x300;
+	logpal->palNumEntries = 256;
+	pal = logpal->palPalEntry;
+
+	c = displaychan;
+	if(c == 0)
+		c = autochan();
+	k = 8;
+	if(TYPE(c) == CGrey){
+		graphicsgmap(pal, NBITS(c));
+		c = GREY8;
+	}else{
+		if(c == RGB15)
+			k = 16;
+		else if(c == RGB24)
+			k = 24;
+		else if(c == XRGB32)
+			k = 32;
+		else
+			c = CMAP8;
+		graphicscmap(pal);
+	}
+
+	palette = CreatePalette(logpal);
+
+	if(k == 8)
+		bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
+	else
+		bmi = malloc(sizeof(BITMAPINFOHEADER));
+	if(bmi == nil)
+		return nil;
+	bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bmi->bmiHeader.biWidth = Xsize;
+	bmi->bmiHeader.biHeight = -Ysize;	/* - => origin upper left */
+	bmi->bmiHeader.biPlanes = 1;	/* always 1 */
+	bmi->bmiHeader.biBitCount = k;
+	bmi->bmiHeader.biCompression = BI_RGB;
+	bmi->bmiHeader.biSizeImage = 0;	/* Xsize*Ysize*(k/8) */
+	bmi->bmiHeader.biXPelsPerMeter = 0;
+	bmi->bmiHeader.biYPelsPerMeter = 0;
+	bmi->bmiHeader.biClrUsed = 0;
+	bmi->bmiHeader.biClrImportant = 0;	/* number of important colors: 0 means all */
+
+	if(k == 8){
+		rgb = bmi->bmiColors;
+		for(i = 0; i < 256; i++){
+			rgb[i].rgbRed = pal[i].peRed;
+			rgb[i].rgbGreen = pal[i].peGreen;
+			rgb[i].rgbBlue = pal[i].peBlue;
+		}
+	}
+
+	screen = CreateCompatibleDC(NULL);
+	if(screen == nil){
+		fprint(2, "screen dc nil\n");
+		return nil;
+	}
+
+	if(SelectPalette(screen, palette, 1) == nil){
+		fprint(2, "select pallete failed\n");
+	}
+	i = RealizePalette(screen);
+	GdiFlush();
+	bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0);
+	if(bits == nil){
+		fprint(2, "CreateDIBSection failed\n");
+		return nil;
+	}
+
+	SelectObject(screen, bits);
+	GdiFlush();
+	CreateThread(0, 16384, winproc, nil, 0, &h);
+	attached = 1;
+
+    Return:
+	r->min.x = 0;
+	r->min.y = 0;
+	r->max.x = Xsize;
+	r->max.y = Ysize;
+	displaychan = c;
+	*chan = c;
+	*d = k;
+	*width = (Xsize/4)*(k/8);
+	*softscreen = 1;
+	return (uchar*)data;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	RECT wr;
+
+	if(r.max.x<=r.min.x || r.max.y<=r.min.y)
+		return;
+	wr.left = r.min.x;
+	wr.top = r.min.y;
+	wr.right = r.max.x;
+	wr.bottom = r.max.y;
+	InvalidateRect(window, &wr, 0);
+}
+
+static void
+scancode(WPARAM wparam, LPARAM lparam, int keyup)
+{
+	uchar buf[2];
+
+	if(!(lparam & (1<<30))) {		/* don't auto-repeat chars */
+		buf[0] = wparam;
+		buf[1] = wparam >> 8;
+		if (keyup)
+			buf[1] |= 0x80;
+		qproduce(gkscanq, buf, sizeof buf);
+	}
+}
+
+LRESULT CALLBACK
+WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+	PAINTSTRUCT paint;
+	HDC hdc;
+	LPMINMAXINFO mmi;
+	LONG x, y, w, h, b;
+	HCURSOR dcurs;
+	POINT m;
+
+	switch(msg) {
+	case WM_SETCURSOR:
+		/* User set */
+		if(hcursor != NULL) {
+			SetCursor(hcursor);
+			break;
+		}
+		/* Pick the default */
+		dcurs = LoadCursor(NULL, IDC_ARROW);
+		SetCursor(dcurs);
+		break;
+	case WM_MOUSEWHEEL:
+		if((int)wparam>0)
+			b = 8;
+		else
+			b = 16;
+		m.x = LOWORD(lparam);
+		m.y = HIWORD(lparam);
+		ScreenToClient(hwnd, &m);
+		goto mok;
+	case WM_LBUTTONDBLCLK:
+		b = (1<<8) | 1;
+		goto process;
+	case WM_MBUTTONDBLCLK:
+		b = (1<<8) | 2;
+		goto process;
+	case WM_RBUTTONDBLCLK:
+		b = (1<<8) | 4;
+		goto process;
+	case WM_MOUSEMOVE:
+	case WM_LBUTTONUP:
+	case WM_MBUTTONUP:
+	case WM_RBUTTONUP:
+	case WM_LBUTTONDOWN:
+	case WM_MBUTTONDOWN:
+	case WM_RBUTTONDOWN:
+		b = 0;
+	process:
+		m.x = LOWORD(lparam);
+		m.y = HIWORD(lparam);
+	mok:
+		if(wparam & MK_LBUTTON)
+			b |= 1;
+		if(wparam & MK_MBUTTON)
+			b |= 2;
+		if(wparam & MK_RBUTTON) {
+			if(wparam & MK_CONTROL)
+				b |= 2;  //simulate middle button
+			else
+				b |= 4;  //right button
+		}
+		mousetrack(b, m.x, m.y, 0);
+		break;
+	case WM_SYSKEYDOWN:
+		if(gkscanq)
+			scancode(wparam, lparam, 0);
+		break;
+	case WM_SYSKEYUP:
+		if(gkscanq)
+			scancode(wparam, lparam, 1);
+		else if(wparam == VK_MENU)
+			gkbdputc(gkbdq, Latin);
+		break;
+	case WM_KEYDOWN:
+		if(gkscanq) {
+			scancode(wparam, lparam, 0);
+			break;
+		}
+		switch(wparam) {
+		default:
+			return 0;
+		case VK_HOME:
+			wparam = Home;
+			break;
+		case VK_END:
+			wparam = End;
+			break;
+		case VK_UP:
+			wparam = Up;
+			break;
+		case VK_DOWN:
+			wparam = Down;
+			break;
+		case VK_LEFT:
+			wparam = Left;
+			break;
+		case VK_RIGHT:
+			wparam = Right;
+			break;
+		case VK_PRIOR:	/* VK_PAGE_UP */
+			wparam = Pgup;
+			break;
+		case VK_NEXT:		/* VK_PAGE_DOWN */
+			wparam = Pgdown;
+			break;
+		case VK_PRINT:
+			wparam = Print;
+			break;
+		case VK_SCROLL:
+			wparam = Scroll;
+			break;
+		case VK_PAUSE:
+			wparam = Pause;
+			break;
+		case VK_INSERT:
+			wparam = Ins;
+			break;
+		case VK_DELETE:
+			wparam = Del;
+			break;
+/*
+		case VK_TAB:
+			if(GetKeyState(VK_SHIFT)<0)
+				wparam = BackTab;
+			else
+				wparam = '\t';
+			break;
+*/
+		}
+		gkbdputc(gkbdq, wparam);
+		break;
+	case WM_KEYUP:
+		if(gkscanq)
+			scancode(wparam, lparam, 1);
+		break;
+	case WM_CHAR:
+		if(gkscanq)
+			break;
+		switch(wparam) {
+		case '\n':
+		  	wparam = '\r';
+		  	break;
+		case '\r':
+		  	wparam = '\n';
+		  	break;
+		case '\t':
+			if(GetKeyState(VK_SHIFT)<0)
+				wparam = BackTab;
+			else
+				wparam = '\t';
+			break;
+		}
+		if(lparam & KF_ALTDOWN) 
+		    	wparam = APP | (wparam & 0xFF);
+		gkbdputc(gkbdq, wparam);
+		break;
+	case WM_CLOSE:
+		DestroyWindow(hwnd);
+		break;
+	case WM_DESTROY:
+		PostQuitMessage(0);
+		cleanexit(0);
+		break;
+	case WM_PALETTECHANGED:
+		if((HWND)wparam == hwnd)
+			break;
+	/* fall through */
+	case WM_QUERYNEWPALETTE:
+		hdc = GetDC(hwnd);
+		SelectPalette(hdc, palette, 0);
+		if(RealizePalette(hdc) != 0)
+			InvalidateRect(hwnd, nil, 0);
+		ReleaseDC(hwnd, hdc);
+		break;
+	case WM_PAINT:
+		hdc = BeginPaint(hwnd, &paint);
+		SelectPalette(hdc, palette, 0);
+		RealizePalette(hdc);
+		x = paint.rcPaint.left;
+		y = paint.rcPaint.top;
+		w = paint.rcPaint.right - x;
+		h = paint.rcPaint.bottom - y;
+		BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
+		EndPaint(hwnd, &paint);
+		break;
+	case WM_GETMINMAXINFO:
+		mmi = (LPMINMAXINFO)lparam;
+		mmi->ptMaxSize.x = maxxsize;
+		mmi->ptMaxSize.y = maxysize;
+		mmi->ptMaxTrackSize.x = maxxsize;
+		mmi->ptMaxTrackSize.y = maxysize;
+		break;
+	case WM_SYSCHAR:
+	case WM_COMMAND:
+	case WM_CREATE:
+	case WM_SETFOCUS:
+	case WM_DEVMODECHANGE:
+	case WM_WININICHANGE:
+	case WM_INITMENU:
+	default:
+		if(isunicode)
+			return DefWindowProcW(hwnd, msg, wparam, lparam);
+		return DefWindowProcA(hwnd, msg, wparam, lparam);
+	}
+	return 0;
+}
+
+static DWORD WINAPI
+winproc(LPVOID x)
+{
+	MSG msg;
+	RECT size;
+	WNDCLASSW wc;
+	WNDCLASSA wca;
+	DWORD ws;
+
+	if(!previnst){
+		wc.style = CS_DBLCLKS;
+		wc.lpfnWndProc = WindowProc;
+		wc.cbClsExtra = 0;
+		wc.cbWndExtra = 0;
+		wc.hInstance = inst;
+		wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
+		wc.hCursor = NULL;
+		wc.hbrBackground = GetStockObject(WHITE_BRUSH);
+
+		wc.lpszMenuName = 0;
+		wc.lpszClassName = L"inferno";
+
+		if(RegisterClassW(&wc) == 0){
+			wca.style = wc.style;
+			wca.lpfnWndProc = wc.lpfnWndProc;
+			wca.cbClsExtra = wc.cbClsExtra;
+			wca.cbWndExtra = wc.cbWndExtra;
+			wca.hInstance = wc.hInstance;
+			wca.hIcon = wc.hIcon;
+			wca.hCursor = wc.hCursor;
+			wca.hbrBackground = wc.hbrBackground;
+
+			wca.lpszMenuName = 0;
+			wca.lpszClassName = "inferno";
+			isunicode = 0;
+
+			RegisterClassA(&wca);
+		}
+	}
+
+	size.left = 0;
+	size.top = 0;
+	size.right = Xsize;
+	size.bottom = Ysize;
+
+	ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
+
+	if(AdjustWindowRect(&size, ws, 0)) {
+		maxxsize = size.right - size.left;
+		maxysize = size.bottom - size.top;
+	}else{
+		maxxsize = Xsize + 40;
+		maxysize = Ysize + 40;
+	}
+
+	if(isunicode) {
+		window = CreateWindowExW(
+			0,			/* extended style */
+			L"inferno",		/* class */
+			L"Inferno",		/* caption */
+			ws,	/* style */
+			CW_USEDEFAULT,		/* init. x pos */
+			CW_USEDEFAULT,		/* init. y pos */
+			maxxsize,		/* init. x size */
+			maxysize,		/* init. y size */
+			NULL,			/* parent window (actually owner window for overlapped) */
+			NULL,			/* menu handle */
+			inst,			/* program handle */
+			NULL			/* create parms */
+			);
+	}else{
+		window = CreateWindowExA(
+			0,			/* extended style */
+			"inferno",		/* class */
+			"Inferno",		/* caption */
+			ws,	/* style */
+			CW_USEDEFAULT,		/* init. x pos */
+			CW_USEDEFAULT,		/* init. y pos */
+			maxxsize,		/* init. x size */
+			maxysize,		/* init. y size */
+			NULL,			/* parent window (actually owner window for overlapped) */
+			NULL,			/* menu handle */
+			inst,			/* program handle */
+			NULL			/* create parms */
+			);
+	}
+
+	if(window == nil){
+		fprint(2, "can't make window\n");
+		ExitThread(0);
+	}
+
+	SetForegroundWindow(window);
+	ShowWindow(window, cmdshow);
+	UpdateWindow(window);
+	// CloseWindow(window);
+
+	if(isunicode) {
+		while(GetMessageW(&msg, NULL, 0, 0)) {
+			TranslateMessage(&msg);
+			DispatchMessageW(&msg);
+		}
+	}else{
+		while(GetMessageA(&msg, NULL, 0, 0)) {
+			TranslateMessage(&msg);
+			DispatchMessageA(&msg);
+		}
+	}
+	attached = 0;
+	ExitThread(msg.wParam);
+	return 0;
+}
+
+void
+setpointer(int x, int y)
+{
+	POINT pt; 
+ 
+	pt.x = x; pt.y = y;
+	ClientToScreen(window, &pt);
+	SetCursorPos(pt.x, pt.y);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	HCURSOR nh, oh;
+	Rectangle ir;
+	int i, h, j, bpl, ch, cw;
+	uchar *bs, *bc, *and, *xor, *cand, *cxor;
+
+	/* Set the default system cursor */
+	if(c->data == nil) {
+		oh = hcursor;
+		hcursor = NULL;
+		if(oh != NULL) {
+			SendMessage(window, WM_SETCURSOR, (int)window, 0);
+			DestroyCursor(oh);
+		}
+		return;
+	}
+
+	ir.min.x = c->minx;
+	ir.min.y = c->miny;
+	ir.max.x = c->maxx;
+	ir.max.y = c->maxy;
+	bpl = bytesperline(ir, 1);
+
+	h = (c->maxy-c->miny)/2;
+
+	ch = GetSystemMetrics(SM_CYCURSOR);
+	cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
+
+	i = ch*cw;
+	and = malloc(2*i);
+	if(and == nil)
+		return;
+	xor = and + i;
+	memset(and, 0xff, i);
+	memset(xor, 0, i);
+
+	cand = and;
+	cxor = xor;
+	bc = c->data;
+	bs = c->data + h*bpl;	
+
+	for(i = 0; i < ch && i < h; i++) {
+		for(j = 0; j < cw && j < bpl; j++) {
+			cand[j] = ~(bs[j] | bc[j]);
+			cxor[j] = ~bs[j] & bc[j];
+		}
+		cand += cw;
+		cxor += cw;
+		bs += bpl;
+		bc += bpl;
+	}
+	nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
+	if(nh != NULL) {
+		oh = hcursor;
+		hcursor = nh;
+		SendMessage(window, WM_SETCURSOR, (int)window, 0);
+		if(oh != NULL)
+			DestroyCursor(oh);
+	}else{
+		print("CreateCursor error %d\n", GetLastError());
+		print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
+		print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
+	}
+	free(and);
+}
+
+/*
+ * thanks to drawterm for these
+ */
+
+static char*
+clipreadunicode(HANDLE h)
+{
+	Rune16 *p;
+	int n;
+	char *q;
+	
+	p = GlobalLock(h);
+	n = rune16nlen(p, runes16len(p)+1);
+	q = malloc(n);
+	if(q != nil)
+		runes16toutf(q, p, n);
+	GlobalUnlock(h);
+
+	if(q == nil)
+		error(Enovmem);
+	return q;
+}
+
+static char *
+clipreadutf(HANDLE h)
+{
+	uchar *p;
+
+	p = GlobalLock(h);
+	p = strdup(p);
+	GlobalUnlock(h);
+
+	if(p == nil)
+		error(Enovmem);
+	return p;
+}
+
+char*
+clipread(void)
+{
+	HANDLE h;
+	char *p;
+
+	if(!OpenClipboard(window))
+		return strdup("");
+
+	if((h = GetClipboardData(CF_UNICODETEXT)))
+		p = clipreadunicode(h);
+	else if((h = GetClipboardData(CF_TEXT)))
+		p = clipreadutf(h);
+	else
+		p = strdup("");
+	
+	CloseClipboard();
+	return p;
+}
+
+int
+clipwrite(char *buf)
+{
+	HANDLE h;
+	char *p;
+	Rune16 *rp;
+	int n;
+
+	n = 0;
+	if(buf != nil)
+		n = strlen(buf);
+	if(!OpenClipboard(window))
+		return -1;
+
+	if(!EmptyClipboard()){
+		CloseClipboard();
+		return -1;
+	}
+
+	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune16));
+	if(h == NULL)
+		error(Enovmem);
+	rp = GlobalLock(h);
+	utftorunes16(rp, buf, n+1);
+	GlobalUnlock(h);
+
+	SetClipboardData(CF_UNICODETEXT, h);
+
+	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
+	if(h == NULL)
+		error(Enovmem);
+	p = GlobalLock(h);
+	memmove(p, buf, n);
+	p[n] = 0;
+	GlobalUnlock(h);
+	
+	SetClipboardData(CF_TEXT, h);
+
+	CloseClipboard();
+	return n;
+}
--- /dev/null
+++ b/emu/OpenBSD/asm-386.S
@@ -1,0 +1,111 @@
+	.file	"asm-OpenBSD-386.S"
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+#include "rfork_thread.S"
+
+/*
+ * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg)
+ */
+
+	.type	 ournewstack,@function
+	.global	executeonnewstack
+executeonnewstack:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%esi
+
+	movl	8(%ebp), %esi	/* get tos */
+	subl	$4, %esi
+	movl	16(%ebp), %eax
+	movl	%eax, (%esi)	/* stash arg on new stack */
+	subl	$4, %esi
+	movl	12(%ebp), %eax
+	movl	%eax, (%esi)	/* stash tramp on new stack */
+	mov	%esi, %esp	/* swap stacks pronto */
+	popl	%eax		/* recover the tramp address */
+	call	*%eax		/* and jump to it (ho ho) */
+
+	/* if we return here, tramp didn't do it's job */
+
+	addl	$8, %esp	/* clean up for pose value */
+
+	leal	SYS_exit, %eax
+	int	$0x80
+
+/*
+ * unlockandexit(int *key)
+ *
+ * NB: the return status may be rubbish if the stack is reused
+ *	between the unlock and the system call, but this should
+ *	not matter since no task is waiting for the result
+ */
+
+	.type	unlockandexit,@function
+	.global	unlockandexit
+unlockandexit:
+	pushl	%ebp
+	movl	%esp, %ebp
+
+	movl	8(%ebp), %esi		/* get the key address */
+	pushl	$0			/* exit status 0 */
+	movl	$0, %eax		/* unlock the stack allocator */
+	movl	%eax, (%esi)
+	leal	SYS_exit, %eax		/* call exit */
+	int	$0x80
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+	.type	umult,@function
+	.global	umult
+umult:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+
+	movl	8(%ebp), %eax
+	movl	12(%ebp), %ebx
+	mull	%ebx
+	movl	16(%ebp), %ebx
+	movl	%edx, (%ebx)
+
+	popl	%ebx
+	popl	%ebp
+	ret
+
+	.type	FPsave,@function
+	.global	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fstenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	FPrestore,@function
+	.global	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	fldenv	(%eax)
+	popl	%ebp
+	ret
+
+	.type	getcallerpc,@function
+	.global	getcallerpc
+getcallerpc:
+	movl	4(%ebp), %eax
+	ret
+
+	.type	_tas,@function
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/OpenBSD/audio.c
@@ -1,0 +1,547 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <soundcard.h>
+
+#define 	Audio_Mic_Val		SOUND_MIXER_MIC
+#define 	Audio_Linein_Val	SOUND_MIXER_LINE
+
+#define	Audio_Speaker_Val	SOUND_MIXER_SPEAKER
+#define	Audio_Headphone_Val	SOUND_MIXER_PHONEOUT
+#define	Audio_Lineout_Val	SOUND_MIXER_VOLUME
+
+#define 	Audio_Pcm_Val		AFMT_S16_LE
+#define 	Audio_Ulaw_Val		AFMT_MU_LAW
+#define 	Audio_Alaw_Val		AFMT_A_LAW
+
+#include "audio-tbls.c"
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+static int debug;
+
+#define AUDIO_FILE_STRING	"/dev/dsp"
+
+enum {
+	A_Pause,
+	A_UnPause
+};
+
+enum {
+	A_In,
+	A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static	int	audio_file  = -1;	/* file in/out */
+static	int	audio_file_in  = -1;	/* copy of above when opened O_READ/O_RDWR */
+static	int	audio_file_out  = -1;	/* copy of above when opened O_WRITE/O_RDWR */
+
+static	int	audio_swap_flag = 0;	/* endian swap */
+
+static	int	audio_in_pause = A_UnPause;
+
+static Audio_t av;
+static int mixerleftvol[32];
+static int mixerrightvol[32];
+
+static int audio_enforce(Audio_t*);
+static int audio_open(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+	int i;
+	static ushort flag = 1;
+
+	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
+	audio_info_init(&av);
+	for (i = 0; i < 32; i++)
+		mixerleftvol[i] = mixerrightvol[i] = 100;
+}
+
+void
+audio_ctl_init(void)
+{
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	char ebuf[ERRMAX];
+
+	if (debug)
+		print("audio_file_open(0x%.8lux, %d)\n", c, omode);
+	switch(omode){
+	case OREAD:
+		qlock(&inlock);
+		if(waserror()){
+			qunlock(&inlock);
+			nexterror();
+		}
+
+		if(audio_file_in >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file;
+		poperror();
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			qunlock(&inlock);
+			nexterror();
+		}
+		if(audio_file_in >= 0 || audio_file_out >= 0)
+			error(Einuse);
+		if (audio_file < 0)
+			audio_file = audio_open();
+		audio_file_in = audio_file_out = audio_file;
+		poperror();
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+	if (debug)
+		print("audio_file_open: success\nin %d out %d both %d\n",
+			audio_file_out, audio_file_in, audio_file);
+}
+
+void
+audio_ctl_open(Chan *c, int omode)
+{
+	USED(c);
+	USED(omode);
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_out < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		qunlock(&outlock);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&inlock);
+		qlock(&outlock);
+		if (audio_file_in < 0) {
+			close(audio_file);
+			audio_file = -1;
+		}
+		audio_file_out = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		close(audio_file);
+		audio_file_in = audio_file_out = audio_file = -1;
+		qunlock(&outlock);
+		qunlock(&inlock);
+		break;
+	}
+}
+
+void
+audio_ctl_close(Chan *c)
+{
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long ba, status, chunk, total;
+	char *pva = (char *) va;
+
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(audio_file_in < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(! audio_pause_in(audio_file_in, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count) {
+		chunk = count - total;
+		osenter();
+		status = read(audio_file_in, pva + total, chunk);
+		osleave(); 
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+
+	if(total != count)
+		error(Eio);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(pva, count); 
+
+	poperror();
+	qunlock(&inlock);
+
+	return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long status = -1;
+	long ba, total, chunk, bufsz;
+
+	if (debug > 1)
+		print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n",
+			c, va, count, offset);
+
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	if(audio_file_out < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(va, count); 
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count) {
+		chunk = min(bufsz, count - total);
+		osenter();
+		status = write(audio_file_out, va, chunk);
+		osleave();
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	return count;
+}
+
+static int
+audio_open(void)
+{
+	int fd;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK);
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		if (debug)
+			print("audio_open: failed to set blocking\n");
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	if (debug)
+		print("audio_open: blocking set\n");
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = 0;
+
+	if(! audio_set_info(fd, &av.in, A_In)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	int	fd;
+	int	ff;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if (!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if (!audio_enforce(&tmpav))
+		error(Ebadarg);
+
+	qlock(&inlock);
+	if (waserror()) {
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+		if (!audio_pause_in(audio_file_in, A_Pause))
+			error(Ebadarg);
+		if (!audio_flush(audio_file_in, A_In))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&inlock);
+
+	qlock(&outlock);
+	if (waserror()) {
+		qunlock(&outlock);
+		nexterror();
+	}
+	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+		if (!audio_pause_out(audio_file_out))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&outlock);
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+doioctl(int fd, int ctl, int *info)
+{
+	int status;
+	osenter();
+	status = ioctl(fd, ctl, info);  /* qlock and load general stuff */
+	osleave();
+	if (status < 0)
+		print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno);
+	return status;
+}
+
+static int
+choosefmt(Audio_d *i)
+{
+	int newbits, newenc;
+	
+	newbits = i->bits;
+	newenc = i->enc;
+	switch (newenc) {
+	case Audio_Alaw_Val:
+		if (newbits == 8)
+			return AFMT_A_LAW;
+		break;
+	case Audio_Ulaw_Val:
+		if (newbits == 8)
+			return AFMT_MU_LAW;
+		break;
+	case Audio_Pcm_Val:
+		if (newbits == 8)
+			return AFMT_U8;
+		else if (newbits == 16)
+			return AFMT_S16_LE;
+		break;
+	}
+	return -1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status;
+	int unequal_stereo = 0;
+
+	if(fd < 0)
+		return 0;
+
+	/* fmt */
+	if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) {
+		int oldfmt, newfmt;
+		oldfmt = AFMT_QUERY;
+		if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0)
+			return 0;
+		if (debug)
+			print("audio_set_info: current format 0x%.8lux\n", oldfmt);
+		newfmt = choosefmt(i);
+		if (debug)
+			print("audio_set_info: new format 0x%.8lux\n", newfmt);
+		if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0)
+			return 0;
+	}
+
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG) {
+		int channels = i->chan;
+		if (debug)
+			print("audio_set_info: new channels %d\n", channels);
+		if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0
+			|| channels != i->chan)
+			return 0;
+	}
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG) {
+		int speed = i->rate;
+		if (debug)
+			print("audio_set_info: new speed %d\n", speed);
+		if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate)
+			return 0;
+	}
+
+	/* dev volume */
+	if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) {
+		int val;
+		if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG))
+			mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val;
+		if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG))
+			mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val;
+		val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8);
+		doioctl(fd, MIXER_WRITE(i->dev), &val);
+	}
+
+	if (i->flags & AUDIO_DEV_FLAG) {
+	}
+	
+	return 1;
+}
+
+void 
+audio_swap_endian(char *p, int n)
+{
+	int b;
+
+	while (n > 1) {
+		b = p[0];
+		p[0] = p[1];
+		p[1] = b;
+		p += 2;
+		n -= 2;
+	}
+}
+
+static int
+audio_pause_out(int fd)
+{
+	USED(fd);
+	return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+	USED(fd);
+	USED(f);
+	return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+	int x;
+	return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && 
+		(t->in.rate != 8000 || t->in.chan != 1))
+		 return 0;
+	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && 
+		(t->out.rate != 8000 || t->out.chan != 1))
+		 return 0;
+	return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/OpenBSD/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgid(0, getpid());
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/OpenBSD/deveia.c
@@ -1,0 +1,39 @@
+/*
+ * FreeBSD serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/cuaa0",
+        "/dev/cuaa1",
+        "/dev/cuaa2",
+        "/dev/cuaa3",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{115200,	B115200},
+	{230400,	B230400},
+	{-1,		-1}
+};
+
--- /dev/null
+++ b/emu/OpenBSD/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/OpenBSD/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/OpenBSD/mkfile
@@ -1,0 +1,45 @@
+<../../mkconfig
+SYSTARG=OpenBSD
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	win-x11a.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS= -lm -lX11 -lXext -lossaudio 
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/OpenBSD/mkfile-OpenBSD
@@ -1,0 +1,17 @@
+#
+#	architecture-dependent files for OpenBSD
+#
+
+LDFLAGS=
+
+TARGFILES=devfs-posix.$O\
+	deveia-OpenBSD.$O\
+	devip.$O\
+	ipif-posix.$O\
+	os-OpenBSD.$O\
+	win-x11.$O\
+	srv.$O\
+	lock.$O\
+	asm-OpenBSD-$OBJTYPE.$O
+
+SYSLIBS=/usr/X11R6/lib/libX11.a -lm 
--- /dev/null
+++ b/emu/OpenBSD/os.c
@@ -1,0 +1,533 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef getwd
+#include <sys/types.h>
+#include <sys/mman.h>
+#include	<sys/param.h>
+#include	<sys/resource.h>
+#include 	<sys/socket.h>
+#include	<sys/time.h>
+#include	<signal.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<sched.h>
+#include	<pwd.h>
+#include	<errno.h>
+#include	<unistd.h>
+
+enum
+{
+	DELETE  = 0x7F,
+	NSTACKSPERALLOC = 16,
+	X11STACK=	256*1024
+};
+char *hosttype = "OpenBSD";
+
+int rfork_thread(int, void *, void (*)(void *), void *);
+
+extern void unlockandexit(int*);
+extern void executeonnewstack(void*, void (*f)(void*), void*);
+static void *stackalloc(Proc *p, void **tos);
+static void stackfreeandexit(void *stack);
+
+extern int dflag;
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	Proc *p;
+	void *kstack;
+
+	lock(&procs.l);
+	p = up;
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(up->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", up->text, msg);
+
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	kstack = p->kstack;
+	free(p->prog);
+	free(p);
+	if(kstack != nil)
+		stackfreeandexit(kstack);
+}
+
+void
+trapBUS(int signo, siginfo_t *info, void *context)
+{
+	if(info)
+		print("trapBUS: signo: %d code: %d addr: %lx\n",
+		info->si_signo, info->si_code, info->si_addr);
+	else
+		print("trapBUS: no info\n"); 
+	disfault(nil, "Bus error");
+}
+
+static void
+trapUSR1(int signo)
+{
+	int intwait;
+
+	USED(signo);
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+	if(intwait == 0)		/* Not posted so its a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+static void
+trapUSR2(int signo)
+{
+	USED(signo);
+	/* we've done our work of interrupting sigsuspend */
+}
+
+static void
+trapILL(int signo)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+trapSEGV(int signo)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(int signo)
+{
+	char buf[64];
+	USED(signo);
+	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
+	disfault(nil, buf);
+}
+
+static sigset_t initmask;
+
+static void
+setsigs(void)
+{
+	struct sigaction act;
+	sigset_t mask;
+
+	memset(&act, 0 , sizeof(act));
+	sigemptyset(&initmask);
+	
+	signal(SIGPIPE, SIG_IGN);	/* prevent signal when devcmd child exits */
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+
+	act.sa_handler = trapUSR1;
+	act.sa_mask = initmask;
+	sigaction(SIGUSR1, &act, nil);
+
+	act.sa_handler = trapUSR2;
+	sigaction(SIGUSR2, &act, nil);
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR2);
+	sigaddset(&initmask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	/*
+ 	 * prevent Zombies forming when any process terminates
+	 */
+	act.sa_sigaction = 0;
+	act.sa_flags |= SA_NOCLDWAIT;
+	if(sigaction(SIGCHLD, &act, nil))
+		panic("sigaction SIGCHLD");
+
+	if(sflag == 0) {
+		act.sa_sigaction = trapBUS;
+		act.sa_flags |= SA_SIGINFO;
+		if(sigaction(SIGBUS, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapILL;
+		if(sigaction(SIGILL, &act, nil))
+			panic("sigaction SIGBUS");
+		act.sa_handler = trapSEGV;
+		if(sigaction(SIGSEGV, &act, nil))
+			panic("sigaction SIGSEGV");
+		act.sa_handler = trapFPE;
+		if(sigaction(SIGFPE, &act, nil))
+			panic("sigaction SIGFPE");
+		if(sigaddset(&initmask, SIGINT) == -1)
+			panic("sigaddset");
+	}
+	if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0)
+		panic("sigprocmask");
+}
+
+static void
+tramp(void *arg)
+{
+	Proc *p;
+
+	p = arg;
+	p->pid = p->sigid = getpid();
+	sigprocmask(SIG_BLOCK, &initmask, nil);	/* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */
+	(*p->func)(p->arg);
+	pexit("{Tramp}", 0);
+	_exit(0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	int pid;
+	void *tos;
+
+	p = newproc();
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(flags & KPX11){
+		p->kstack = nil;	/* never freed; also up not defined */
+		tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*);
+	}else
+		p->kstack = stackalloc(p, &tos);
+	pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p);
+	if(pid < 0)
+		panic("rfork");
+}
+
+void
+oshostintr(Proc *p)
+{
+	kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	sigset_t mask;
+
+	sigprocmask(SIG_SETMASK, NULL, &mask);
+	sigdelset(&mask, SIGUSR2);
+	sigsuspend(&mask);
+}
+
+void
+osready(Proc *p)
+{
+	if(kill(p->sigid, SIGUSR2) < 0)
+		fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+
+	kill(0, SIGKILL);
+	exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody()
+{
+	struct passwd *pwd;
+	
+	if(pwd = getpwnam("nobody")) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+libinit(char *imod)
+{
+	struct passwd *pw;
+	Proc *p;
+	void *tos;
+	char sys[64];
+
+	setsid();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	if(dflag == 0)
+		termset();
+
+	setsigs();
+
+	p = newproc();
+	p->kstack = stackalloc(p, &tos);
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+ 
+	p->env->uid = getuid();
+	p->env->gid = getgid();
+
+	executeonnewstack(tos, emuinit, imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t,(struct timezone*)0)<0)
+		return 0;
+	if(sec0==0) {
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+ 
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct timespec time;
+
+	time.tv_sec = milsec / 1000;
+	time.tv_nsec = (milsec % 1000) * 1000000;
+	nanosleep(&time, 0);
+	return 0;
+}
+
+void
+osyield(void)
+{
+	sched_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
+
+static struct {
+	Lock l;
+	void *free;
+} stacklist;
+
+static void
+_stackfree(void *stack)
+{
+	*((void **)stack) = stacklist.free;
+	stacklist.free = stack;
+}
+
+static void
+stackfreeandexit(void *stack)
+{
+	lock(&stacklist.l);
+	_stackfree(stack);
+	unlockandexit(&stacklist.l.val);
+}
+
+static void *
+stackalloc(Proc *p, void **tos)
+{
+	void *rv;
+	lock(&stacklist.l);
+	if (stacklist.free == 0) {
+		int x;
+		/*
+		 * obtain some more by using sbrk()
+		 */
+		void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
+		if (more == 0)
+			panic("stackalloc: no more stacks");
+		/*
+		 * align to KSTACK
+		 */
+		more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
+		/*
+		 * free all the new stacks onto the freelist
+		 */
+		for (x = 0; x < NSTACKSPERALLOC; x++)
+			_stackfree((char *)more + KSTACK * x);
+	}
+	rv = stacklist.free;
+	stacklist.free = *(void **)rv;
+	unlock(&stacklist.l);
+	*tos = rv + KSTACK - sizeof(void*);
+	*(Proc **)rv = p;
+	return rv;
+}
+
+int
+segflush(void *p, ulong n)
+{
+	return mprotect(p, n, PROT_EXEC|PROT_READ|PROT_WRITE);
+}
--- /dev/null
+++ b/emu/OpenBSD/rfork_thread.S
@@ -1,0 +1,104 @@
+/*-
+ * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *                8      12          16         20
+ * rfork_thread(flags, stack_addr, start_fnc, start_arg);
+ *
+ * flags:		Flags to rfork system call.  See rfork(2).
+ * stack_addr:		Top of stack for thread.
+ * start_fnc:		Address of thread function to call in child.
+ * start_arg:		Argument to pass to the thread function in child.
+ */
+
+ENTRY(rfork_thread)
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%esi
+
+	/*
+	 * Push thread info onto the new thread's stack
+	 */
+	movl	12(%ebp), %esi	# get stack addr
+
+	subl	$4, %esi
+	movl	20(%ebp), %eax	# get start argument
+	movl	%eax, (%esi)
+
+	subl	$4, %esi
+	movl	16(%ebp), %eax	# get start thread address
+	movl	%eax, (%esi)
+
+	/*
+	 * Prepare and execute the thread creation syscall
+	 */
+	pushl	8(%ebp)
+	pushl	$0
+	movl	$SYS_rfork, %eax
+	int	$0x80
+	jb 	2f
+
+	/*
+	 * Check to see if we are in the parent or child
+	 */
+	cmpl	$0, %edx
+	jnz	1f
+	addl	$8, %esp
+	popl	%esi
+	movl	%ebp, %esp
+	popl	%ebp
+	ret
+	.p2align 2
+
+	/*
+	 * If we are in the child (new thread), then
+	 * set-up the call to the internal subroutine.  If it
+	 * returns, then call __exit.
+	 */
+1:
+	movl	%esi,%esp
+	popl	%eax 
+	call	*%eax
+	addl	$4, %esp
+
+	/*
+	 * Exit system call
+	 */
+	pushl	%eax
+	pushl	$0
+	movl	$SYS_threxit, %eax
+	int	$0x80
+
+	/*
+	 * Branch here if the thread creation fails:
+	 */
+2:
+	addl	$8, %esp
+	popl	%esi
+	movl	%ebp, %esp
+	popl	%ebp
+	PIC_PROLOGUE
+	jmp     PIC_PLT(_C_LABEL(__cerror))
--- /dev/null
+++ b/emu/Plan9/asm-386.s
@@ -1,0 +1,33 @@
+
+TEXT	tramp(SB),$0
+	MOVL	nsp+0(FP), BX		/* new stack */
+	MOVL	fn+4(FP), CX		/* func to exec */
+	MOVL	arg+8(FP),DX
+
+	LEAL	-8(BX), SP		/* new stack */
+	PUSHL	DX
+	CALL	*CX
+	POPL	AX
+
+	PUSHL	$0
+	CALL	_exits(SB)
+	POPL	AX
+	RET
+
+TEXT	vstack(SB),$0
+	MOVL	arg+0(FP), AX
+	MOVL	ustack(SB), SP
+	PUSHL	AX
+	CALL	exectramp(SB)
+	POPL	AX			/* dammit ken! */
+	RET
+
+TEXT	FPsave(SB), 1, $0
+	MOVL	fpu+0(FP), AX
+	FSTENV	0(AX)
+	RET
+
+TEXT	FPrestore(SB), 1, $0
+	MOVL	fpu+0(FP), AX
+	FLDENV	0(AX)
+	RET
--- /dev/null
+++ b/emu/Plan9/asm-arm.s
@@ -1,0 +1,32 @@
+/* for VFP */
+#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
+#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
+
+#define Fpscr 1
+
+	TEXT	tramp(SB), 1, $0
+	MOVW	fn+4(FP), R1		/* func to exec */
+	MOVW	arg+8(FP), R2		/* argument */
+	SUB	$8, R0			/* new stack */
+	MOVW	R0, SP
+	MOVW	R2, R0
+	BL	(R1)
+
+	MOVW	$0, R0
+	BL	_exits(SB)
+	RET
+
+	TEXT	vstack(SB), 1, $0
+	MOVW	ustack(SB), SP
+	BL		exectramp(SB)
+	RET
+
+	TEXT	FPsave(SB), 1, $0
+	VMRS(Fpscr, 1)
+	MOVW	R1, 0(R0)
+	RET
+
+	TEXT	FPrestore(SB), 1, $0
+	MOVW	(R0), R0
+	VMSR(0, Fpscr)
+	RET
--- /dev/null
+++ b/emu/Plan9/asm-mips.s
@@ -1,0 +1,24 @@
+
+	TEXT	tramp(SB), 1, $0
+	ADDU	$-8, R1, R3		/* new stack */
+	MOVW	4(FP), R2		/* func to exec */
+	MOVW	8(FP), R1		/* arg to reg */
+	MOVW	R3, R29			/* new stack */
+	JAL	(R2)
+	MOVW	R0, R1
+	JMP	_exits(SB)
+
+	TEXT	vstack(SB), 1, $0	/* Passes &targ through R1 */
+	MOVW	ustack(SB), R29
+	JMP	exectramp(SB)
+	RET
+
+	TEXT	FPsave(SB), 1, $0
+	MOVW	FCR31, R2
+	MOVW	R2, 0(R1)
+	RET
+
+	TEXT	FPrestore(SB), 1, $0
+	MOVW	0(R1), R2
+	MOVW	R2, FCR31
+	RET
--- /dev/null
+++ b/emu/Plan9/asm-power.s
@@ -1,0 +1,28 @@
+	TEXT	tramp(SB), 1, $0
+	ADD	$-8, R3, R4		/* new stack */
+	MOVW	4(FP), R5		/* func to exec */
+	MOVW	R5, LR
+	MOVW	8(FP), R3		/* arg to reg */
+	MOVW	R4, R1			/* new stack */
+	BL	(LR)
+	MOVW	R0, R3
+	MOVW	$_exits(SB), R4
+	MOVW	R4, LR
+	BR	(LR)
+
+	TEXT	vstack(SB), 1, $0	/* Passes &targ through R3 */
+	MOVW	ustack(SB), R1
+	MOVW	$exectramp(SB), R4
+	MOVW	R4, CTR
+	BR	(CTR)
+	RETURN
+
+	TEXT	FPsave(SB), 1, $0
+	MOVFL	FPSCR, F0
+	FMOVD	F0, 0(R3)
+	RETURN
+
+	TEXT	FPrestore(SB), 1, $0
+	FMOVD	0(R3), F0
+	MOVFL	F0, FPSCR
+	RETURN
--- /dev/null
+++ b/emu/Plan9/asm-sparc.s
@@ -1,0 +1,22 @@
+	TEXT	tramp(SB), 1, $0
+	ADD	$-8, R7, R3		/* new stack */
+	MOVW	4(FP), R4		/* func to exec */
+	MOVW	8(FP), R7		/* arg to reg */
+	MOVW	R3, R1			/* new stack */
+	JMPL	(R4)
+	MOVW	R0, R7
+	JMPL	_exits(SB)		/* Leaks the stack in R29 */
+
+	TEXT	vstack(SB), 1, $0	/* Passes &targ through R7 */
+	MOVW	ustack(SB), R1
+	MOVW	$exectramp(SB), R3
+	JMP	(R3)
+	RETURN
+
+	TEXT	FPsave(SB), 1, $0
+	MOVW	FSR, 0(R7)
+	RETURN
+
+	TEXT	FPrestore(SB), 1, $0
+	MOVW	0(R7), FSR
+	RETURN
--- /dev/null
+++ b/emu/Plan9/cmd.c
@@ -1,0 +1,189 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+extern	void	vstack(void*);
+
+/*
+ * all this is for the benefit of devcmd.
+ * i hope it's grateful.
+ */
+
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* standard input, output and error */
+	int	wfd;
+	int*	spin;
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	nice;
+};
+
+/*
+ * called by vstack once it has moved to
+ * the unshared stack in the new process.
+ */
+void
+exectramp(Targ *t)
+{
+	int *fd, i, nfd;
+	char filename[128], err[ERRMAX], status[2*ERRMAX];
+
+	t->pid = getpid();
+	*t->spin = 0;	/* allow parent to proceed: can't just rendezvous: see below */
+	fd = t->fd;
+
+	snprint(filename, sizeof(filename), "#d/%d", t->wfd);
+	t->wfd = open(filename, OWRITE|OCEXEC);
+	/* if it failed, we'll manage */
+
+	nfd = MAXNFD;	/* TO DO: should read from /fd */
+	for(i = 0; i < nfd; i++)
+		if(i != fd[0] && i != fd[1] && i != fd[2] && i != t->wfd)
+			close(i);
+
+	if(fd[0] != 0){
+		dup(fd[0], 0);
+		close(fd[0]);
+	}
+	if(fd[1] != 1){
+		dup(fd[1], 1);
+		close(fd[1]);
+	}
+	if(fd[2] != 2){
+		dup(fd[2], 2);
+		close(fd[2]);
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		if(t->wfd > 0)
+			fprint(t->wfd, "chdir: %s: %r", t->dir);
+		_exits("bad dir");
+	}
+	if(t->nice)
+		oslopri();
+
+	exec(t->args[0], t->args);
+	err[0] = 0;
+	errstr(err, sizeof(err));
+	if(t->args[0][0] != '/' && t->args[0][0] != '#' &&
+	   strncmp(t->args[0], "../", 3) != 0 && strncmp(t->args[0], "./", 2) != 0 &&
+	   strlen(t->args[0])+5 < sizeof(filename)){
+		snprint(filename, sizeof(filename), "/bin/%s", t->args[0]);
+		exec(filename, t->args);
+		errstr(err, sizeof(err));
+	}
+	snprint(status, sizeof(status), "%s: can't exec: %s", t->args[0], err);
+	if(t->wfd > 0)
+		write(t->wfd, status, strlen(status));
+	_exits(status);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int spin, *spinptr, fd0[2], fd1[2], fd2[2], wfd[2], n;
+	Dir *d;
+
+	up->genbuf[0] = 0;
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+	t->args = args;
+	t->dir = dir;
+	t->nice = nice;
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(dir != nil){
+		d = dirstat(dir);
+		if(d == nil)
+			goto Error;
+		free(d);
+	}
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+
+	spinptr = &spin;
+	spin = 1;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->spin = spinptr;
+	switch(rfork(RFPROC|RFMEM|RFREND|RFNOTEG|RFFDG|RFNAMEG|RFENVG)) {
+	case -1:
+		goto Error;
+	case 0:
+		/* if child returns first from rfork, its call to vstack replaces ... */
+		vstack(t);
+		/* ... parent's return address from rfork and parent returns here */
+	default:
+		/* if parent returns first from rfork, it comes here */
+		/* can't call anything: on shared stack until child releases spin in exectramp */
+		while(*spinptr)
+			;
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		up->genbuf[n] = 0;
+		errstr(up->genbuf, sizeof(up->genbuf));
+		free(t);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	errstr(up->genbuf, sizeof(up->genbuf));	/* save the message before close */
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	free(t);
+	errstr(up->genbuf, sizeof(up->genbuf));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	return postnote(PNGROUP, t->pid, "kill");
+}
+
+int
+oscmdwait(void*, char *buf, int n)
+{
+	return await(buf, n);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/Plan9/devfs.c
@@ -1,0 +1,365 @@
+/*
+ * Plan 9 file system interface
+ */
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+	int	fd;
+	QLock;		/* serialise access to offset */
+	ulong	offset;	/* offset used only for directory reads */
+	Cname*	name;	/* Plan 9's name for file */
+	Qid	rootqid;		/* Plan 9's qid for Inferno's root */
+	char*	root;		/* prefix to strip from all names in diagnostics */
+};
+#define	FS(c)	((Fsinfo*)((c)->aux))
+
+char	rootdir[MAXROOT] = ROOT;
+
+static void
+fserr(Fsinfo *f)
+{
+	int n;
+	char *p;
+
+	oserrstr(up->env->errstr, ERRMAX);
+	if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){
+		/* don't reveal full names */
+		if(strncmp(up->env->errstr+1, f->root, n-1) == 0){
+			p = up->env->errstr+1+n;
+			memmove(up->env->errstr+1, p, strlen(p)+1);
+		}
+	}
+	error(up->env->errstr);
+}
+
+static void
+fsfree(Chan *c)
+{
+	cnameclose(FS(c)->name);
+	free(c->aux);
+}
+
+Chan*
+fsattach(char *spec)
+{
+	Chan *c;
+	Dir *d;
+	char *root;
+	Qid rootqid;
+	static int devno;
+	static Lock l;
+
+	if(!emptystr(spec)){
+		if(strcmp(spec, "*") != 0)
+			error(Ebadspec);
+		root = "/";
+	}else
+		root = rootdir;
+
+	d = dirstat(root);
+	if(d == nil)
+		fserr(nil);
+	rootqid = d->qid;
+	free(d);
+
+	c = devattach('U', spec);
+	lock(&l);
+	c->dev = devno++;
+	c->qid = rootqid;
+	unlock(&l);
+	c->aux = smalloc(sizeof(Fsinfo));
+	FS(c)->name = newcname(root);
+	FS(c)->rootqid = rootqid;
+	FS(c)->fd = -1;
+	FS(c)->root = root;
+
+	return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int j, alloc;
+	Walkqid *wq;
+	Dir *dir;
+	char *n;
+	Cname *current, *next;
+	Qid rootqid;
+
+	if(nname > 0)
+		isdir(c);	/* do we need this? */
+
+	alloc = 0;
+	current = nil;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone!=nil)
+			cclose(wq->clone);
+		cnameclose(current);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	rootqid = FS(c)->rootqid;
+	current = FS(c)->name;
+	if(current != nil)
+		incref(&current->r);
+	for(j=0; j<nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			break;
+		}
+		n = name[j];
+		if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){	/* TO DO: underlying qids aliased */
+			//print("** ufs walk '%s' -> %s\n", current->s, n);
+			next = current;
+			incref(&next->r);
+			next = addelem(current, n);
+			dir = dirstat(next->s);
+			if(dir == nil){
+				cnameclose(next);
+				if(j == 0)
+					error(Enonexist);
+				strcpy(up->env->errstr, Enonexist);
+				break;
+			}
+			nc->qid = dir->qid;
+			free(dir);
+			cnameclose(current);
+			current = next;
+		}
+		wq->qid[wq->nqid++] = nc->qid;
+	}
+//	print("** ufs walk '%s'\n", current->s);
+
+	poperror();
+	if(wq->nqid < nname){
+		cnameclose(current);
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else if(wq->clone){
+		/* now attach to our device */
+		nc->aux = smalloc(sizeof(Fsinfo));
+		nc->type = c->type;
+		FS(nc)->rootqid = FS(c)->rootqid;
+		FS(nc)->name = current;
+		FS(nc)->fd = -1;
+		FS(nc)->root = FS(c)->root;
+	}else
+		panic("fswalk: can't happen");
+	return wq;
+}
+
+int
+fsstat(Chan *c, uchar *dp, int n)
+{
+	if(FS(c)->fd >= 0)
+		n = fstat(FS(c)->fd, dp, n);
+	else
+		n = stat(FS(c)->name->s, dp, n);
+	if(n < 0)
+		fserr(FS(c));
+	/* TO DO: change name to / if rootqid */
+	return n;
+}
+
+Chan*
+fsopen(Chan *c, int mode)
+{
+	osenter();
+	FS(c)->fd = open(FS(c)->name->s, mode);
+	osleave();
+	if(FS(c)->fd < 0)
+		fserr(FS(c));
+	c->mode = openmode(mode);
+	c->offset = 0;
+	FS(c)->offset = 0;
+	c->flag |= COPEN;
+	return c;
+}
+
+void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+	Dir *d;
+	Cname *n;
+
+	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+		error(Efilename);
+	n = addelem(newcname(FS(c)->name->s), name);
+	osenter();
+	FS(c)->fd = create(n->s, mode, perm);
+	osleave();
+	if(FS(c)->fd < 0) {
+		cnameclose(n);
+		fserr(FS(c));
+	}
+	d = dirfstat(FS(c)->fd);
+	if(d == nil) {
+		cnameclose(n);
+		close(FS(c)->fd);
+		FS(c)->fd = -1;
+		fserr(FS(c));
+	}
+	c->qid = d->qid;
+	free(d);
+
+	cnameclose(FS(c)->name);
+	FS(c)->name = n;
+
+	c->mode = openmode(mode);
+	c->offset = 0;
+	FS(c)->offset = 0;
+	c->flag |= COPEN;
+}
+
+void
+fsclose(Chan *c)
+{
+	if(c->flag & COPEN){
+		osenter();
+		close(FS(c)->fd);
+		osleave();
+	}
+	/* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */
+	fsfree(c);
+}
+
+static long
+fsdirread(Chan *c, void *va, long count, vlong offset)
+{
+	long n, r;
+	static char slop[16384];
+
+	if(FS(c)->offset != offset){
+		seek(FS(c)->fd, 0, 0);
+		for(n=0; n<offset;) {
+			r = offset - n;
+			if(r > sizeof(slop))
+				r = sizeof(slop);
+			osenter();
+			r = read(FS(c)->fd, slop, r);
+			osleave();
+			if(r <= 0){
+				FS(c)->offset = n;
+				return 0;
+			}
+			n += r;
+		}
+		FS(c)->offset = offset;
+	}
+	osenter();
+	r = read(FS(c)->fd, va, count);
+	osleave();
+	if(r < 0)
+		return r;
+	FS(c)->offset = offset+r;
+	return r;
+}
+
+long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+	int r;
+
+	if(c->qid.type & QTDIR){	/* need to maintain offset only for directories */
+		qlock(FS(c));
+		if(waserror()){
+			qunlock(FS(c));
+			nexterror();
+		}
+		r = fsdirread(c, va, n, offset);
+		poperror();
+		qunlock(FS(c));
+	}else{
+		osenter();
+		r = pread(FS(c)->fd, va, n, offset);
+		osleave();
+	}
+	if(r < 0)
+		fserr(FS(c));
+	return r;
+}
+
+long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+	int r;
+
+	osenter();
+	r = pwrite(FS(c)->fd, va, n, offset);
+	osleave();
+	if(r < 0)
+		fserr(FS(c));
+	return r;
+}
+
+void
+fsremove(Chan *c)
+{
+	int r;
+
+	if(waserror()){
+		fsfree(c);
+		nexterror();
+	}
+	osenter();
+	r = remove(FS(c)->name->s);
+	osleave();
+	if(r < 0)
+		fserr(FS(c));
+	poperror();
+	fsfree(c);
+}
+
+int
+fswstat(Chan *c, uchar *dp, int n)
+{
+	osenter();
+	if(FS(c)->fd >= 0)
+		n = fwstat(FS(c)->fd, dp, n);
+	else
+		n = wstat(FS(c)->name->s, dp, n);
+	osleave();
+	if(n < 0)
+		fserr(FS(c));
+	return n;
+}
+
+void
+setid(char *name, int owner)
+{
+	if(!owner || iseve())
+		kstrdup(&up->env->user, name);
+}
+
+Dev fsdevtab = {
+	'U',
+	"fs",
+
+	devinit,
+	fsattach,
+	fswalk,
+	fsstat,
+	fsopen,
+	fscreate,
+	fsclose,
+	fsread,
+	devbread,
+	fswrite,
+	devbwrite,
+	fsremove,
+	fswstat
+};
--- /dev/null
+++ b/emu/Plan9/devsrv9.c
@@ -1,0 +1,395 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+typedef struct Srv Srv;
+struct Srv
+{
+	Ref;
+	int	fd;	/* fd for opened /srv or /srv/X, or -1 */
+	int	sfd;	/* fd for created /srv entry or -1 */
+	uvlong	path;
+	Srv	*next;
+};
+
+static QLock	srv9lk;
+static Srv	*srv9;
+static Srv	*srvroot;
+
+static char*
+srvname(Chan *c)
+{
+	char *p;
+
+	p = strrchr(c->name->s, '/');
+	if(p == nil)
+		return "";
+	return p+1;
+}
+
+static Srv*
+srvget(uvlong path)
+{
+	Srv *sv;
+
+	qlock(&srv9lk);
+	for(sv = srv9; sv != nil; sv = sv->next)
+		if(sv->path == path){
+			incref(sv);
+			qunlock(&srv9lk);
+			return sv;
+		}
+	sv = smalloc(sizeof(*sv));
+	sv->path = path;
+	sv->fd = -1;
+	sv->sfd = -1;
+	sv->ref = 1;
+	sv->next = srv9;
+	srv9 = sv;
+	qunlock(&srv9lk);
+	return sv;
+}
+
+static void
+srvput(Srv *sv)
+{
+	Srv **l;
+	int fd, sfd;
+
+	if(sv != nil && decref(sv) == 0){
+		qlock(&srv9lk);
+		for(l = &srv9; *l != nil; l = &(*l)->next)
+			if(*l == sv){
+				*l = sv->next;
+				break;
+			}
+		qunlock(&srv9lk);
+		fd = sv->fd;
+		sfd = sv->sfd;
+		free(sv);
+		if(sfd >= 0){
+			osenter();
+			close(sfd);
+			osleave();
+		}
+		if(fd >= 0){
+			osenter();
+			close(fd);
+			osleave();
+		}
+	}
+}
+
+static void
+srv9init(void)
+{
+	Srv *sv;
+
+	sv = mallocz(sizeof(*srvroot), 1);
+	sv->path = 0;
+	sv->fd = -1;
+	sv->ref = 1;	/* subsequently never reaches zero */
+	srvroot = srv9 = sv;
+}
+
+static Chan*
+srv9attach(char *spec)
+{
+	Chan *c;
+
+	if(*spec)
+		error(Ebadspec);
+	c = devattach(L'₪', spec);
+	if(c != nil){
+		incref(srvroot);
+		c->aux = srvroot;
+	}
+	return c;
+}
+
+static Walkqid*
+srv9walk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int j, alloc;
+	Walkqid *wq;
+	char *n;
+	Dir *d;
+
+	if(nname > 0)
+		isdir(c);
+
+	alloc = 0;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc)
+			cclose(wq->clone);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;	/* device doesn't know about this channel yet */
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	for(j=0; j<nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			break;
+		}
+		n = name[j];
+		if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){
+			snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n);
+			d = dirstat(up->genbuf);
+			if(d == nil){
+				if(j == 0)
+					error(Enonexist);
+				kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+				break;
+			}
+			nc->qid = d->qid;
+			free(d);
+		}
+		wq->qid[wq->nqid++] = nc->qid;
+	}
+	poperror();
+	if(wq->nqid < nname){
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else{
+		/* attach cloned channel to device */
+		wq->clone->type = c->type;
+		if(wq->clone != c)
+			nc->aux = srvget(nc->qid.path);
+	}
+	return wq;
+}
+
+static int
+srv9stat(Chan *c, uchar *db, int n)
+{
+	Srv *sv;
+	Dir d;
+
+	if(c->qid.type & QTDIR){
+		devdir(c, c->qid, "#₪", 0, eve, 0775, &d);
+		n = convD2M(&d, db, n);
+		if(n == 0)
+			error(Eshortstat);
+		return n;
+	}
+	sv = c->aux;
+	if(sv->fd >= 0){
+		osenter();
+		n = fstat(sv->fd, db, n);
+		osleave();
+	}else{
+		osenter();
+		n = stat(srvname(c), db, n);
+		osleave();
+	}
+	return n;
+}
+
+static Chan*
+srv9open(Chan *c, int omode)
+{
+	Srv *sv;
+	char *args[10];
+	int fd[2], i, ifd, is9p;
+	Dir *d;
+
+	sv = c->aux;
+	if(c->qid.type == QTDIR){
+		osenter();
+		sv->fd = open("/srv", omode);
+		osleave();
+		if(sv->fd < 0)
+			oserror();
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	if(omode&OTRUNC || openmode(omode) != ORDWR)
+		error(Eperm);
+	if(sv->fd < 0){
+		snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c));
+
+		/* check permission */
+		osenter();
+		ifd = open(up->genbuf, omode);
+		osleave();
+		if(ifd < 0)
+			oserror();
+		osenter();
+		d = dirfstat(ifd);
+		is9p = d != nil && d->qid.type & QTMOUNT;
+		free(d);
+		osleave();
+
+		if(is9p){
+			close(ifd);
+
+			/* spawn exportfs */
+			args[0] = "exportfs";
+			args[1] = "-S";
+			args[2] = up->genbuf;
+			args[3] = nil;
+			if(pipe(fd) < 0)
+				oserror();
+			/* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */
+			switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){	/* no sharing except NOTEG */
+			case -1:
+				oserrstr(up->genbuf, sizeof(up->genbuf));
+				close(fd[0]);
+				close(fd[1]);
+				error(up->genbuf);
+			case 0:
+				for(i=3; i<MAXNFD; i++)
+					if(i != fd[1])
+						close(i);
+				dup(fd[1], 0);
+				if(fd[0] != 0)
+					close(fd[0]);
+				dup(0, 1);
+				exec("/bin/exportfs", args);
+				exits("exportfs failed");
+			default:
+				sv->fd = fd[0];
+				close(fd[1]);
+				break;
+			}
+		}else
+			sv->fd = ifd;
+	}
+
+	c->mode = ORDWR;
+	c->offset = 0;
+	c->flag |= COPEN;
+	return c;
+}
+
+static void
+srv9close(Chan *c)
+{
+	srvput(c->aux);
+}
+
+static long
+srv9read(Chan *c, void *va, long n, vlong off)
+{
+	Srv *sv;
+
+	sv = c->aux;
+	osenter();
+	n = pread(sv->fd, va, n, off);
+	osleave();
+	if(n < 0)
+		oserror();
+	return n;
+}
+
+static long
+srv9write(Chan *c, void *va, long n, vlong off)
+{
+	Srv *sv;
+
+	sv = c->aux;
+	osenter();
+	n = pwrite(sv->fd, va, n, off);
+	osleave();
+	if(n == 0)
+		error(Ehungup);
+	if(n < 0)
+		oserror();
+	return n;
+}
+
+static void
+srv9create(Chan *c, char *name, int omode, ulong perm)
+{
+	Srv *sv;
+	int sfd, fd[2];
+	vlong path;
+	Dir *d;
+
+	if(openmode(omode) != ORDWR)
+		error(Eperm);
+
+	if(pipe(fd) < 0)
+		oserror();
+	if(waserror()){
+		close(fd[0]);
+		close(fd[1]);
+		nexterror();
+	}
+
+	snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name);
+	osenter();
+	sfd = create(up->genbuf, OWRITE|ORCLOSE, perm);
+	osleave();
+	if(sfd < 0)
+		oserror();
+	if(waserror()){
+		close(sfd);
+		nexterror();
+	}
+	osenter();
+	if(fprint(sfd, "%d", fd[1]) < 0){
+		osleave();
+		oserror();
+	}
+	d = dirfstat(sfd);
+	osleave();
+	if(d != nil){
+		path = d->qid.path;
+		free(d);
+	}else
+		oserror();
+
+	poperror();
+	poperror();
+	close(fd[1]);
+
+	if(waserror()){
+		close(sfd);
+		close(fd[0]);
+		nexterror();
+	}
+	sv = srvget(path);
+	sv->fd = fd[0];
+	sv->sfd = sfd;
+	poperror();
+
+	srvput((Srv*)c->aux);
+	c->qid.type = QTFILE;
+	c->qid.path = path;
+	c->aux = sv;
+	c->flag |= COPEN;
+	c->mode = ORDWR;
+	c->offset = 0;
+}
+
+Dev srv9devtab = {
+	L'₪',
+	"srv9",
+
+	srv9init,	
+	srv9attach,
+	srv9walk,
+	srv9stat,
+	srv9open,
+	srv9create,	/* TO DO */
+	srv9close,
+	srv9read,
+	devbread,
+	srv9write,
+	devbwrite,
+	devremove,	/* TO DO */
+	devwstat,	/* TO DO */
+};
--- /dev/null
+++ b/emu/Plan9/emu
@@ -1,0 +1,114 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+#	sign
+
+	draw	win
+	pointer
+
+	dynld
+	mem
+	srv9
+
+#	ip and eia are simply bound in from Plan 9
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+	dynld
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+#	srv		not used on Plan 9
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	exptab
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/chan	/
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/nvfs	/
+	/env	/
+	/root	/
+	/srv	/
+#	/tmp	/
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Plan9/emusig
@@ -1,0 +1,100 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+	sign
+
+	draw	win
+
+#	ip and eia are simply bound in from Plan 9
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+	memlayer
+	memdraw
+	keyring
+	crypt
+	9
+
+link
+
+mod
+	sys
+	draw
+	tk
+	math
+#	srv		not used on Plan 9
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Plan9/mkfile
@@ -1,0 +1,45 @@
+SYSTARG=Plan9
+OBJTYPE=$objtype
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	$DEVS\
+	$PORT\
+
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD  -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+safeinstall:V:	$O.$CONF
+	mv $INSTALLDIR/$CONF $INSTALLDIR/$CONF.`{date -n}
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Plan9/os.c
@@ -1,0 +1,428 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	KSTACK	= 16*1024,
+	DELETE	= 0x7F,
+};
+
+Proc	**Xup;
+
+extern	void	killrefresh(void);
+extern	void	tramp(char*, void (*)(void*), void*);
+
+extern	int	usenewwin;
+
+int	*ustack;	/* address on unshared stack: see vstack in asm*.s */
+extern	int	dflag;
+char *hosttype = "Plan9";
+char *cputype;
+
+void
+osblock(void)
+{
+	rendezvous(up, nil);
+}
+
+void
+osready(Proc *p)
+{
+	rendezvous(p, nil);
+}
+
+void
+pexit(char *msg, int)
+{
+	Osenv *e;
+
+	USED(msg);
+
+	lock(&procs.l);
+	if(up->prev) 
+		up->prev->next = up->next;
+	else
+		procs.head = up->next;
+
+	if(up->next)
+		up->next->prev = up->prev;
+	else
+		procs.tail = up->prev;
+	unlock(&procs.l);
+
+/*	print("pexit: %s: %s\n", up->text, msg);	/**/
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(e->user);
+	free(up->prog);
+	up->prog = nil;
+	up->type = Moribund;
+	longjmp(up->privstack, 1);
+}
+
+int
+kproc1(char *name, void (*func)(void*), void *arg, int flags)
+{
+	int pid;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+
+	p = newproc();
+	if(p == nil)
+		panic("kproc: no memory");
+	p->kstack = mallocz(KSTACK, 0);
+	if(p->kstack == nil)
+		panic("kproc: no memory");
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	/*
+	 * switch back to the unshared stack to do the fork
+	 * only the parent returns from kproc
+	 */
+	up->kid = p;
+	up->kidsp = p->kstack;
+	pid = setjmp(up->sharestack);
+	if(pid == 0)
+		longjmp(up->privstack, 1);
+	return pid;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	kproc1(name, func, arg, flags);
+}
+
+void
+traphandler(void *reg, char *msg)
+{
+	int intwait;
+
+	intwait = up->intwait;
+	up->intwait = 0;
+	/* Ignore pipe writes from devcmd */
+	if(strstr(msg, "write on closed pipe") != nil)
+		noted(NCONT);
+
+	if(sflag) {
+		if(intwait && strcmp(msg, Eintr) == 0)
+			noted(NCONT);
+		else
+			noted(NDFLT);
+	}
+	if(intwait == 0)
+		disfault(reg, msg);
+	noted(NCONT);
+}
+
+int
+readfile(char *path, char *buf, int n)
+{
+	int fd;
+
+	fd = open(path, OREAD);
+	if(fd >= 0) {
+		n = read(fd, buf, n-1);
+		if(n > 0)			/* both calls to readfile() have a ``default'' */
+			buf[n] = '\0';
+		close(fd);
+		return n;
+	}
+	return 0;
+}
+
+static void
+dobinds(void)
+{
+	char dir[MAXROOT+9];
+
+	snprint(dir, sizeof(dir), "%s/net", rootdir);
+	bind("/net", dir, MREPL);
+
+	snprint(dir, sizeof(dir), "%s/net.alt", rootdir);
+	bind("/net.alt", dir, MREPL);
+
+	snprint(dir, sizeof(dir), "%s/dev", rootdir);
+	bind("#t", dir, MAFTER);
+	bind("#A", dir, MAFTER);
+}
+
+void
+libinit(char *imod)
+{
+	char *sp;
+	Proc *xup, *p;
+	int fd, n, pid;
+	char nbuf[64];
+
+	xup = nil;
+	Xup = &xup;
+
+	/*
+	 * setup personality
+	 */
+	if(readfile("/dev/user", nbuf, sizeof nbuf))
+		kstrdup(&eve, nbuf);
+	if(readfile("/dev/sysname", nbuf, sizeof nbuf))
+		kstrdup(&ossysname, nbuf);
+	if(readfile("/env/cputype", nbuf, sizeof nbuf))
+		kstrdup(&cputype, nbuf);
+
+	/*
+	 * guess at a safe stack for vstack
+	 */
+	ustack = &fd;
+
+	rfork(RFNAMEG|RFREND);
+
+	if(!dflag){
+		fd = open("/dev/consctl", OWRITE);
+		if(fd < 0)
+			fprint(2, "libinit: open /dev/consctl: %r\n");
+		n = write(fd, "rawon", 5);
+		if(n != 5)
+			fprint(2, "keyboard rawon (n=%d, %r)\n", n);
+	}
+
+	osmillisec();	/* set the epoch */
+	dobinds();
+
+	notify(traphandler);
+
+	/*
+	 * dummy up a up and stack so the first proc
+	 * calls emuinit after setting up his private jmp_buf
+	 */
+	p = newproc();
+	p->kstack = mallocz(KSTACK, 0);
+	if(p == nil || p->kstack == nil)
+		panic("libinit: no memory");
+	sp = p->kstack;
+	p->func = emuinit;
+	p->arg = imod;
+
+	/*
+	 * set up a stack for forking kids on separate stacks.
+	 * longjmp back here from kproc.
+	 */
+	while(setjmp(p->privstack)){
+		if(up->type == Moribund){
+			free(up->kstack);
+			free(up);
+			_exits("");
+		}
+		p = up->kid;
+		sp = up->kidsp;
+		up->kid = nil;
+		switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
+		case 0:
+			/*
+			 * send the kid around the loop to set up his private jmp_buf
+			 */
+			break;
+		default:
+			/*
+			 * parent just returns to his shared stack in kproc
+			 */
+			longjmp(up->sharestack, pid);
+			panic("longjmp failed");
+		}
+	}
+
+	/*
+	 * you get here only once per Proc
+	 * go to the shared memory stack
+	 */
+	up = p;
+	up->pid = up->sigid = getpid();
+	tramp(sp+KSTACK, up->func, up->arg);
+	panic("tramp returned");
+}
+
+void
+oshostintr(Proc *p)
+{
+	postnote(PNPROC, p->sigid, Eintr);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	if(regs != nil)
+		notejmp(regs, env, val);
+	else
+		longjmp(env, val);
+}
+
+void
+osreboot(char*, char**)
+{
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+	killrefresh();
+	postnote(PNGROUP, getpid(), "interrupt");
+	exits("interrupt");
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		fprint(2, "emu: keyboard read error: %r\n");
+	if(n <= 0)
+		pexit("keyboard", 0);
+	switch(buf[0]) {
+	case DELETE:
+		cleanexit(0);
+	case '\r':
+		buf[0] = '\n';
+	}
+	return buf[0];
+}
+
+static vlong
+b2v(uchar *p)
+{
+	int i;
+	vlong v;
+
+	v = 0;
+	for(i=0; i<sizeof(uvlong); i++)
+		v = (v<<8)|p[i];
+	return v;
+}
+
+vlong
+nsec(void)
+{
+	int n;
+	static int nsecfd = -1;
+	uchar buf[sizeof(uvlong)];
+
+	if(nsecfd < 0){
+		nsecfd = open("/dev/bintime", OREAD|OCEXEC);  /* never closed */
+		if(nsecfd<0){
+			fprint(2,"can't open /dev/bintime: %r\n");
+			return 0;
+		}
+	}
+	n = read(nsecfd, buf, sizeof(buf));
+	if(n!=sizeof(buf)) {
+		fprint(2,"read err on /dev/bintime: %r\n");
+		return 0;
+	}
+	return b2v(buf);
+}
+
+long
+osmillisec(void)
+{
+	static vlong nsec0 = 0;
+
+	if(nsec0 == 0){
+		nsec0 = nsec();
+		return 0;
+	}
+	return (nsec()-nsec0)/1000000;
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osusectime(void)
+{
+	return nsec()/1000;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	sleep(milsec);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	sleep(0);
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		sleep(1000000);
+}
+
+void
+oslopri(void)
+{
+	int fd;
+	char buf[32];
+
+	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+	if((fd = open(buf, OWRITE)) >= 0){
+		fprint(fd, "pri 8");
+		close(fd);
+	}
+}
--- /dev/null
+++ b/emu/Plan9/win.c
@@ -1,0 +1,566 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"kernel.h"
+#include	"error.h"
+
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<cursor.h>
+#include	"keyboard.h"
+
+enum
+{
+	Margin	= 4,
+	Lsize		= 100,
+};
+
+extern Memimage *screenimage;
+
+extern int kproc1(char*, void (*)(void*), void*, int);
+
+static	ulong*	attachwindow(Rectangle*, ulong*, int*, int*);
+
+static void	plan9readmouse(void*);
+static void	plan9readkeybd(void*);
+static int	mapspecials(char *s1, char *s2, int *n);
+
+int	usenewwin = 1;
+int	kbdiscons;
+static int	truedepth;
+
+static int		datafd;
+static int		ctlfd;
+static int		mousefd = -1;
+static int		keybdfd;
+static int		mousepid = -1;
+static int		keybdpid = -1;
+static int		cursfd;
+static char	winname[64];
+
+/* Following updated by attachwindow() asynchronously */
+static QLock		ql;
+static Rectangle	tiler;
+static ulong*		data;
+static uchar*		loadbuf;
+static int		cursfd;
+static int		imageid;
+static Rectangle	imager;
+static uchar	*chunk;
+static int	chunksize;
+static	int	dispbufsize;
+
+#define	NINFO	12*12
+#define	HDR		21
+
+
+void
+killrefresh(void)
+{
+	if(mousepid < 0)
+		return;
+	close(mousefd);
+	close(ctlfd);
+	close(datafd);
+	postnote(PNPROC, mousepid, Eintr);
+	postnote(PNPROC, keybdpid, Eintr);
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+	int fd;
+	char *p, buf[128], info[NINFO+1];
+
+	if(usenewwin){
+		p = getenv("wsys");
+		if(p == nil)
+			return nil;
+
+		fd = open(p, ORDWR);
+		if(fd < 0) {
+			fprint(2, "attachscreen: can't open window manager: %r\n");
+			return nil;
+		}
+		sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin);
+		if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) {
+			fprint(2, "attachscreen: can't mount window manager: %r\n");
+			return nil;
+		}
+		if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
+			fprint(2, "attachscreen: can't bind /mnt/wsys before /dev: %r\n");
+			return nil;
+		}
+	}
+
+	cursfd = open("/dev/cursor", OWRITE);
+	if(cursfd < 0) {
+		fprint(2, "attachscreen: open cursor: %r\n");
+		return nil;
+	}
+	
+	/* Set up graphics window console (chars->gkbdq) */
+	keybdfd = open("/dev/cons", OREAD);
+	if(keybdfd < 0) {
+		fprint(2, "attachscreen: open keyboard: %r\n");
+		return nil;
+	}
+	mousefd = open("/dev/mouse", ORDWR);
+	if(mousefd < 0){
+		fprint(2, "attachscreen: can't open mouse: %r\n");
+		return nil;
+	}
+	if(usenewwin || 1){
+		fd = open("/dev/consctl", OWRITE);
+		if(fd < 0)
+			fprint(2, "attachscreen: open /dev/consctl: %r\n");
+		if(write(fd, "rawon", 5) != 5)
+			fprint(2, "attachscreen: write /dev/consctl: %r\n");
+	}
+
+	/* Set up graphics files */
+	ctlfd = open("/dev/draw/new", ORDWR);
+	if(ctlfd < 0){
+		fprint(2, "attachscreen: can't open graphics control file: %r\n");
+		return nil;
+	}
+	if(read(ctlfd, info, sizeof info) < NINFO){
+		close(ctlfd);
+		fprint(2, "attachscreen: can't read graphics control file: %r\n");
+		return nil;
+	}
+	sprint(buf, "/dev/draw/%d/data", atoi(info+0*12));
+	datafd = open(buf, ORDWR|OCEXEC);
+	if(datafd < 0){
+		close(ctlfd);
+		fprint(2, "attachscreen: can't read graphics data file: %r\n");
+		return nil;
+	}
+	dispbufsize = iounit(datafd);
+	if(dispbufsize <= 0)
+		dispbufsize = 8000;
+	if(dispbufsize < 512){
+		close(ctlfd);
+		close(datafd);
+		fprint(2, "attachscreen: iounit %d too small\n", dispbufsize);
+		return nil;
+	}
+	chunksize = dispbufsize - 64;
+
+	if(attachwindow(r, chan, d, width) == nil){
+		close(ctlfd);
+		close(datafd);
+		return nil;
+	}
+	
+	mousepid = kproc1("readmouse", plan9readmouse, nil, 0);
+	keybdpid = kproc1("readkbd", plan9readkeybd, nil, 0);
+
+	fd = open("/dev/label", OWRITE);
+	if(fd >= 0){
+		snprint(buf, sizeof(buf), "inferno %d", getpid());
+		write(fd, buf, strlen(buf));
+		close(fd);
+	}
+
+	*softscreen = 1;
+	return (uchar*)data;
+}
+
+static ulong*
+attachwindow(Rectangle *r, ulong *chan, int *d, int *width)
+{
+	int n, fd, nb;
+	char buf[256];
+	uchar ubuf[128];
+	ulong imagechan;
+
+	/*
+	 * Discover name of window
+	 */
+	fd = open("/mnt/wsys/winname", OREAD);
+	if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){
+		fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n");
+		return nil;
+	}
+	close(fd);
+	/*
+	 * If had previous window, release it
+	 */
+	if(imageid > 0){
+		ubuf[0] = 'f';
+		BPLONG(ubuf+1, imageid);
+		if(write(datafd, ubuf, 1+4) != 1+4)
+			fprint(2, "attachwindow: cannot free old window: %r\n");
+	}
+	/*
+	 * Allocate image pointing to window, and discover its ID
+	 */
+	ubuf[0] = 'n';
+	++imageid;
+	BPLONG(ubuf+1, imageid);
+	ubuf[5] = n;
+	memmove(ubuf+6, winname, n);
+	if(write(datafd, ubuf, 6+n) != 6+n){
+		fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname);
+		return nil;
+	}
+	if(read(ctlfd, buf, sizeof buf) < 12*12){
+		fprint(2, "attachwindow: cannot read window id: %r\n");
+		return nil;
+	}
+	imagechan = strtochan(buf+2*12);
+	truedepth = chantodepth(imagechan);
+	if(truedepth == 0){
+		fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12);
+		return nil;
+	}
+
+	/*
+	 * Report back
+	 */
+	if(chan != nil)
+		*chan = imagechan;
+	if(d != nil)
+		*d = chantodepth(imagechan);
+	nb = 0;
+	if(r != nil){
+		Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin;
+		Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin;
+		r->min.x = 0;
+		r->min.y = 0;
+		r->max.x = Xsize;
+		r->max.y = Ysize;
+		nb = bytesperline(*r, truedepth);
+		data = malloc(nb*Ysize);
+		loadbuf = malloc(nb*Lsize+1);
+		chunk = malloc(HDR+chunksize+5);	/* +5 for flush (1 old, 5 new) */
+	}
+	imager.min.x = atoi(buf+4*12);
+	imager.min.y = atoi(buf+5*12);
+	imager.max.x = atoi(buf+6*12);
+	imager.max.y = atoi(buf+7*12);
+
+	if(width != nil)
+		*width = nb/4;
+
+	tiler.min.x = atoi(buf+4*12)+Margin;
+	tiler.min.y = atoi(buf+5*12)+Margin;
+	tiler.max.x = atoi(buf+6*12)-Margin;
+	tiler.max.y = atoi(buf+7*12)-Margin;
+
+	return data;
+}
+
+static int
+plan9loadimage(Rectangle r, uchar *data, int ndata)
+{
+	long dy;
+	int n, bpl;
+
+	if(!rectinrect(r, imager)){
+		werrstr("loadimage: bad rectangle");
+		return -1;
+	}
+	bpl = bytesperline(r, truedepth);
+	n = bpl*Dy(r);
+	if(n > ndata){
+		werrstr("loadimage: insufficient data");
+		return -1;
+	}
+	ndata = 0;
+	while(r.max.y > r.min.y){
+		dy = r.max.y - r.min.y;
+		if(dy*bpl> chunksize)
+			dy = chunksize/bpl;
+		n = dy*bpl;
+		chunk[0] = 'y';
+		BPLONG(chunk+1, imageid);
+		BPLONG(chunk+5, r.min.x);
+		BPLONG(chunk+9, r.min.y);
+		BPLONG(chunk+13, r.max.x);
+		BPLONG(chunk+17, r.min.y+dy);
+		memmove(chunk+21, data, n);
+		ndata += n;
+		data += n;
+		r.min.y += dy;
+		n += 21;
+		if(r.min.y >= r.max.y)	/* flush to screen */
+			chunk[n++] = 'v';
+		if(write(datafd, chunk, n) != n)
+			return -1;
+	}
+	return ndata;
+}
+
+static void
+_flushmemscreen(Rectangle r)
+{
+	int n, dy, l;
+	Rectangle rr;
+
+	if(data == nil || loadbuf == nil || chunk==nil)
+		return;
+	if(!rectclip(&r, Rect(0, 0, Xsize, Ysize)))
+		return;
+	if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler))))
+		return;
+	if(Dx(r)<=0 || Dy(r)<=0)
+		return;
+	l = bytesperline(r, truedepth);
+	while(r.min.y < r.max.y){
+		dy = Dy(r);
+		if(dy > Lsize)
+			dy = Lsize;
+		rr = r;
+		rr.max.y = rr.min.y+dy;
+		n = unloadmemimage(screenimage, rr, loadbuf, l*dy);
+		/* offset from (0,0) to window */
+		rr.min.x += tiler.min.x;
+		rr.min.y += tiler.min.y;
+		rr.max.x += tiler.min.x;
+		rr.max.y += tiler.min.y;
+		if(plan9loadimage(rr, loadbuf, n) != n)
+			fprint(2, "flushmemscreen: %d bytes: %r\n", n);
+		r.min.y += dy;
+	}
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	qlock(&ql);
+	_flushmemscreen(r);
+	qunlock(&ql);
+}
+
+void
+drawcursor(Drawcursor *c)
+{
+	int j, i, h, w, bpl;
+	uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16];
+
+	/* Set the default system cursor */
+	if(c->data == nil) {
+		write(cursfd, curs, 0);
+		return;
+	}
+
+	BPLONG(curs+0*4, c->hotx);
+	BPLONG(curs+1*4, c->hoty);
+
+	w = (c->maxx-c->minx);
+	h = (c->maxy-c->miny)/2;
+
+	cclr = curs+2*4;
+	cset = curs+2*4+2*16;
+	bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+	bc = c->data;
+	bs = c->data + h*bpl;
+
+	if(h > 16)
+		h = 16;
+	if(w > 16)
+		w = 16;
+	w /= 8;
+	for(i = 0; i < h; i++) {
+		for(j = 0; j < w; j++) {
+			cclr[j] = bc[j];
+			cset[j] = bs[j];
+		}
+		bc += bpl;
+		bs += bpl;
+		cclr += 2;
+		cset += 2;
+	}
+	write(cursfd, curs, sizeof curs);
+}
+
+static int
+checkmouse(char *buf, int n)
+{
+	int x, y, tick, b;
+	static int lastb, lastt, lastx, lasty, lastclick;
+
+	switch(n){
+	default:
+		kwerrstr("atomouse: bad count");
+		return -1;
+
+	case 1+4*12:
+		if(buf[0] == 'r'){
+			qlock(&ql);
+			if(attachwindow(nil, nil, nil, nil) == nil) {
+				qunlock(&ql);
+				return -1;
+			}
+			_flushmemscreen(Rect(0, 0, Xsize, Ysize));
+			qunlock(&ql);
+		}
+		x = atoi(buf+1+0*12) - tiler.min.x;
+		y = atoi(buf+1+1*12) - tiler.min.y;
+		b = atoi(buf+1+2*12);
+		tick = atoi(buf+1+3*12);
+		if(b && lastb == 0){	/* button newly pressed */
+			if(b==lastclick && tick-lastt<400
+			   && abs(x-lastx)<10 && abs(y-lasty)<10)
+				b |= (1<<8);
+			lastt = tick;
+			lastclick = b&0xff;
+			lastx = x;
+			lasty = y;
+		}
+		lastb = b&0xff;
+		//mouse.msec = tick;
+		mousetrack(b, x, y, 0);
+		return n;
+	}
+}
+
+static void
+plan9readmouse(void *v)
+{
+	int n;
+	char buf[128];
+
+	USED(v);
+	for(;;){
+		n = read(mousefd, buf, sizeof(buf));
+		if(n < 0)	/* probably interrupted */
+			_exits(0);
+		checkmouse(buf, n);
+	}
+}
+
+static void
+plan9readkeybd(void*)
+{
+	int n, partial;
+	char buf[32];
+	char dbuf[32 * 3];		/* overestimate but safe */
+
+	partial = 0;
+	for(;;){
+		n = read(keybdfd, buf + partial, sizeof(buf) - partial);
+		if(n < 0)	/* probably interrupted */
+			_exits(0);
+		partial += n;
+		n = mapspecials(dbuf, buf, &partial);
+		qproduce(gkbdq, dbuf, n);
+	}
+}
+
+void
+setpointer(int x, int y)
+{
+	char buf[50];
+	int n;
+
+	if(mousefd < 0)
+		return;
+	x += tiler.min.x;
+	y += tiler.min.y;
+	n = snprint(buf, sizeof buf, "m%11d %11d ", x, y);
+	write(mousefd, buf, n);
+}
+
+/*
+ * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly
+ * because constant names clash.
+ */
+enum {
+	P9KF=	0xF000,	/* Rune: beginning of private Unicode space */
+	P9Spec=	0xF800,
+	/* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+	Khome=	P9KF|0x0D,
+	Kup=	P9KF|0x0E,
+	Kpgup=	P9KF|0x0F,
+	Kprint=	P9KF|0x10,
+	Kleft=	P9KF|0x11,
+	Kright=	P9KF|0x12,
+	Kdown=	P9Spec|0x00,
+	Kview=	P9Spec|0x00,
+	Kpgdown=	P9KF|0x13,
+	Kins=	P9KF|0x14,
+	Kend=	KF|0x18,
+
+	Kalt=		P9KF|0x15,
+	Kshift=	P9KF|0x16,
+	Kctl=		P9KF|0x17,
+};
+
+/*
+ * translate plan 9 special characters from s2 (of length *n) into s1;
+ * return number of chars placed into s1.
+ * any trailing incomplete chars are moved to the beginning of s2,
+ * and *n set to the number moved there.
+ */
+static int
+mapspecials(char *s1, char *s2, int *n)
+{
+	char *s, *d, *es2;
+	Rune r;
+	d = s1;
+	s = s2;
+	es2 = s2 + *n;
+	while (fullrune(s, es2 - s)) {
+		s += chartorune(&r, s);
+		switch (r) {
+		case Kshift:
+			r = LShift;
+			break;
+		case Kctl:
+			r = LCtrl;
+			break;
+		case Kalt:
+			r = LAlt;
+			break;
+		case Khome:
+			r = Home;
+			break;
+		case Kend:
+			r = End;
+			break;
+		case Kup:
+			r = Up;
+			break;
+		case Kdown:
+			r = Down;
+			break;
+		case Kleft:
+			r = Left;
+			break;
+		case Kright:
+			r = Right;
+			break;
+		case Kpgup:
+			r = Pgup;
+			break;
+		case Kpgdown:
+			r = Pgdown;
+			break;
+		case Kins:
+			r = Ins;
+			break;
+		/*
+		 * function keys
+		 */
+		case P9KF|1:
+		case P9KF|2:
+		case P9KF|3:
+		case P9KF|4:
+		case P9KF|5:
+		case P9KF|6:
+		case P9KF|7:
+		case P9KF|8:
+		case P9KF|9:
+		case P9KF|10:
+		case P9KF|11:
+		case P9KF|12:
+			r = (r - P9KF) + KF;
+		}
+		d += runetochar(d, &r);
+	}
+	*n = es2 - s;
+	memmove(s2, s, *n);
+	return d - s1;
+}
--- /dev/null
+++ b/emu/Solaris/asm-386.s
@@ -1,0 +1,72 @@
+	.section	.bss
+	.align	4
+.L4_.bss:
+	.align	4
+Solaris_Asm_IntP: / Offset 0
+	.type	Solaris_Asm_IntP,@object
+	.size	Solaris_Asm_IntP,4
+	.set	.,.+4
+Solaris_Asm_VoidP: / Offset 4
+	.type	Solaris_Asm_VoidP,@object
+	.size	Solaris_Asm_VoidP,4
+	.set	.,.+4
+	.section	.text
+	.align	4
+.L1_.text:
+
+/====================
+/ FPsave
+/--------------------
+	.align	4
+	.align	4
+	.globl	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp,%ebp
+	movl	8(%ebp),%eax
+	movl	%eax,Solaris_Asm_VoidP
+	fstenv	(%eax)
+	leave	
+	ret	
+	.align	4
+	.type	FPsave,@function
+	.size	FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+	.align	4
+	.globl	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp,%ebp
+	movl	8(%ebp),%eax
+	movl	%eax,Solaris_Asm_VoidP
+	fldenv	(%eax)
+	leave	
+	ret	
+	.align	4
+	.type	FPrestore,@function
+	.size	FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+	.align	4
+	.globl	getcallerpc
+getcallerpc:
+	movl	4(%ebp),%eax
+	ret	
+	.align	4
+	.type	getcallerpc,@function
+	.size	getcallerpc,.-getcallerpc
+
+/ test-and-set
+	.align	4
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/Solaris/asm-sparc.s
@@ -1,0 +1,76 @@
+
+	.section	".text", #alloc, #execinstr
+	.align		8
+	.skip		16
+	.global		segflush
+	.type		segflush, #function
+
+	!	The flush instruction works on 8-byte chunks.
+	!	We truncate the pointer and increase the count
+	!	to make sure we flush the right range.
+
+	!	SPARC requires 5 instructions after flush to
+	!	let the caches settle.  The loop code supplies
+	!	the delay instructions.
+
+segflush:				! int segflush(void *p, ulong len)
+
+	and	%o0,-8,%o0		! clear low 3 bits of p
+	add	%o1, 7, %o1		! len += 7
+1:
+	flush	%o0			! synchronize cache
+	sub	%o1, 8, %o1		! len -= 8
+	cmp	%o1, 0			! if len > 0, repeat
+	bg	1b
+	add	%o0, 8, %o0		! p += 8 in delay slot
+
+	retl
+	add	%g0, %g0, %o0		! return 0
+	.size	segflush,(.-segflush)
+
+
+        .section        ".text", #alloc, #execinstr
+        .align          8
+        .skip           16
+        .global FPsave
+        .type   FPsave, #function
+FPsave:
+	retl
+	st	%fsr,[%o0]
+        .size   FPsave,(.-FPsave)
+
+
+        .section        ".text", #alloc, #execinstr
+        .align          8
+        .skip           16
+        .global FPrestore
+        .type   FPrestore, #function
+FPrestore:
+	retl
+	ld	[%o0],%fsr
+        .size   FPrestore,(.-FPrestore)
+
+
+	.section	".text", #alloc, #execinstr
+	.align		8
+	.skip		16
+	.global getcallerpc
+	.type getcallerpc, #function
+getcallerpc:                  ! ignore argument
+	retl                    
+	add %i7,0,%o0
+
+	.size   getcallerpc,(.-getcallerpc)
+
+
+	.section	".text", #alloc, #execinstr
+	.align	8
+	.skip	16
+	.global	_tas
+	.type	_tas, #function
+_tas:
+	or	%g0,1,%o1
+	swap	[%o0],%o1
+	retl
+	or	%g0,%o1,%o0
+	.size	_tas,(.-_tas)
--- /dev/null
+++ b/emu/Solaris/audio.c
@@ -1,0 +1,593 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#define __EXTENSIONS__
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <sys/audioio.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/audioio.h>
+
+#define 	Audio_Mic_Val		AUDIO_MICROPHONE
+#define 	Audio_Linein_Val	AUDIO_LINE_IN
+
+#define	Audio_Speaker_Val	AUDIO_SPEAKER
+#define	Audio_Headphone_Val	AUDIO_HEADPHONE
+#define	Audio_Lineout_Val	AUDIO_LINE_OUT
+
+#define 	Audio_Pcm_Val		AUDIO_ENCODING_LINEAR
+#define 	Audio_Ulaw_Val		AUDIO_ENCODING_ULAW
+#define 	Audio_Alaw_Val		AUDIO_ENCODING_ALAW
+
+#include "audio-tbls.c"
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+static int debug = 0;
+
+extern int nanosleep(const struct timespec *, struct timespec *);
+
+#define AUDIO_FILE_STRING	"/dev/audio"
+
+enum {
+	A_Pause,
+	A_UnPause
+};
+
+enum {
+	A_In,
+	A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static	int	audio_file_in  = -1;	/* file in */
+static	int	audio_file_out = -1;	/* file out */
+
+static	int	audio_swap_flag = 0;	/* endian swap */
+
+static	int	audio_in_pause = A_UnPause;
+
+static Audio_t av;
+
+static int audio_enforce(Audio_t*);
+static int audio_open_in(void);
+static int audio_open_out(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+	static ushort flag = 1;
+	audio_swap_flag = *((uchar*)&flag) == 0;	/* big-endian? */
+	audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+	switch(omode){
+	case OREAD:
+		qlock(&inlock);
+		if(waserror()){
+			qunlock(&inlock);
+			nexterror();
+		}
+
+		if(audio_file_in >= 0)
+			error(Einuse);
+		if((audio_file_in = audio_open_in()) < 0)
+			oserror();
+
+		poperror();
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_out >= 0)
+			error(Einuse);
+		if((audio_file_out = audio_open_out() ) < 0)
+			oserror();
+		poperror();
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		qlock(&outlock);
+		if(waserror()){
+			qunlock(&inlock);
+			qunlock(&outlock);
+			nexterror();
+		}
+		if(audio_file_in >= 0 || audio_file_out >= 0)
+			error(Einuse);
+
+		if((audio_file_in = audio_open_in()) < 0)
+			oserror();
+		if(waserror()){
+			close(audio_file_in);
+			audio_file_in = -1;
+			nexterror();
+		}
+		if((audio_file_out = audio_open_out()) < 0)
+			oserror();
+		poperror();
+
+		poperror();
+		qunlock(&inlock);
+		qunlock(&outlock);
+		break;
+	}
+}
+
+void
+audio_file_close(Chan *c)
+{
+	switch(c->mode){
+	case OREAD:
+		qlock(&inlock);
+		close(audio_file_in);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		break;
+	case OWRITE:
+		qlock(&outlock);
+		close(audio_file_out);
+		audio_file_out = -1;
+		qunlock(&outlock);
+		break;
+	case ORDWR:
+		qlock(&inlock);
+		close(audio_file_in);
+		audio_file_in = -1;
+		qunlock(&inlock);
+		qlock(&outlock);
+		close(audio_file_out);
+		audio_file_out = -1;
+		qunlock(&outlock);
+		break;
+	}
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long ba, status, chunk, total;
+	char *pva = (char *) va;
+
+	qlock(&inlock);
+	if(waserror()){
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if(audio_file_in < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(!audio_pause_in(audio_file_in, A_UnPause))
+		error(Eio);
+	
+	total = 0;
+	while(total < count) {
+		chunk = count - total;
+		osenter();
+		status = read(audio_file_in, pva + total, chunk);
+		osleave(); 
+		if(status < 0)
+			error(Eio);
+		total += status;
+	}
+
+	if(total != count)
+		error(Eio);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(pva, count); 
+
+	poperror();
+	qunlock(&inlock);
+
+	time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+	time.tv_nsec= 1;
+	nanosleep(&time,nil);
+
+	return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+	struct  timespec time;
+	long status = -1;
+	long ba, total, chunk, bufsz;
+
+	qlock(&outlock);
+	if(waserror()){
+		qunlock(&outlock);
+		nexterror();
+	}
+
+	if(audio_file_out < 0)
+		error(Eperm);
+
+	/* check block alignment */
+	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+	if(count % ba)
+		error(Ebadarg);
+
+	if(audio_swap_flag && av.out.bits == 16)
+		audio_swap_endian(va, count); 
+
+	total = 0;
+	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+	if(bufsz == 0)
+		error(Ebadarg);
+
+	while(total < count) {
+		chunk = min(bufsz, count - total);
+		osenter();
+		status = write(audio_file_out, va, chunk);
+		osleave();
+		if(status <= 0)
+			error(Eio);
+		total += status;
+	}
+
+	poperror();
+	qunlock(&outlock);
+
+	time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+	time.tv_nsec= 1;
+	nanosleep(&time,nil);
+
+	return count;
+}
+
+int
+audio_open_in(void)
+{
+	int fd;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_RDONLY|O_NONBLOCK);
+
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		close(fd);
+		error(Eio);
+	}
+
+	if(!audio_pause_in(fd, A_Pause)) {
+		close(fd);
+		error(Eio);
+	}
+
+	if(!audio_flush(fd, A_In)) {
+		close(fd);
+		error(Eio);
+	}
+
+	/* set audio info */
+	av.in.flags = ~0;
+	av.out.flags = 0;
+
+	if(!audio_set_info(fd, &av.in, A_In)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.in.flags = 0;
+
+	/* tada, we're open, blocking, paused and flushed */
+	return fd;
+}
+
+int
+audio_open_out(void)
+{
+	int fd;
+	struct audio_info	hdr;
+
+	/* open non-blocking in case someone already has it open */
+	/* otherwise we would block until they close! */
+	fd = open(AUDIO_FILE_STRING, O_WRONLY|O_NONBLOCK);
+
+	if(fd < 0)
+		oserror();
+
+	/* change device to be blocking */
+	if(!audio_set_blocking(fd)) {
+		close(fd);
+		error("cannot set blocking mode");
+	}
+
+	/* set audio info */
+	av.in.flags = 0;
+	av.out.flags = ~0;
+
+	if(!audio_set_info(fd, &av.out, A_Out)) {
+		close(fd);
+		error(Ebadarg);
+	}
+
+	av.out.flags = 0;
+
+	return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+	int	fd;
+	int	ff;
+	Audio_t tmpav = av;
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	if (!audioparse(va, count, &tmpav))
+		error(Ebadarg);
+
+	if (!audio_enforce(&tmpav))
+		error(Ebadarg);
+
+	qlock(&inlock);
+	if (waserror()) {
+		qunlock(&inlock);
+		nexterror();
+	}
+
+	if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+		if (!audio_pause_in(audio_file_in, A_Pause))
+			error(Ebadarg);
+		if (!audio_flush(audio_file_in, A_In))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&inlock);
+
+	qlock(&outlock);
+	if (waserror()) {
+		qunlock(&outlock);
+		nexterror();
+	}
+	if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+		if (!audio_pause_out(audio_file_out))
+			error(Ebadarg);
+		if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+			error(Ebadarg);
+	}
+	poperror();
+	qunlock(&outlock);
+
+	tmpav.in.flags = 0;
+	tmpav.out.flags = 0;
+
+	av = tmpav;
+
+	return count;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+	int val;
+
+	if((val = fcntl(fd, F_GETFL, 0)) == -1)
+		return 0;
+	
+	val &= ~O_NONBLOCK;
+
+	if(fcntl(fd, F_SETFL, val) < 0)
+		return 0;
+
+	return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+	int status;
+	int unequal_stereo = 0;
+	audio_info_t	info;
+	audio_prinfo_t  *dev;
+
+	if(fd < 0)
+		return 0;
+
+	/* devitialize header */
+	AUDIO_INITINFO(&info);
+
+	if(d == A_In)
+		dev = &info.record;
+	else
+		dev = &info.play;
+
+	/* sample rate */
+	if(i->flags & AUDIO_RATE_FLAG)
+		dev->sample_rate = i->rate;
+
+	/* channels */
+	if(i->flags & AUDIO_CHAN_FLAG)
+		dev->channels = i->chan;
+
+	/* precision */
+	if(i->flags & AUDIO_BITS_FLAG)
+		dev->precision = i->bits;
+
+	/* encoding */
+	if(i->flags & AUDIO_ENC_FLAG)
+		dev->encoding = i->enc;
+
+	/* devices */
+	if(i->flags & AUDIO_DEV_FLAG)
+		dev->port = i->dev;
+
+	/* dev volume */
+	if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) {
+		dev->gain = (i->left * AUDIO_MAX_GAIN) / Audio_Max_Val;
+
+		/* do left first then right later */
+		if(i->left == i->right) 
+			dev->balance = AUDIO_MID_BALANCE;
+		else {
+			dev->balance = AUDIO_LEFT_BALANCE;
+			if(i->chan != 1)
+				unequal_stereo = 1;
+		}
+	}
+
+	osenter();
+	status = ioctl(fd, AUDIO_SETINFO, &info);  /* qlock and load general stuff */
+	osleave();
+
+	if(status == -1) {
+		if(debug) print("audio_set_info 1 failed: fd = %d errno = %d\n", fd, errno);
+		return 0;
+	}
+
+	/* check for different right and left for dev */
+	if(unequal_stereo) {
+
+		/* re-init header */
+		AUDIO_INITINFO(&info);
+
+		dev->gain = (i->right * AUDIO_MAX_GAIN) / Audio_Max_Val;
+		dev->balance == AUDIO_RIGHT_BALANCE;
+
+		osenter();
+		status = ioctl(fd, AUDIO_SETINFO, &info);
+		osleave();
+
+		if(status == -1) {
+			if(debug) print("audio_set_info 2 failed: fd = %d errno = %d\n",fd, errno);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+void 
+audio_swap_endian(char *p, int n)
+{
+	int b;
+
+	while (n > 1) {
+		b = p[0];
+		p[0] = p[1];
+		p[1] = b;
+		p += 2;
+		n -= 2;
+	}
+}
+
+static int
+audio_pause_out(int fd)
+{
+	audio_info_t	info;
+	int	foo = 0;
+	int status;
+
+	osenter();
+	status = ioctl(fd, AUDIO_DRAIN, &foo);
+	osleave();
+
+	if(status == -1) 
+		return 0;
+	return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+	audio_info_t	info;
+	int status;
+
+	if(fd < 0)
+		return 0;
+
+	if(audio_in_pause == f)
+		return 1;
+	
+	/* initialize header */
+	AUDIO_INITINFO(&info);
+
+	/* unpause input */
+	if(f == A_Pause)
+		info.record.pause = 1;
+	else
+		info.record.pause = 0;
+
+	osenter();
+	status = ioctl(fd, AUDIO_SETINFO, &info);
+	osleave();
+
+	if(status == -1) 
+		return 0;
+
+	audio_in_pause = f;
+
+	return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+	int flag = d==A_In? FLUSHR: FLUSHW;
+	int status;
+
+	osenter();
+	status = ioctl(fd, I_FLUSH, flag); /* drain anything already put into buffer */
+	osleave();
+
+	if(status == -1) 
+		return 0;
+	return 1;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+	if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && 
+		(t->in.rate != 8000 || t->in.chan != 1))
+		 return 0;
+	if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && 
+		(t->out.rate != 8000 || t->out.chan != 1))
+		 return 0;
+	return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+	return &av;
+}
--- /dev/null
+++ b/emu/Solaris/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgrp();
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/Solaris/deveia.c
@@ -1,0 +1,38 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/term/a",
+        "/dev/term/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+static struct tcdef_t bps[] = {
+	{0,		B0},
+	{50,		B50},
+	{75,		B75},
+	{110,		B110},
+	{134,		B134},
+	{150,		B150},
+	{200,		B200},
+	{300,		B300},
+	{600,		B600},
+	{1200,	B1200},
+	{1800,	B1800},
+	{2400,	B2400},
+	{4800,	B4800},
+	{9600,	B9600},
+	{19200,	B19200},
+	{38400,	B38400},
+	{57600,	B57600},
+	{76800,	B76800},
+	{115200,	B115200},
+	{153600,	B153600},
+	{230400,	B230400},
+	{307200,	B307200},
+	{460800,	B460800},
+	{-1,		-1}
+};
--- /dev/null
+++ b/emu/Solaris/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/Solaris/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Solaris/mkfile
@@ -1,0 +1,46 @@
+SYSTARG=Solaris
+OBJTYPE=sparc
+<../../mkconfig
+SYSTARG=Solaris
+OBJTYPE=sparc
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS=$EMULIBS
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBFILES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
+
+devfs.$O:	../port/devfs-posix.c
--- /dev/null
+++ b/emu/Solaris/os.c
@@ -1,0 +1,436 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef _POSIX_C_SOURCE 
+#undef getwd
+#include	<unistd.h>
+#include	<thread.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/time.h>
+
+enum
+{
+	DELETE  = 0x7F
+};
+char *hosttype = "Solaris";
+
+static thread_key_t	prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+	void *vp;
+
+	if (thr_getspecific(prdakey, &vp))
+		return nil;
+	return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+
+	lock(&procs.l);
+	if(up->prev)
+		up->prev->next = up->next;
+	else
+		procs.head = up->next;
+
+	if(up->next)
+		up->next->prev = up->prev;
+	else
+		procs.tail = up->prev;
+	unlock(&procs.l);
+
+	/*print("pexit: %s: %s\n", up->text, msg);*/
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(up->prog);
+	sema_destroy(up->os);
+	free(up->os);
+	free(up);
+	thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+	struct Proc *Up;
+
+	if(thr_setspecific(prdakey, v)) {
+		print("set specific data failed in tramp\n");
+		thr_exit(0);
+	}
+	Up = v;
+	Up->sigid = thr_self();
+	Up->func(Up->arg);
+	pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	thread_t thread;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	sema_t *sem;
+
+	p = newproc();
+
+	sem = malloc(sizeof(*sem));
+	if(sem == nil)
+		panic("can't allocate semaphore");
+	sema_init(sem, 0, USYNC_THREAD, 0);
+	p->os = sem;
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+		panic("thr_create failed\n");
+	thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+	as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+	int intwait;
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+	if(intwait == 0)		/* Not posted so its a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+	panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+		siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+	disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+	disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+	thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	while(sema_wait(up->os))
+		;	/* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+	sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+	exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+	struct passwd *pwd;
+	
+	if (pwd=getpwnam("nobody")) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+	struct Proc *Up;
+	struct sigaction act;
+	struct passwd *pw;
+	char sys[64];
+
+	setsid();
+
+	if(dflag == 0)
+		termset();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	memset(&act, 0 , sizeof(act));
+	act.sa_handler=trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+	/*
+	 * For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGPIPE, SIG_IGN);
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+	if(sflag == 0) {
+		act.sa_handler = trapBUS;
+		sigaction(SIGBUS, &act, nil);
+		act.sa_handler = trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_handler = trapSEGV;
+		sigaction(SIGSEGV, &act, nil);
+		act.sa_handler = trapFPE;
+		sigaction(SIGFPE, &act, nil);
+		if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+			signal(SIGINT, cleanexit);
+	} else{
+		act.sa_sigaction = printILL;
+		act.sa_flags=SA_SIGINFO;
+		sigaction(SIGILL, &act, nil);
+	}	
+
+	if(thr_keycreate(&prdakey,NULL))
+		print("keycreate failed\n");
+
+	Up = newproc();
+	if(thr_setspecific(prdakey,Up))
+		panic("set specific thread data failed\n");
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+
+	up->env->uid = getuid();
+	up->env->gid = getgid();
+	emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		fprint(2, "keyboard read: %s\n", strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t, NULL)<0)
+		return(0);
+	if(sec0==0){
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct  timespec time;
+
+	time.tv_sec = milsec/1000;
+	time.tv_nsec= (milsec%1000)*1000000;
+	nanosleep(&time,nil);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	thr_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/Unixware/asm-386.s
@@ -1,0 +1,71 @@
+	.section	.bss
+	.align	4
+.L4_.bss:
+	.align	4
+Unixware_Asm_IntP: / Offset 0
+	.type	Unixware_Asm_IntP,@object
+	.size	Unixware_Asm_IntP,4
+	.set	.,.+4
+Unixware_Asm_VoidP: / Offset 4
+	.type	Unixware_Asm_VoidP,@object
+	.size	Unixware_Asm_VoidP,4
+	.set	.,.+4
+	.section	.text
+	.align	4
+.L1_.text:
+/====================
+/ FPsave
+/--------------------
+	.align	4
+	.align	4
+	.globl	FPsave
+FPsave:
+	pushl	%ebp
+	movl	%esp,%ebp
+	movl	8(%ebp),%eax
+	movl	%eax,Unixware_Asm_VoidP
+	fstenv	(%eax)
+	leave	
+	ret	
+	.align	4
+	.type	FPsave,@function
+	.size	FPsave,.-FPsave
+
+/====================
+/ FPrestore
+/--------------------
+	.align	4
+	.globl	FPrestore
+FPrestore:
+	pushl	%ebp
+	movl	%esp,%ebp
+	movl	8(%ebp),%eax
+	movl	%eax,Unixware_Asm_VoidP
+	fldenv	(%eax)
+	leave	
+	ret	
+	.align	4
+	.type	FPrestore,@function
+	.size	FPrestore,.-FPrestore
+
+
+/====================
+/ getcallerpc
+/--------------------
+	.align	4
+	.globl	getcallerpc
+getcallerpc:
+	movl	4(%ebp),%eax
+	ret	
+	.align	4
+	.type	getcallerpc,@function
+	.size	getcallerpc,.-getcallerpc
+
+/ test-and-set
+	.align	4
+	.globl	_tas
+_tas:
+	movl	$1, %eax
+	movl	4(%esp), %ecx
+	xchgl	%eax, 0(%ecx)
+	ret
--- /dev/null
+++ b/emu/Unixware/cmd.c
@@ -1,0 +1,213 @@
+#include	<sys/types.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/wait.h>
+#include	<fcntl.h>
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+	char**	args;
+	char*	dir;
+	int	pid;
+	int	wfd;	/* child writes errors that occur after the fork or on exec */
+	int	uid;
+	int	gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+	int i, nfd;
+
+	if(Debug)
+		print("devcmd: '%s'", t->args[0]);
+
+	nfd = getdtablesize();
+	for(i = 0; i < nfd; i++)
+		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+			close(i);
+
+	dup2(t->fd[0], 0);
+	dup2(t->fd[1], 1);
+	dup2(t->fd[2], 2);
+	close(t->fd[0]);
+	close(t->fd[1]);
+	close(t->fd[2]);
+
+	/* should have an auth file to do host-specific authorisation? */
+	if(t->gid != -1){
+		if(setgid(t->gid) < 0 && getegid() == 0){
+			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->uid != -1){
+		if(setuid(t->uid) < 0 && geteuid() == 0){
+			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+			_exit(1);
+		}
+	}
+
+	if(t->dir != nil && chdir(t->dir) < 0){
+		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+		_exit(1);
+	}
+
+	signal(SIGPIPE, SIG_DFL);
+
+	execvp(t->args[0], t->args);
+	if(Debug)
+		print("execvp: %s\n",strerror(errno));
+	fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+	_exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+	Targ *t;
+	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+	t = mallocz(sizeof(*t), 1);
+	if(t == nil)
+		return nil;
+
+	fd0[0] = fd0[1] = -1;
+	fd1[0] = fd1[1] = -1;
+	fd2[0] = fd2[1] = -1;
+	wfd[0] = wfd[1] = -1;
+	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+		goto Error;
+	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
+		goto Error;
+
+	t->fd[0] = fd0[0];
+	t->fd[1] = fd1[1];
+	t->fd[2] = fd2[1];
+	t->wfd = wfd[1];
+	t->args = args;
+	t->dir = dir;
+	t->gid = up->env->gid;
+	if(t->gid == -1)
+		t->gid = gidnobody;
+	t->uid = up->env->uid;
+	if(t->uid == -1)
+		t->uid = uidnobody;
+
+	signal(SIGCHLD, SIG_DFL);
+	switch(pid = fork()) {
+	case -1:
+		goto Error;
+	case 0:
+		setpgrp();
+		if(nice)
+			oslopri();
+		childproc(t);
+		_exit(1);
+	default:
+		t->pid = pid;
+		if(Debug)
+			print("cmd pid %d\n", t->pid);
+		break;
+	}
+
+	close(fd0[0]);
+	close(fd1[1]);
+	close(fd2[1]);
+	close(wfd[1]);
+
+	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+	close(wfd[0]);
+	if(n > 0){
+		close(fd0[1]);
+		close(fd1[0]);
+		close(fd2[0]);
+		free(t);
+		up->genbuf[n] = 0;
+		if(Debug)
+			print("oscmd: bad exec: %q\n", up->genbuf);
+		error(up->genbuf);
+		return nil;
+	}
+
+	fd[0] = fd0[1];
+	fd[1] = fd1[0];
+	fd[2] = fd2[0];
+	return t;
+
+Error:
+	r = errno;
+	if(Debug)
+		print("oscmd: %q\n",strerror(r));
+	close(fd0[0]);
+	close(fd0[1]);
+	close(fd1[0]);
+	close(fd1[1]);
+	close(fd2[0]);
+	close(fd2[1]);
+	close(wfd[0]);
+	close(wfd[1]);
+	error(strerror(r));
+	return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+	Targ *t = a;
+
+	if(Debug)
+		print("kill: %d\n", t->pid);
+	return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+	Targ *t = a;
+	int s;
+
+	if(waitpid(t->pid, &s, 0) == -1){
+		if(Debug)
+			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+		return -1;
+	}
+	if(WIFEXITED(s)){
+		if(WEXITSTATUS(s) == 0)
+			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+	}
+	if(WIFSIGNALED(s)){
+		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+	}
+	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+	free(a);
+}
--- /dev/null
+++ b/emu/Unixware/deveia.c
@@ -1,0 +1,11 @@
+/*
+ * Solaris serial port definitions
+ */
+
+static char *sysdev[] = {
+        "/dev/cua/a",
+        "/dev/cua/b"
+};
+
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
--- /dev/null
+++ b/emu/Unixware/devfs.c
@@ -1,0 +1,7 @@
+#include "devfs-posix.c"
+
+static vlong
+osdisksize(int fd)
+{
+	return 0;
+}
--- /dev/null
+++ b/emu/Unixware/emu
@@ -1,0 +1,111 @@
+dev
+	root
+	cons
+	env
+	mnt
+	pipe
+	prog
+	prof
+	srv
+	dup
+	ssl
+	cap
+	fs
+	cmd	cmd
+	indir
+
+	draw	win-x11a
+	pointer
+	snarf
+
+	ip	ipif-posix ipaux
+	eia
+#	audio	audio
+	mem
+
+lib
+	interp
+	tk
+	freetype
+	math
+	draw
+
+	memlayer
+	memdraw
+	keyring
+	sec
+	mp
+
+	9
+
+link
+
+mod
+	sys
+	draw
+
+	tk
+	math
+	srv	srv
+	keyring
+	crypt
+	ipints
+	loader
+	freetype
+
+port
+	alloc
+	cache
+	chan
+	dev
+	devtab
+
+	dial
+	dis
+	discall
+	env
+	error
+	errstr
+	exception
+	exportfs
+	inferno
+	latin1
+	main
+	parse
+	pgrp
+	print
+	proc
+	qio
+	random
+	sysfile
+	uqid
+
+code
+
+init
+	emuinit
+
+root
+	/dev	/
+	/fd	/
+	/prog	/
+	/prof	/
+	/net	/
+	/net.alt	/
+	/chan	/
+	/nvfs	/
+	/env	/
+#	/chan
+#	/dev
+#	/dis
+#	/env
+#	/n
+#	/net
+#	/nvfs /
+#	/prog
+#	/icons
+#	/osinit.dis
+#	/dis/emuinit.dis
+#	/dis/lib/auth.dis
+#	/dis/lib/ssl.dis
+#	/n/local /
--- /dev/null
+++ b/emu/Unixware/mkfile
@@ -1,0 +1,44 @@
+SYSTARG=Unixware
+OBJTYPE=386
+<../../mkconfig
+SYSTARG=Unixware
+OBJTYPE=386
+
+#Configurable parameters
+
+CONF=emu			#default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin	#path of directory where kernel is installed
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	#set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF	#sets $IP, $DEVS, $PORT, $LIBS
+
+OBJ=\
+	asm-$OBJTYPE.$O\
+	os.$O\
+	$CONF.root.$O\
+	lock.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+
+CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+SYSLIBS=	-lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
+KERNDATE=`{$NDATE}
+
+default:V:	$O.$CONF
+
+<../port/portmkfile
+
+$O.$CONF:	$OBJ $CONF.c $CONF.root.h $LIBNAMES
+	$CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+	$LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+	cp $O.$CONF $INSTALLDIR/$CONF
--- /dev/null
+++ b/emu/Unixware/mkfile-Unixware
@@ -1,0 +1,15 @@
+#
+#	architecture-dependent files for Unixware
+#
+
+TARGFILES=asm-Unixware-$OBJTYPE.$O\
+	devfs-posix.$O\
+	devip.$O\
+	ipif-posix.$O\
+	os-Unixware.$O\
+	win-x11.$O\
+	deveia-Unixware.$O\
+	srv.$O\
+	lock.$O\
+
+SYSLIBS=	-lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread
--- /dev/null
+++ b/emu/Unixware/os.c
@@ -1,0 +1,437 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef _POSIX_C_SOURCE 
+#undef getwd
+#include	<unistd.h>
+#include	<thread.h>
+#include	<time.h>
+#include	<termios.h>
+#include	<signal.h>
+#include 	<pwd.h>
+#include	<sys/resource.h>
+#include	<sys/time.h>
+
+enum
+{
+	DELETE  = 0x7F
+};
+char *hosttype = "Unixware";
+
+static thread_key_t	prdakey;
+
+static siginfo_t siginfo;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+	void *vp;
+
+	if (thr_getspecific(prdakey, &vp))
+		return nil;
+	return vp;
+}
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+
+	lock(&procs.l);
+	if(up->prev)
+		up->prev->next = up->next;
+	else
+		procs.head = up->next;
+
+	if(up->next)
+		up->next->prev = up->prev;
+	else
+		procs.tail = up->prev;
+	unlock(&procs.l);
+
+	/*print("pexit: %s: %s\n", up->text, msg);*/
+	e = up->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+	}
+	free(up->prog);
+	sema_destroy(up->os);
+	free(up->os);
+	free(up);
+	thr_exit(0);
+}
+
+static void *
+tramp(void *v)
+{
+	struct Proc *Up;
+
+	if(thr_setspecific(prdakey, v)) {
+		print("set specific data failed in tramp\n");
+		thr_exit(0);
+	}
+	Up = v;
+	Up->sigid = thr_self();
+	Up->func(Up->arg);
+	pexit("", 0);
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	thread_t thread;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	sema_t *sem;
+
+	p = newproc();
+
+	sem = malloc(sizeof(*sem));
+	if(sem == nil)
+		panic("can't allocate semaphore");
+	sema_init(sem, 0, USYNC_THREAD, 0);
+	p->os = sem;
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	}
+	else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread))
+		panic("thr_create failed\n");
+	thr_yield();
+}
+
+/* to get pc on trap use siginfo.si_pc field and define all trap handlers
+	as printILL - have to set sa_sigaction, sa_flags not sa_handler
+*/
+
+static void
+trapUSR1(void)
+{
+	int intwait;
+
+	intwait = up->intwait;
+	up->intwait = 0;	/* clear it to let proc continue in osleave */
+
+	if(up->type != Interp)		/* Used to unblock pending I/O */
+		return;
+
+	if(intwait == 0)		/* Not posted so it's a sync error */
+		disfault(nil, Eintr);	/* Should never happen */
+}
+
+static void
+trapILL(void)
+{
+	disfault(nil, "Illegal instruction");
+}
+
+static void
+printILL(int sig, siginfo_t *siginfo, void *v)
+{
+	panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n",
+		siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr);
+}
+
+static void
+trapBUS(void)
+{
+	disfault(nil, "Bus error");
+}
+
+static void
+trapSEGV(void)
+{
+	disfault(nil, "Segmentation violation");
+}
+
+static void
+trapFPE(void)
+{
+	disfault(nil, "Floating point exception");
+}
+
+void
+oshostintr(Proc *p)
+{
+	thr_kill(p->sigid, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	while(sema_wait(up->os))
+		;	/* retry on signals */
+}
+
+void
+osready(Proc *p)
+{
+	sema_post(p->os);
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+	USED(regs);
+	siglongjmp(env, val);
+}
+
+static struct termios tinit;
+
+static void
+termset(void)
+{
+	struct termios t;
+
+	tcgetattr(0, &t);
+	tinit = t;
+	t.c_lflag &= ~(ICANON|ECHO|ISIG);
+	t.c_cc[VMIN] = 1;
+	t.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSANOW, &t);
+}
+
+static void
+termrestore(void)
+{
+	tcsetattr(0, TCSANOW, &tinit);
+}
+
+void
+cleanexit(int x)
+{
+	USED(x);
+
+	if(up->intwait) {
+		up->intwait = 0;
+		return;
+	}
+
+	if(dflag == 0)
+		termrestore();
+	exit(0);
+}
+
+int gidnobody= -1, uidnobody= -1;
+
+void
+getnobody(void)
+{
+	struct passwd *pwd;
+	
+	if (pwd=getpwnam("nobody")) {
+		uidnobody = pwd->pw_uid;
+		gidnobody = pwd->pw_gid;
+	}
+}
+
+void
+osreboot(char *file, char **argv)
+{
+	if(dflag == 0)
+		termrestore();
+	execvp(file, argv);
+	panic("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+	struct Proc *Up;
+	struct sigaction act;
+	struct passwd *pw;
+	char sys[64];
+
+	setsid();
+
+	if(dflag == 0)
+		termset();
+
+	gethostname(sys, sizeof(sys));
+	kstrdup(&ossysname, sys);
+	getnobody();
+
+	memset(&act, 0 , sizeof(act));
+	act.sa_handler=trapUSR1;
+	sigaction(SIGUSR1, &act, nil);
+	/*
+	 * For the correct functioning of devcmd in the
+	 * face of exiting slaves
+	 */
+	signal(SIGPIPE, SIG_IGN);
+	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+		signal(SIGTERM, cleanexit);
+	if(sflag == 0) {
+		act.sa_handler = trapBUS;
+		sigaction(SIGBUS, &act, nil);
+		act.sa_handler = trapILL;
+		sigaction(SIGILL, &act, nil);
+		act.sa_handler = trapSEGV;
+		sigaction(SIGSEGV, &act, nil);
+		act.sa_handler = trapFPE;
+		sigaction(SIGFPE, &act, nil);
+		if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+			signal(SIGINT, cleanexit);
+	} else{
+		act.sa_sigaction = printILL;
+		act.sa_flags=SA_SIGINFO;
+		sigaction(SIGILL, &act, nil);
+	}	
+
+	if(thr_keycreate(&prdakey,NULL))
+		print("keycreate failed\n");
+
+	Up = newproc();
+	if(thr_setspecific(prdakey,Up))
+		panic("set specific thread data failed\n");
+
+	pw = getpwuid(getuid());
+	if(pw != nil)
+		kstrdup(&eve, pw->pw_name);
+	else
+		print("cannot getpwuid\n");
+
+	up->env->uid = getuid();
+	up->env->gid = getgid();
+	emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+	int n;
+	char buf[1];
+
+	n = read(0, buf, sizeof(buf));
+	if(n < 0)
+		fprint(2, "keyboard read: %s\n", strerror(errno));
+	if(n <= 0)
+		pexit("keyboard thread", 0);
+
+	switch(buf[0]) {
+	case '\r':
+		buf[0] = '\n';
+		break;
+	case DELETE:
+		cleanexit(0);
+		break;
+	}
+	return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+	static long sec0 = 0, usec0;
+	struct timeval t;
+
+	if(gettimeofday(&t, NULL)<0)
+		return(0);
+	if(sec0==0){
+		sec0 = t.tv_sec;
+		usec0 = t.tv_usec;
+	}
+	return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000);
+}
+
+/*
+ * Return the time since the epoch in microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+	struct timeval t;
+
+	gettimeofday(&t, nil);
+	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+	struct  timespec time;
+
+	time.tv_sec = milsec/1000;
+	time.tv_nsec= (milsec%1000)*1000000;
+	nanosleep(&time,nil);
+	return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+	return osmillisleep(milsec);
+}
+
+void
+osyield(void)
+{
+	thr_yield();
+}
+
+void
+ospause(void)
+{
+	for(;;)
+		pause();
+}
+
+void
+oslopri(void)
+{
+	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
+}
--- /dev/null
+++ b/emu/mkfile
@@ -1,0 +1,19 @@
+<../mkconfig
+
+all:V:	all-$HOSTMODEL
+install:V:	install-$HOSTMODEL
+safeinstall:V:	safeinstall-$HOSTMODEL
+clean:V:	clean-$HOSTMODEL
+nuke:V:	nuke-$HOSTMODEL
+
+&-Posix:QV:
+	echo "(cd $SYSTARG; mk $MKFLAGS $stem)"
+	(cd $SYSTARG; mk $MKFLAGS $stem) || exit 1
+
+&-Nt:QV:
+	echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+	@{builtin cd $SYSTARG; mk $MKFLAGS $stem }
+
+&-Plan9:QV:
+	echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}'
+	@{builtin cd $SYSTARG; mk $MKFLAGS $stem }
--- /dev/null
+++ b/emu/port/acme-offset
@@ -1,0 +1,1 @@
+X ,x/b?(read|write)\(.*ulong.*\)$/v/(bread|bwrite)/x/ulong offset/c/vlong offset
--- /dev/null
+++ b/emu/port/alloc.c
@@ -1,0 +1,1021 @@
+#include "dat.h"
+#include "fns.h"
+#include "interp.h"
+#include "error.h"
+
+enum
+{
+	MAXPOOL		= 4
+};
+
+#define left	u.s.bhl
+#define right	u.s.bhr
+#define fwd	u.s.bhf
+#define prev	u.s.bhv
+#define parent	u.s.bhp
+
+#define RESERVED	512*1024
+
+struct Pool
+{
+	char*	name;
+	int	pnum;
+	ulong	maxsize;
+	int	quanta;
+	int	chunk;
+	int	monitor;
+	ulong	ressize;	/* restricted size */
+	ulong	cursize;
+	ulong	arenasize;
+	ulong	hw;
+	Lock	l;
+	Bhdr*	root;
+	Bhdr*	chain;
+	ulong	nalloc;
+	ulong	nfree;
+	int	nbrk;
+	int	lastfree;
+	void	(*move)(void*, void*);
+};
+
+void*	initbrk(ulong);
+
+struct
+{
+	int	n;
+	Pool	pool[MAXPOOL];
+	/* Lock l; */
+} table = {
+	3,
+	{
+		{ "main",  0, 	32*1024*1024, 31,  512*1024, 0, 31*1024*1024 },
+		{ "heap",  1, 	32*1024*1024, 31,  512*1024, 0, 31*1024*1024 },
+		{ "image", 2,   64*1024*1024+256, 31, 4*1024*1024, 1, 63*1024*1024 },
+	}
+};
+
+Pool*	mainmem = &table.pool[0];
+Pool*	heapmem = &table.pool[1];
+Pool*	imagmem = &table.pool[2];
+
+static void _auditmemloc(char *, void *);
+void (*auditmemloc)(char *, void *) = _auditmemloc;
+static void _poolfault(void *, char *, ulong);
+void (*poolfault)(void *, char *, ulong) = _poolfault;
+
+/*	non tracing
+ *
+enum {
+	Npadlong	= 0,
+	MallocOffset = 0,
+	ReallocOffset = 0
+};
+ *
+ */
+
+/* tracing */
+enum {
+	Npadlong	= 2,
+	MallocOffset = 0,
+	ReallocOffset = 1
+};
+
+enum {
+	Monitor = 1
+};
+
+void	(*memmonitor)(int, ulong, ulong, ulong) = nil;
+#define	MM(v,pc,base,size)	if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size))
+
+#define CKLEAK	0
+int	ckleak;
+#define	ML(v, sz, pc)	if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); }
+
+int
+memusehigh(void)
+{
+	return 	mainmem->cursize > mainmem->ressize ||
+			heapmem->cursize > heapmem->ressize ||
+			0 && imagmem->cursize > imagmem->ressize;
+}
+
+int
+memlow(void)
+{
+	return heapmem->cursize > (heapmem->maxsize)/2;
+}
+
+int
+poolsetsize(char *s, int size)
+{
+	int i;
+
+	for(i = 0; i < table.n; i++) {
+		if(strcmp(table.pool[i].name, s) == 0) {
+			table.pool[i].maxsize = size;
+			table.pool[i].ressize = size-RESERVED;
+			if(size < RESERVED)
+				panic("not enough memory");
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+poolimmutable(void *v)
+{
+	Bhdr *b;
+
+	D2B(b, v);
+	b->magic = MAGIC_I;
+}
+
+void
+poolmutable(void *v)
+{
+	Bhdr *b;
+
+	D2B(b, v);
+	b->magic = MAGIC_A;
+	((Heap*)v)->color = mutator;
+}
+
+char*
+poolname(Pool *p)
+{
+	return p->name;
+}
+
+Bhdr*
+poolchain(Pool *p)
+{
+	return p->chain;
+}
+
+void
+pooldel(Pool *p, Bhdr *t)
+{
+	Bhdr *s, *f, *rp, *q;
+
+	if(t->parent == nil && p->root != t) {
+		t->prev->fwd = t->fwd;
+		t->fwd->prev = t->prev;
+		return;
+	}
+
+	if(t->fwd != t) {
+		f = t->fwd;
+		s = t->parent;
+		f->parent = s;
+		if(s == nil)
+			p->root = f;
+		else {
+			if(s->left == t)
+				s->left = f;
+			else
+				s->right = f;
+		}
+
+		rp = t->left;
+		f->left = rp;
+		if(rp != nil)
+			rp->parent = f;
+		rp = t->right;
+		f->right = rp;
+		if(rp != nil)
+			rp->parent = f;
+
+		t->prev->fwd = t->fwd;
+		t->fwd->prev = t->prev;
+		return;
+	}
+
+	if(t->left == nil)
+		rp = t->right;
+	else {
+		if(t->right == nil)
+			rp = t->left;
+		else {
+			f = t;
+			rp = t->right;
+			s = rp->left;
+			while(s != nil) {
+				f = rp;
+				rp = s;
+				s = rp->left;
+			}
+			if(f != t) {
+				s = rp->right;
+				f->left = s;
+				if(s != nil)
+					s->parent = f;
+				s = t->right;
+				rp->right = s;
+				if(s != nil)
+					s->parent = rp;
+			}
+			s = t->left;
+			rp->left = s;
+			s->parent = rp;
+		}
+	}
+	q = t->parent;
+	if(q == nil)
+		p->root = rp;
+	else {
+		if(t == q->left)
+			q->left = rp;
+		else
+			q->right = rp;
+	}
+	if(rp != nil)
+		rp->parent = q;
+}
+
+void
+pooladd(Pool *p, Bhdr *q)
+{
+	int size;
+	Bhdr *tp, *t;
+
+	q->magic = MAGIC_F;
+
+	q->left = nil;
+	q->right = nil;
+	q->parent = nil;
+	q->fwd = q;
+	q->prev = q;
+
+	t = p->root;
+	if(t == nil) {
+		p->root = q;
+		return;
+	}
+
+	size = q->size;
+
+	tp = nil;
+	while(t != nil) {
+		if(size == t->size) {
+			q->prev = t->prev;
+			q->prev->fwd = q;
+			q->fwd = t;
+			t->prev = q;
+			return;
+		}
+		tp = t;
+		if(size < t->size)
+			t = t->left;
+		else
+			t = t->right;
+	}
+
+	q->parent = tp;
+	if(size < tp->size)
+		tp->left = q;
+	else
+		tp->right = q;
+}
+
+static void*
+dopoolalloc(Pool *p, ulong asize, ulong pc)
+{
+	Bhdr *q, *t;
+	int alloc, ldr, ns, frag;
+	int osize, size;
+
+	if(asize >= 1024*1024*1024)	/* for sanity and to avoid overflow */
+		return nil;
+	size = asize;
+	osize = size;
+	size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
+
+	lock(&p->l);
+	p->nalloc++;
+
+	t = p->root;
+	q = nil;
+	while(t) {
+		if(t->size == size) {
+			t = t->fwd;
+			pooldel(p, t);
+			t->magic = MAGIC_A;
+			p->cursize += t->size;
+			if(p->cursize > p->hw)
+				p->hw = p->cursize;
+			unlock(&p->l);
+			if(p->monitor)
+				MM(p->pnum, pc, (ulong)B2D(t), size);
+			return B2D(t);
+		}
+		if(size < t->size) {
+			q = t;
+			t = t->left;
+		}
+		else
+			t = t->right;
+	}
+	if(q != nil) {
+		pooldel(p, q);
+		q->magic = MAGIC_A;
+		frag = q->size - size;
+		if(frag < (size>>2) && frag < 0x8000) {
+			p->cursize += q->size;
+			if(p->cursize > p->hw)
+				p->hw = p->cursize;
+			unlock(&p->l);
+			if(p->monitor)
+				MM(p->pnum, pc, (ulong)B2D(q), size);
+			return B2D(q);
+		}
+		/* Split */
+		ns = q->size - size;
+		q->size = size;
+		B2T(q)->hdr = q;
+		t = B2NB(q);
+		t->size = ns;
+		B2T(t)->hdr = t;
+		pooladd(p, t);
+		p->cursize += q->size;
+		if(p->cursize > p->hw)
+			p->hw = p->cursize;
+		unlock(&p->l);
+		if(p->monitor)
+			MM(p->pnum, pc, (ulong)B2D(q), size);
+		return B2D(q);
+	}
+
+	ns = p->chunk;
+	if(size > ns)
+		ns = size;
+	ldr = p->quanta+1;
+
+	alloc = ns+ldr+ldr;
+	p->arenasize += alloc;
+	if(p->arenasize > p->maxsize) {
+		p->arenasize -= alloc;
+		ns = p->maxsize-p->arenasize-ldr-ldr;
+		ns &= ~p->quanta;
+		if (ns < size) {
+			if(poolcompact(p)) {
+				unlock(&p->l);
+				return poolalloc(p, osize);
+			}
+
+			unlock(&p->l);
+			print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n",
+			 p->name, size, p->cursize, p->arenasize, p->maxsize);
+			return nil;
+		}
+		alloc = ns+ldr+ldr;
+		p->arenasize += alloc;
+	}
+
+	p->nbrk++;
+	t = (Bhdr *)sbrk(alloc);
+	if(t == (void*)-1) {
+		p->nbrk--;
+		unlock(&p->l);
+		return nil;
+	}
+	/* Double alignment */
+	t = (Bhdr *)(((ulong)t + 7) & ~7);
+
+	if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
+		/* can merge chains */
+		if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
+		q = B2LIMIT(p->chain);
+		q->magic = MAGIC_A;
+		q->size = alloc;
+		B2T(q)->hdr = q;
+		t = B2NB(q);
+		t->magic = MAGIC_E;
+		p->chain->csize += alloc;
+		p->cursize += alloc;
+		unlock(&p->l);
+		poolfree(p, B2D(q));		/* for backward merge */
+		return poolalloc(p, osize);
+	}
+	
+	t->magic = MAGIC_E;		/* Make a leader */
+	t->size = ldr;
+	t->csize = ns+ldr;
+	t->clink = p->chain;
+	p->chain = t;
+	B2T(t)->hdr = t;
+	t = B2NB(t);
+
+	t->magic = MAGIC_A;		/* Make the block we are going to return */
+	t->size = size;
+	B2T(t)->hdr = t;
+	q = t;
+
+	ns -= size;			/* Free the rest */
+	if(ns > 0) {
+		q = B2NB(t);
+		q->size = ns;
+		B2T(q)->hdr = q;
+		pooladd(p, q);
+	}
+	B2NB(q)->magic = MAGIC_E;	/* Mark the end of the chunk */
+
+	p->cursize += t->size;
+	if(p->cursize > p->hw)
+		p->hw = p->cursize;
+	unlock(&p->l);
+	if(p->monitor)
+		MM(p->pnum, pc, (ulong)B2D(t), size);
+	return B2D(t);
+}
+
+void *
+poolalloc(Pool *p, ulong asize)
+{
+	Prog *prog;
+
+	if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
+		return nil;
+	return dopoolalloc(p, asize, getcallerpc(&p));
+}
+
+void
+poolfree(Pool *p, void *v)
+{
+	Bhdr *b, *c;
+	extern Bhdr *ptr;
+
+	D2B(b, v);
+	if(p->monitor)
+		MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size);
+
+	lock(&p->l);
+	p->nfree++;
+	p->cursize -= b->size;
+	c = B2NB(b);
+	if(c->magic == MAGIC_F) {	/* Join forward */
+		if(c == ptr)
+			ptr = b;
+		pooldel(p, c);
+		c->magic = 0;
+		b->size += c->size;
+		B2T(b)->hdr = b;
+	}
+
+	c = B2PT(b)->hdr;
+	if(c->magic == MAGIC_F) {	/* Join backward */
+		if(b == ptr)
+			ptr = c;
+		pooldel(p, c);
+		b->magic = 0;
+		c->size += b->size;
+		b = c;
+		B2T(b)->hdr = b;
+	}
+	pooladd(p, b);
+	unlock(&p->l);
+}
+
+void *
+poolrealloc(Pool *p, void *v, ulong size)
+{
+	Bhdr *b;
+	void *nv;
+	int osize;
+
+	if(size >= 1024*1024*1024)	/* for sanity and to avoid overflow */
+		return nil;
+	if(size == 0){
+		poolfree(p, v);
+		return nil;
+	}
+	SET(osize);
+	if(v != nil){
+		lock(&p->l);
+		D2B(b, v);
+		osize = b->size - BHDRSIZE;
+		unlock(&p->l);
+		if(osize >= size)
+			return v;
+	}
+	nv = poolalloc(p, size);
+	if(nv != nil && v != nil){
+		memmove(nv, v, osize);
+		poolfree(p, v);
+	}
+	return nv;
+}
+
+ulong
+poolmsize(Pool *p, void *v)
+{
+	Bhdr *b;
+	ulong size;
+
+	if(v == nil)
+		return 0;
+	lock(&p->l);
+	D2B(b, v);
+	size = b->size - BHDRSIZE;
+	unlock(&p->l);
+	return size;
+}
+
+static ulong
+poolmax(Pool *p)
+{
+	Bhdr *t;
+	ulong size;
+
+	lock(&p->l);
+	size = p->maxsize - p->cursize;
+	t = p->root;
+	if(t != nil) {
+		while(t->right != nil)
+			t = t->right;
+		if(size < t->size)
+			size = t->size;
+	}
+	if(size >= BHDRSIZE)
+		size -= BHDRSIZE;
+	unlock(&p->l);
+	return size;
+}
+
+ulong
+poolmaxsize(void)
+{
+	int i;
+	ulong total;
+
+	total = 0;
+	for(i = 0; i < nelem(table.pool); i++)
+		total += table.pool[i].maxsize;
+	return total;
+}
+
+int
+poolread(char *va, int count, ulong offset)
+{
+	Pool *p;
+	int n, i, signed_off;
+
+	n = 0;
+	signed_off = offset;
+	for(i = 0; i < table.n; i++) {
+		p = &table.pool[i];
+		n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
+			p->cursize,
+			p->maxsize,
+			p->hw,
+			p->nalloc,
+			p->nfree,
+			p->nbrk,
+			poolmax(p),
+			p->name);
+
+		if(signed_off > 0) {
+			signed_off -= n;
+			if(signed_off < 0) {
+				memmove(va, va+n+signed_off, -signed_off);
+				n = -signed_off;
+			}
+			else
+				n = 0;
+		}
+
+	}
+	return n;
+}
+
+void*
+smalloc(size_t size)
+{
+	void *v;
+
+	for(;;){
+		v = malloc(size);
+		if(v != nil)
+			break;
+		if(0)
+			print("smalloc waiting from %lux\n", getcallerpc(&size));
+		osenter();
+		osmillisleep(100);
+		osleave();
+	}
+	setmalloctag(v, getcallerpc(&size));
+	setrealloctag(v, 0);
+	return v;
+}
+
+void*
+kmalloc(size_t size)
+{
+	void *v;
+
+	v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size));
+	if(v != nil){
+		ML(v, size, getcallerpc(&size));
+		if(Npadlong){
+			v = (ulong*)v+Npadlong;
+			setmalloctag(v, getcallerpc(&size));
+			setrealloctag(v, 0);
+		}
+		memset(v, 0, size);
+		MM(0, getcallerpc(&size), (ulong)v, size);
+	}
+	return v;
+}
+
+
+
+void*
+malloc(size_t size)
+{
+	void *v;
+
+	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+	if(v != nil){
+		ML(v, size, getcallerpc(&size));
+		if(Npadlong){
+			v = (ulong*)v+Npadlong;
+			setmalloctag(v, getcallerpc(&size));
+			setrealloctag(v, 0);
+		}
+		memset(v, 0, size);
+		MM(0, getcallerpc(&size), (ulong)v, size);
+	} else 
+		print("malloc failed from %lux\n", getcallerpc(&size));
+	return v;
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+	void *v;
+
+	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+	if(v != nil){
+		ML(v, size, getcallerpc(&size));
+		if(Npadlong){
+			v = (ulong*)v+Npadlong;
+			setmalloctag(v, getcallerpc(&size));
+			setrealloctag(v, 0);
+		}
+		if(clr)
+			memset(v, 0, size);
+		MM(0, getcallerpc(&size), (ulong)v, size);
+	} else 
+		print("mallocz failed from %lux\n", getcallerpc(&size));
+	return v;
+}
+
+void
+free(void *v)
+{
+	Bhdr *b;
+
+	if(v != nil) {
+		if(Npadlong)
+			v = (ulong*)v-Npadlong;
+		D2B(b, v);
+		ML(v, 0, 0);
+		MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size);
+		poolfree(mainmem, v);
+	}
+}
+
+void*
+realloc(void *v, size_t size)
+{
+	void *nv;
+
+	if(size == 0)
+		return malloc(size);	/* temporary change until realloc calls can be checked */
+	if(v != nil)
+		v = (ulong*)v-Npadlong;
+	if(Npadlong!=0 && size!=0)
+		size += Npadlong*sizeof(ulong);
+	nv = poolrealloc(mainmem, v, size);
+	ML(v, 0, 0);
+	ML(nv, size, getcallerpc(&v));
+	if(nv != nil) {
+		nv = (ulong*)nv+Npadlong;
+		setrealloctag(nv, getcallerpc(&v));
+		if(v == nil)
+			setmalloctag(v, getcallerpc(&v));
+	} else 
+		print("realloc failed from %lux\n", getcallerpc(&v));
+	return nv;
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+	ulong *u;
+
+	USED(v);
+	USED(pc);
+	if(Npadlong <= MallocOffset || v == nil)
+		return;
+	u = v;
+	u[-Npadlong+MallocOffset] = pc;
+}
+
+ulong
+getmalloctag(void *v)
+{
+	USED(v);
+	if(Npadlong <= MallocOffset)
+		return ~0;
+	return ((ulong*)v)[-Npadlong+MallocOffset];
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+	ulong *u;
+
+	USED(v);
+	USED(pc);
+	if(Npadlong <= ReallocOffset || v == nil)
+		return;
+	u = v;
+	u[-Npadlong+ReallocOffset] = pc;
+}
+
+ulong
+getrealloctag(void *v)
+{
+	USED(v);
+	if(Npadlong <= ReallocOffset)
+		return ((ulong*)v)[-Npadlong+ReallocOffset];
+	return ~0;
+}
+
+ulong
+msize(void *v)
+{
+	if(v == nil)
+		return 0;
+	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
+}
+
+void*
+calloc(size_t n, size_t szelem)
+{
+	return malloc(n*szelem);
+}
+
+/*
+void
+pooldump(Pool *p)
+{
+	Bhdr *b, *base, *limit, *ptr;
+
+	b = p->chain;
+	if(b == nil)
+		return;
+	base = b;
+	ptr = b;
+	limit = B2LIMIT(b);
+
+	while(base != nil) {
+		print("\tbase #%.8lux ptr #%.8lux", base, ptr);
+		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I)
+			print("\tA%.5d\n", ptr->size);
+		else if(ptr->magic == MAGIC_E)
+			print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize);
+		else
+			print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n",
+				ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent);
+		ptr = B2NB(ptr);
+		if(ptr >= limit) {
+			print("link to #%.8lux\n", base->clink);
+			base = base->clink;
+			if(base == nil)
+				break;
+			ptr = base;
+			limit = B2LIMIT(base);
+		}
+	}
+}
+*/
+
+void
+poolsetcompact(Pool *p, void (*move)(void*, void*))
+{
+	p->move = move;
+}
+
+int
+poolcompact(Pool *pool)
+{
+	Bhdr *base, *limit, *ptr, *end, *next;
+	int compacted, nb;
+
+	if(pool->move == nil || pool->lastfree == pool->nfree)
+		return 0;
+
+	pool->lastfree = pool->nfree;
+
+	base = pool->chain;
+	ptr = B2NB(base);	/* First Block in arena has clink */
+	limit = B2LIMIT(base);
+	compacted = 0;
+
+	pool->root = nil;
+	end = ptr;
+	while(base != nil) {
+		next = B2NB(ptr);
+		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
+			if(ptr != end) {
+				memmove(end, ptr, ptr->size);
+				pool->move(B2D(ptr), B2D(end));
+				compacted = 1;
+			}
+			end = B2NB(end);
+		}
+		if(next >= limit) {
+			nb = (uchar*)limit - (uchar*)end;
+			if(nb > 0){
+				if(nb < pool->quanta+1){
+					print("poolcompact: leftover too small\n");
+					abort();
+				}
+				end->size = nb;
+				B2T(end)->hdr = end;
+				pooladd(pool, end);
+			}
+			base = base->clink;
+			if(base == nil)
+				break;
+			ptr = B2NB(base);
+			end = ptr;	/* could do better by copying between chains */
+			limit = B2LIMIT(base);
+		} else
+			ptr = next;
+	}
+
+	return compacted;
+}
+
+static void
+_poolfault(void *v, char *msg, ulong c)
+{
+	auditmemloc(msg, v);
+	panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
+}
+
+static void
+dumpvl(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = print("%s at %p: ", msg, v);
+	for (i = 0; i < n; i++) {
+		if (l >= 60) {
+			print("\n");
+			l = print("    %p: ", v);
+		}
+		l += print(" %lux", *v++);
+	}
+	print("\n");
+}
+
+static void
+corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
+{
+	print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
+		str, v, p->name, msg, b, b->size, b->magic);
+	dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
+}
+
+static void
+_auditmemloc(char *str, void *v)
+{
+	Pool *p;
+	Bhdr *bc, *ec, *b, *nb, *fb = nil;
+	char *fmsg, *msg;
+	ulong fsz;
+
+	SET(fsz);
+	SET(fmsg);
+	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+		lock(&p->l);
+		for (bc = p->chain; bc != nil; bc = bc->clink) {
+			if (bc->magic != MAGIC_E) {
+				unlock(&p->l);
+				corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
+				goto nextpool;
+			}
+			ec = B2LIMIT(bc);
+			if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
+				goto found;
+		}
+		unlock(&p->l);
+nextpool:	;
+	}
+	print("%s: %p not in pools\n", str, v);
+	return;
+
+found:
+	for (b = bc; b < ec; b = nb) {
+		switch(b->magic) {
+		case MAGIC_F:
+			msg = "free blk";
+			break;
+		case MAGIC_I:
+			msg = "immutable block";
+			break;
+		case MAGIC_A:
+			msg = "block";
+			break;
+		default:
+			if (b == bc && b->magic == MAGIC_E) {
+				msg = "pool hdr";
+				break;
+			}
+			unlock(&p->l);
+			corrupted(str, "bad magic", p, b, v);
+			goto badchunk;
+		}
+		if (b->size <= 0 || (b->size & p->quanta)) {
+			unlock(&p->l);
+			corrupted(str, "bad size", p, b, v);
+			goto badchunk;
+		}
+		if (fb != nil)
+			break;
+		nb = B2NB(b);
+		if ((Bhdr*)v < nb) {
+			fb = b;
+			fsz = b->size;
+			fmsg = msg;
+		}
+	}
+	unlock(&p->l);
+	if (b >= ec) {
+		if (b > ec)
+			corrupted(str, "chain size mismatch", p, b, v);
+		else if (b->magic != MAGIC_E)
+			corrupted(str, "chain end!=MAGIC_E", p, b, v);
+	}
+badchunk:
+	if (fb != nil) {
+		print("%s: %p in %s:", str, v, p->name);
+		if (fb == v)
+			print(" is %s '%lux\n", fmsg, fsz);
+		else
+			print(" in %s at %p'%lux\n", fmsg, fb, fsz);
+		dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
+	}
+}
+
+char *
+poolaudit(char*(*audit)(int, Bhdr *))
+{
+	Pool *p;
+	Bhdr *bc, *ec, *b;
+	char *r = nil;
+
+	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
+		lock(&p->l);
+		for (bc = p->chain; bc != nil; bc = bc->clink) {
+			if (bc->magic != MAGIC_E) {
+				unlock(&p->l);
+				return "bad chain hdr";
+			}
+			ec = B2LIMIT(bc);
+			for (b = bc; b < ec; b = B2NB(b)) {
+				if (b->size <= 0 || (b->size & p->quanta))
+					r = "bad size in bhdr";
+				else
+					switch(b->magic) {
+					case MAGIC_E:
+						if (b != bc) {
+							r = "unexpected MAGIC_E";
+							break;
+						}
+					case MAGIC_F:
+					case MAGIC_A:
+					case MAGIC_I:
+						r = audit(p->pnum, b);
+						break;
+					default:
+						r = "bad magic";
+					}
+				if (r != nil) {
+					unlock(&p->l);
+					return r;
+				}
+			}
+			if (b != ec || b->magic != MAGIC_E) {
+				unlock(&p->l);
+				return "bad chain ending";
+			}
+		}
+		unlock(&p->l);
+	}
+	return r;
+}
--- /dev/null
+++ b/emu/port/audio-tbls.c
@@ -1,0 +1,53 @@
+svp_t audio_bits_tbl[] = {
+	{ "8", 8 } ,	 /* 8 bits per sample */
+	{ "16", 16 },  /* 16 bits per sample */
+	{nil},
+};
+
+svp_t audio_chan_tbl[] = {
+	{ "1", 1 },		/* 1 channel */
+	{ "2", 2 },	/* 2 channels */
+	{nil},
+};
+
+svp_t audio_indev_tbl[] = {
+	{ "mic", Audio_Mic_Val }, 		/* input microphone */
+	{ "line", Audio_Linein_Val }, 	/* line in */
+	{nil},
+};
+
+svp_t audio_outdev_tbl[] = {
+	{ "spkr", Audio_Speaker_Val },	/* output speaker */
+	{ "hdph", Audio_Headphone_Val },/* head phones */
+	{ "line", Audio_Lineout_Val },	/* line out */
+	{nil},
+};
+
+svp_t audio_enc_tbl[] = {
+	{ "ulaw", Audio_Ulaw_Val },	/* u-law encoding */
+	{ "alaw", Audio_Alaw_Val },	/* A-law encoding */
+	{ "pcm", Audio_Pcm_Val },	/* Pulse Code Modulation */
+	{nil},
+};
+
+svp_t audio_rate_tbl[] = {
+	{ "8000", 8000 },	/* 8000 samples per second */
+	{ "11025", 11025 },	/* 11025 samples per second */
+	{ "22050", 22050 },	/* 22050 samples per second */
+	{ "44100", 44100 },	/* 44100 samples per second */
+	{nil},
+};
+
+Audio_d Default_Audio_Format =  {
+	0,
+	16,				/* bits per sample */
+	Audio_Max_Val,			/* buffer size (as percentage) */
+	2,				/* number of channels */
+	-1,				/* device */
+	Audio_Pcm_Val,			/* encoding format */
+	8000,				/* samples per second */
+	Audio_Max_Val,			/* left channel gain */
+	Audio_Max_Val,			/* right channel gain */
+};
+int Default_Audio_Input = Audio_Mic_Val;
+int Default_Audio_Output = Audio_Speaker_Val;
--- /dev/null
+++ b/emu/port/audio.h
@@ -1,0 +1,81 @@
+#define	AUDIO_BITS_FLAG		0x00000001
+#define	AUDIO_BUF_FLAG		0x00000002
+#define	AUDIO_CHAN_FLAG		0x00000004
+#define	AUDIO_COUNT_FLAG	0x00000008
+#define	AUDIO_DEV_FLAG		0x00000010
+#define	AUDIO_ENC_FLAG		0x00000020
+#define	AUDIO_RATE_FLAG		0x00000040
+#define	AUDIO_VOL_FLAG		0x00000080
+#define	AUDIO_LEFT_FLAG		0x00000100
+#define	AUDIO_RIGHT_FLAG	0x00000200
+#define	AUDIO_IN_FLAG		0x00000400
+#define	AUDIO_OUT_FLAG		0x00000800
+#define	AUDIO_MOD_FLAG		0x10000000
+
+#define	Audio_Min_Val		0
+#define	Audio_Max_Val		100
+
+#define 	Audio_No_Val		0
+#define 	Audio_In_Val		1
+#define 	Audio_Out_Val		2
+
+#define	Audio_Max_Buf		32768
+#define	Bits_Per_Byte		8
+
+typedef struct Audio_d Audio_d;
+struct Audio_d {
+	ulong	flags;		/* bit flag for fields */
+	ulong	bits;		/* bits per sample */
+	ulong	buf;		/* buffer size */
+	ulong	chan;		/* number of channels */
+	ulong	dev;		/* device */
+	ulong	enc;		/* encoding format */
+	ulong	rate;		/* samples per second */
+	ulong	left;		/* left channel gain */
+	ulong	right;		/* right channel gain */
+};
+
+typedef struct Audio_t Audio_t;
+struct Audio_t {
+	Audio_d	in;		/* input device */
+	Audio_d	out;		/* output device */
+};
+
+#define AUDIO_CMD_MAXNUM 32
+
+void audio_info_init(Audio_t*);
+int audioparse(char*, int n, Audio_t*);
+
+enum
+{
+	Qdir = 0,		/* must start at 0 representing a directory */
+	Qaudio,
+	Qaudioctl
+};
+
+/* required external platform specific functions */
+void	audio_file_init(void);
+void	audio_file_open(Chan*, int);
+long	audio_file_read(Chan*, void*, long, vlong);
+long	audio_file_write(Chan*, void*, long, vlong);
+long	audio_ctl_write(Chan*, void*, long, vlong);
+void	audio_file_close(Chan*);
+
+typedef struct _svp_t {
+	char*		s;	/* string */
+	unsigned long	v;	/* value */
+} svp_t;
+
+/* string value pairs for default audio values */
+extern svp_t audio_chan_tbl[];
+extern svp_t audio_indev_tbl[];
+extern svp_t audio_outdev_tbl[];
+extern svp_t audio_enc_tbl[];
+extern svp_t audio_rate_tbl[];
+extern svp_t audio_val_tbl[];
+extern svp_t audio_bits_tbl[];
+
+extern Audio_d Default_Audio_Format;
+extern int Default_Audio_Input, Default_Audio_Output;
+
+extern Audio_t* getaudiodev(void);
--- /dev/null
+++ b/emu/port/cache.c
@@ -1,0 +1,45 @@
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * no cache in hosted mode
+ */
+void
+cinit(void)
+{
+}
+
+void
+copen(Chan *c)
+{
+	c->flag &= ~CCACHE;
+}
+
+int
+cread(Chan *c, uchar *b, int n, vlong off)
+{
+	USED(c);
+	USED(b);
+	USED(n);
+	USED(off);
+	return 0;
+}
+
+void
+cwrite(Chan *c, uchar *buf, int n, vlong off)
+{
+	USED(c);
+	USED(buf);
+	USED(n);
+	USED(off);
+}
+
+void
+cupdate(Chan *c, uchar *buf,  int n, vlong off)
+{
+	USED(c);
+	USED(buf);
+	USED(n);
+	USED(off);
+}
+
--- /dev/null
+++ b/emu/port/chan.c
@@ -1,0 +1,1403 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+char*
+c2name(Chan *c)		/* DEBUGGING */
+{
+	if(c == nil)
+		return "<nil chan>";
+	if(c->name == nil)
+		return "<nil name>";
+	if(c->name->s == nil)
+		return "<nil name.s>";
+	return c->name->s;
+}
+
+enum
+{
+	CNAMESLOP	= 20
+};
+
+struct
+{
+	Lock	l;
+	int	fid;
+	Chan	*free;
+	Chan	*list;
+}chanalloc;
+
+typedef struct Elemlist Elemlist;
+
+struct Elemlist
+{
+	char	*name;	/* copy of name, so '/' can be overwritten */
+	int	nelems;
+	char	**elems;
+	int	*off;
+	int	mustbedir;
+};
+
+#define SEP(c) ((c) == 0 || (c) == '/')
+void cleancname(Cname*);
+
+int
+isdotdot(char *p)
+{
+	return p[0]=='.' && p[1]=='.' && p[2]=='\0';
+}
+
+int
+incref(Ref *r)
+{
+	int x;
+
+	lock(&r->lk);
+	x = ++r->ref;
+	unlock(&r->lk);
+	return x;
+}
+
+int
+decref(Ref *r)
+{
+	int x;
+
+	lock(&r->lk);
+	x = --r->ref;
+	unlock(&r->lk);
+	if(x < 0) 
+		panic("decref, pc=0x%lux", getcallerpc(&r));
+
+	return x;
+}
+
+/*
+ * Rather than strncpy, which zeros the rest of the buffer, kstrcpy
+ * truncates if necessary, always zero terminates, does not zero fill,
+ * and puts ... at the end of the string if it's too long.  Usually used to
+ * save a string in up->genbuf;
+ */
+void
+kstrcpy(char *s, char *t, int ns)
+{
+	int nt;
+
+	nt = strlen(t);
+	if(nt+1 <= ns){
+		memmove(s, t, nt+1);
+		return;
+	}
+	/* too long */
+	if(ns < 4){
+		/* but very short! */
+		strncpy(s, t, ns);
+		return;
+	}
+	/* truncate with ... at character boundary (very rare case) */
+	memmove(s, t, ns-4);
+	ns -= 4;
+	s[ns] = '\0';
+	/* look for first byte of UTF-8 sequence by skipping continuation bytes */
+	while(ns>0 && (s[--ns]&0xC0)==0x80)
+		;
+	strcpy(s+ns, "...");
+}
+
+int
+emptystr(char *s)
+{
+	return s == nil || s[0] == '\0';
+}
+
+/*
+ * Atomically replace *p with copy of s
+ */
+void
+kstrdup(char **p, char *s)
+{
+	int n;
+	char *t, *prev;
+
+	n = strlen(s)+1;
+	t = kmalloc(n);
+	if(t == nil)
+		panic("kstrdup: no memory");
+	memmove(t, s, n);
+	prev = *p;
+	*p = t;
+	free(prev);
+}
+
+static char isfrog[256]=
+{
+	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1
+};
+
+void
+chandevinit(void)
+{
+	int i;
+
+
+/*	isfrog[' '] = 1; */	/* let's see what happens */
+	isfrog['/'] = 1;
+	isfrog[0x7f] = 1;
+
+	for(i=0; devtab[i] != nil; i++)
+		devtab[i]->init();
+}
+
+Chan*
+newchan(void)
+{
+	Chan *c;
+
+	lock(&chanalloc.l);
+	c = chanalloc.free;
+	if(c != 0)
+		chanalloc.free = c->next;
+	unlock(&chanalloc.l);
+
+	if(c == nil) {
+		c = malloc(sizeof(Chan));
+		if(c == nil)
+			error(Enomem);
+		lock(&chanalloc.l);
+		c->fid = ++chanalloc.fid;
+		c->link = chanalloc.list;
+		chanalloc.list = c;
+		unlock(&chanalloc.l);
+	}
+
+	/* if you get an error before associating with a dev,
+	   close calls rootclose, a nop */
+	c->type = 0;
+	c->flag = 0;
+	c->r.ref = 1;
+	c->dev = 0;
+	c->offset = 0;
+	c->iounit = 0;
+	c->umh = 0;
+	c->uri = 0;
+	c->dri = 0;
+	c->aux = 0;
+	c->mchan = 0;
+	c->mcp = 0;
+	c->mux = 0;
+	c->mqid.path = 0;
+	c->mqid.vers = 0;
+	c->mqid.type = 0;
+	c->name = 0;
+	return c;
+}
+
+static Ref ncname;
+
+Cname*
+newcname(char *s)
+{
+	Cname *n;
+	int i;
+
+	n = smalloc(sizeof(Cname));
+	i = strlen(s);
+	n->len = i;
+	n->alen = i+CNAMESLOP;
+	n->s = smalloc(n->alen);
+	memmove(n->s, s, i+1);
+	n->r.ref = 1;
+	incref(&ncname);
+	return n;
+}
+
+void
+cnameclose(Cname *n)
+{
+	if(n == nil)
+		return;
+	if(decref(&n->r))
+		return;
+	decref(&ncname);
+	free(n->s);
+	free(n);
+}
+
+Cname*
+addelem(Cname *n, char *s)
+{
+	int i, a;
+	char *t;
+	Cname *new;
+
+	if(s[0]=='.' && s[1]=='\0')
+		return n;
+
+	if(n->r.ref > 1){
+		/* copy on write */
+		new = newcname(n->s);
+		cnameclose(n);
+		n = new;
+	}
+
+	i = strlen(s);
+	if(n->len+1+i+1 > n->alen){
+		a = n->len+1+i+1 + CNAMESLOP;
+		t = smalloc(a);
+		memmove(t, n->s, n->len+1);
+		free(n->s);
+		n->s = t;
+		n->alen = a;
+	}
+	if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/')	/* don't insert extra slash if one is present */
+		n->s[n->len++] = '/';
+	memmove(n->s+n->len, s, i+1);
+	n->len += i;
+	if(isdotdot(s))
+		cleancname(n);
+	return n;
+}
+
+void
+chanfree(Chan *c)
+{
+	c->flag = CFREE;
+
+	if(c->umh != nil){
+		putmhead(c->umh);
+		c->umh = nil;
+	}
+	if(c->umc != nil){
+		cclose(c->umc);
+		c->umc = nil;
+	}
+	if(c->mux != nil){
+		muxclose(c->mux);
+		c->mux = nil;
+	}
+	if(c->mchan != nil){
+		cclose(c->mchan);
+		c->mchan = nil;
+	}
+
+	cnameclose(c->name);
+
+	lock(&chanalloc.l);
+	c->next = chanalloc.free;
+	chanalloc.free = c;
+	unlock(&chanalloc.l);
+}
+
+void
+cclose(Chan *c)
+{
+	if(c == 0)
+		return;
+
+	if(c->flag&CFREE)
+		panic("cclose %lux", getcallerpc(&c));
+
+	if(decref(&c->r))
+		return;
+
+	if(!waserror()){
+		devtab[c->type]->close(c);
+		poperror();
+	}
+
+	chanfree(c);
+}
+
+/*
+ * Make sure we have the only copy of c.  (Copy on write.)
+ */
+Chan*
+cunique(Chan *c)
+{
+	Chan *nc;
+
+	if(c->r.ref != 1) {
+		nc = cclone(c);
+		cclose(c);
+		c = nc;
+	}
+
+	return c;
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+	return a.path==b.path && a.vers==b.vers;
+}
+
+int
+eqchan(Chan *a, Chan *b, int pathonly)
+{
+	if(a->qid.path != b->qid.path)
+		return 0;
+	if(!pathonly && a->qid.vers!=b->qid.vers)
+		return 0;
+	if(a->type != b->type)
+		return 0;
+	if(a->dev != b->dev)
+		return 0;
+	return 1;
+}
+
+int
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+{
+	if(a->qid.path != qid.path)
+		return 0;
+	if(!pathonly && a->qid.vers!=qid.vers)
+		return 0;
+	if(a->type != type)
+		return 0;
+	if(a->dev != dev)
+		return 0;
+	return 1;
+}
+
+Mhead*
+newmhead(Chan *from)
+{
+	Mhead *mh;
+
+	mh = smalloc(sizeof(Mhead));
+	mh->r.ref = 1;
+	mh->from = from;
+	incref(&from->r);
+
+/*
+	n = from->name->len;
+	if(n >= sizeof(mh->fromname))
+		n = sizeof(mh->fromname)-1;
+	memmove(mh->fromname, from->name->s, n);
+	mh->fromname[n] = 0;
+*/
+	return mh;
+}
+
+int
+cmount(Chan *new, Chan *old, int flag, char *spec)
+{
+	Pgrp *pg;
+	int order, flg;
+	Mhead *m, **l, *mh;
+	Mount *nm, *f, *um, **h;
+
+	if(QTDIR & (old->qid.type^new->qid.type))
+		error(Emount);
+
+if(old->umh)
+	print("cmount old extra umh\n");
+
+	order = flag&MORDER;
+
+	if((old->qid.type&QTDIR)==0 && order != MREPL)
+		error(Emount);
+
+	mh = new->umh;
+
+	/*
+	 * Not allowed to bind when the old directory
+	 * is itself a union.  (Maybe it should be allowed, but I don't see
+	 * what the semantics would be.)
+	 *
+	 * We need to check mh->mount->next to tell unions apart from
+	 * simple mount points, so that things like
+	 *	mount -c fd /root
+	 *	bind -c /root /
+	 * work.  The check of mount->mflag catches things like
+	 *	mount fd /root
+	 *	bind -c /root /
+	 * 
+	 * This is far more complicated than it should be, but I don't
+	 * see an easier way at the moment.		-rsc
+	 */
+	if((flag&MCREATE) && mh && mh->mount
+	&& (mh->mount->next || !(mh->mount->mflag&MCREATE)))
+		error(Emount);
+
+	pg = up->env->pgrp;
+	wlock(&pg->ns);
+
+	l = &MOUNTH(pg, old->qid);
+	for(m = *l; m; m = m->hash) {
+		if(eqchan(m->from, old, 1))
+			break;
+		l = &m->hash;
+	}
+
+	if(m == nil) {
+		/*
+		 *  nothing mounted here yet.  create a mount
+		 *  head and add to the hash table.
+		 */
+		m = newmhead(old);
+		*l = m;
+
+		/*
+		 *  if this is a union mount, add the old
+		 *  node to the mount chain.
+		 */
+		if(order != MREPL)
+			m->mount = newmount(m, old, 0, 0);
+	}
+	wlock(&m->lock);
+	if(waserror()){
+		wunlock(&m->lock);
+		nexterror();
+	}
+	wunlock(&pg->ns);
+
+	nm = newmount(m, new, flag, spec);
+	if(mh != nil && mh->mount != nil) {
+		/*
+		 *  copy a union when binding it onto a directory
+		 */
+		flg = order;
+		if(order == MREPL)
+			flg = MAFTER;
+		h = &nm->next;
+		um = mh->mount;
+		for(um = um->next; um; um = um->next) {
+			f = newmount(m, um->to, flg, um->spec);
+			*h = f;
+			h = &f->next;
+		}
+	}
+
+	if(m->mount && order == MREPL) {
+		mountfree(m->mount);
+		m->mount = 0;
+	}
+
+	if(flag & MCREATE)
+		nm->mflag |= MCREATE;
+
+	if(m->mount && order == MAFTER) {
+		for(f = m->mount; f->next; f = f->next)
+			;
+		f->next = nm;
+	}
+	else {
+		for(f = nm; f->next; f = f->next)
+			;
+		f->next = m->mount;
+		m->mount = nm;
+	}
+
+	wunlock(&m->lock);
+	poperror();
+	return nm->mountid;
+}
+
+void
+cunmount(Chan *mnt, Chan *mounted)
+{
+	Pgrp *pg;
+	Mhead *m, **l;
+	Mount *f, **p;
+
+	if(mnt->umh)	/* should not happen */
+		print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
+
+	/*
+	 * It _can_ happen that mounted->umh is non-nil, 
+	 * because mounted is the result of namec(Aopen)
+	 * (see sysfile.c:/^sysunmount).
+	 * If we open a union directory, it will have a umh.
+	 * Although surprising, this is okay, since the
+	 * cclose will take care of freeing the umh.
+	 */
+
+	pg = up->env->pgrp;
+	wlock(&pg->ns);
+
+	l = &MOUNTH(pg, mnt->qid);
+	for(m = *l; m; m = m->hash) {
+		if(eqchan(m->from, mnt, 1))
+			break;
+		l = &m->hash;
+	}
+
+	if(m == 0) {
+		wunlock(&pg->ns);
+		error(Eunmount);
+	}
+
+	wlock(&m->lock);
+	if(mounted == 0) {
+		*l = m->hash;
+		wunlock(&pg->ns);
+		mountfree(m->mount);
+		m->mount = nil;
+		cclose(m->from);
+		wunlock(&m->lock);
+		putmhead(m);
+		return;
+	}
+
+	p = &m->mount;
+	for(f = *p; f; f = f->next) {
+		/* BUG: Needs to be 2 pass */
+		if(eqchan(f->to, mounted, 1) ||
+		  (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+			*p = f->next;
+			f->next = 0;
+			mountfree(f);
+			if(m->mount == nil) {
+				*l = m->hash;
+				cclose(m->from);
+				wunlock(&m->lock);
+				wunlock(&pg->ns);
+				putmhead(m);
+				return;
+			}
+			wunlock(&m->lock);
+			wunlock(&pg->ns);
+			return;
+		}
+		p = &f->next;
+	}
+	wunlock(&m->lock);
+	wunlock(&pg->ns);
+	error(Eunion);
+}
+
+Chan*
+cclone(Chan *c)
+{
+	Chan *nc;
+	Walkqid *wq;
+
+	wq = devtab[c->type]->walk(c, nil, nil, 0);
+	if(wq == nil)
+		error("clone failed");
+	nc = wq->clone;
+	free(wq);
+	nc->name = c->name;
+	if(c->name)
+		incref(&c->name->r);
+	return nc;
+}
+
+int
+findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
+{
+	Pgrp *pg;
+	Mhead *m;
+
+	pg = up->env->pgrp;
+	rlock(&pg->ns);
+	for(m = MOUNTH(pg, qid); m; m = m->hash){
+		rlock(&m->lock);
+if(m->from == nil){
+	print("m %p m->from 0\n", m);
+	runlock(&m->lock);
+	continue;
+}
+		if(eqchantdqid(m->from, type, dev, qid, 1)) {
+			runlock(&pg->ns);
+			if(mp != nil){
+				incref(&m->r);
+				if(*mp != nil)
+					putmhead(*mp);
+				*mp = m;
+			}
+			if(*cp != nil)
+				cclose(*cp);
+			incref(&m->mount->to->r);
+			*cp = m->mount->to;
+			runlock(&m->lock);
+			return 1;
+		}
+		runlock(&m->lock);
+	}
+
+	runlock(&pg->ns);
+	return 0;
+}
+
+int
+domount(Chan **cp, Mhead **mp)
+{
+	return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid);
+}
+
+Chan*
+undomount(Chan *c, Cname *name)
+{
+	Chan *nc;
+	Pgrp *pg;
+	Mount *t;
+	Mhead **h, **he, *f;
+
+	pg = up->env->pgrp;
+	rlock(&pg->ns);
+	if(waserror()) {
+		runlock(&pg->ns);
+		nexterror();
+	}
+
+	he = &pg->mnthash[MNTHASH];
+	for(h = pg->mnthash; h < he; h++) {
+		for(f = *h; f; f = f->hash) {
+			if(strcmp(f->from->name->s, name->s) != 0)
+				continue;
+			for(t = f->mount; t; t = t->next) {
+				if(eqchan(c, t->to, 1)) {
+					/*
+					 * We want to come out on the left hand side of the mount
+					 * point using the element of the union that we entered on.
+					 * To do this, find the element that has a from name of
+					 * c->name->s.
+					 */
+					if(strcmp(t->head->from->name->s, name->s) != 0)
+						continue;
+					nc = t->head->from;
+					incref(&nc->r);
+					cclose(c);
+					c = nc;
+					break;
+				}
+			}
+		}
+	}
+	poperror();
+	runlock(&pg->ns);
+	return c;
+}
+
+/*
+ * Either walks all the way or not at all.  No partial results in *cp.
+ * *nerror is the number of names to display in an error message.
+ */
+static char Edoesnotexist[] = "does not exist";
+int
+walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+	int dev, dotdot, i, n, nhave, ntry, type;
+	Chan *c, *nc;
+	Cname *cname;
+	Mount *f;
+	Mhead *mh, *nmh;
+	Walkqid *wq;
+
+	c = *cp;
+	incref(&c->r);
+	cname = c->name;
+	incref(&cname->r);
+	mh = nil;
+
+	/*
+	 * While we haven't gotten all the way down the path:
+	 *    1. step through a mount point, if any
+	 *    2. send a walk request for initial dotdot or initial prefix without dotdot
+	 *    3. move to the first mountpoint along the way.
+	 *    4. repeat.
+	 *
+	 * An invariant is that each time through the loop, c is on the undomount
+	 * side of the mount point, and c's name is cname.
+	 */
+	for(nhave=0; nhave<nnames; nhave+=n){
+		if((c->qid.type&QTDIR)==0){
+			if(nerror)
+				*nerror = nhave;
+			cnameclose(cname);
+			cclose(c);
+			strcpy(up->env->errstr, Enotdir);
+			if(mh != nil)
+				putmhead(mh);
+			return -1;
+		}
+		ntry = nnames - nhave;
+		if(ntry > MAXWELEM)
+			ntry = MAXWELEM;
+		dotdot = 0;
+		for(i=0; i<ntry; i++){
+			if(isdotdot(names[nhave+i])){
+				if(i==0) {
+					dotdot = 1;
+					ntry = 1;
+				} else
+					ntry = i;
+				break;
+			}
+		}
+
+		if(!dotdot && !nomount)
+			domount(&c, &mh);
+
+		type = c->type;
+		dev = c->dev;
+
+		if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){
+			/* try a union mount, if any */
+			if(mh && !nomount){
+				/*
+				 * mh->mount == c, so start at mh->mount->next
+				 */
+				rlock(&mh->lock);
+				for(f = mh->mount->next; f; f = f->next)
+					if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil)
+						break;
+				runlock(&mh->lock);
+				if(f != nil){
+					type = f->to->type;
+					dev = f->to->dev;
+				}
+			}
+			if(wq == nil){
+				cclose(c);
+				cnameclose(cname);
+				if(nerror)
+					*nerror = nhave+1;
+				if(mh != nil)
+					putmhead(mh);
+				return -1;
+			}
+		}
+
+		nmh = nil;
+		if(dotdot) {
+			assert(wq->nqid == 1);
+			assert(wq->clone != nil);
+
+			cname = addelem(cname, "..");
+			nc = undomount(wq->clone, cname);
+			n = 1;
+		} else {
+			nc = nil;
+			if(!nomount)
+				for(i=0; i<wq->nqid && i<ntry-1; i++)
+					if(findmount(&nc, &nmh, type, dev, wq->qid[i]))
+						break;
+			if(nc == nil){	/* no mount points along path */
+				if(wq->clone == nil){
+					cclose(c);
+					cnameclose(cname);
+					if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){
+						if(nerror)
+							*nerror = nhave+wq->nqid+1;
+						strcpy(up->env->errstr, Edoesnotexist);
+					}else{
+						if(nerror)
+							*nerror = nhave+wq->nqid;
+						strcpy(up->env->errstr, Enotdir);
+					}
+					free(wq);
+					if(mh != nil)
+						putmhead(mh);
+					return -1;
+				}
+				n = wq->nqid;
+				nc = wq->clone;
+			}else{		/* stopped early, at a mount point */
+				if(wq->clone != nil){
+					cclose(wq->clone);
+					wq->clone = nil;
+				}
+				n = i+1;
+			}
+			for(i=0; i<n; i++)
+				cname = addelem(cname, names[nhave+i]);
+		}
+		cclose(c);
+		c = nc;
+		putmhead(mh);
+		mh = nmh;
+		free(wq);
+	}
+
+	putmhead(mh);
+
+	c = cunique(c);
+
+	if(c->umh != nil){	/* BUG */
+		print("walk umh\n");
+		putmhead(c->umh);
+		c->umh = nil;
+	}
+
+	cnameclose(c->name);
+	c->name = cname;
+
+	cclose(*cp);
+	*cp = c;
+	if(nerror)
+		*nerror = 0;
+	return 0;
+}
+
+/*
+ * c is a mounted non-creatable directory.  find a creatable one.
+ */
+Chan*
+createdir(Chan *c, Mhead *m)
+{
+	Chan *nc;
+	Mount *f;
+
+	rlock(&m->lock);
+	if(waserror()) {
+		runlock(&m->lock);
+		nexterror();
+	}
+	for(f = m->mount; f; f = f->next) {
+		if(f->mflag&MCREATE) {
+			nc = cclone(f->to);
+			runlock(&m->lock);
+			poperror();
+			cclose(c);
+			return nc;
+		}
+	}
+	error(Enocreate);
+	return 0;
+}
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+void
+cleancname(Cname *n)
+{
+	char *p;
+
+	if(n->s[0] == '#'){
+		p = strchr(n->s, '/');
+		if(p == nil)
+			return;
+		cleanname(p);
+
+		/*
+		 * The correct name is #i rather than #i/,
+		 * but the correct name of #/ is #/.
+		 */
+		if(strcmp(p, "/")==0 && n->s[1] != '/')
+			*p = '\0';
+	}else
+		cleanname(n->s);
+	n->len = strlen(n->s);
+}
+
+static void
+growparse(Elemlist *e)
+{
+	char **new;
+	int *inew;
+	enum { Delta = 8 };
+
+	if(e->nelems % Delta == 0){
+		new = smalloc((e->nelems+Delta) * sizeof(char*));
+		memmove(new, e->elems, e->nelems*sizeof(char*));
+		free(e->elems);
+		e->elems = new;
+		inew = smalloc((e->nelems+Delta+1) * sizeof(int));
+		memmove(inew, e->off, e->nelems*sizeof(int));
+		free(e->off);
+		e->off = inew;
+	}
+}
+
+/*
+ * The name is known to be valid.
+ * Copy the name so slashes can be overwritten.
+ * An empty string will set nelem=0.
+ * A path ending in / or /. or /.//./ etc. will have
+ * e.mustbedir = 1, so that we correctly
+ * reject, e.g., "/adm/users/." when /adm/users is a file
+ * rather than a directory.
+ */
+static void
+parsename(char *name, Elemlist *e)
+{
+	char *slash;
+
+	kstrdup(&e->name, name);
+	name = e->name;
+	e->nelems = 0;
+	e->elems = nil;
+	e->off = smalloc(sizeof(int));
+	e->off[0] = skipslash(name) - name;
+	for(;;){
+		name = skipslash(name);
+		if(*name=='\0'){
+			e->mustbedir = 1;
+			break;
+		}
+		growparse(e);
+		
+		e->elems[e->nelems++] = name;
+		slash = utfrune(name, '/');
+		if(slash == nil){
+			e->off[e->nelems] = name+strlen(name) - e->name;
+			e->mustbedir = 0;
+			break;
+		}
+		e->off[e->nelems] = slash - e->name;
+		*slash++ = '\0';
+		name = slash;
+	}
+}
+
+static void*
+kmemrchr(void *va, int c, long n)
+{
+	uchar *a, *e;
+
+	a = va;
+	for(e=a+n-1; e>a; e--)
+		if(*e == c)
+			return e;
+	return nil;
+}
+
+static void
+saveregisters(void)
+{
+}
+
+/*
+ * Turn a name into a channel.
+ * &name[0] is known to be a valid address.  It may be a kernel address.
+ *
+ * Opening with amode Aopen, Acreate, or Aremove guarantees
+ * that the result will be the only reference to that particular fid.
+ * This is necessary since we might pass the result to
+ * devtab[]->remove().
+ *
+ * Opening Atodir, Amount, or Aaccess does not guarantee this.
+ *
+ * Opening Aaccess can, under certain conditions, return a
+ * correct Chan* but with an incorrect Cname attached.
+ * Since the functions that open Aaccess (sysstat, syswstat, sys_stat)
+ * do not use the Cname*, this avoids an unnecessary clone.
+ */
+Chan*
+namec(char *aname, int amode, int omode, ulong perm)
+{
+	int n, prefix, len, t, nomount, npath;
+	Chan *c, *cnew;
+	Cname *cname;
+	Elemlist e;
+	Rune r;
+	Mhead *m;
+	char *createerr, tmperrbuf[ERRMAX];
+	char *name;
+
+	name = aname;
+	if(name[0] == '\0')
+		error("empty file name");
+	validname(name, 1);
+
+	/*
+	 * Find the starting off point (the current slash, the root of
+	 * a device tree, or the current dot) as well as the name to
+	 * evaluate starting there.
+	 */
+	nomount = 0;
+	switch(name[0]){
+	case '/':
+		c = up->env->pgrp->slash;
+		incref(&c->r);
+		break;
+	
+	case '#':
+		nomount = 1;
+		up->genbuf[0] = '\0';
+		n = 0;
+		while(*name!='\0' && (*name != '/' || n < 2)){
+			if(n >= sizeof(up->genbuf)-1)
+				error(Efilename);
+			up->genbuf[n++] = *name++;
+		}
+		up->genbuf[n] = '\0';
+		n = chartorune(&r, up->genbuf+1)+1;
+		if(r == 'M')
+			error(Enoattach);
+		/*
+		 *  the nodevs exceptions are
+		 *	|  it only gives access to pipes you create
+		 *	e  this process's environment
+		 *	s  private file2chan creation space
+		 *	D private secure sockets name space
+		 *	a private TLS name space
+		 */
+		if(up->env->pgrp->nodevs &&
+		   (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0'))
+			error(Enoattach);
+		t = devno(r, 1);
+		if(t == -1)
+			error(Ebadsharp);
+		c = devtab[t]->attach(up->genbuf+n);
+		break;
+
+	default:
+		c = up->env->pgrp->dot;
+		incref(&c->r);
+		break;
+	}
+	prefix = name - aname;
+
+	e.name = nil;
+	e.elems = nil;
+	e.off = nil;
+	e.nelems = 0;
+	if(waserror()){
+		cclose(c);
+		free(e.name);
+		free(e.elems);
+		free(e.off);
+//dumpmount();
+		nexterror();
+	}
+
+	/*
+	 * Build a list of elements in the path.
+	 */
+	parsename(name, &e);
+
+	/*
+	 * On create, ....
+	 */
+	if(amode == Acreate){
+		/* perm must have DMDIR if last element is / or /. */
+		if(e.mustbedir && !(perm&DMDIR)){
+			npath = e.nelems;
+			strcpy(tmperrbuf, "create without DMDIR");
+			goto NameError;
+		}
+
+		/* don't try to walk the last path element just yet. */
+		if(e.nelems == 0)
+			error(Eexist);
+		e.nelems--;
+	}
+
+	if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){
+		if(npath < 0 || npath > e.nelems){
+			print("namec %s walk error npath=%d\n", aname, npath);
+			nexterror();
+		}
+		strcpy(tmperrbuf, up->env->errstr);
+	NameError:
+		len = prefix+e.off[npath];
+		if(len < ERRMAX/3 || (name=kmemrchr(aname, '/', len))==nil || name==aname)
+			snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname);
+		else
+			snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name);
+		snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf);
+		nexterror();
+	}
+
+	if(e.mustbedir && !(c->qid.type&QTDIR)){
+		npath = e.nelems;
+		strcpy(tmperrbuf, "not a directory");
+		goto NameError;
+	}
+
+	if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
+		npath = e.nelems;
+		error("cannot exec directory");
+	}
+
+	switch(amode){
+	case Aaccess:
+		if(!nomount)
+			domount(&c, nil);
+		break;
+
+	case Abind:
+		m = nil;
+		if(!nomount)
+			domount(&c, &m);
+		if(c->umh != nil)
+			putmhead(c->umh);
+		c->umh = m;
+		break;
+
+	case Aremove:
+	case Aopen:
+	Open:
+		/* save the name; domount might change c */
+		cname = c->name;
+		incref(&cname->r);
+		m = nil;
+		if(!nomount)
+			domount(&c, &m);
+
+		/* our own copy to open or remove */
+		c = cunique(c);
+
+		/* now it's our copy anyway, we can put the name back */
+		cnameclose(c->name);
+		c->name = cname;
+
+		switch(amode){
+		case Aremove:
+			putmhead(m);
+			break;
+
+		case Aopen:
+		case Acreate:
+if(c->umh != nil){
+	print("cunique umh\n");
+	putmhead(c->umh);
+	c->umh = nil;
+}
+
+			/* only save the mount head if it's a multiple element union */
+			if(m && m->mount && m->mount->next)
+				c->umh = m;
+			else
+				putmhead(m);
+
+			/* save registers else error() in open has wrong value of c saved */
+			saveregisters();
+
+			if(omode == OEXEC)
+				c->flag &= ~CCACHE;
+
+			c = devtab[c->type]->open(c, omode&~OCEXEC);
+
+			if(omode & OCEXEC)
+				c->flag |= CCEXEC;
+			if(omode & ORCLOSE)
+				c->flag |= CRCLOSE;
+			break;
+		}
+		break;
+
+	case Atodir:
+		/*
+		 * Directories (e.g. for cd) are left before the mount point,
+		 * so one may mount on / or . and see the effect.
+		 */
+		if(!(c->qid.type & QTDIR))
+			error(Enotdir);
+		break;
+
+	case Amount:
+		/*
+		 * When mounting on an already mounted upon directory,
+		 * one wants subsequent mounts to be attached to the
+		 * original directory, not the replacement.  Don't domount.
+		 */
+		break;
+
+	case Acreate:
+		/*
+		 * We've already walked all but the last element.
+		 * If the last exists, try to open it OTRUNC.
+		 * If omode&OEXCL is set, just give up.
+		 */
+		e.nelems++;
+		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
+			if(omode&OEXCL)
+				error(Eexist);
+			omode |= OTRUNC;
+			goto Open;
+		}
+
+		/*
+		 * The semantics of the create(2) system call are that if the
+		 * file exists and can be written, it is to be opened with truncation.
+		 * On the other hand, the create(5) message fails if the file exists.
+		 * If we get two create(2) calls happening simultaneously, 
+		 * they might both get here and send create(5) messages, but only 
+		 * one of the messages will succeed.  To provide the expected create(2)
+		 * semantics, the call with the failed message needs to try the above
+		 * walk again, opening for truncation.  This correctly solves the 
+		 * create/create race, in the sense that any observable outcome can
+		 * be explained as one happening before the other.
+		 * The create/create race is quite common.  For example, it happens
+		 * when two rc subshells simultaneously update the same
+		 * environment variable.
+		 *
+		 * The implementation still admits a create/create/remove race:
+		 * (A) walk to file, fails
+		 * (B) walk to file, fails
+		 * (A) create file, succeeds, returns 
+		 * (B) create file, fails
+		 * (A) remove file, succeeds, returns
+		 * (B) walk to file, return failure.
+		 *
+		 * This is hardly as common as the create/create race, and is really
+		 * not too much worse than what might happen if (B) got a hold of a
+		 * file descriptor and then the file was removed -- either way (B) can't do
+		 * anything with the result of the create call.  So we don't care about this race.
+		 *
+		 * Applications that care about more fine-grained decision of the races
+		 * can use the OEXCL flag to get at the underlying create(5) semantics;
+		 * by default we provide the common case.
+		 *
+		 * We need to stay behind the mount point in case we
+		 * need to do the first walk again (should the create fail).
+		 *
+		 * We also need to cross the mount point and find the directory
+		 * in the union in which we should be creating.
+		 *
+		 * The channel staying behind is c, the one moving forward is cnew.
+		 */
+		m = nil;
+		cnew = nil;	/* is this assignment necessary? */
+		if(!waserror()){	/* try create */
+			if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid))
+				cnew = createdir(cnew, m);
+			else{
+				cnew = c;
+				incref(&cnew->r);
+			}
+
+			/*
+			 * We need our own copy of the Chan because we're
+			 * about to send a create, which will move it.  Once we have
+			 * our own copy, we can fix the name, which might be wrong
+			 * if findmount gave us a new Chan.
+			 */
+			cnew = cunique(cnew);
+			cnameclose(cnew->name);
+			cnew->name = c->name;
+			incref(&cnew->name->r);
+
+			devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
+			poperror();
+			if(omode & OCEXEC)
+				cnew->flag |= CCEXEC;
+			if(omode & ORCLOSE)
+				cnew->flag |= CRCLOSE;
+			if(m)
+				putmhead(m);
+			cclose(c);
+			c = cnew;
+			c->name = addelem(c->name, e.elems[e.nelems-1]);
+			break;
+		}else{		/* create failed */
+			cclose(cnew);
+			if(m)
+				putmhead(m);
+			if(omode & OEXCL)
+				nexterror();
+			/* save error */
+			createerr = up->env->errstr;
+			up->env->errstr = tmperrbuf;
+			/* note: we depend that walk does not error */
+			if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
+				up->env->errstr = createerr;
+				error(createerr);	/* report true error */
+			}
+			up->env->errstr = createerr;
+			omode |= OTRUNC;
+			goto Open;
+		}
+
+	default:
+		panic("unknown namec access %d\n", amode);
+	}
+
+	poperror();
+
+	/* place final element in genbuf for e.g. exec */
+	if(e.nelems > 0)
+		kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf);
+	else
+		kstrcpy(up->genbuf, ".", sizeof up->genbuf);
+	free(e.name);
+	free(e.elems);
+	free(e.off);
+
+	return c;
+}
+
+/*
+ * name is valid. skip leading / and ./ as much as possible
+ */
+char*
+skipslash(char *name)
+{
+	while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/')))
+		name++;
+	return name;
+}
+
+/*
+ * Check that the name
+ *  a) is in valid memory.
+ *  b) is shorter than 2^16 bytes, so it can fit in a 9P string field.
+ *  c) contains no frogs.
+ * The first byte is known to be addressible by the requester, so the
+ * routine works for kernel and user memory both.
+ * The parameter slashok flags whether a slash character is an error
+ * or a valid character.
+ */
+void
+validname(char *aname, int slashok)
+{
+	char *ename, *name;
+	int c;
+	Rune r;
+
+	name = aname;
+	ename = memchr(name, 0, (1<<16));
+
+	if(ename==nil || ename-name>=(1<<16))
+		error("name too long");
+
+	while(*name){
+		/* all characters above '~' are ok */
+		c = *(uchar*)name;
+		if(c >= Runeself)
+			name += chartorune(&r, name);
+		else{
+			if(isfrog[c])
+				if(!slashok || c!='/'){
+					snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
+					error(up->genbuf);
+			}
+			name++;
+		}
+	}
+}
+
+void
+isdir(Chan *c)
+{
+	if(c->qid.type & QTDIR)
+		return;
+	error(Enotdir);
+}
+
+/*
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ *	- the mhead in the namespace hash table
+ *	- the mhead in chans returned from findmount:
+ *	  used in namec and then by unionread.
+ *	- the mhead in chans returned from createdir:
+ *	  used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted when we cunmount.
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as 
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+	if(m && decref(&m->r) == 0){
+		m->mount = (Mount*)0xCafeBeef;
+		free(m);
+	}
+}
--- /dev/null
+++ b/emu/port/dat.h
@@ -1,0 +1,513 @@
+typedef struct Block	Block;
+typedef struct Chan	Chan;
+typedef struct Cmdbuf	Cmdbuf;
+typedef struct Cmdtab	Cmdtab;
+typedef struct Cname	Cname;
+typedef struct Dev	Dev;
+typedef struct Dirtab	Dirtab;
+typedef struct Egrp	Egrp;
+typedef struct Evalue	Evalue;
+typedef struct Fgrp	Fgrp;
+typedef struct Mount	Mount;
+typedef struct Mntcache Mntcache;
+typedef struct Mntparam Mntparam;
+typedef struct Mntrpc	Mntrpc;
+typedef struct Mntwalk	Mntwalk;
+typedef struct Mnt	Mnt;
+typedef struct Mhead	Mhead;
+typedef struct Osenv	Osenv;
+typedef struct Pgrp	Pgrp;
+typedef struct Proc	Proc;
+typedef struct Queue	Queue;
+typedef struct Ref	Ref;
+typedef struct Rendez	Rendez;
+typedef struct Rept	Rept;
+typedef struct Rootdata Rootdata;
+/*typedef struct RWlock	RWlock;*/
+typedef struct RWLock	RWlock;
+typedef struct Procs	Procs;
+typedef struct Signerkey Signerkey;
+typedef struct Skeyset	Skeyset;
+typedef struct Uqid	Uqid;
+typedef struct Uqidtab	Uqidtab;
+typedef struct Walkqid	Walkqid;
+
+#include "lib9.h"
+#undef CHDIR
+#undef NAMELEN
+#undef ERRLEN
+
+#include "emu.h"
+
+#pragma incomplete Queue
+#pragma incomplete Mntrpc
+
+#include "fcall.h"
+
+#include "pool.h"
+
+typedef int    Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
+
+enum
+{
+	NERR		= 32,
+	KNAMELEN	= 28,
+	MAXROOT		= 5*KNAMELEN, 	/* Maximum root pathname len of devfs-* */
+	NUMSIZE		= 11,
+	PRINTSIZE	= 256,
+	READSTR		= 1000		/* temporary buffer size for device reads */
+};
+
+struct Ref
+{
+	Lock	lk;
+	long	ref;
+};
+
+struct Rendez
+{
+	Lock	l;
+	Proc*	p;
+};
+
+struct Rept
+{
+	Lock	l;
+	Rendez	r;
+	void	*o;
+	int	t;
+	int	(*active)(void*);
+	int	(*ck)(void*, int);
+	void	(*f)(void*);	/* called with VM acquire()'d */
+};
+
+/*
+ * Access types in namec & channel flags
+ */
+enum
+{
+	Aaccess,			/* as in access, stat */
+	Abind,			/* for left-hand-side of bind */
+	Atodir,				/* as in chdir */
+	Aopen,				/* for i/o */
+	Amount,				/* to be mounted upon */
+	Acreate,			/* file is to be created */
+	Aremove,			/* will be removed by caller */
+
+	COPEN	= 0x0001,		/* for i/o */
+	CMSG	= 0x0002,		/* the message channel for a mount */
+/*rsc	CCREATE	= 0x0004,		*//* permits creation if c->mnt */
+	CCEXEC	= 0x0008,		/* close on exec */
+	CFREE	= 0x0010,		/* not in use */
+	CRCLOSE	= 0x0020,		/* remove on close */
+	CCACHE	= 0x0080		/* client cache */
+};
+
+struct Chan
+{
+	Lock	l;
+	Ref	r;
+	Chan*	next;			/* allocation */
+	Chan*	link;
+	vlong	offset;			/* in file */
+	ushort	type;
+	ulong	dev;
+	ushort	mode;			/* read/write */
+	ushort	flag;
+	Qid	qid;
+	int	fid;			/* for devmnt */
+	ulong	iounit;	/* chunk size for i/o; 0==default */
+	Mhead*	umh;			/* mount point that derived Chan; used in unionread */
+	Chan*	umc;			/* channel in union; held for union read */
+	QLock	umqlock;		/* serialize unionreads */
+	int	uri;			/* union read index */
+	int	dri;			/* devdirread index */
+	ulong	mountid;
+	Mntcache *mcp;			/* Mount cache pointer */
+	Mnt		*mux;		/* Mnt for clients using me for messages */
+	void*	aux;		/* device specific data */
+	Chan*	mchan;			/* channel to mounted server */
+	Qid	mqid;			/* qid of root of mount point */
+	Cname	*name;
+};
+
+struct Cname
+{
+	Ref	r;
+	int	alen;			/* allocated length */
+	int	len;			/* strlen(s) */
+	char	*s;
+};
+
+struct Dev
+{
+	int	dc;
+	char*	name;
+
+	void	(*init)(void);
+	Chan*	(*attach)(char*);
+	Walkqid*	(*walk)(Chan*, Chan*, char**, int);
+	int	(*stat)(Chan*, uchar*, int);
+	Chan*	(*open)(Chan*, int);
+	void	(*create)(Chan*, char*, int, ulong);
+	void	(*close)(Chan*);
+	long	(*read)(Chan*, void*, long, vlong);
+	Block*	(*bread)(Chan*, long, ulong);
+	long	(*write)(Chan*, void*, long, vlong);
+	long	(*bwrite)(Chan*, Block*, ulong);
+	void	(*remove)(Chan*);
+	int	(*wstat)(Chan*, uchar*, int);
+};
+
+enum
+{
+	BINTR		=	(1<<0),
+	BFREE		=	(1<<1),
+	BMORE		=	(1<<2)		/* continued in next block */
+};
+
+struct Block
+{
+	Block*	next;
+	Block*	list;
+	uchar*	rp;			/* first unconsumed byte */
+	uchar*	wp;			/* first empty byte */
+	uchar*	lim;			/* 1 past the end of the buffer */
+	uchar*	base;			/* start of the buffer */
+	void	(*free)(Block*);
+	ulong	flag;
+};
+#define BLEN(s)	((s)->wp - (s)->rp)
+#define BALLOC(s) ((s)->lim - (s)->base)
+
+struct Dirtab
+{
+	char	name[KNAMELEN];
+	Qid	qid;
+	vlong	length;
+	long	perm;
+};
+
+struct Walkqid
+{
+	Chan	*clone;
+	int	nqid;
+	Qid	qid[1];
+};
+
+enum
+{
+	NSMAX	=	1000,
+	NSLOG	=	7,
+	NSCACHE	=	(1<<NSLOG)
+};
+
+struct Mntwalk				/* state for /proc/#/ns */
+{
+	int		cddone;
+	ulong	id;
+	Mhead*	mh;
+	Mount*	cm;
+};
+
+struct Mount
+{
+	ulong	mountid;
+	Mount*	next;
+	Mhead*	head;
+	Mount*	copy;
+	Mount*	order;
+	Chan*	to;			/* channel replacing channel */
+	int	mflag;
+	char	*spec;
+};
+
+struct Mhead
+{
+	Ref	r;
+	RWlock	lock;
+	Chan*	from;			/* channel mounted upon */
+	Mount*	mount;			/* what's mounted upon it */
+	Mhead*	hash;			/* Hash chain */
+};
+
+struct Mnt
+{
+	Lock	l;
+	/* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */
+	Chan*	c;		/* Channel to file service */
+	Proc*	rip;		/* Reader in progress */
+	Mntrpc*	queue;		/* Queue of pending requests on this channel */
+	ulong	id;		/* Multiplexor id for channel check */
+	Mnt*	list;		/* Free list */
+	int	flags;		/* cache */
+	int	msize;		/* data + IOHDRSZ */
+	char	*version;			/* 9P version */
+	Queue	*q;		/* input queue */
+};
+
+enum
+{
+	MNTLOG	=	5,
+	MNTHASH =	1<<MNTLOG,		/* Hash to walk mount table */
+	DELTAFD=		20,		/* allocation quantum for process file descriptors */
+	MAXNFD =		4000,		/* max per process file descriptors */
+	MAXKEY =		8	/* keys for signed modules */
+};
+#define MOUNTH(p,qid)	((p)->mnthash[(qid).path&((1<<MNTLOG)-1)])
+
+struct Mntparam {
+	Chan*	chan;
+	Chan*	authchan;
+	char*	spec;
+	int	flags;
+};
+
+struct Pgrp
+{
+	Ref	r;			/* also used as a lock when mounting */
+	ulong	pgrpid;
+	RWlock	ns;			/* Namespace n read/one write lock */
+	QLock	nsh;
+	Mhead*	mnthash[MNTHASH];
+	int	progmode;
+	Chan*	dot;
+	Chan*	slash;
+	int	nodevs;
+	int	pin;
+};
+
+enum
+{
+	Nopin =	-1
+};
+
+struct Fgrp
+{
+	Lock	l;
+	Ref	r;
+	Chan**	fd;
+	int	nfd;			/* number of fd slots */
+	int	maxfd;			/* highest fd in use */
+	int	minfd;			/* lower bound on free fd */
+};
+
+struct Evalue
+{
+	char	*var;
+	char	*val;
+	int	len;
+	Qid	qid;
+	Evalue	*next;
+};
+
+struct Egrp
+{
+	Ref	r;
+	QLock	l;
+	ulong	path;
+	ulong	vers;
+	Evalue	*entries;
+};
+
+struct Signerkey
+{
+	Ref	r;
+	char*	owner;
+	ushort	footprint;
+	ulong	expires;
+	void*	alg;
+	void*	pk;
+	void	(*pkfree)(void*);
+};
+
+struct Skeyset
+{
+	Ref	r;
+	QLock	l;
+	ulong	flags;
+	char*	devs;
+	int	nkey;
+	Signerkey	*keys[MAXKEY];
+};
+
+struct Uqid
+{
+	Ref	r;
+	int	type;
+	int	dev;
+	vlong	oldpath;
+	vlong	newpath;
+	Uqid*	next;
+};
+
+enum
+{
+	Nqidhash = 32
+};
+
+struct Uqidtab
+{
+	QLock	l;
+	Uqid*	qids[Nqidhash];
+	ulong	pathgen;
+};
+
+struct Osenv
+{
+	char	*syserrstr;	/* last error from a system call, errbuf0 or 1 */
+	char	*errstr;	/* reason we're unwinding the error stack, errbuf1 or 0 */
+	char	errbuf0[ERRMAX];
+	char	errbuf1[ERRMAX];
+	Pgrp*	pgrp;		/* Ref to namespace, working dir and root */
+	Fgrp*	fgrp;		/* Ref to file descriptors */
+	Egrp*	egrp;	/* Environment vars */
+	Skeyset*		sigs;		/* Signed module keys */
+	Rendez*	rend;		/* Synchro point */
+	Queue*	waitq;		/* Info about dead children */
+	Queue*	childq;		/* Info about children for debuggers */
+	void*	debug;		/* Debugging master */
+	char*	user;	/* Inferno user name */
+	FPU	fpu;		/* Floating point thread state */
+	int	uid;		/* Numeric user id for host system */
+	int	gid;		/* Numeric group id for host system */
+	void	*ui;		/* User info for NT */
+};
+
+enum
+{
+	Unknown	= 0xdeadbabe,
+	IdleGC	= 0x16,
+	Interp	= 0x17,
+	BusyGC	= 0x18,
+	Moribund
+};
+
+struct Proc
+{
+	int	type;		/* interpreter or not */
+	char	text[KNAMELEN];
+	Proc*	qnext;		/* list of processes waiting on a Qlock */
+	long	pid;
+	Proc*	next;		/* list of created processes */
+	Proc*	prev;
+	Lock	rlock;	/* sync between sleep/swiproc for r */
+	Rendez*	r;		/* rendezvous point slept on */
+	Rendez	sleep;		/* place to sleep */
+	int		killed;		/* by swiproc */
+	int	swipend;	/* software interrupt pending for Prog */
+	int	syscall;	/* set true under sysio for interruptable syscalls */
+	int	intwait;	/* spin wait for note to turn up */
+	int	sigid;		/* handle used for signal/note/exception */
+	Lock	sysio;		/* note handler lock */
+	char	genbuf[128];	/* buffer used e.g. for last name element from namec */
+	int	nerr;		/* error stack SP */
+	osjmpbuf	estack[NERR];	/* vector of error jump labels */
+	char*	kstack;
+	void	(*func)(void*);	/* saved trampoline pointer for kproc */
+	void*	arg;		/* arg for invoked kproc function */
+	void*	iprog;		/* work for Prog after release */
+	void*	prog;		/* fake prog for slaves eg. exportfs */
+	Osenv*	env;		/* effective operating system environment */
+	Osenv	defenv;		/* default env for slaves with no prog */
+	osjmpbuf	privstack;	/* private stack for making new kids */
+	osjmpbuf	sharestack;
+	Proc	*kid;
+	void	*kidsp;
+	void	*os;		/* host os specific data */
+};
+
+#define poperror()	up->nerr--
+#define	waserror()	(up->nerr++, ossetjmp(up->estack[up->nerr-1]))
+
+enum
+{
+	/* kproc flags */
+	KPDUPPG		= (1<<0),
+	KPDUPFDG	= (1<<1),
+	KPDUPENVG	= (1<<2),
+	KPX11		= (1<<8),		/* needs silly amount of stack */
+	KPDUP		= (KPDUPPG|KPDUPFDG|KPDUPENVG)
+};
+
+struct Procs
+{
+	Lock	l;
+	Proc*	head;
+	Proc*	tail;
+};
+
+struct Rootdata
+{
+	int	dotdot;
+	void	*ptr;
+	int	size;
+	int	*sizep;
+};
+
+extern	Dev*	devtab[];
+extern	char	*ossysname;
+extern	char	*eve;
+extern	Queue*	kbdq;
+extern	Queue*	gkbdq;
+extern	Queue*	gkscanq;
+extern	int	Xsize;
+extern	int	Ysize;
+extern	Pool*	mainmem;
+extern	char	rootdir[MAXROOT];		/* inferno root */
+extern	Procs	procs;
+extern	int	sflag;
+extern	int	xtblbit;
+extern	int	globfs;
+extern	int	greyscale;
+extern	uint	qiomaxatomic;
+
+/*
+ * floating point control and status register masks
+ */
+enum
+{
+	INVAL		= 0x0001,
+	ZDIV		= 0x0002,
+	OVFL		= 0x0004,
+	UNFL		= 0x0008,
+	INEX		= 0x0010,
+	RND_NR		= 0x0000,
+	RND_NINF	= 0x0100,
+	RND_PINF	= 0x0200,
+	RND_Z		= 0x0300,
+	RND_MASK	= 0x0300
+};
+
+struct Cmdbuf
+{
+	char	*buf;
+	char	**f;
+	int	nf;
+};
+
+struct Cmdtab
+{
+	int	index;	/* used by client to switch on result */
+	char	*cmd;	/* command name */
+	int	narg;	/* expected #args; 0 ==> variadic */
+};
+
+/* queue state bits,  Qmsg, Qcoalesce, and Qkick can be set in qopen */
+enum
+{
+	/* Queue.state */
+	Qstarve		= (1<<0),	/* consumer starved */
+	Qmsg		= (1<<1),	/* message stream */
+	Qclosed		= (1<<2),	/* queue has been closed/hungup */
+	Qflow		= (1<<3),	/* producer flow controlled */
+	Qcoalesce	= (1<<4),	/* coallesce packets on read */
+	Qkick		= (1<<5),	/* always call the kick routine after qwrite */
+};
+
+#define DEVDOTDOT -1
+
+#pragma varargck	type	"I" uchar*
+#pragma	varargck	type	"E" uchar*
+
+extern void	(*mainmonitor)(int, void*, ulong);
--- /dev/null
+++ b/emu/port/dev.c
@@ -1,0 +1,446 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+extern ulong	kerndate;
+
+void
+mkqid(Qid *q, vlong path, ulong vers, int type)
+{
+	q->type = type;
+	q->vers = vers;
+	q->path = path;
+}
+
+int
+devno(int c, int user)
+{
+	int i;
+
+	for(i = 0; devtab[i] != nil; i++) {
+		if(devtab[i]->dc == c)
+			return i;
+	}
+	if(user == 0)
+		panic("devno %C 0x%ux", c, c);
+
+	return -1;
+}
+
+void
+devdir(Chan *c, Qid qid, char *n, long length, char *user, long perm, Dir *db)
+{
+	db->name = n;
+	if(c->flag&CMSG)
+		qid.type |= QTMOUNT;
+	db->qid = qid;
+	db->type = devtab[c->type]->dc;
+	db->dev = c->dev;
+	db->mode = perm | (qid.type << 24);
+	db->atime = time(0);
+	db->mtime = kerndate;
+	db->length = length;
+	db->uid = user;
+	db->gid = eve;
+	db->muid = user;
+}
+
+/*
+ * the zeroth element of the table MUST be the directory itself for ..
+ */
+int
+devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	USED(name);
+	if(tab == 0)
+		return -1;
+	if(i != DEVDOTDOT){
+		/* skip over the first element, that for . itself */
+		i++;
+		if(i >= ntab)
+			return -1;
+		tab += i;
+	}
+	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+	return 1;
+}
+
+void
+devinit(void)
+{
+}
+
+void
+devshutdown(void)
+{
+}
+
+Chan*
+devattach(int tc, char *spec)
+{
+	Chan *c;
+	char *buf;
+
+	c = newchan();
+	mkqid(&c->qid, 0, 0, QTDIR);
+	c->type = devno(tc, 0);
+	if(spec == nil)
+		spec = "";
+	buf = smalloc(4+strlen(spec)+1);
+	sprint(buf, "#%C%s", tc, spec);
+	c->name = newcname(buf);
+	free(buf);
+	return c;
+}
+
+Chan*
+devclone(Chan *c)
+{
+	Chan *nc;
+
+	if(c->flag & COPEN)
+		panic("clone of open file type %C\n", devtab[c->type]->dc);
+
+	nc = newchan();
+	nc->type = c->type;
+	nc->dev = c->dev;
+	nc->mode = c->mode;
+	nc->qid = c->qid;
+	nc->offset = c->offset;
+	nc->umh = nil;
+	nc->mountid = c->mountid;
+	nc->aux = c->aux;
+	nc->mqid = c->mqid;
+	nc->mcp = c->mcp;
+	return nc;
+}
+
+Walkqid*
+devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i, j;
+	volatile int alloc;
+	Walkqid *wq;
+	char *n;
+	Dir dir;
+
+	if(nname > 0)
+		isdir(c);
+
+	alloc = 0;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone!=nil)
+			cclose(wq->clone);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;	/* device doesn't know about this channel yet */
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	for(j=0; j<nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			goto Done;
+		}
+		n = name[j];
+		if(strcmp(n, ".") == 0){
+    Accept:
+			wq->qid[wq->nqid++] = nc->qid;
+			continue;
+		}
+		if(strcmp(n, "..") == 0){
+			(*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir);
+			nc->qid = dir.qid;
+			goto Accept;
+		}
+		/*
+		 * Ugly problem: If we're using devgen, make sure we're
+		 * walking the directory itself, represented by the first
+		 * entry in the table, and not trying to step into a sub-
+		 * directory of the table, e.g. /net/net. Devgen itself
+		 * should take care of the problem, but it doesn't have
+		 * the necessary information (that we're doing a walk).
+		 */
+		if(gen==devgen && nc->qid.path!=tab[0].qid.path)
+			goto Notfound;
+		for(i=0;; i++) {
+			switch((*gen)(nc, n, tab, ntab, i, &dir)){
+			case -1:
+			Notfound:
+				if(j == 0)
+					error(Enonexist);
+				kstrcpy(up->env->errstr, Enonexist, ERRMAX);
+				goto Done;
+			case 0:
+				continue;
+			case 1:
+				if(strcmp(n, dir.name) == 0){
+					nc->qid = dir.qid;
+					goto Accept;
+				}
+				continue;
+			}
+		}
+	}
+	/*
+	 * We processed at least one name, so will return some data.
+	 * If we didn't process all nname entries succesfully, we drop
+	 * the cloned channel and return just the Qids of the walks.
+	 */
+Done:
+	poperror();
+	if(wq->nqid < nname){
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else if(wq->clone){
+		/* attach cloned channel to same device */
+		wq->clone->type = c->type;
+	}
+	return wq;
+}
+
+int
+devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i;
+	Dir dir;
+	char *p, *elem;
+
+	for(i=0;; i++)
+		switch((*gen)(c, nil, tab, ntab, i, &dir)){
+		case -1:
+			if(c->qid.type & QTDIR){
+				if(c->name == nil)
+					elem = "???";
+				else if(strcmp(c->name->s, "/") == 0)
+					elem = "/";
+				else
+					for(elem=p=c->name->s; *p; p++)
+						if(*p == '/')
+							elem = p+1;
+				devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
+				n = convD2M(&dir, db, n);
+				if(n == 0)
+					error(Ebadarg);
+				return n;
+			}
+			print("%s %s: devstat %C %llux\n",
+				up->text, up->env->user,
+				devtab[c->type]->dc, c->qid.path);
+
+			error(Enonexist);
+		case 0:
+			break;
+		case 1:
+			if(c->qid.path == dir.qid.path) {
+				if(c->flag&CMSG)
+					dir.mode |= DMMOUNT;
+				n = convD2M(&dir, db, n);
+				if(n == 0)
+					error(Ebadarg);
+				return n;
+			}
+			break;
+		}
+}
+
+long
+devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
+{
+	long m, dsz;
+	struct{
+		Dir d;
+		char slop[100];	/* TO DO */
+	}dir;
+
+	for(m=0; m<n; c->dri++) {
+		switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){
+		case -1:
+			return m;
+
+		case 0:
+			break;
+
+		case 1:
+			dsz = convD2M(&dir.d, (uchar*)d, n-m);
+			if(dsz <= BIT16SZ){	/* <= not < because this isn't stat; read is stuck */
+				if(m == 0)
+					error(Eshort);
+				return m;
+			}
+			m += dsz;
+			d += dsz;
+			break;
+		}
+	}
+
+	return m;
+}
+
+/*
+ * error(Eperm) if open permission not granted for up->env->user.
+ */
+void
+devpermcheck(char *fileuid, ulong perm, int omode)
+{
+	ulong t;
+	static int access[] = { 0400, 0200, 0600, 0100 };
+
+	if(strcmp(up->env->user, fileuid) == 0)
+		perm <<= 0;
+	else
+	if(strcmp(up->env->user, eve) == 0)
+		perm <<= 3;
+	else
+		perm <<= 6;
+
+	t = access[omode&3];
+	if((t&perm) != t)
+		error(Eperm);
+}
+
+Chan*
+devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
+{
+	int i;
+	Dir dir;
+
+	for(i=0;; i++) {
+		switch((*gen)(c, nil, tab, ntab, i, &dir)){
+		case -1:
+			goto Return;
+		case 0:
+			break;
+		case 1:
+			if(c->qid.path == dir.qid.path) {
+				devpermcheck(dir.uid, dir.mode, omode);
+				goto Return;
+			}
+			break;
+		}
+	}
+Return:
+	c->offset = 0;
+	if((c->qid.type&QTDIR) && omode!=OREAD)
+		error(Eperm);
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	return c;
+}
+
+Block*
+devbread(Chan *c, long n, ulong offset)
+{
+	Block *bp;
+
+	bp = allocb(n);
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
+	poperror();
+	return bp;
+}
+
+long
+devbwrite(Chan *c, Block *bp, ulong offset)
+{
+	long n;
+
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
+	poperror();
+	freeb(bp);
+
+	return n;
+}
+
+void
+devcreate(Chan *c, char *name, int mode, ulong perm)
+{
+	USED(c);
+	USED(name);
+	USED(mode);
+	USED(perm);
+	error(Eperm);
+}
+
+void
+devremove(Chan *c)
+{
+	USED(c);
+	error(Eperm);
+}
+
+int
+devwstat(Chan *c, uchar *dp, int n)
+{
+	USED(c);
+	USED(dp);
+	USED(n);
+	error(Eperm);
+	return 0;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+	int size;
+
+	size = strlen(str);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, str+off, n);
+	return n;
+}
+
+int
+readnum(ulong off, char *buf, ulong n, ulong val, int size)
+{
+	char tmp[64];
+
+	if(size > 64) size = 64;
+
+	snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, tmp+off, n);
+	return n;
+}
+
+/*
+ * check that the name in a wstat is plausible
+ */
+void
+validwstatname(char *name)
+{
+	validname(name, 0);
+	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+		error(Efilename);
+}
+
+Dev*
+devbyname(char *name)
+{
+	int i;
+
+	for(i = 0; devtab[i] != nil; i++)
+		if(strcmp(devtab[i]->name, name) == 0)
+			return devtab[i];
+	return nil;
+}
--- /dev/null
+++ b/emu/port/devaudio.c
@@ -1,0 +1,397 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"audio.h"
+
+Dirtab audiotab[] =
+{
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"audio",	{Qaudio},	0,	0666,
+	"audioctl",	{Qaudioctl},	0,	0666,
+};
+
+static void
+audioinit(void)
+{
+	audio_file_init();
+}
+
+static Chan*
+audioattach(char *spec)
+{
+	return devattach('A', spec);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, audiotab, nelem(audiotab), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, audiotab, nelem(audiotab), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int omode)
+{
+	c = devopen(c, omode, audiotab, nelem(audiotab), devgen);
+	if(waserror()){
+		c->flag &= ~COPEN;
+		nexterror();
+	}
+	switch(c->qid.path) {
+	case Qdir:
+	case Qaudioctl:
+		break;
+	case Qaudio:
+		audio_file_open(c, c->mode);
+		break;
+	default:
+		error(Egreg);
+	}
+	poperror();
+	return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+
+	switch(c->qid.path) {
+	case Qdir:
+	case Qaudioctl:
+		break;
+	case Qaudio:
+		audio_file_close(c);
+		break;
+	default:
+		error(Egreg);
+	}
+}
+
+static int ctlsummary(char*, int, Audio_t*);
+
+static long
+audioread(Chan *c, void *va, long count, vlong offset)
+{
+	char *buf;
+	int n;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, count, audiotab, nelem(audiotab), devgen);
+	switch(c->qid.path) {
+	case Qaudio:
+		return audio_file_read(c, va, count, offset);
+	case Qaudioctl:
+		buf = smalloc(READSTR);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		n = ctlsummary(buf, READSTR, getaudiodev());
+		count = readstr(offset, va, n, buf);
+		poperror();
+		free(buf);
+		return count;
+	}
+	return 0;
+}
+
+static long
+audiowrite(Chan *c, void *va, long count, vlong offset)
+{
+	switch(c->qid.path) {
+	case Qaudio:
+		return audio_file_write(c, va, count, offset);
+	case Qaudioctl:
+		return audio_ctl_write(c, va, count, offset);
+	}
+	return 0;
+}
+
+static int sval(char*, unsigned long*, ulong, ulong);
+static int str2val(svp_t*, char*, ulong*);
+static char* val2str(svp_t*, ulong);
+
+int
+audioparse(char* args, int len, Audio_t *t)
+{
+	int i, n;
+	ulong v;
+	Cmdbuf *cb;
+	ulong tf;
+	Audio_t info = *t;
+
+	cb = parsecmd(args, len);
+	if(waserror()){
+		free(cb);
+		return 0;
+	}
+
+	tf = 0;
+	n = cb->nf;
+	for(i = 0; i < cb->nf-1; i++) {
+		if(strcmp(cb->f[i], "in") == 0){
+			tf |= AUDIO_IN_FLAG;
+			continue;
+		}
+		if(strcmp(cb->f[i], "out") == 0) {
+			tf |= AUDIO_OUT_FLAG;
+			continue;
+		}
+		if(tf == 0)
+			tf = AUDIO_IN_FLAG | AUDIO_OUT_FLAG;
+		if(strcmp(cb->f[i], "bits") == 0) {
+			if(!str2val(audio_bits_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG;
+				info.in.bits = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG;
+				info.out.bits = v;
+			}
+		} else if(strcmp(cb->f[i], "buf") == 0) {
+			if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG;
+				info.in.buf = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG;
+				info.out.buf = v;
+			}
+		} else if(strcmp(cb->f[i], "chans") == 0) {
+			if(!str2val(audio_chan_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG;
+				info.in.chan = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG;
+				info.out.chan = v;
+			}
+		} else if(strcmp(cb->f[i], "indev") == 0) {
+			if(!str2val(audio_indev_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			info.in.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG;
+			info.in.dev = v;
+		} else if(strcmp(cb->f[i], "outdev") == 0) {
+			if(!str2val(audio_outdev_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			info.out.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG;
+			info.out.dev = v;
+		} else if(strcmp(cb->f[i], "enc") == 0) {
+			if(!str2val(audio_enc_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG;
+				info.in.enc = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG;
+				info.out.enc = v;
+			}
+		} else if(strcmp(cb->f[i], "rate") == 0) {
+			if(!str2val(audio_rate_tbl, cb->f[i+1], &v))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG;
+				info.in.rate = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG;
+				info.out.rate = v;
+			}
+		} else if(strcmp(cb->f[i], "vol") == 0) {
+			if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG;
+				info.in.left = v;
+				info.in.right = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG;
+				info.out.left = v;
+				info.out.right = v;
+			}
+		} else if(strcmp(cb->f[i], "left") == 0) {
+			if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG;
+				info.in.left = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG;
+				info.out.left = v;
+			}
+		} else if(strcmp(cb->f[i], "right") == 0) {
+			if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val))
+				break;
+			i++;
+			if(tf & AUDIO_IN_FLAG) {
+				info.in.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG;
+				info.in.right = v;
+			}
+			if(tf & AUDIO_OUT_FLAG) {
+				info.out.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG;
+				info.out.right = v;
+			}
+		}else
+			break;
+	}
+	poperror();
+	free(cb);
+
+	if(i < n)
+		return 0;
+
+	*t = info;	/* set information back */
+	return 1;
+}
+
+static char*
+audioparam(char* p, char* e, char* name, int val, svp_t* tbl)
+{
+	char *s;
+	svp_t *sv;
+
+	if((s = val2str(tbl, val)) != nil){
+		p = seprint(p, e, "%s %s", name, s);	/* current setting */
+		for(sv = tbl; sv->s != nil; sv++)
+			if(sv->v != val)
+				p = seprint(p, e, " %s", sv->s);	/* other possible values */
+		p = seprint(p, e, "\n");
+	}else
+		p = seprint(p, e, "%s unknown\n", name);
+	return p;
+}
+
+static char*
+audioioparam(char* p, char* e, char* name, int ival, int oval, svp_t* tbl)
+{
+	if(ival == oval)
+		return audioparam(p, e, name, ival, tbl);
+	p = audioparam(seprint(p, e, "in "), e, name, ival, tbl);
+	p = audioparam(seprint(p, e, "out "), e, name, oval, tbl);
+	return p;
+}
+
+static int
+ctlsummary(char *buf, int bsize, Audio_t *adev)
+{
+	Audio_d *in, *out;
+	char	*p, *e;
+
+	in = &adev->in;
+	out = &adev->out;
+
+	p = buf;
+	e = p + bsize;
+
+	p = audioparam(p, e, "indev", in->dev, audio_indev_tbl);
+	p = audioparam(p, e, "outdev", out->dev, audio_outdev_tbl);
+	p = audioioparam(p, e, "enc", in->enc, out->enc, audio_enc_tbl);
+	p = audioioparam(p, e, "rate", in->rate, out->rate, audio_rate_tbl);
+	p = audioioparam(p, e, "bits", in->bits, out->bits, audio_bits_tbl);	/* this one is silly */
+	p = audioioparam(p, e, "chans", in->chan, out->chan, audio_chan_tbl);
+	/* TO DO: minimise in/out left/right where possible */
+	if(in->left != in->right){
+		p = seprint(p, e, "in left %d 0 100\n", in->left);
+		p = seprint(p, e, "in right %d 0 100\n", in->right);
+	}else
+		p = seprint(p, e, "in %d 0 100\n", in->right);
+	if(out->left != out->right){
+		p = seprint(p, e, "out left %d 0 100\n", out->left);
+		p = seprint(p, e, "out right %d 0 100\n", out->right);
+	}else
+		p = seprint(p, e, "out %d 0 100\n", out->right);
+	p = seprint(p, e, "in buf %d %d %d\n", in->buf, Audio_Min_Val, Audio_Max_Val);
+	p = seprint(p, e, "out buf %d %d %d\n", out->buf, Audio_Min_Val, Audio_Max_Val);
+
+	return p-buf;
+}
+
+void
+audio_info_init(Audio_t *t)
+{
+	t->in = Default_Audio_Format;
+	t->in.dev = Default_Audio_Input;
+	t->out = Default_Audio_Format;
+	t->out.dev = Default_Audio_Output;
+}
+
+static int
+str2val(svp_t* t, char* s, ulong *v)
+{
+	if(t == nil || s == nil)
+		return 0;
+	for(; t->s != nil; t++) {
+		if(strncmp(t->s, s, strlen(t->s)) == 0) {
+			*v = t->v;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static char*
+val2str(svp_t* t, ulong v)
+{
+	if(t == nil)
+		return nil;
+	for(; t->s != nil; t++)
+		if(t->v == v)
+			return t->s;
+	return nil;
+}
+
+static int 
+sval(char* buf, ulong* v, ulong max, ulong min)
+{
+	unsigned long val = strtoul(buf, 0, 10);
+
+	if(val > max || val < min)
+		return 0;
+	*v = val;
+	return 1;
+}
+
+Dev audiodevtab = {
+        'A',
+        "audio",
+
+        audioinit,
+        audioattach,
+        audiowalk,
+        audiostat,
+        audioopen,
+        devcreate,
+        audioclose,
+        audioread,
+        devbread,
+        audiowrite,
+        devbwrite,
+        devremove,
+        devwstat
+};
+
--- /dev/null
+++ b/emu/port/devcap.c
@@ -1,0 +1,243 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"mp.h"
+#include	"libsec.h"
+
+/*
+ * Copyright © 2003 Vita Nuova Holdings Limited.  All rights reserved.
+ */
+
+enum {
+	Captimeout = 15,	/* seconds until expiry */
+	Capidletime = 60	/* idle seconds before capwatch exits */
+};
+
+typedef struct Caps Caps;
+struct Caps
+{
+	uchar	hash[SHA1dlen];
+	ulong	time;
+	Caps*	next;
+};
+
+struct {
+	QLock	l;
+	Caps*	caps;
+	int	kpstarted;
+} allcaps;
+
+enum {
+	Qdir,
+	Qhash,
+	Quse
+};
+
+static Dirtab capdir[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"capuse",		{Quse, 0},			0,	0222,
+	"caphash",	{Qhash, 0},		0,	0200,
+};
+
+static int ncapdir = nelem(capdir);
+
+static void
+capwatch(void *a)
+{
+	Caps *c, **l;
+	int idletime;
+
+	USED(a);
+	idletime = 0;
+	for(;;){
+		osmillisleep(30*1000);
+		qlock(&allcaps.l);
+		for(l = &allcaps.caps; (c = *l) != nil;)
+			if(++c->time > Captimeout){
+				*l = c->next;
+				free(c);
+			}else
+				l = &c->next;
+		if(allcaps.caps == nil){
+			if(++idletime > Capidletime){
+				allcaps.kpstarted = 0;
+				qunlock(&allcaps.l);
+				pexit("", 0);
+			}
+		}else
+			idletime = 0;
+		qunlock(&allcaps.l);
+	}
+}
+
+static Chan *
+capattach(char *spec)
+{
+	return devattach(0x00A4, spec);	/* L'¤' */
+}
+
+static Walkqid*
+capwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen);
+}
+
+static int
+capstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, capdir, nelem(capdir), devgen);
+}
+
+static Chan*
+capopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	if(c->qid.path == Qhash && !iseve())
+		error(Eperm);
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+capclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+capread(Chan *c, void *va, long n, vlong vl)
+{
+	USED(vl);
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, va, n, capdir, ncapdir, devgen);
+
+	default:
+		error(Eperm);
+		break;
+	}
+	return n;
+}
+
+static int
+capwritehash(uchar *a, int l)
+{
+	Caps *c;
+
+	if(l != SHA1dlen)
+		return -1;
+	c = malloc(sizeof(*c));
+	if(c == nil)
+		return -1;
+	memmove(c->hash, a, l);
+	c->time = 0;
+	qlock(&allcaps.l);
+	c->next = allcaps.caps;
+	allcaps.caps = c;
+	if(!allcaps.kpstarted){
+		allcaps.kpstarted = 1;
+		kproc("capwatch", capwatch, 0, 0);
+	}
+	qunlock(&allcaps.l);
+	return 0;
+}
+
+static int
+capwriteuse(uchar *a, int len)
+{
+	int n;
+	uchar digest[SHA1dlen];
+	char buf[128], *p, *users[3];
+	Caps *c, **l;
+
+	if(len >= sizeof(buf)-1)
+		return -1;
+	memmove(buf, a, len);
+	buf[len] = 0;
+	p = strrchr(buf, '@');
+	if(p == nil)
+		return -1;
+	*p++ = 0;
+	len = strlen(p);
+	n = strlen(buf);
+	if(len == 0 || n == 0)
+		return -1;
+	hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil);
+	n = getfields(buf, users, nelem(users), 0, "@");
+	if(n == 1)
+		users[1] = users[0];
+	else if(n != 2)
+		return -1;
+	if(*users[0] == 0 || *users[1] == 0)
+		return -1;
+	qlock(&allcaps.l);
+	for(l = &allcaps.caps; (c = *l) != nil; l = &c->next)
+		if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){
+			*l = c->next;
+			qunlock(&allcaps.l);
+			free(c);
+			if(n == 2 && strcmp(up->env->user, users[0]) != 0)
+				return -1;
+			setid(users[1], 0);	/* could use users[2] to mean `host OS user' */
+			return 0;
+		}
+	qunlock(&allcaps.l);
+	return -1;
+}
+
+static long	 
+capwrite(Chan* c, void* buf, long n, vlong offset)
+{
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qhash:
+		if(capwritehash(buf, n) < 0)
+			error(Ebadarg);
+		return n;
+	case Quse:
+		if(capwriteuse(buf, n) < 0)
+			error("invalid capability");
+		return n;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+static void
+capremove(Chan *c)
+{
+	if(c->qid.path != Qhash || !iseve())
+		error(Eperm);
+	ncapdir = nelem(capdir)-1;
+}
+
+Dev capdevtab = {
+	0x00A4,	/* L'¤' */
+	"cap",
+
+	devinit,
+	capattach,
+	capwalk,
+	capstat,
+	capopen,
+	devcreate,
+	capclose,
+	capread,
+	devbread,
+	capwrite,
+	devbwrite,
+	capremove,
+	devwstat
+};
--- /dev/null
+++ b/emu/port/devcmd.c
@@ -1,0 +1,683 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Qtopdir,	/* top level directory */
+	Qcmd,
+	Qclonus,
+	Qconvdir,
+	Qconvbase,
+	Qdata = Qconvbase,
+	Qstderr,
+	Qctl,
+	Qstatus,
+	Qwait,
+
+	Debug=0	/* to help debug os.c */
+};
+#define TYPE(x) 	((ulong)(x).path & 0xf)
+#define CONV(x) 	(((ulong)(x).path >> 4)&0xfff)
+#define QID(c, y) 	(((c)<<4) | (y))
+
+typedef struct Conv	Conv;
+struct Conv
+{
+	int	x;
+	int	inuse;
+	int	fd[3];	/* stdin, stdout, and stderr */
+	int	count[3];	/* number of readers on stdin/stdout/stderr */
+	int	perm;
+	char*	owner;
+	char*	state;
+	Cmdbuf*	cmd;
+	char*	dir;
+	QLock	l;	/* protects state changes */
+	Queue*	waitq;
+	void*	child;
+	char*	error;	/* on start up */
+	int	nice;
+	short	killonclose;
+	short	killed;
+	Rendez	startr;
+};
+
+static struct
+{
+	QLock	l;
+	int	nc;
+	int	maxconv;
+	Conv**	conv;
+} cmd;
+
+static	Conv*	cmdclone(char*);
+static	void	cmdproc(void*);
+
+static int
+cmd3gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+
+	cv = cmd.conv[CONV(c->qid)];
+	switch(i){
+	default:
+		return -1;
+	case Qdata:
+		mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE);
+		devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qstderr:
+		mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE);
+		devdir(c, q, "stderr", 0, cv->owner, 0444, dp);
+		return 1;
+	case Qctl:
+		mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE);
+		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qstatus:
+		mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE);
+		devdir(c, q, "status", 0, cv->owner, 0444, dp);
+		return 1;
+	case Qwait:
+		mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE);
+		devdir(c, q, "wait", 0, cv->owner, 0444, dp);
+		return 1;
+	}
+}
+
+static int
+cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+
+	USED(name);
+	USED(nd);
+	USED(d);
+
+	if(s == DEVDOTDOT){
+		switch(TYPE(c->qid)){
+		case Qtopdir:
+		case Qcmd:
+			mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
+			devdir(c, q, "#C", 0, eve, DMDIR|0555, dp);
+			break;
+		case Qconvdir:
+			mkqid(&q, QID(0, Qcmd), 0, QTDIR);
+			devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp);
+			break;
+		default:
+			panic("cmdgen %llux", c->qid.path);
+		}
+		return 1;
+	}
+
+	switch(TYPE(c->qid)) {
+	case Qtopdir:
+		if(s >= 1)
+			return -1;
+		mkqid(&q, QID(0, Qcmd), 0, QTDIR);
+		devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp);
+		return 1;
+	case Qcmd:
+		if(s < cmd.nc) {
+			cv = cmd.conv[s];
+			mkqid(&q, QID(s, Qconvdir), 0, QTDIR);
+			sprint(up->genbuf, "%d", s);
+			devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);
+			return 1;
+		}
+		s -= cmd.nc;
+		if(s == 0){
+			mkqid(&q, QID(0, Qclonus), 0, QTFILE);
+			devdir(c, q, "clone", 0, "cmd", 0666, dp);
+			return 1;
+		}
+		return -1;
+	case Qclonus:
+		if(s == 0){
+			mkqid(&q, QID(0, Qclonus), 0, QTFILE);
+			devdir(c, q, "clone", 0, "cmd", 0666, dp);
+			return 1;
+		}
+		return -1;
+	case Qconvdir:
+		return cmd3gen(c, Qconvbase+s, dp);
+	case Qdata:
+	case Qstderr:
+	case Qctl:
+	case Qstatus:
+	case Qwait:
+		return cmd3gen(c, TYPE(c->qid), dp);
+	}
+	return -1;
+}
+
+static void
+cmdinit(void)
+{
+	cmd.maxconv = 1000;
+	cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1);
+	/* cmd.conv is checked by cmdattach, below */
+}
+
+static Chan *
+cmdattach(char *spec)
+{
+	Chan *c;
+
+	if(cmd.conv == nil)
+		error(Enomem);
+	c = devattach('C', spec);
+	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
+	return c;
+}
+
+static Walkqid*
+cmdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, cmdgen);
+}
+
+static int
+cmdstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, cmdgen);
+}
+
+static Chan *
+cmdopen(Chan *c, int omode)
+{
+	int perm;
+	Conv *cv;
+	char *user;
+
+	perm = 0;
+	omode = openmode(omode);
+	switch(omode) {
+	case OREAD:
+		perm = 4;
+		break;
+	case OWRITE:
+		perm = 2;
+		break;
+	case ORDWR:
+		perm = 6;
+		break;
+	}
+
+	switch(TYPE(c->qid)) {
+	default:
+		break;
+	case Qtopdir:
+	case Qcmd:
+	case Qconvdir:
+	case Qstatus:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qclonus:
+		qlock(&cmd.l);
+		if(waserror()){
+			qunlock(&cmd.l);
+			nexterror();
+		}
+		cv = cmdclone(up->env->user);
+		poperror();
+		qunlock(&cmd.l);
+		if(cv == 0)
+			error(Enodev);
+		mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE);
+		break;
+	case Qdata:
+	case Qstderr:
+	case Qctl:
+	case Qwait:
+		qlock(&cmd.l);
+		cv = cmd.conv[CONV(c->qid)];
+		qlock(&cv->l);
+		if(waserror()){
+			qunlock(&cv->l);
+			qunlock(&cmd.l);
+			nexterror();
+		}
+		user = up->env->user;
+		if((perm & (cv->perm>>6)) != perm) {
+			if(strcmp(user, cv->owner) != 0 ||
+		 	  (perm & cv->perm) != perm)
+				error(Eperm);
+		}
+		switch(TYPE(c->qid)){
+		case Qdata:
+			if(omode == OWRITE || omode == ORDWR)
+				cv->count[0]++;
+			if(omode == OREAD || omode == ORDWR)
+				cv->count[1]++;
+			break;
+		case Qstderr:
+			if(omode != OREAD)
+				error(Eperm);
+			cv->count[2]++;
+			break;
+		case Qwait:
+			if(cv->waitq == nil)
+				cv->waitq = qopen(1024, Qmsg, nil, 0);
+			break;
+		}
+		cv->inuse++;
+		if(cv->inuse == 1) {
+			cv->state = "Open";
+			kstrdup(&cv->owner, user);
+			cv->perm = 0660;
+			cv->nice = 0;
+		}
+		poperror();
+		qunlock(&cv->l);
+		qunlock(&cmd.l);
+		break;
+	}
+	c->mode = omode;
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+closeconv(Conv *c)
+{
+	kstrdup(&c->owner, "cmd");
+	kstrdup(&c->dir, rootdir);
+	c->perm = 0666;
+	c->state = "Closed";
+	c->killonclose = 0;
+	c->killed = 0;
+	c->nice = 0;
+	free(c->cmd);
+	c->cmd = nil;
+	if(c->waitq != nil){
+		qfree(c->waitq);
+		c->waitq = nil;
+	}
+	free(c->error);
+	c->error = nil;
+}
+
+static void
+cmdfdclose(Conv *c, int fd)
+{
+	if(--c->count[fd] == 0 && c->fd[fd] != -1){
+		close(c->fd[fd]);
+		c->fd[fd] = -1;
+	}
+}
+
+static void
+cmdclose(Chan *c)
+{
+	Conv *cc;
+	int r;
+
+	if((c->flag & COPEN) == 0)
+		return;
+
+	switch(TYPE(c->qid)) {
+	case Qctl:
+	case Qdata:
+	case Qstderr:
+	case Qwait:
+		cc = cmd.conv[CONV(c->qid)];
+		qlock(&cc->l);
+		if(TYPE(c->qid) == Qdata){
+			if(c->mode == OWRITE || c->mode == ORDWR)
+				cmdfdclose(cc, 0);
+			if(c->mode == OREAD || c->mode == ORDWR)
+				cmdfdclose(cc, 1);
+		}else if(TYPE(c->qid) == Qstderr)
+			cmdfdclose(cc, 2);
+
+		r = --cc->inuse;
+		if(cc->child != nil){
+			if(!cc->killed)
+			if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){
+				oscmdkill(cc->child);
+				cc->killed = 1;
+			}
+		}else if(r == 0)
+			closeconv(cc);
+
+		qunlock(&cc->l);
+		break;
+	}
+}
+
+static long
+cmdread(Chan *ch, void *a, long n, vlong offset)
+{
+	Conv *c;
+	char *p, *cmds;
+	int fd;
+
+	USED(offset);
+
+	p = a;
+	switch(TYPE(ch->qid)) {
+	default:
+		error(Eperm);
+	case Qcmd:
+	case Qtopdir:
+	case Qconvdir:
+		return devdirread(ch, a, n, 0, 0, cmdgen);
+	case Qctl:
+		sprint(up->genbuf, "%ld", CONV(ch->qid));
+		return readstr(offset, p, n, up->genbuf);
+	case Qstatus:
+		c = cmd.conv[CONV(ch->qid)];
+		cmds = "";
+		if(c->cmd != nil)
+			cmds = c->cmd->f[1];
+		snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n",
+			c->x, c->inuse, c->state, c->dir, cmds);
+		return readstr(offset, p, n, up->genbuf);
+	case Qdata:
+	case Qstderr:
+		fd = 1;
+		if(TYPE(ch->qid) == Qstderr)
+			fd = 2;
+		c = cmd.conv[CONV(ch->qid)];
+		qlock(&c->l);
+		if(c->fd[fd] == -1){
+			qunlock(&c->l);
+			return 0;
+		}
+		qunlock(&c->l);
+		osenter();
+		n = read(c->fd[fd], a, n);
+		osleave();
+		if(n < 0)
+			oserror();
+		return n;
+	case Qwait:
+		c = cmd.conv[CONV(ch->qid)];
+		return qread(c->waitq, a, n);
+	}
+}
+
+static int
+cmdstarted(void *a)
+{
+	Conv *c;
+
+	c = a;
+	return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0;
+}
+
+enum
+{
+	CMdir,
+	CMexec,
+	CMkill,
+	CMnice,
+	CMkillonclose
+};
+
+static
+Cmdtab cmdtab[] = {
+	CMdir,	"dir",	2,
+	CMexec,	"exec",	0,
+	CMkill,	"kill",	1,
+	CMnice,	"nice",	0,
+	CMkillonclose, "killonclose", 0,
+};
+
+static long
+cmdwrite(Chan *ch, void *a, long n, vlong offset)
+{
+	int i, r;
+	Conv *c;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	USED(offset);
+
+	switch(TYPE(ch->qid)) {
+	default:
+		error(Eperm);
+	case Qctl:
+		c = cmd.conv[CONV(ch->qid)];
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, cmdtab, nelem(cmdtab));
+		switch(ct->index){
+		case CMdir:
+			kstrdup(&c->dir, cb->f[1]);
+			break;
+		case CMexec:
+			poperror();	/* cb */
+			qlock(&c->l);
+			if(waserror()){
+				qunlock(&c->l);
+				free(cb);
+				nexterror();
+			}
+			if(c->child != nil || c->cmd != nil)
+				error(Einuse);
+			for(i = 0; i < nelem(c->fd); i++)
+				if(c->fd[i] != -1)
+					error(Einuse);
+			if(cb->nf < 1)
+				error(Etoosmall);
+			kproc("cmdproc", cmdproc, c, 0);	/* cmdproc held back until unlock below */
+			free(c->cmd);
+			c->cmd = cb;	/* don't free cb */
+			c->state = "Execute";
+			poperror();
+			qunlock(&c->l);
+			while(waserror())
+				;
+			Sleep(&c->startr, cmdstarted, c);
+			poperror();
+			if(c->error)
+				error(c->error);
+			return n;	/* avoid free(cb) below */
+		case CMkill:
+			qlock(&c->l);
+			if(waserror()){
+				qunlock(&c->l);
+				nexterror();
+			}
+			if(c->child == nil)
+				error("not started");
+			if(oscmdkill(c->child) < 0)
+				oserror();
+			poperror();
+			qunlock(&c->l);
+			break;
+		case CMnice:
+			c->nice = cb->nf > 1? atoi(cb->f[1]): 1;
+			break;
+		case CMkillonclose:
+			c->killonclose = 1;
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+	case Qdata:
+		c = cmd.conv[CONV(ch->qid)];
+		qlock(&c->l);
+		if(c->fd[0] == -1){
+			qunlock(&c->l);
+			error(Ehungup);
+		}
+		qunlock(&c->l);
+		osenter();
+		r = write(c->fd[0], a, n);
+		osleave();
+		if(r == 0)
+			error(Ehungup);
+		if(r < 0) {
+			/* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */
+			oserror();
+		}
+		return r;
+	}
+	return n;
+}
+
+static int
+cmdwstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	Conv *cv;
+
+	switch(TYPE(c->qid)){
+	default:
+		error(Eperm);
+	case Qctl:
+	case Qdata:
+	case Qstderr:
+		d = malloc(sizeof(*d)+n);
+		if(d == nil)
+			error(Enomem);
+		if(waserror()){
+			free(d);
+			nexterror();
+		}
+		n = convM2D(dp, n, d, (char*)&d[1]);
+		if(n == 0)
+			error(Eshortstat);
+		cv = cmd.conv[CONV(c->qid)];
+		if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
+			error(Eperm);
+		if(!emptystr(d->uid))
+			kstrdup(&cv->owner, d->uid);
+		if(d->mode != ~0UL)
+			cv->perm = d->mode & 0777;
+		poperror();
+		free(d);
+		break;
+	}
+	return n;
+}
+
+static Conv*
+cmdclone(char *user)
+{
+	Conv *c, **pp, **ep;
+	int i;
+
+	c = nil;
+	ep = &cmd.conv[cmd.maxconv];
+	for(pp = cmd.conv; pp < ep; pp++) {
+		c = *pp;
+		if(c == nil) {
+			c = malloc(sizeof(Conv));
+			if(c == nil)
+				error(Enomem);
+			qlock(&c->l);
+			c->inuse = 1;
+			c->x = pp - cmd.conv;
+			cmd.nc++;
+			*pp = c;
+			break;
+		}
+		if(canqlock(&c->l)){
+			if(c->inuse == 0 && c->child == nil)
+				break;
+			qunlock(&c->l);
+		}
+	}
+	if(pp >= ep)
+		return nil;
+
+	c->inuse = 1;
+	kstrdup(&c->owner, user);
+	kstrdup(&c->dir, rootdir);
+	c->perm = 0660;
+	c->state = "Closed";
+	for(i=0; i<nelem(c->fd); i++)
+		c->fd[i] = -1;
+
+	qunlock(&c->l);
+	return c;
+}
+
+static void
+cmdproc(void *a)
+{
+	Conv *c;
+	int n;
+	char status[ERRMAX];
+	void *t;
+
+	c = a;
+	qlock(&c->l);
+	if(Debug)
+		print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]);
+	if(waserror()){
+		if(Debug)
+			print("failed: %q\n", up->env->errstr);
+		kstrdup(&c->error, up->env->errstr);
+		c->state = "Done";
+		qunlock(&c->l);
+		Wakeup(&c->startr);
+		pexit("cmdproc", 0);
+	}
+	t = oscmd(c->cmd->f+1, c->nice, c->dir, c->fd);
+	if(t == nil)
+		oserror();
+	c->child = t;	/* to allow oscmdkill */
+	poperror();
+	qunlock(&c->l);
+	Wakeup(&c->startr);
+	if(Debug)
+		print("started\n");
+	while(waserror())
+		oscmdkill(t);
+	osenter();
+	n = oscmdwait(t, status, sizeof(status));
+	osleave();
+	if(n < 0){
+		oserrstr(up->genbuf, sizeof(up->genbuf));
+		n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf);
+	}
+	qlock(&c->l);
+	c->child = nil;
+	oscmdfree(t);
+	if(Debug){
+		status[n]=0;
+		print("done %d %d %d: %q\n", c->fd[0], c->fd[1], c->fd[2], status);
+	}
+	if(c->inuse > 0){
+		c->state = "Done";
+		if(c->waitq != nil)
+			qproduce(c->waitq, status, n);
+	}else
+		closeconv(c);
+	qunlock(&c->l);
+	pexit("", 0);
+}
+
+Dev cmddevtab = {
+	'C',
+	"cmd",
+
+	cmdinit,
+	cmdattach,
+	cmdwalk,
+	cmdstat,
+	cmdopen,
+	devcreate,
+	cmdclose,
+	cmdread,
+	devbread,
+	cmdwrite,
+	devbwrite,
+	devremove,
+	cmdwstat
+};
--- /dev/null
+++ b/emu/port/devcons.c
@@ -1,0 +1,643 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"version.h"
+#include	"mp.h"
+#include	"libsec.h"
+#include	"keyboard.h"
+
+extern int cflag;
+int	exdebug;
+extern int keepbroken;
+
+enum
+{
+	Qdir,
+	Qcons,
+	Qconsctl,
+	Qdrivers,
+	Qhostowner,
+	Qhoststdin,
+	Qhoststdout,
+	Qhoststderr,
+	Qjit,
+	Qkeyboard,
+	Qkprint,
+	Qmemory,
+	Qmsec,
+	Qnotquiterandom,
+	Qnull,
+	Qrandom,
+	Qscancode,
+	Qsysctl,
+	Qsysname,
+	Qtime,
+	Quser
+};
+
+Dirtab contab[] =
+{
+	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
+	"cons",		{Qcons},	0,	0666,
+	"consctl",	{Qconsctl},	0,	0222,
+	"drivers",	{Qdrivers},	0,	0444,
+	"hostowner",	{Qhostowner},	0,	0644,
+	"hoststdin",	{Qhoststdin},	0,	0444,
+	"hoststdout",	{Qhoststdout},	0,	0222,
+	"hoststderr",	{Qhoststderr},	0,	0222,
+	"jit",	{Qjit},	0,	0666,
+	"keyboard",	{Qkeyboard},	0,	0666,
+	"kprint",	{Qkprint},	0,	0444,
+	"memory",	{Qmemory},	0,	0444,
+	"msec",		{Qmsec},	NUMSIZE,	0444,
+	"notquiterandom",	{Qnotquiterandom},	0,	0444,
+	"null",		{Qnull},	0,	0666,
+	"random",	{Qrandom},	0,	0444,
+	"scancode",	{Qscancode},	0,	0444,
+	"sysctl",	{Qsysctl},	0,	0644,
+	"sysname",	{Qsysname},	0,	0644,
+	"time",		{Qtime},	0,	0644,
+	"user",		{Quser},	0,	0644,
+};
+
+Queue*	gkscanq;		/* Graphics keyboard raw scancodes */
+char*	gkscanid;		/* name of raw scan format (if defined) */
+Queue*	gkbdq;			/* Graphics keyboard unprocessed input */
+Queue*	kbdq;			/* Console window unprocessed keyboard input */
+Queue*	lineq;			/* processed console input */
+
+char	*ossysname;
+
+static struct
+{
+	RWlock l;
+	Queue*	q;
+} kprintq;
+
+vlong	timeoffset;
+
+extern int	dflag;
+
+static int	sysconwrite(void*, ulong);
+extern char**	rebootargv;
+
+static struct
+{
+	QLock	q;
+	QLock	gq;		/* separate lock for the graphical input */
+
+	int	raw;		/* true if we shouldn't process input */
+	Ref	ctl;		/* number of opens to the control file */
+	Ref	ptr;		/* number of opens to the ptr file */
+	int	scan;		/* true if reading raw scancodes */
+	int	x;		/* index into line */
+	char	line[1024];	/* current input line */
+
+	Rune	c;
+	int	count;
+} kbd;
+
+void
+kbdslave(void *a)
+{
+	char b;
+
+	USED(a);
+	for(;;) {
+		b = readkbd();
+		if(kbd.raw == 0){
+			switch(b){
+			case 0x15:
+				write(1, "^U\n", 3);
+				break;
+			default:
+				write(1, &b, 1);
+				break;
+			}
+		}
+		qproduce(kbdq, &b, 1);
+	}
+	/* pexit("kbdslave", 0); */	/* not reached */
+}
+
+void
+gkbdputc(Queue *q, int ch)
+{
+	int n;
+	Rune r;
+	static uchar kc[5*UTFmax];
+	static int nk, collecting = 0;
+	char buf[UTFmax];
+
+	r = ch;
+	if(r == Latin) {
+		collecting = 1;
+		nk = 0;
+		return;
+	}
+	if(collecting) {
+		int c;
+		nk += runetochar((char*)&kc[nk], &r);
+		c = latin1(kc, nk);
+		if(c < -1)	/* need more keystrokes */
+			return;
+		collecting = 0;
+		if(c == -1) {	/* invalid sequence */
+			qproduce(q, kc, nk);
+			return;
+		}
+		r = (Rune)c;
+	}
+	n = runetochar(buf, &r);
+	if(n == 0)
+		return;
+	/* if(!isdbgkey(r)) */ 
+		qproduce(q, buf, n);
+}
+
+void
+consinit(void)
+{
+	kbdq = qopen(512, 0, nil, nil);
+	if(kbdq == 0)
+		panic("no memory");
+	lineq = qopen(2*1024, 0, nil, nil);
+	if(lineq == 0)
+		panic("no memory");
+	gkbdq = qopen(512, 0, nil, nil);
+	if(gkbdq == 0)
+		panic("no memory");
+	randominit();
+}
+
+/*
+ *  return true if current user is eve
+ */
+int
+iseve(void)
+{
+	return strcmp(eve, up->env->user) == 0;
+}
+
+static Chan*
+consattach(char *spec)
+{
+	static int kp;
+
+	if(kp == 0 && !dflag) {
+		kp = 1;
+		kproc("kbd", kbdslave, 0, 0);
+	}
+	return devattach('c', spec);
+}
+
+static Walkqid*
+conswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, contab, nelem(contab), devgen);
+}
+
+static int
+consstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, contab, nelem(contab), devgen);
+}
+
+static Chan*
+consopen(Chan *c, int omode)
+{
+	c = devopen(c, omode, contab, nelem(contab), devgen);
+	switch((ulong)c->qid.path) {
+	case Qconsctl:
+		incref(&kbd.ctl);
+		break;
+
+	case Qscancode:
+		qlock(&kbd.gq);
+		if(gkscanq != nil || gkscanid == nil) {
+			qunlock(&kbd.q);
+			c->flag &= ~COPEN;
+			if(gkscanq)
+				error(Einuse);
+			else
+				error("not supported");
+		}
+		gkscanq = qopen(256, 0, nil, nil);
+		qunlock(&kbd.gq);
+		break;
+
+	case Qkprint:
+		wlock(&kprintq.l);
+		if(waserror()){
+			wunlock(&kprintq.l);
+			c->flag &= ~COPEN;
+			nexterror();
+		}
+		if(kprintq.q != nil)
+			error(Einuse);
+		kprintq.q = qopen(32*1024, Qcoalesce, nil, nil);
+		if(kprintq.q == nil)
+			error(Enomem);
+		qnoblock(kprintq.q, 1);
+		poperror();
+		wunlock(&kprintq.l);
+		c->iounit = qiomaxatomic;
+		break;
+	}
+	return c;
+}
+
+static void
+consclose(Chan *c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+
+	switch((ulong)c->qid.path) {
+	case Qconsctl:
+		/* last close of control file turns off raw */
+		if(decref(&kbd.ctl) == 0)
+			kbd.raw = 0;
+		break;
+
+	case Qscancode:
+		qlock(&kbd.gq);
+		if(gkscanq) {
+			qfree(gkscanq);
+			gkscanq = nil;
+		}
+		qunlock(&kbd.gq);
+		break;
+
+	case Qkprint:
+		wlock(&kprintq.l);
+		qfree(kprintq.q);
+		kprintq.q = nil;
+		wunlock(&kprintq.l);
+		break;
+	}
+}
+
+static long
+consread(Chan *c, void *va, long n, vlong offset)
+{
+	int send;
+	char buf[64], ch;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, n, contab, nelem(contab), devgen);
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Egreg);
+
+	case Qsysctl:
+		return readstr(offset, va, n, VERSION);
+
+	case Qsysname:
+		if(ossysname == nil)
+			return 0;
+		return readstr(offset, va, n, ossysname);
+
+	case Qrandom:
+		return randomread(va, n);
+
+	case Qnotquiterandom:
+		genrandom(va, n);
+		return n;
+
+	case Qhostowner:
+		return readstr(offset, va, n, eve);
+
+	case Qhoststdin:
+		return read(0, va, n);	/* should be pread */
+
+	case Quser:
+		return readstr(offset, va, n, up->env->user);
+
+	case Qjit:
+		snprint(buf, sizeof(buf), "%d", cflag);
+		return readstr(offset, va, n, buf);
+
+	case Qtime:
+		snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime());
+		return readstr(offset, va, n, buf);
+
+	case Qdrivers:
+		return devtabread(c, va, n, offset);
+
+	case Qmemory:
+		return poolread(va, n, offset);
+
+	case Qnull:
+		return 0;
+
+	case Qmsec:
+		return readnum(offset, va, n, osmillisec(), NUMSIZE);
+
+	case Qcons:
+		qlock(&kbd.q);
+		if(waserror()){
+			qunlock(&kbd.q);
+			nexterror();
+		}
+
+		if(dflag)
+			error(Enonexist);
+
+		while(!qcanread(lineq)) {
+			if(qread(kbdq, &ch, 1) == 0)
+				continue;
+			send = 0;
+			if(ch == 0){
+				/* flush output on rawoff -> rawon */
+				if(kbd.x > 0)
+					send = !qcanread(kbdq);
+			}else if(kbd.raw){
+				kbd.line[kbd.x++] = ch;
+				send = !qcanread(kbdq);
+			}else{
+				switch(ch){
+				case '\b':
+					if(kbd.x)
+						kbd.x--;
+					break;
+				case 0x15:
+					kbd.x = 0;
+					break;
+				case 0x04:
+					send = 1;
+					break;
+				case '\n':
+					send = 1;
+				default:
+					kbd.line[kbd.x++] = ch;
+					break;
+				}
+			}
+			if(send || kbd.x == sizeof kbd.line){
+				qwrite(lineq, kbd.line, kbd.x);
+				kbd.x = 0;
+			}
+		}
+		n = qread(lineq, va, n);
+		qunlock(&kbd.q);
+		poperror();
+		return n;
+
+	case Qscancode:
+		if(offset == 0)
+			return readstr(0, va, n, gkscanid);
+		return qread(gkscanq, va, n);
+
+	case Qkeyboard:
+		return qread(gkbdq, va, n);
+
+	case Qkprint:
+		rlock(&kprintq.l);
+		if(waserror()){
+			runlock(&kprintq.l);
+			nexterror();
+		}
+		n = qread(kprintq.q, va, n);
+		poperror();
+		runlock(&kprintq.l);
+		return n;
+	}
+}
+
+static long
+conswrite(Chan *c, void *va, long n, vlong offset)
+{
+	char buf[128], *a, ch;
+	int x;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Egreg);
+
+	case Qcons:
+		if(canrlock(&kprintq.l)){
+			if(kprintq.q != nil){
+				if(waserror()){
+					runlock(&kprintq.l);
+					nexterror();
+				}
+				qwrite(kprintq.q, va, n);
+				poperror();
+				runlock(&kprintq.l);
+				return n;
+			}
+			runlock(&kprintq.l);
+		}
+		return write(1, va, n);
+
+	case Qsysctl:
+		return sysconwrite(va, n);
+
+	case Qconsctl:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, va, n);
+		buf[n] = 0;
+		for(a = buf; a;){
+			if(strncmp(a, "rawon", 5) == 0){
+				kbd.raw = 1;
+				/* clumsy hack - wake up reader */
+				ch = 0;
+				qwrite(kbdq, &ch, 1);
+			} else if(strncmp(buf, "rawoff", 6) == 0){
+				kbd.raw = 0;
+			}
+			if((a = strchr(a, ' ')) != nil)
+				a++;
+		}
+		break;
+
+	case Qkeyboard:
+		for(x=0; x<n; ) {
+			Rune r;
+			x += chartorune(&r, &((char*)va)[x]);
+			gkbdputc(gkbdq, r);
+		}
+		break;
+
+	case Qnull:
+		break;
+
+	case Qtime:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, va, n);
+		buf[n] = '\0';
+		timeoffset = strtoll(buf, 0, 0)-osusectime();
+		break;
+
+	case Qhostowner:
+		if(!iseve())
+			error(Eperm);
+		if(offset != 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		memmove(buf, va, n);
+		buf[n] = '\0';
+		if(n > 0 && buf[n-1] == '\n')
+			buf[--n] = '\0';
+		if(n == 0)
+			error(Ebadarg);
+		/* renameuser(eve, buf); */
+		/* renameproguser(eve, buf); */
+		kstrdup(&eve, buf);
+		kstrdup(&up->env->user, buf);
+		break;
+
+	case Quser:
+		if(!iseve())
+			error(Eperm);
+		if(offset != 0)
+			error(Ebadarg);
+		if(n <= 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		strncpy(buf, va, n);
+		buf[n] = '\0';
+		if(n > 0 && buf[n-1] == '\n')
+			buf[--n] = '\0';
+		if(n == 0)
+			error(Ebadarg);
+		setid(buf, 0);
+		break;
+
+	case Qhoststdout:
+		return write(1, va, n);
+
+	case Qhoststderr:
+		return write(2, va, n);
+
+	case Qjit:
+		if(n >= sizeof(buf))
+			n = sizeof(buf)-1;
+		strncpy(buf, va, n);
+		buf[n] = '\0';
+		x = atoi(buf);
+		if(x < 0 || x > 9)
+			error(Ebadarg);
+		cflag = x;
+		break;
+
+	case Qsysname:
+		if(offset != 0)
+			error(Ebadarg);
+		if(n < 0 || n >= sizeof(buf))
+			error(Ebadarg);
+		strncpy(buf, va, n);
+		buf[n] = '\0';
+		if(buf[n-1] == '\n')
+			buf[n-1] = 0;
+		kstrdup(&ossysname, buf);
+		break;
+	}
+	return n;
+}
+
+static int	
+sysconwrite(void *va, ulong count)
+{
+	Cmdbuf *cb;
+	int e;
+	cb = parsecmd(va, count);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	if(cb->nf == 0)
+		error(Enoctl);
+	if(strcmp(cb->f[0], "reboot") == 0){
+		osreboot(rebootargv[0], rebootargv);
+		error("reboot not supported");
+	}else if(strcmp(cb->f[0], "halt") == 0){
+		if(cb->nf > 1)
+			e = atoi(cb->f[1]);
+		else
+			e = 0;
+		cleanexit(e);		/* XXX ignored for the time being (and should be a string anyway) */
+	}else if(strcmp(cb->f[0], "broken") == 0)
+		keepbroken = 1;
+	else if(strcmp(cb->f[0], "nobroken") == 0)
+		keepbroken = 0;
+	else if(strcmp(cb->f[0], "exdebug") == 0)
+		exdebug = !exdebug;
+	else
+		error(Enoctl);
+	poperror();
+	free(cb);
+	return count;
+} 
+
+Dev consdevtab = {
+	'c',
+	"cons",
+
+	consinit,
+	consattach,
+	conswalk,
+	consstat,
+	consopen,
+	devcreate,
+	consclose,
+	consread,
+	devbread,
+	conswrite,
+	devbwrite,
+	devremove,
+	devwstat
+};
+
+static	ulong	randn;
+
+static void
+seedrand(void)
+{
+	randomread((void*)&randn, sizeof(randn));
+}
+
+int
+nrand(int n)
+{
+	if(randn == 0)
+		seedrand();
+	randn = randn*1103515245 + 12345 + osusectime();
+	return (randn>>16) % n;
+}
+
+int
+rand(void)
+{
+	nrand(1);
+	return randn;
+}
+
+ulong
+truerand(void)
+{
+	ulong x;
+
+	randomread(&x, sizeof(x));
+	return x;
+}
+
+QLock grandomlk;
+
+void
+_genrandomqlock(void)
+{
+	qlock(&grandomlk);
+}
+
+
+void
+_genrandomqunlock(void)
+{
+	qunlock(&grandomlk);
+}
--- /dev/null
+++ b/emu/port/devdraw.c
@@ -1,0 +1,2016 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<memlayer.h>
+#include	<cursor.h>
+//#include	"screen.h"
+
+enum
+{
+	Qtopdir		= 0,
+	Qnew,
+	Q3rd,
+	Q2nd,
+	Qcolormap,
+	Qctl,
+	Qdata,
+	Qrefresh
+};
+
+/*
+ * Qid path is:
+ *	 4 bits of file type (qids above)
+ *	24 bits of mux slot number +1; 0 means not attached to client
+ */
+#define	QSHIFT	4	/* location in qid of client # */
+
+#define	QID(q)		((((ulong)(q).path)&0x0000000F)>>0)
+#define	CLIENTPATH(q)	((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
+#define	CLIENT(q)	CLIENTPATH((q).path)
+
+#define	NHASH		(1<<5)
+#define	HASHMASK	(NHASH-1)
+#define	IOUNIT	(64*1024)
+
+typedef struct Client Client;
+typedef struct Draw Draw;
+typedef struct DImage DImage;
+typedef struct DScreen DScreen;
+typedef struct CScreen CScreen;
+typedef struct FChar FChar;
+typedef struct Refresh Refresh;
+typedef struct Refx Refx;
+typedef struct DName DName;
+
+ulong blanktime = 30;	/* in minutes; a half hour */
+
+struct Draw
+{
+	QLock	q;
+	int		clientid;
+	int		nclient;
+	Client**	client;
+	int		nname;
+	DName*	name;
+	int		vers;
+	int		softscreen;
+	int		blanked;	/* screen turned off */
+	ulong		blanktime;	/* time of last operation */
+	ulong		savemap[3*256];
+};
+
+struct Client
+{
+	Ref		r;
+	DImage*		dimage[NHASH];
+	CScreen*	cscreen;
+	Refresh*	refresh;
+	Rendez		refrend;
+	uchar*		readdata;
+	int		nreaddata;
+	int		busy;
+	int		clientid;
+	int		slot;
+	int		refreshme;
+	int		infoid;
+	int	op;	/* compositing operator - SoverD by default */
+};
+
+struct Refresh
+{
+	DImage*		dimage;
+	Rectangle	r;
+	Refresh*	next;
+};
+
+struct Refx
+{
+	Client*		client;
+	DImage*		dimage;
+};
+
+struct DName
+{
+	char			*name;
+	Client	*client;
+	DImage*		dimage;
+	int			vers;
+};
+
+struct FChar
+{
+	int		minx;	/* left edge of bits */
+	int		maxx;	/* right edge of bits */
+	uchar		miny;	/* first non-zero scan-line */
+	uchar		maxy;	/* last non-zero scan-line + 1 */
+	schar		left;	/* offset of baseline */
+	uchar		width;	/* width of baseline */
+};
+
+/*
+ * Reference counts in DImages:
+ *	one per open by original client
+ *	one per screen image or fill
+ * 	one per image derived from this one by name
+ */
+struct DImage
+{
+	int		id;
+	int		ref;
+	char		*name;
+	int		vers;
+	Memimage*	image;
+	int		ascent;
+	int		nfchar;
+	FChar*		fchar;
+	DScreen*	dscreen;	/* 0 if not a window */
+	DImage*	fromname;	/* image this one is derived from, by name */
+	DImage*		next;
+};
+
+struct CScreen
+{
+	DScreen*	dscreen;
+	CScreen*	next;
+};
+
+struct DScreen
+{
+	int		id;
+	int		public;
+	int		ref;
+	DImage	*dimage;
+	DImage	*dfill;
+	Memscreen*	screen;
+	Client*		owner;
+	DScreen*	next;
+};
+
+static	Draw		sdraw;
+	Memimage	*screenimage;	/* accessed by some win-*.c */
+static	Memdata	screendata;
+static	Rectangle	flushrect;
+static	int		waste;
+static	DScreen*	dscreen;
+extern	void		flushmemscreen(Rectangle);
+	void		drawmesg(Client*, void*, int);
+	void		drawuninstall(Client*, int);
+	void		drawfreedimage(DImage*);
+	Client*		drawclientofpath(ulong);
+static int	drawclientop(Client*);
+
+static	char Enodrawimage[] =	"unknown id for draw image";
+static	char Enodrawscreen[] =	"unknown id for draw screen";
+static	char Eshortdraw[] =	"short draw message";
+static	char Eshortread[] =	"draw read too short";
+static	char Eimageexists[] =	"image id in use";
+static	char Escreenexists[] =	"screen id in use";
+static	char Edrawmem[] =	"out of memory: image";
+static	char Ereadoutside[] =	"readimage outside image";
+static	char Ewriteoutside[] =	"writeimage outside image";
+static	char Enotfont[] =	"image not a font";
+static	char Eindex[] =		"character index out of range";
+static	char Enoclient[] =	"no such draw client";
+static	char Enameused[] =	"image name in use";
+static	char Enoname[] =	"no image with that name";
+static	char Eoldname[] =	"named image no longer valid";
+static	char Enamed[] = 	"image already has name";
+static	char Ewrongname[] = 	"wrong name for image";
+
+static int
+drawgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp)
+{
+	int t;
+	Qid q;
+	ulong path;
+	Client *cl;
+
+	USED(name);
+	USED(tab);
+	USED(x);
+	q.vers = 0;
+
+	if(s == DEVDOTDOT){
+		switch(QID(c->qid)){
+		case Qtopdir:
+		case Q2nd:
+			mkqid(&q, Qtopdir, 0, QTDIR);
+			devdir(c, q, "#i", 0, eve, 0500, dp);
+			break;
+		case Q3rd:
+			cl = drawclientofpath(c->qid.path);
+			if(cl == nil)
+				strcpy(up->genbuf, "??");
+			else
+				sprint(up->genbuf, "%d", cl->clientid);
+			mkqid(&q, Q2nd, 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0500, dp);
+			break;
+		default:
+			panic("drawwalk %llux", c->qid.path);
+		}
+		return 1;
+	}
+
+	/*
+	 * Top level directory contains the name of the device.
+	 */
+	t = QID(c->qid);
+	if(t == Qtopdir){
+		switch(s){
+		case 0:
+			mkqid(&q, Q2nd, 0, QTDIR);
+			devdir(c, q, "draw", 0, eve, 0555, dp);
+			break;
+		default:
+			return -1;
+		}
+		return 1;
+	}
+
+	/*
+	 * Second level contains "new" plus all the clients.
+	 */
+	if(t == Q2nd || t == Qnew){
+		if(s == 0){
+			mkqid(&q, Qnew, 0, QTFILE);
+			devdir(c, q, "new", 0, eve, 0666, dp);
+		}
+		else if(s <= sdraw.nclient){
+			cl = sdraw.client[s-1];
+			if(cl == 0)
+				return 0;
+			sprint(up->genbuf, "%d", cl->clientid);
+			mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+			return 1;
+		}
+		else
+			return -1;
+		return 1;
+	}
+
+	/*
+	 * Third level.
+	 */
+	path = c->qid.path&~((1<<QSHIFT)-1);	/* slot component */
+	q.vers = c->qid.vers;
+	q.type = QTFILE;
+	switch(s){
+	case 0:
+		q.path = path|Qcolormap;
+		devdir(c, q, "colormap", 0, eve, 0600, dp);
+		break;
+	case 1:
+		q.path = path|Qctl;
+		devdir(c, q, "ctl", 0, eve, 0600, dp);
+		break;
+	case 2:
+		q.path = path|Qdata;
+		devdir(c, q, "data", 0, eve, 0600, dp);
+		break;
+	case 3:
+		q.path = path|Qrefresh;
+		devdir(c, q, "refresh", 0, eve, 0400, dp);
+		break;
+	default:
+		return -1;
+	}
+	return 1;
+}
+
+static
+int
+drawrefactive(void *a)
+{
+	Client *c;
+
+	c = a;
+	return c->refreshme || c->refresh!=0;
+}
+
+static
+void
+drawrefreshscreen(DImage *l, Client *client)
+{
+	while(l != nil && l->dscreen == nil)
+		l = l->fromname;
+	if(l != nil && l->dscreen->owner != client)
+		l->dscreen->owner->refreshme = 1;
+}
+
+static
+void
+drawrefresh(Memimage *l, Rectangle r, void *v)
+{
+	Refx *x;
+	DImage *d;
+	Client *c;
+	Refresh *ref;
+
+	USED(l);
+	if(v == 0)
+		return;
+	x = v;
+	c = x->client;
+	d = x->dimage;
+	for(ref=c->refresh; ref; ref=ref->next)
+		if(ref->dimage == d){
+			combinerect(&ref->r, r);
+			return;
+		}
+	ref = malloc(sizeof(Refresh));
+	if(ref){
+		ref->dimage = d;
+		ref->r = r;
+		ref->next = c->refresh;
+		c->refresh = ref;
+	}
+}
+
+static void
+addflush(Rectangle r)
+{
+	int abb, ar, anbb;
+	Rectangle nbb;
+
+	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+		return;
+
+	if(flushrect.min.x >= flushrect.max.x){
+		flushrect = r;
+		waste = 0;
+		return;
+	}
+	nbb = flushrect;
+	combinerect(&nbb, r);
+	ar = Dx(r)*Dy(r);
+	abb = Dx(flushrect)*Dy(flushrect);
+	anbb = Dx(nbb)*Dy(nbb);
+	/*
+	 * Area of new waste is area of new bb minus area of old bb,
+	 * less the area of the new segment, which we assume is not waste.
+	 * This could be negative, but that's OK.
+	 */
+	waste += anbb-abb - ar;
+	if(waste < 0)
+		waste = 0;
+	/*
+	 * absorb if:
+	 *	total area is small
+	 *	waste is less than half total area
+	 * 	rectangles touch
+	 */
+	if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
+		flushrect = nbb;
+		return;
+	}
+	/* emit current state */
+	if(flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
+	flushrect = r;
+	waste = 0;
+}
+
+static
+void
+dstflush(Memimage *dst, Rectangle r)
+{
+	Memlayer *l;
+
+	if(dst == screenimage){
+		combinerect(&flushrect, r);
+		return;
+	}
+	l = dst->layer;
+	if(l == nil)
+		return;
+	do{
+		if(l->screen->image->data != screenimage->data)
+			return;
+		r = rectaddpt(r, l->delta);
+		l = l->screen->image->layer;
+	}while(l);
+	addflush(r);
+}
+
+static
+void
+drawflush(void)
+{
+	if(flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
+	flushrect = Rect(10000, 10000, -10000, -10000);
+}
+
+static
+int
+drawcmp(char *a, char *b, int n)
+{
+	if(strlen(a) != n)
+		return 1;
+	return memcmp(a, b, n);
+}
+
+DName*
+drawlookupname(int n, char *str)
+{
+	DName *name, *ename;
+
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for(; name<ename; name++)
+		if(drawcmp(name->name, str, n) == 0)
+			return name;
+	return 0;
+}
+
+int
+drawgoodname(DImage *d)
+{
+	DName *n;
+
+	/* if window, validate the screen's own images */
+	if(d->dscreen)
+		if(drawgoodname(d->dscreen->dimage) == 0
+		|| drawgoodname(d->dscreen->dfill) == 0)
+			return 0;
+	if(d->name == nil)
+		return 1;
+	n = drawlookupname(strlen(d->name), d->name);
+	if(n==nil || n->vers!=d->vers)
+		return 0;
+	return 1;
+}
+
+DImage*
+drawlookup(Client *client, int id, int checkname)
+{
+	DImage *d;
+
+	d = client->dimage[id&HASHMASK];
+	while(d){
+		if(d->id == id){
+			if(checkname && !drawgoodname(d))
+				error(Eoldname);
+			return d;
+		}
+		d = d->next;
+	}
+	return 0;
+}
+
+DScreen*
+drawlookupdscreen(int id)
+{
+	DScreen *s;
+
+	s = dscreen;
+	while(s){
+		if(s->id == id)
+			return s;
+		s = s->next;
+	}
+	return 0;
+}
+
+DScreen*
+drawlookupscreen(Client *client, int id, CScreen **cs)
+{
+	CScreen *s;
+
+	s = client->cscreen;
+	while(s){
+		if(s->dscreen->id == id){
+			*cs = s;
+			return s->dscreen;
+		}
+		s = s->next;
+	}
+	error(Enodrawscreen);
+	return 0;
+}
+
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+	DImage *d;
+
+	d = malloc(sizeof(DImage));
+	if(d == 0)
+		return 0;
+	d->id = id;
+	d->ref = 1;
+	d->name = 0;
+	d->vers = 0;
+	d->image = i;
+	d->nfchar = 0;
+	d->fchar = 0;
+	d->fromname = 0;
+	d->dscreen = dscreen;
+	d->next = client->dimage[id&HASHMASK];
+	client->dimage[id&HASHMASK] = d;
+	return i;
+}
+
+Memscreen*
+drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
+{
+	Memscreen *s;
+	CScreen *c;
+
+	c = malloc(sizeof(CScreen));
+	if(dimage && dimage->image && dimage->image->chan == 0)
+		panic("bad image %p in drawinstallscreen", dimage->image);
+
+	if(c == 0)
+		return 0;
+	if(d == 0){
+		d = malloc(sizeof(DScreen));
+		if(d == 0){
+			free(c);
+			return 0;
+		}
+		s = malloc(sizeof(Memscreen));
+		if(s == 0){
+			free(c);
+			free(d);
+			return 0;
+		}
+		s->frontmost = 0;
+		s->rearmost = 0;
+		d->dimage = dimage;
+		if(dimage){
+			s->image = dimage->image;
+			dimage->ref++;
+		}
+		d->dfill = dfill;
+		if(dfill){
+			s->fill = dfill->image;
+			dfill->ref++;
+		}
+		d->ref = 0;
+		d->id = id;
+		d->screen = s;
+		d->public = public;
+		d->next = dscreen;
+		d->owner = client;
+		dscreen = d;
+	}
+	c->dscreen = d;
+	d->ref++;
+	c->next = client->cscreen;
+	client->cscreen = c;
+	return d->screen;
+}
+
+void
+drawdelname(DName *name)
+{
+	int i;
+
+	i = name-sdraw.name;
+	memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
+	sdraw.nname--;
+}
+
+void
+drawfreedscreen(DScreen *this)
+{
+	DScreen *ds, *next;
+
+	this->ref--;
+	if(this->ref < 0)
+		print("negative ref in drawfreedscreen\n");
+	if(this->ref > 0)
+		return;
+	ds = dscreen;
+	if(ds == this){
+		dscreen = this->next;
+		goto Found;
+	}
+	while(next = ds->next){	/* assign = */
+		if(next == this){
+			ds->next = this->next;
+			goto Found;
+		}
+		ds = next;
+	}
+	error(Enodrawimage);
+
+    Found:
+	if(this->dimage)
+		drawfreedimage(this->dimage);
+	if(this->dfill)
+		drawfreedimage(this->dfill);
+	free(this->screen);
+	free(this);
+}
+
+void
+drawfreedimage(DImage *dimage)
+{
+	int i;
+	Memimage *l;
+	DScreen *ds;
+
+	dimage->ref--;
+	if(dimage->ref < 0)
+		print("negative ref in drawfreedimage\n");
+	if(dimage->ref > 0)
+		return;
+
+	/* any names? */
+	for(i=0; i<sdraw.nname; )
+		if(sdraw.name[i].dimage == dimage)
+			drawdelname(sdraw.name+i);
+		else
+			i++;
+	if(dimage->fromname){	/* acquired by name; owned by someone else*/
+		drawfreedimage(dimage->fromname);
+		goto Return;
+	}
+	if(dimage->image == screenimage)	/* don't free the display */
+		goto Return;
+	ds = dimage->dscreen;
+	if(ds){
+		l = dimage->image;
+		if(l->data == screenimage->data)
+			dstflush(l->layer->screen->image, l->layer->screenr);
+		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
+			free(l->layer->refreshptr);
+		l->layer->refreshptr = nil;
+		if(drawgoodname(dimage))
+			memldelete(l);
+		else
+			memlfree(l);
+		drawfreedscreen(ds);
+	}else
+		freememimage(dimage->image);
+    Return:
+	free(dimage->fchar);
+	free(dimage);
+}
+
+void
+drawuninstallscreen(Client *client, CScreen *this)
+{
+	CScreen *cs, *next;
+
+	cs = client->cscreen;
+	if(cs == this){
+		client->cscreen = this->next;
+		drawfreedscreen(this->dscreen);
+		free(this);
+		return;
+	}
+	while(next = cs->next){	/* assign = */
+		if(next == this){
+			cs->next = this->next;
+			drawfreedscreen(this->dscreen);
+			free(this);
+			return;
+		}
+		cs = next;
+	}
+}
+
+void
+drawuninstall(Client *client, int id)
+{
+	DImage *d, *next;
+
+	d = client->dimage[id&HASHMASK];
+	if(d == 0)
+		error(Enodrawimage);
+	if(d->id == id){
+		client->dimage[id&HASHMASK] = d->next;
+		drawfreedimage(d);
+		return;
+	}
+	while(next = d->next){	/* assign = */
+		if(next->id == id){
+			d->next = next->next;
+			drawfreedimage(next);
+			return;
+		}
+		d = next;
+	}
+	error(Enodrawimage);
+}
+
+void
+drawaddname(Client *client, DImage *di, int n, char *str)
+{
+	DName *name, *ename, *new, *t;
+
+	name = sdraw.name;
+	ename = &name[sdraw.nname];
+	for(; name<ename; name++)
+		if(drawcmp(name->name, str, n) == 0)
+			error(Enameused);
+	t = smalloc((sdraw.nname+1)*sizeof(DName));
+	memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
+	free(sdraw.name);
+	sdraw.name = t;
+	new = &sdraw.name[sdraw.nname++];
+	new->name = smalloc(n+1);
+	memmove(new->name, str, n);
+	new->name[n] = 0;
+	new->dimage = di;
+	new->client = client;
+	new->vers = ++sdraw.vers;
+}
+
+Client*
+drawnewclient(void)
+{
+	Client *cl, **cp;
+	int i;
+
+	for(i=0; i<sdraw.nclient; i++){
+		cl = sdraw.client[i];
+		if(cl == 0)
+			break;
+	}
+	if(i == sdraw.nclient){
+		cp = malloc((sdraw.nclient+1)*sizeof(Client*));
+		if(cp == 0)
+			return 0;
+		memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
+		free(sdraw.client);
+		sdraw.client = cp;
+		sdraw.nclient++;
+		cp[i] = 0;
+	}
+	cl = malloc(sizeof(Client));
+	if(cl == 0)
+		return 0;
+	memset(cl, 0, sizeof(Client));
+	cl->slot = i;
+	cl->clientid = ++sdraw.clientid;
+	cl->op = SoverD;
+	sdraw.client[i] = cl;
+	return cl;
+}
+
+static int
+drawclientop(Client *cl)
+{
+	int op;
+
+	op = cl->op;
+	cl->op = SoverD;
+	return op;
+}
+	
+int
+drawhasclients(void)
+{
+	/*
+	 * if draw has ever been used, we can't resize the frame buffer,
+	 * even if all clients have exited (nclients is cumulative); it's too
+	 * hard to make work.
+	 */
+	return sdraw.nclient != 0;
+}
+
+Client*
+drawclientofpath(ulong path)
+{
+	Client *cl;
+	int slot;
+
+	slot = CLIENTPATH(path);
+	if(slot == 0)
+		return nil;
+	cl = sdraw.client[slot-1];
+	if(cl==0 || cl->clientid==0)
+		return nil;
+	return cl;
+}
+
+
+Client*
+drawclient(Chan *c)
+{
+	Client *client;
+
+	client = drawclientofpath(c->qid.path);
+	if(client == nil)
+		error(Enoclient);
+	return client;
+}
+
+Memimage*
+drawimage(Client *client, uchar *a)
+{
+	DImage *d;
+
+	d = drawlookup(client, BGLONG(a), 1);
+	if(d == nil)
+		error(Enodrawimage);
+	return d->image;
+}
+
+void
+drawrectangle(Rectangle *r, uchar *a)
+{
+	r->min.x = BGLONG(a+0*4);
+	r->min.y = BGLONG(a+1*4);
+	r->max.x = BGLONG(a+2*4);
+	r->max.y = BGLONG(a+3*4);
+}
+
+void
+drawpoint(Point *p, uchar *a)
+{
+	p->x = BGLONG(a+0*4);
+	p->y = BGLONG(a+1*4);
+}
+
+Point
+drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
+{
+	FChar *fc;
+	Rectangle r;
+	Point sp1;
+
+	fc = &font->fchar[index];
+	r.min.x = p.x+fc->left;
+	r.min.y = p.y-(font->ascent-fc->miny);
+	r.max.x = r.min.x+(fc->maxx-fc->minx);
+	r.max.y = r.min.y+(fc->maxy-fc->miny);
+	sp1.x = sp->x+fc->left;
+	sp1.y = sp->y+fc->miny;
+	memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+	p.x += fc->width;
+	sp->x += fc->width;
+	return p;
+}
+
+static int
+initscreenimage(void)
+{
+	int width, depth;
+	ulong chan;
+	Rectangle r;
+
+	if(screenimage != nil)
+		return 1;
+
+	memimageinit();
+	screendata.base = nil;
+	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
+	if(screendata.bdata == nil)
+		return 0;
+	screendata.ref = 1;
+
+	screenimage = allocmemimaged(r, chan, &screendata);
+	if(screenimage == nil){
+		/* RSC: BUG: detach screen */
+		return 0;
+	}
+
+	screenimage->width = width;
+	screenimage->clipr = r;
+	return 1;
+}
+
+void
+deletescreenimage(void)
+{
+	qlock(&sdraw.q);
+	/* RSC: BUG: detach screen */
+	if(screenimage)
+		freememimage(screenimage);
+	screenimage = nil;
+	qunlock(&sdraw.q);
+}
+
+Chan*
+drawattach(char *spec)
+{
+	qlock(&sdraw.q);
+	if(!initscreenimage()){
+		qunlock(&sdraw.q);
+		error("no frame buffer");
+	}
+	qunlock(&sdraw.q);
+	return devattach('i', spec);
+}
+
+static Walkqid*
+drawwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	if(screendata.bdata == nil)
+		error("no frame buffer");
+	return devwalk(c, nc, name, nname, 0, 0, drawgen);
+}
+
+static int
+drawstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, drawgen);
+}
+
+static Chan*
+drawopen(Chan *c, int omode)
+{
+	Client *cl;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, 0, 0, drawgen);
+
+	qlock(&sdraw.q);
+	if(waserror()){
+		qunlock(&sdraw.q);
+		nexterror();
+	}
+
+	if(QID(c->qid) == Qnew){
+		cl = drawnewclient();
+		if(cl == 0)
+			error(Enodev);
+		c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
+	}
+
+	switch(QID(c->qid)){
+	case Qnew:
+		break;
+
+	case Qctl:
+		cl = drawclient(c);
+		if(cl->busy)
+			error(Einuse);
+		cl->busy = 1;
+		flushrect = Rect(10000, 10000, -10000, -10000);
+		drawinstall(cl, 0, screenimage, 0);
+		incref(&cl->r);
+		break;
+	case Qcolormap:
+	case Qdata:
+	case Qrefresh:
+		cl = drawclient(c);
+		incref(&cl->r);
+		break;
+	}
+	qunlock(&sdraw.q);
+	poperror();
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = IOUNIT;
+	return c;
+}
+
+static void
+drawclose(Chan *c)
+{
+	int i;
+	DImage *d, **dp;
+	Client *cl;
+	Refresh *r;
+
+	if(QID(c->qid) < Qcolormap)	/* Qtopdir, Qnew, Q3rd, Q2nd have no client */
+		return;
+	qlock(&sdraw.q);
+	if(waserror()){
+		qunlock(&sdraw.q);
+		nexterror();
+	}
+
+	cl = drawclient(c);
+	if(QID(c->qid) == Qctl)
+		cl->busy = 0;
+	if((c->flag&COPEN) && (decref(&cl->r)==0)){
+		while(r = cl->refresh){	/* assign = */
+			cl->refresh = r->next;
+			free(r);
+		}
+		/* free names */
+		for(i=0; i<sdraw.nname; )
+			if(sdraw.name[i].client == cl)
+				drawdelname(sdraw.name+i);
+			else
+				i++;
+		while(cl->cscreen)
+			drawuninstallscreen(cl, cl->cscreen);
+		/* all screens are freed, so now we can free images */
+		dp = cl->dimage;
+		for(i=0; i<NHASH; i++){
+			while((d = *dp) != nil){
+				*dp = d->next;
+				drawfreedimage(d);
+			}
+			dp++;
+		}
+		sdraw.client[cl->slot] = 0;
+		drawflush();	/* to erase visible, now dead windows */
+		free(cl);
+	}
+	qunlock(&sdraw.q);
+	poperror();
+}
+
+long
+drawread(Chan *c, void *a, long n, vlong off)
+{
+	int index, m;
+	ulong red, green, blue;
+	Client *cl;
+	uchar *p;
+	Refresh *r;
+	DImage *di;
+	Memimage *i;
+	ulong offset = off;
+	char buf[16];
+
+	USED(offset);
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, drawgen);
+	cl = drawclient(c);
+	qlock(&sdraw.q);
+	if(waserror()){
+		qunlock(&sdraw.q);
+		nexterror();
+	}
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n < 12*12)
+			error(Eshortread);
+		if(cl->infoid < 0)
+			error(Enodrawimage);
+		if(cl->infoid == 0){
+			i = screenimage;
+			if(i == nil)
+				error(Enodrawimage);
+		}else{
+			di = drawlookup(cl, cl->infoid, 1);
+			if(di == nil)
+				error(Enodrawimage);
+			i = di->image;
+		}
+		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
+			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
+			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
+			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
+		cl->infoid = -1;
+		break;
+
+	case Qcolormap:
+#ifdef COLORMAP
+		p = malloc(4*12*256+1);
+		if(p == 0)
+			error(Enomem);
+		m = 0;
+		for(index = 0; index < 256; index++){
+			getcolor(index, &red, &green, &blue);
+			m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
+		}
+		n = readstr(offset, a, n, (char*)p);
+		free(p);
+#else
+		n = 0;
+#endif
+		break;
+
+	case Qdata:
+		if(cl->readdata == nil)
+			error("no draw data");
+		if(n < cl->nreaddata)
+			error(Eshortread);
+		n = cl->nreaddata;
+		memmove(a, cl->readdata, cl->nreaddata);
+		free(cl->readdata);
+		cl->readdata = nil;
+		break;
+
+	case Qrefresh:
+		if(n < 5*4)
+			error(Ebadarg);
+		for(;;){
+			if(cl->refreshme || cl->refresh)
+				break;
+			qunlock(&sdraw.q);
+			if(waserror()){
+				qlock(&sdraw.q);	/* restore lock for waserror() above */
+				nexterror();
+			}
+			Sleep(&cl->refrend, drawrefactive, cl);
+			poperror();
+			qlock(&sdraw.q);
+		}
+		p = a;
+		while(cl->refresh && n>=5*4){
+			r = cl->refresh;
+			BPLONG(p+0*4, r->dimage->id);
+			BPLONG(p+1*4, r->r.min.x);
+			BPLONG(p+2*4, r->r.min.y);
+			BPLONG(p+3*4, r->r.max.x);
+			BPLONG(p+4*4, r->r.max.y);
+			cl->refresh = r->next;
+			free(r);
+			p += 5*4;
+			n -= 5*4;
+		}
+		cl->refreshme = 0;
+		n = p-(uchar*)a;
+	}
+	qunlock(&sdraw.q);
+	poperror();
+	return n;
+}
+
+void
+drawwakeall(void)
+{
+	Client *cl;
+	int i;
+
+	for(i=0; i<sdraw.nclient; i++){
+		cl = sdraw.client[i];
+		if(cl && (cl->refreshme || cl->refresh))
+			Wakeup(&cl->refrend);
+	}
+}
+
+static long
+drawwrite(Chan *c, void *a, long n, vlong off)
+{
+	char buf[128], *fields[4], *q;
+	Client *cl;
+	int i, m, red, green, blue, x;
+	ulong offset = off;
+
+	USED(offset);
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	cl = drawclient(c);
+	qlock(&sdraw.q);
+	if(waserror()){
+		drawwakeall();
+		qunlock(&sdraw.q);
+		nexterror();
+	}
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n != 4)
+			error("unknown draw control request");
+		cl->infoid = BGLONG((uchar*)a);
+		break;
+
+	case Qcolormap:
+#ifdef COLORMAP
+		m = n;
+		n = 0;
+		while(m > 0){
+			x = m;
+			if(x > sizeof(buf)-1)
+				x = sizeof(buf)-1;
+			q = memccpy(buf, a, '\n', x);
+			if(q == 0)
+				break;
+			i = q-buf;
+			n += i;
+			a = (char*)a + i;
+			m -= i;
+			*q = 0;
+			if(tokenize(buf, fields, nelem(fields)) != 4)
+				error(Ebadarg);
+			i = strtoul(fields[0], 0, 0);
+			red = strtoul(fields[1], 0, 0);
+			green = strtoul(fields[2], 0, 0);
+			blue = strtoul(fields[3], &q, 0);
+			if(fields[3] == q)
+				error(Ebadarg);
+			if(red>255 || green>255 || blue>255 || i<0 || i>255)
+				error(Ebadarg);
+			red |= red<<8;
+			red |= red<<16;
+			green |= green<<8;
+			green |= green<<16;
+			blue |= blue<<8;
+			blue |= blue<<16;
+			setcolor(i, red, green, blue);
+		}
+#else
+		n = 0;
+#endif
+		break;
+
+	case Qdata:
+		drawmesg(cl, a, n);
+		drawwakeall();
+		break;
+
+	default:
+		error(Ebadusefd);
+	}
+	qunlock(&sdraw.q);
+	poperror();
+	return n;
+}
+
+uchar*
+drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
+{
+	int b, x;
+
+	if(p >= maxp)
+		error(Eshortdraw);
+	b = *p++;
+	x = b & 0x7F;
+	if(b & 0x80){
+		if(p+1 >= maxp)
+			error(Eshortdraw);
+		x |= *p++ << 7;
+		x |= *p++ << 15;
+		if(x & (1<<22))
+			x |= ~0<<23;
+	}else{
+		if(b & 0x40)
+			x |= ~0<<7;
+		x += oldx;
+	}
+	*newx = x;
+	return p;
+}
+
+static void
+printmesg(char *fmt, uchar *a, int plsprnt)
+{
+	char buf[256];
+	char *p, *q;
+	int s;
+
+	if(1|| plsprnt==0){
+		SET(s); SET(q); SET(p);
+		USED(fmt); USED(a); USED(buf); USED(p); USED(q); USED(s);
+		return;
+	}
+	q = buf;
+	*q++ = *a++;
+	for(p=fmt; *p; p++){
+		switch(*p){
+		case 'l':
+			q += sprint(q, " %ld", (long)BGLONG(a));
+			a += 4;
+			break;
+		case 'L':
+			q += sprint(q, " %.8lux", (ulong)BGLONG(a));
+			a += 4;
+			break;
+		case 'R':
+			q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
+			a += 16;
+			break;
+		case 'P':
+			q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
+			a += 8;
+			break;
+		case 'b':
+			q += sprint(q, " %d", *a++);
+			break;
+		case 's':
+			q += sprint(q, " %d", BGSHORT(a));
+			a += 2;
+			break;
+		case 'S':
+			q += sprint(q, " %.4ux", BGSHORT(a));
+			a += 2;
+			break;
+		}
+	}
+	*q++ = '\n';
+	*q = 0;
+	iprint("%.*s", (int)(q-buf), buf);
+}
+
+void
+drawmesg(Client *client, void *av, int n)
+{
+	int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush;
+	uchar *u, *a, refresh;
+	char *fmt;
+	ulong value, chan;
+	Rectangle r, clipr;
+	Point p, q, *pp, sp;
+	Memimage *i, *dst, *src, *mask;
+	Memimage *l, **lp;
+	Memscreen *scrn;
+	DImage *font, *ll, *di, *ddst, *dsrc;
+	DName *dn;
+	DScreen *dscrn;
+	FChar *fc;
+	Refx *refx;
+	CScreen *cs;
+	Refreshfn reffn;
+
+	a = av;
+	m = 0;
+	fmt = nil;
+	if(waserror()){
+		if(fmt) printmesg(fmt, a, 1);
+	/*	iprint("error: %s\n", up->env->errstr);	*/
+		nexterror();
+	}
+	while((n-=m) > 0){
+		USED(fmt);
+		a += m;
+		switch(*a){
+		default:
+			error("bad draw command");
+		/* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
+		case 'b':
+			printmesg(fmt="LLbLbRRL", a, 0);
+			m = 1+4+4+1+4+1+4*4+4*4+4;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			scrnid = BGSHORT(a+5);
+			refresh = a[9];
+			chan = BGLONG(a+10);
+			repl = a[14];
+			drawrectangle(&r, a+15);
+			drawrectangle(&clipr, a+31);
+			value = BGLONG(a+47);
+			if(drawlookup(client, dstid, 0))
+				error(Eimageexists);
+			if(scrnid){
+				dscrn = drawlookupscreen(client, scrnid, &cs);
+				scrn = dscrn->screen;
+				if(repl || chan!=scrn->image->chan)
+					error("image parameters incompatible with screen");
+				reffn = nil;
+				switch(refresh){
+				case Refbackup:
+					break;
+				case Refnone:
+					reffn = memlnorefresh;
+					break;
+				case Refmesg:
+					reffn = drawrefresh;
+					break;
+				default:
+					error("unknown refresh method");
+				}
+				l = memlalloc(scrn, r, reffn, 0, value);
+				if(l == 0)
+					error(Edrawmem);
+				dstflush(l->layer->screen->image, l->layer->screenr);
+				l->clipr = clipr;
+				rectclip(&l->clipr, r);
+				if(drawinstall(client, dstid, l, dscrn) == 0){
+					memldelete(l);
+					error(Edrawmem);
+				}
+				dscrn->ref++;
+				if(reffn){
+					refx = nil;
+					if(reffn == drawrefresh){
+						refx = malloc(sizeof(Refx));
+						if(refx == 0){
+							drawuninstall(client, dstid);
+							error(Edrawmem);
+						}
+						refx->client = client;
+						refx->dimage = drawlookup(client, dstid, 1);
+					}
+					memlsetrefresh(l, reffn, refx);
+				}
+				continue;
+			}
+			i = allocmemimage(r, chan);
+			if(i == 0)
+				error(Edrawmem);
+			if(repl)
+				i->flags |= Frepl;
+			i->clipr = clipr;
+			if(!repl)
+				rectclip(&i->clipr, r);
+			if(drawinstall(client, dstid, i, 0) == 0){
+				freememimage(i);
+				error(Edrawmem);
+			}
+			memfillcolor(i, value);
+			continue;
+
+		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
+		case 'A':
+			printmesg(fmt="LLLb", a, 1);
+			m = 1+4+4+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(dstid == 0)
+				error(Ebadarg);
+			if(drawlookupdscreen(dstid))
+				error(Escreenexists);
+			ddst = drawlookup(client, BGLONG(a+5), 1);
+			dsrc = drawlookup(client, BGLONG(a+9), 1);
+			if(ddst==0 || dsrc==0)
+				error(Enodrawimage);
+			if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
+				error(Edrawmem);
+			continue;
+
+		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
+		case 'c':
+			printmesg(fmt="LbR", a, 0);
+			m = 1+4+1+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			ddst = drawlookup(client, BGLONG(a+1), 1);
+			if(ddst == nil)
+				error(Enodrawimage);
+			if(ddst->name)
+				error("cannot change repl/clipr of shared image");
+			dst = ddst->image;
+			if(a[5])
+				dst->flags |= Frepl;
+			drawrectangle(&dst->clipr, a+6);
+			continue;
+
+		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
+		case 'd':
+			printmesg(fmt="LLLRPP", a, 0);
+			m = 1+4+4+4+4*4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			mask = drawimage(client, a+9);
+			drawrectangle(&r, a+13);
+			drawpoint(&p, a+29);
+			drawpoint(&q, a+37);
+			op = drawclientop(client);
+			memdraw(dst, r, src, p, mask, q, op);
+			dstflush(dst, r);
+			continue;
+
+		/* toggle debugging: 'D' val[1] */
+		case 'D':
+			printmesg(fmt="b", a, 0);
+			m = 1+1;
+			if(n < m)
+				error(Eshortdraw);
+			drawdebug = a[1];
+			continue;
+
+		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
+		case 'e':
+		case 'E':
+			printmesg(fmt="LLPlllPll", a, 0);
+			m = 1+4+4+2*4+4+4+4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			drawpoint(&p, a+9);
+			e0 = BGLONG(a+17);
+			e1 = BGLONG(a+21);
+			if(e0<0 || e1<0)
+				error("invalid ellipse semidiameter");
+			j = BGLONG(a+25);
+			if(j < 0)
+				error("negative ellipse thickness");
+			drawpoint(&sp, a+29);
+			c = j;
+			if(*a == 'E')
+				c = -1;
+			ox = BGLONG(a+37);
+			oy = BGLONG(a+41);
+			op = drawclientop(client);
+			/* high bit indicates arc angles are present */
+			if(ox & (1<<31)){
+				if((ox & (1<<30)) == 0)
+					ox &= ~(1<<31);
+				memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
+			}else
+				memellipse(dst, p, e0, e1, c, src, sp, op);
+			dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
+			continue;
+
+		/* free: 'f' id[4] */
+		case 'f':
+			printmesg(fmt="L", a, 1);
+			m = 1+4;
+			if(n < m)
+				error(Eshortdraw);
+			ll = drawlookup(client, BGLONG(a+1), 0);
+			if(ll && ll->dscreen && ll->dscreen->owner != client)
+				ll->dscreen->owner->refreshme = 1;
+			drawuninstall(client, BGLONG(a+1));
+			continue;
+
+		/* free screen: 'F' id[4] */
+		case 'F':
+			printmesg(fmt="L", a, 1);
+			m = 1+4;
+			if(n < m)
+				error(Eshortdraw);
+			drawlookupscreen(client, BGLONG(a+1), &cs);
+			drawuninstallscreen(client, cs);
+			continue;
+
+		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
+		case 'i':
+			printmesg(fmt="Llb", a, 1);
+			m = 1+4+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			dst = drawimage(client, a+1);
+			if(dstid == 0 || dst == screenimage)
+				error("cannot use display as font");
+			font = drawlookup(client, dstid, 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->image->layer)
+				error("cannot use window as font");
+			ni = BGLONG(a+5);
+			if(ni<=0 || ni>4096)
+				error("bad font size (4096 chars max)");
+			free(font->fchar);	/* should we complain if non-zero? */
+			font->fchar = malloc(ni*sizeof(FChar));
+			if(font->fchar == 0)
+				error(Enomem);
+			memset(font->fchar, 0, ni*sizeof(FChar));
+			font->nfchar = ni;
+			font->ascent = a[9];
+			continue;
+
+		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
+		case 'l':
+			printmesg(fmt="LLSRPbb", a, 0);
+			m = 1+4+4+2+4*4+2*4+1+1;
+			if(n < m)
+				error(Eshortdraw);
+			font = drawlookup(client, BGLONG(a+1), 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->nfchar == 0)
+				error(Enotfont);
+			src = drawimage(client, a+5);
+			ci = BGSHORT(a+9);
+			if(ci >= font->nfchar)
+				error(Eindex);
+			drawrectangle(&r, a+11);
+			drawpoint(&p, a+27);
+			memdraw(font->image, r, src, p, memopaque, p, SoverD);
+			fc = &font->fchar[ci];
+			fc->minx = r.min.x;
+			fc->maxx = r.max.x;
+			fc->miny = r.min.y;
+			fc->maxy = r.max.y;
+			fc->left = a[35];
+			fc->width = a[36];
+			continue;
+
+		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
+		case 'L':
+			printmesg(fmt="LPPlllLP", a, 0);
+			m = 1+4+2*4+2*4+4+4+4+4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			drawpoint(&p, a+5);
+			drawpoint(&q, a+13);
+			e0 = BGLONG(a+21);
+			e1 = BGLONG(a+25);
+			j = BGLONG(a+29);
+			if(j < 0)
+				error("negative line width");
+			src = drawimage(client, a+33);
+			drawpoint(&sp, a+37);
+			op = drawclientop(client);
+			memline(dst, p, q, e0, e1, j, src, sp, op);
+			/* avoid memlinebbox if possible */
+			if(dst == screenimage || dst->layer!=nil){
+				/* BUG: this is terribly inefficient: update maximal containing rect*/
+				r = memlinebbox(p, q, e0, e1, j);
+				dstflush(dst, insetrect(r, -(1+1+j)));
+			}
+			continue;
+
+		/* create image mask: 'm' newid[4] id[4] */
+/*
+ *
+		case 'm':
+			printmesg("LL", a, 0);
+			m = 4+4;
+			if(n < m)
+				error(Eshortdraw);
+			break;
+ *
+ */
+
+		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
+		case 'n':
+			printmesg(fmt="Lz", a, 0);
+			m = 1+4+1;
+			if(n < m)
+				error(Eshortdraw);
+			j = a[5];
+			if(j == 0)	/* give me a non-empty name please */
+				error(Eshortdraw);
+			m += j;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(drawlookup(client, dstid, 0))
+				error(Eimageexists);
+			dn = drawlookupname(j, (char*)a+6);
+			if(dn == nil)
+				error(Enoname);
+			if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
+				error(Edrawmem);
+			di = drawlookup(client, dstid, 0);
+			if(di == 0)
+				error("draw: cannot happen");
+			di->vers = dn->vers;
+			di->name = smalloc(j+1);
+			di->fromname = dn->dimage;
+			di->fromname->ref++;
+			memmove(di->name, a+6, j);
+			di->name[j] = 0;
+			client->infoid = dstid;
+			continue;
+
+		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
+		case 'N':
+			printmesg(fmt="Lbz", a, 0);
+			m = 1+4+1+1;
+			if(n < m)
+				error(Eshortdraw);
+			c = a[5];
+			j = a[6];
+			if(j == 0)	/* give me a non-empty name please */
+				error(Eshortdraw);
+			m += j;
+			if(n < m)
+				error(Eshortdraw);
+			di = drawlookup(client, BGLONG(a+1), 0);
+			if(di == 0)
+				error(Enodrawimage);
+			if(di->name)
+				error(Enamed);
+			if(c)
+				drawaddname(client, di, j, (char*)a+7);
+			else{
+				dn = drawlookupname(j, (char*)a+7);
+				if(dn == nil)
+					error(Enoname);
+				if(dn->dimage != di)
+					error(Ewrongname);
+				drawdelname(dn);
+			}
+			continue;
+
+		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
+		case 'o':
+			printmesg(fmt="LPP", a, 0);
+			m = 1+4+2*4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			if(dst->layer){
+				drawpoint(&p, a+5);
+				drawpoint(&q, a+13);
+				r = dst->layer->screenr;
+				ni = memlorigin(dst, p, q);
+				if(ni < 0)
+					error("image origin failed");
+				if(ni > 0){
+					dstflush(dst->layer->screen->image, r);
+					dstflush(dst->layer->screen->image, dst->layer->screenr);
+					ll = drawlookup(client, BGLONG(a+1), 1);
+					drawrefreshscreen(ll, client);
+				}
+			}
+			continue;
+
+		/* set compositing operator for next draw operation: 'O' op */
+		case 'O':
+			printmesg(fmt="b", a, 0);
+			m = 1+1;
+			if(n < m)
+				error(Eshortdraw);
+			client->op = *(a+1);
+			continue;
+
+		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+		case 'p':
+		case 'P':
+			printmesg(fmt="LslllLPP", a, 0);
+			m = 1+4+2+4+4+4+4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			ni = BGSHORT(a+5);
+			if(ni < 0)
+				error("negative count in polygon");
+			e0 = BGLONG(a+7);
+			e1 = BGLONG(a+11);
+			j = 0;
+			if(*a == 'p'){
+				j = BGLONG(a+15);
+				if(j < 0)
+					error("negative polygon line width");
+			}
+			src = drawimage(client, a+19);
+			drawpoint(&sp, a+23);
+			drawpoint(&p, a+31);
+			ni++;
+			pp = malloc(ni*sizeof(Point));
+			if(pp == nil)
+				error(Enomem);
+			doflush = 0;
+			if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+				doflush = 1;	/* simplify test in loop */
+			ox = oy = 0;
+			esize = 0;
+			u = a+m;
+			for(y=0; y<ni; y++){
+				q = p;
+				oesize = esize;
+				u = drawcoord(u, a+n, ox, &p.x);
+				u = drawcoord(u, a+n, oy, &p.y);
+				ox = p.x;
+				oy = p.y;
+				if(doflush){
+					esize = j;
+					if(*a == 'p'){
+						if(y == 0){
+							c = memlineendsize(e0);
+							if(c > esize)
+								esize = c;
+						}
+						if(y == ni-1){
+							c = memlineendsize(e1);
+							if(c > esize)
+								esize = c;
+						}
+					}
+					if(*a=='P' && e0!=1 && e0 !=~0)
+						r = dst->clipr;
+					else if (y>0){
+						r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
+						combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+					}
+					if (rectclip(&r, dst->clipr))		/* should perhaps be an arg to dstflush */
+						dstflush(dst, r);
+				}
+				pp[y] = p;
+			}
+			if (y == 1)
+				dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+			op = drawclientop(client);
+			if(*a == 'p')
+				mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
+			else
+				memfillpoly(dst, pp, ni, e0, src, sp, op);
+			free(pp);
+			m = u-a;
+			continue;
+
+		/* read: 'r' id[4] R[4*4] */
+		case 'r':
+			printmesg(fmt="LR", a, 0);
+			m = 1+4+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			i = drawimage(client, a+1);
+			drawrectangle(&r, a+5);
+			if(!rectinrect(r, i->r))
+				error(Ereadoutside);
+			c = bytesperline(r, i->depth);
+			c *= Dy(r);
+			free(client->readdata);
+			client->readdata = mallocz(c, 0);
+			if(client->readdata == nil)
+				error("readimage malloc failed");
+			client->nreaddata = memunload(i, r, client->readdata, c);
+			if(client->nreaddata < 0){
+				free(client->readdata);
+				client->readdata = nil;
+				error("bad readimage call");
+			}
+			continue;
+
+		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
+		/* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
+		case 's':
+		case 'x':
+			printmesg(fmt="LLLPRPs", a, 0);
+			m = 1+4+4+4+2*4+4*4+2*4+2;
+			if(*a == 'x')
+				m += 4+2*4;
+			if(n < m)
+				error(Eshortdraw);
+
+			dst = drawimage(client, a+1);
+			src = drawimage(client, a+5);
+			font = drawlookup(client, BGLONG(a+9), 1);
+			if(font == 0)
+				error(Enodrawimage);
+			if(font->nfchar == 0)
+				error(Enotfont);
+			drawpoint(&p, a+13);
+			drawrectangle(&r, a+21);
+			drawpoint(&sp, a+37);
+			ni = BGSHORT(a+45);
+			u = a+m;
+			m += ni*2;
+			if(n < m)
+				error(Eshortdraw);
+			clipr = dst->clipr;
+			dst->clipr = r;
+			op = drawclientop(client);
+			if(*a == 'x'){
+				/* paint background */
+				l = drawimage(client, a+47);
+				drawpoint(&q, a+51);
+				r.min.x = p.x;
+				r.min.y = p.y-font->ascent;
+				r.max.x = p.x;
+				r.max.y = r.min.y+Dy(font->image->r);
+				j = ni;
+				while(--j >= 0){
+					ci = BGSHORT(u);
+					if(ci<0 || ci>=font->nfchar){
+						dst->clipr = clipr;
+						error(Eindex);
+					}
+					r.max.x += font->fchar[ci].width;
+					u += 2;
+				}
+				memdraw(dst, r, l, q, memopaque, ZP, op);
+				u -= 2*ni;
+			}
+			q = p;
+			while(--ni >= 0){
+				ci = BGSHORT(u);
+				if(ci<0 || ci>=font->nfchar){
+					dst->clipr = clipr;
+					error(Eindex);
+				}
+				q = drawchar(dst, q, src, &sp, font, ci, op);
+				u += 2;
+			}
+			dst->clipr = clipr;
+			p.y -= font->ascent;
+			dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
+			continue;
+
+		/* use public screen: 'S' id[4] chan[4] */
+		case 'S':
+			printmesg(fmt="Ll", a, 0);
+			m = 1+4+4;
+			if(n < m)
+				error(Eshortdraw);
+			dstid = BGLONG(a+1);
+			if(dstid == 0)
+				error(Ebadarg);
+			dscrn = drawlookupdscreen(dstid);
+			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
+				error(Enodrawscreen);
+			if(dscrn->screen->image->chan != BGLONG(a+5))
+				error("inconsistent chan");
+			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
+				error(Edrawmem);
+			continue;
+
+		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
+		case 't':
+			printmesg(fmt="bsL", a, 0);
+			m = 1+1+2;
+			if(n < m)
+				error(Eshortdraw);
+			nw = BGSHORT(a+2);
+			if(nw < 0)
+				error(Ebadarg);
+			if(nw == 0)
+				continue;
+			m += nw*4;
+			if(n < m)
+				error(Eshortdraw);
+			lp = malloc(nw*sizeof(Memimage*));
+			if(lp == 0)
+				error(Enomem);
+			if(waserror()){
+				free(lp);
+				nexterror();
+			}
+			for(j=0; j<nw; j++)
+				lp[j] = drawimage(client, a+1+1+2+j*4);
+			if(lp[0]->layer == 0)
+				error("images are not windows");
+			for(j=1; j<nw; j++)
+				if(lp[j]->layer->screen != lp[0]->layer->screen)
+					error("images not on same screen");
+			if(a[1])
+				memltofrontn(lp, nw);
+			else
+				memltorearn(lp, nw);
+			if(lp[0]->layer->screen->image->data == screenimage->data)
+				for(j=0; j<nw; j++)
+					dstflush(lp[j]->layer->screen->image, lp[j]->layer->screenr);
+			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
+			drawrefreshscreen(ll, client);
+			poperror();
+			free(lp);
+			continue;
+
+		/* visible: 'v' */
+		case 'v':
+			printmesg(fmt="", a, 0);
+			m = 1;
+			drawflush();
+			continue;
+
+		/* write: 'y' id[4] R[4*4] data[x*1] */
+		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
+		case 'y':
+		case 'Y':
+			printmesg(fmt="LR", a, 0);
+		//	iprint("load %c\n", *a);
+			m = 1+4+4*4;
+			if(n < m)
+				error(Eshortdraw);
+			dst = drawimage(client, a+1);
+			drawrectangle(&r, a+5);
+			if(!rectinrect(r, dst->r))
+				error(Ewriteoutside);
+			y = memload(dst, r, a+m, n-m, *a=='Y');
+			if(y < 0)
+				error("bad writeimage call");
+			dstflush(dst, r);
+			m += y;
+			continue;
+		}
+	}
+	poperror();
+}
+
+int
+drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx)
+{
+	DImage *d;
+	Memimage *i;
+	Client *client;
+
+	client = drawclientofpath(qidpath);
+	if(client == 0)
+		return 0;
+	d = drawlookup(client, id, 0);
+	if(d == nil)
+		return 0;
+	i = d->image;
+	if(i->layer == nil)
+		return 0;
+	return memlsetrefresh(i, reffn, refx);
+}
+
+void
+drawqlock(void)
+{
+	qlock(&sdraw.q);
+}
+
+void
+drawqunlock(void)
+{
+	qunlock(&sdraw.q);
+}
+
+void
+interf(void)
+{
+	/* force it to load */
+	drawreplxy(0, 0, 0);
+}
+
+Dev drawdevtab = {
+	'i',
+	"draw",
+
+	devinit,
+	drawattach,
+	drawwalk,
+	drawstat,
+	drawopen,
+	devcreate,
+	drawclose,
+	drawread,
+	devbread,
+	drawwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devdup.c
@@ -1,0 +1,150 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+
+/* Qid is (2*fd + (file is ctl))+1 */
+
+static int
+dupgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+	Fgrp *fgrp = up->env->fgrp;
+	Chan *f;
+	static int perm[] = { 0400, 0200, 0600, 0 };
+	int p;
+	Qid q;
+
+	USED(name); USED(tab); USED(ntab);
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp);
+		return 1;
+	}
+	if(s == 0)
+		return 0;
+	s--;
+	if(s/2 > fgrp->maxfd)
+		return -1;
+	if((f=fgrp->fd[s/2]) == nil)
+		return 0;
+	if(s & 1){
+		p = 0400;
+		sprint(up->genbuf, "%dctl", s/2);
+	}else{
+		p = perm[f->mode&3];
+		sprint(up->genbuf, "%d", s/2);
+	}
+	mkqid(&q, s+1, 0, QTFILE);
+	devdir(c, q, up->genbuf, 0, eve, p, dp);
+	return 1;
+}
+
+static Chan*
+dupattach(char *spec)
+{
+	return devattach('d', spec);
+}
+
+static Walkqid*
+dupwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, dupgen);
+}
+
+static int
+dupstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, dupgen);
+}
+
+static Chan*
+dupopen(Chan *c, int omode)
+{
+	Chan *f;
+	int fd, twicefd;
+
+	if(c->qid.type & QTDIR){
+		if(omode != 0)
+			error(Eisdir);
+		c->mode = 0;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+	if(c->qid.type & QTAUTH)
+		error(Eperm);
+	twicefd = c->qid.path - 1;
+	fd = twicefd/2;
+	if((twicefd & 1)){
+		/* ctl file */
+		f = c;
+		f->mode = openmode(omode);
+		f->flag |= COPEN;
+		f->offset = 0;
+	}else{
+		/* fd file */
+		f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1);
+		cclose(c);
+	}
+	if(omode & OCEXEC)
+		f->flag |= CCEXEC;
+	return f;
+}
+
+static void
+dupclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+dupread(Chan *c, void *va, long n, vlong offset)
+{
+	char *a = va;
+	char buf[256];
+	int fd, twicefd;
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, dupgen);
+	twicefd = c->qid.path - 1;
+	fd = twicefd/2;
+	if(twicefd & 1){
+		c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+		if(waserror()){
+			cclose(c);
+			nexterror();
+		}
+		progfdprint(c, fd, 0, buf, sizeof buf);
+		poperror();
+		cclose(c);
+		return readstr((ulong)offset, va, n, buf);
+	}
+	panic("dupread");
+	return 0;
+}
+
+static long
+dupwrite(Chan *c, void *a, long n, vlong o)
+{
+	USED(c); USED(a); USED(n); USED(o);
+	panic("dupwrite");
+	return 0;		/* not reached */
+}
+
+Dev dupdevtab = {
+	'd',
+	"dup",
+
+	devinit,
+	dupattach,
+	dupwalk,
+	dupstat,
+	dupopen,
+	devcreate,
+	dupclose,
+	dupread,
+	devbread,
+	dupwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devdynld.c
@@ -1,0 +1,357 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<a.out.h>
+#include	<dynld.h>
+
+#define	DBG	if(1) print
+
+extern ulong ndevs;
+
+enum
+{
+	Qdir,
+	Qdynld,
+	Qdynsyms,
+
+	DEVCHAR	= 'L',
+};
+
+static Dirtab	dltab[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"dynld",		{Qdynld},	0,	0644,
+	"dynsyms",	{Qdynsyms},	0,	0444,
+};
+
+enum
+{
+	DLdev,
+	DLudev,
+};
+
+static Cmdtab	dlcmd[] =
+{
+	DLdev,	"dev",	2,
+	DLudev,	"udev",	2,
+};
+
+typedef struct Dyndev Dyndev;
+
+struct Dyndev
+{
+	char	*path;
+	Dynobj	*o;
+	Dev	*dev;
+	Dyndev	*next;
+};
+
+static	Dyndev	*loaded;
+static	QLock	dllock;
+
+typedef struct Fd Fd;
+struct Fd {
+	int	fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+	return kread(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+	return kseek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+	kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+static void
+dlfree(Dyndev *l)
+{
+	if(l != nil){
+		free(l->path);
+		dynobjfree(l->o);
+		free(l);
+	}
+}
+
+static Dyndev*
+dlload(char *path, Dynsym *tab, int ntab)
+{
+	Fd f;
+	Dyndev *l;
+	
+	f.fd = kopen(path, OREAD);
+	if(f.fd < 0)
+		error("cannot open");
+	if(waserror()){
+		kclose(f.fd);
+		nexterror();
+	}
+	l = mallocz(sizeof(Dyndev), 1);
+	if(l == nil)
+		error(Enomem);
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	l->path = strdup(path);
+	if(l->path == nil)
+		error(Enomem);
+	l->o = dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0);
+	if(l->o == nil)
+		error(up->env->errstr);
+	poperror();
+	poperror();
+	kclose(f.fd);
+	return l;
+}
+
+static void
+devload(char *path)
+{
+	int i;
+	Dyndev *l;
+	Dev *dev;
+	char devname[32];
+
+	l = dlload(path, _exporttab, dyntabsize(_exporttab));
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	snprint(devname, sizeof(devname), "%sdevtab", "XXX");	/* TO DO */
+	dev = dynimport(l->o, devname, signof(*dev));
+	if(dev == nil)
+		error("no devtab");
+	if(devno(dev->dc, 1) >= 0)
+		error("device loaded");
+	for(i = 0; devtab[i] != nil; i++)
+		;
+	if(i >= ndevs || devtab[i+1] != nil)
+		error("device table full");
+	l->dev = devtab[i] = dev;
+	dev->init();
+	l->next = loaded;
+	loaded = l;
+	poperror();
+}
+
+static void
+devunload(char *path)
+{
+	int i, dc;
+	Dyndev *l, **ll;
+
+	dc = 0;
+	if(strlen(path) == 1)
+		dc = path[0];
+	for(ll = &loaded; *ll != nil; ll = &(*ll)->next){
+		if(path != nil && strcmp(path, (*ll)->path) == 0)
+			break;
+		if(dc != 0 && (*ll)->dev && dc == (*ll)->dev->dc)
+			break;
+	}
+	if((l = *ll) != nil){
+		for(i = 0; i < ndevs; i++)
+			if(l->dev == devtab[i]){
+				devtab[i] = nil;
+				break;
+			}
+/*
+		if(l->dev)
+			l->dev->shutdown();
+*/
+		*ll = l->next;
+		dlfree(l);
+	}
+}
+
+static long
+readdl(void *a, ulong n, ulong offset)
+{
+	char *p;
+	Dyndev *l;
+	int m, len;
+
+	m = 0;
+	for(l = loaded; l != nil; l = l->next)
+		m++;
+	m *= 48;
+	p = malloc(m);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	*p = 0;
+	len = 0;
+	for(l = loaded; l != nil; l = l->next)
+		if(l->dev)
+			len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lux\t%s\n",
+					l->dev->dc, l->o->base, l->o->size, l->dev->name);
+	n = readstr(offset, a, n, p);
+	poperror();
+	free(p);
+	return n;
+}
+
+static long
+readsyms(char *a, ulong n, ulong offset)
+{
+	char *p;
+	Dynsym *t;
+	long l, nr;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	nr = 0;
+	for(t = _exporttab; n > 0 && t->name != nil; t++){
+		l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name);
+		if(offset >= l){
+			offset -= l;
+			continue;
+		}
+		l = readstr(offset, a, n, p);
+		offset = 0;
+		n -= l;
+		a += l;
+		nr += l;
+	}
+	poperror();
+	free(p);
+	return nr;
+}
+
+static Chan*
+dlattach(char *spec)
+{
+	return devattach(DEVCHAR, spec);
+}
+
+static Walkqid*
+dlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen);
+}
+
+static int
+dlstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, dltab, nelem(dltab), devgen);
+}
+
+static Chan*
+dlopen(Chan *c, int omode)
+{
+	return devopen(c, omode, dltab, nelem(dltab), devgen);
+}
+
+static void
+dlclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+dlread(Chan *c, void *a, long n, vlong voffset)
+{
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, dltab, nelem(dltab), devgen);
+	case Qdynld:
+		return readdl(a, n, (ulong)voffset);
+	case Qdynsyms:
+		return readsyms(a, n, (ulong)voffset);
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static long
+dlwrite(Chan *c, void *a, long n, vlong voffset)
+{
+	Cmdbuf *cmd;
+	Cmdtab *ct;
+
+	USED(voffset);
+	switch((ulong)c->qid.path){
+	case Qdynld:
+		cmd = parsecmd(a, n);
+		qlock(&dllock);
+		if(waserror()){
+			qunlock(&dllock);
+			free(cmd);
+			nexterror();
+		}
+		ct = lookupcmd(cmd, dlcmd, nelem(dlcmd));
+		switch(ct->index){
+		case DLdev:
+			devload(cmd->f[1]);
+			break;
+		case DLudev:
+			devunload(cmd->f[1]);
+			break;
+		}
+		poperror();
+		qunlock(&dllock);
+		free(cmd);
+		break;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+Dev dynlddevtab = {
+	DEVCHAR,
+	"dynld",
+
+	devinit,
+	dlattach,
+	dlwalk,
+	dlstat,
+	dlopen,
+	devcreate,
+	dlclose,
+	dlread,
+	devbread,
+	dlwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+/* auxiliary routines for dynamic loading of C modules */
+
+Dynobj*
+dynld(int fd)
+{
+	Fd f;
+	
+	f.fd = fd;
+	return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0);
+}
+
+int
+dynldable(int fd)
+{
+	Fd f;
+
+	f.fd = fd;
+	return dynloadable(&f, readfd, seekfd);
+}
--- /dev/null
+++ b/emu/port/devdynldx.c
@@ -1,0 +1,357 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<a.out.h>
+#include	<dynld.h>
+
+/*
+ * TO DO
+ *	- dynamic allocation of Dev.dc
+ *	- inter-module dependencies
+ *	- reference count on Dev to allow error("inuse") [or how else to do it]
+ *	- is Dev.shutdown the right function to call?  Dev.config perhaps?
+ */
+
+#define	DBG	if(1) print
+
+extern ulong ndevs;
+
+enum
+{
+	Qdir,
+	Qdynld,
+	Qdynsyms,
+
+	DEVCHAR	= 'L',
+};
+
+static Dirtab	dltab[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"dynld",		{Qdynld},	0,	0644,
+	"dynsyms",	{Qdynsyms},	0,	0444,
+};
+
+typedef struct Dyndev Dyndev;
+
+struct Dyndev
+{
+	char*	name;	/* device name (eg, "dynld") */
+	char*	tag;	/* version tag (eg, MD5 or SHA1 hash of content) */
+	char*	path;	/* file from whence it came */
+	Dynobj*	o;
+	Dev*	dev;
+	Dyndev*	next;
+};
+
+static	Dyndev	*loaded;
+static	QLock	dllock;
+
+static	Dyndev**	finddyndev(char*);
+static	int	matched(Dyndev*, char*, char*);
+
+extern	Dynobj*	kdynloadfd(int, Dynsym*, int, ulong);
+
+static void
+dlfree(Dyndev *l)
+{
+	if(l != nil){
+		free(l->tag);
+		free(l->name);
+		free(l->path);
+		dynobjfree(l->o);
+		free(l);
+	}
+}
+
+static Dyndev*
+dlload(char *path, Dynsym *tab, int ntab)
+{
+	Dyndev *l;
+	int fd;
+
+	/* in Plan 9, would probably use Chan* interface here */
+	fd = kopen(path, OREAD);
+	if(fd < 0)
+		error("cannot open");
+	if(waserror()){
+		kclose(fd);
+		nexterror();
+	}
+	l = mallocz(sizeof(Dyndev), 1);
+	if(l == nil)
+		error(Enomem);
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	l->path = strdup(path);
+	if(l->path == nil)
+		error(Enomem);
+	l->o = kdynloadfd(fd, tab, ntab, 0);
+	if(l->o == nil)
+		error(up->env->errstr);
+	poperror();
+	poperror();
+	kclose(fd);
+	return l;
+}
+
+static void
+devload(char *name, char *path, char *tag)
+{
+	int i;
+	Dyndev *l, **lp;
+	Dev *dev;
+	char tabname[32];
+
+	lp = finddyndev(name);
+	if(*lp != nil)
+		error("already loaded");	/* i'm assuming the name (eg, "cons") is to be unique */
+	l = dlload(path, _exporttab, dyntabsize(_exporttab));
+	if(waserror()){
+		dlfree(l);
+		nexterror();
+	}
+	snprint(tabname, sizeof(tabname), "%sdevtab", name);
+	dev = dynimport(l->o, tabname, signof(*dev));
+	if(dev == nil)
+		errorf("can't find %sdevtab in module", name);
+	kstrdup(&l->name, name);
+	kstrdup(&l->tag, tag != nil? tag: "");
+	if(dev->name == nil)
+		dev->name = l->name;
+	else if(strcmp(dev->name, l->name) != 0)
+		errorf("module file has device %s", dev->name);
+	/* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */
+	if(devno(dev->dc, 1) >= 0)
+		errorf("devchar %C already used", dev->dc);
+	for(i = 0; devtab[i] != nil; i++)
+		;
+	if(i >= ndevs || devtab[i+1] != nil)
+		error("device table full");
+#ifdef NATIVE
+	i = splhi();
+	dev->reset();
+	splx(i);
+#endif
+	dev->init();
+	l->dev = devtab[i] = dev;
+	l->next = loaded;
+	loaded = l;			/* recently loaded ones first: good unload order? */
+	poperror();
+}
+
+static Dyndev**
+finddyndev(char *name)
+{
+	Dyndev *l, **lp;
+
+	for(lp = &loaded; (l = *lp) != nil; lp = &l->next)
+		if(strcmp(l->name, name) == 0)
+			break;
+	return lp;
+}
+
+static int
+matched(Dyndev *l, char *path, char *tag)
+{
+	if(path != nil && strcmp(l->path, path) != 0)
+		return 0;
+	if(tag != nil && strcmp(l->tag, tag) != 0)
+		return 0;
+	return 1;
+}
+
+static void
+devunload(char *name, char *path, char *tag)
+{
+	int i;
+	Dyndev *l, **lp;
+
+	lp = finddyndev(name);
+	l = *lp;
+	if(l == nil)
+		error("not loaded");
+	if(!matched(l, path, tag))
+		error("path/tag mismatch");
+	for(i = 0; i < ndevs; i++)
+		if(l->dev == devtab[i]){
+			devtab[i] = nil;		/* TO DO: ensure driver is not currently in use */
+			break;
+		}
+#ifdef NATIVE
+	l->dev->shutdown();
+#endif
+	*lp = l->next;
+	dlfree(l);
+}
+
+static long
+readdynld(void *a, ulong n, ulong offset)
+{
+	char *p;
+	Dyndev *l;
+	int m, len;
+
+	m = 0;
+	for(l = loaded; l != nil; l = l->next)
+		m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag);
+	p = malloc(m);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	*p = 0;
+	len = 0;
+	for(l = loaded; l != nil; l = l->next)
+		if(l->dev)
+			len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n",
+					l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag);
+	n = readstr(offset, a, n, p);
+	poperror();
+	free(p);
+	return n;
+}
+
+static long
+readsyms(char *a, ulong n, ulong offset)
+{
+	char *p;
+	Dynsym *t;
+	long l, nr;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	if(waserror()){
+		free(p);
+		nexterror();
+	}
+	nr = 0;
+	for(t = _exporttab; n > 0 && t->name != nil; t++){
+		l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name);
+		if(offset >= l){
+			offset -= l;
+			continue;
+		}
+		l = readstr(offset, a, n, p);
+		offset = 0;
+		n -= l;
+		a += l;
+		nr += l;
+	}
+	poperror();
+	free(p);
+	return nr;
+}
+
+static Chan*
+dlattach(char *spec)
+{
+	return devattach(DEVCHAR, spec);
+}
+
+static Walkqid*
+dlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen);
+}
+
+static int
+dlstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, dltab, nelem(dltab), devgen);
+}
+
+static Chan*
+dlopen(Chan *c, int omode)
+{
+	return devopen(c, omode, dltab, nelem(dltab), devgen);
+}
+
+static void
+dlclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+dlread(Chan *c, void *a, long n, vlong voffset)
+{
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, dltab, nelem(dltab), devgen);
+	case Qdynld:
+		return readdynld(a, n, voffset);
+	case Qdynsyms:
+		return readsyms(a, n, voffset);
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+static long
+dlwrite(Chan *c, void *a, long n, vlong voffset)
+{
+	Cmdbuf *cb;
+	char *name, *tag, *path;
+
+	USED(voffset);
+	switch((ulong)c->qid.path){
+	case Qdynld:
+		cb = parsecmd(a, n);
+		qlock(&dllock);
+		if(waserror()){
+			qunlock(&dllock);
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0)	/* only do devices */
+			cmderror(cb, Ebadctl);
+		name = cb->f[2];
+		path = nil;
+		if(cb->nf > 3)
+			path = cb->f[3];
+		tag = nil;
+		if(cb->nf > 4)
+			tag = cb->f[4];
+		if(strcmp(cb->f[0], "load") == 0){
+			if(path == nil)
+				cmderror(cb, "missing load path");
+			devload(name, path, tag);	/* TO DO: remaining parameters might be dependencies? */
+		}else if(strcmp(cb->f[0], "unload") == 0)
+			devunload(name, path, tag);
+		else
+			cmderror(cb, Ebadctl);
+		poperror();
+		qunlock(&dllock);
+		free(cb);
+		break;
+	default:
+		error(Egreg);
+	}
+	return n;
+}
+
+Dev dynlddevtab = {
+	DEVCHAR,
+	"dynld",
+
+	devinit,
+	dlattach,
+	dlwalk,
+	dlstat,
+	dlopen,
+	devcreate,
+	dlclose,
+	dlread,
+	devbread,
+	dlwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/deveia-bsd.c
@@ -1,0 +1,95 @@
+/*
+ * BSD serial port control features not found in POSIX
+ * including modem line control and hardware flow control
+ */
+
+static struct flagmap lines[] = {
+        {"cts",         TIOCM_CTS},
+        {"dsr",         TIOCM_DSR},
+        {"ring",        TIOCM_RI},
+        {"dcd",         TIOCM_CD},
+        {"dtr",         TIOCM_DTR},
+        {"rts",         TIOCM_RTS},
+	{0,		-1}
+};
+
+static void
+resxtra(int port, struct termios *ts)
+{
+        int fd = eia[port].fd;
+
+	USED(ts);
+
+        if(eia[port].dtr)
+	        ioctl(fd, TIOCM_DTR, eia[port].dtr);
+	if(eia[port].rts)
+	        ioctl(fd, TIOCM_RTS, eia[port].rts);
+	if(eia[port].cts)
+	        ioctl(fd, TIOCM_CTS, eia[port].cts);
+}
+
+static char *
+rdxtra(int port, struct termios *ts, char *str)
+{
+	int fd = eia[port].fd;
+	int line;
+//	struct flagmap *lp;
+	char *s = str;
+
+	USED(ts);
+
+	if(ioctl(fd, TIOCMGET, &line) < 0)
+		oserror();
+
+//	for(lp = lines; lp->str; lp++)
+//	        if(line&lp->flag)
+//		        s += sprint(s, " %s", lp->str);
+
+	return s;
+}
+
+static char *
+wrxtra(int port, struct termios *ts, char *cmd)
+{
+	int fd = eia[port].fd;
+	int n, r, flag, iocmd, *l;
+
+	USED(ts);
+
+	switch(*cmd) {
+	case 'D':
+	case 'd':
+		flag = TIOCM_DTR;
+		l = &eia[port].dtr;
+		break;
+	case 'R':
+	case 'r':
+		flag = TIOCM_RTS;
+		l = &eia[port].rts;
+		break;
+	case 'M':
+	case 'm':
+		flag = TIOCM_CTS;
+		l = &eia[port].cts;
+		break;
+	default:
+		return nil;
+	}
+
+	n = atoi(cmd+1);
+	if(n)
+		iocmd = TIOCMBIS;
+	else
+		iocmd = TIOCMBIC;
+
+	osenter();
+	r = ioctl(fd, iocmd, &flag);
+	osleave();
+	if(r < 0)	
+		oserror();
+	
+	eia[port].restore = 1;
+	*l = iocmd;
+
+	return nil;
+}
--- /dev/null
+++ b/emu/port/deveia-posix.c
@@ -1,0 +1,463 @@
+/*
+ * Driver for POSIX serial ports
+ */
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#undef _POSIX_C_SOURCE       /* for deveia-bsd.c */
+#include <sys/stat.h>
+#include	<termios.h>
+
+enum
+{
+	Devchar = 't',
+
+	Ndataqid = 1,
+	Nctlqid,
+	Nstatqid,
+	Nqid = 3,		/* number of QIDs */
+
+	CTLS=	023,
+	CTLQ=	021,
+
+	Maxctl = 128,
+	Maxfield = 32
+};
+
+/*
+ *  Macros to manage QIDs
+ */
+#define NETTYPE(x)	((x)&0x0F)
+#define NETID(x)	((x)>>4)
+#define NETQID(i,t)	(((i)<<4)|(t))
+
+static Dirtab *eiadir;
+static int ndir;
+
+static char Devname[] = "eia";
+
+typedef struct Eia Eia;
+struct Eia {
+	Ref		r;
+	int		fd;
+	int		overrun;
+	int		frame;
+	int		restore;       /* flag to restore prev. states */
+	struct termios 	ts;
+	int		dtr;
+	int		rts;
+	int		cts;
+};
+
+static Eia *eia;
+
+struct tcdef_t {
+	int	val;
+	tcflag_t	flag;
+};
+
+struct flagmap {
+	char*	s;
+	tcflag_t	flag;
+};
+
+static struct tcdef_t bps[];
+
+static struct tcdef_t size[] = {
+	{5,	CS5},
+	{6,	CS6},
+	{7,	CS7},
+	{8,	CS8},
+	{-1,	-1}
+};
+
+static char *
+ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag)
+{
+	for(; tbl->val >= 0; tbl++)
+		if(tbl->flag == flag){
+			sprint(buf, "%d", tbl->val);
+			return buf;
+		}
+	return "unknown";
+}
+
+static tcflag_t
+stof(struct tcdef_t *tbl, int val)
+{
+	for(; tbl->val >= 0 && tbl->val != val; tbl++)
+		{}
+	return tbl->flag;
+}
+
+static char *
+rdxtra(int port, struct termios *ts, char *str);	/* non-POSIX extensions */
+
+static long
+rdstat(int port, void *buf, long n, ulong offset)
+{
+	int  fd = eia[port].fd;
+	struct termios ts;
+	char str[Maxctl];
+	char sbuf[20];
+	char *s;
+
+	if(tcgetattr(fd, &ts) < 0)
+		oserror();
+
+	s = str;
+	s += sprint(s, "opens %d ferr %d oerr %d baud %s", 
+		    eia[port].r.ref-1, eia[port].frame, eia[port].overrun,
+		    ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts)));
+	s = rdxtra(port, &ts, s);
+	sprint(s, "\n");
+
+	return readstr(offset, buf, n, str);
+}
+
+static char *
+wrxtra(int port, struct termios *ts, char *cmd);  /* non-POSIX extensions */
+
+static void
+wrctl(int port, char *cmd)
+{
+	struct termios ts;
+	char *xerr;
+	int r, nf, n, i;
+	char *f[Maxfield];
+	int fd = eia[port].fd;
+	tcflag_t flag;
+
+	if(tcgetattr(fd, &ts) < 0) {
+Error:
+		oserror();
+	}
+
+	nf = tokenize(cmd, f, nelem(f));
+	for(i = 0; i < nf; i++){
+		if(strncmp(f[i], "break", 5) == 0){
+			tcsendbreak(fd, 0);
+			continue;
+		}
+		n = atoi(f[i]+1);
+		switch(*f[i]) {
+		case 'F':
+		case 'f':
+			if(tcflush(fd, TCOFLUSH) < 0)
+				goto Error;
+			break;
+		case 'K':
+		case 'k':
+			if(tcsendbreak(fd, 0) < 0)
+				;	/* ignore it */
+			break;
+		case 'H':
+		case 'h':
+			cfsetospeed(&ts, B0);
+			break;
+		case 'B':
+		case 'b':
+			flag = stof(bps, n);
+			if((int)flag == -1)
+				error(Ebadarg);
+			cfsetispeed(&ts, (speed_t)flag);
+			cfsetospeed(&ts, (speed_t)flag);
+			break;
+		case 'L':
+		case 'l':
+			flag = stof(size, n);
+			if((int)flag == -1)
+				error(Ebadarg);
+			ts.c_cflag &= ~CSIZE;
+			ts.c_cflag |= flag;
+			break;
+		case 'S':
+		case 's':
+			if(n == 1)
+				ts.c_cflag &= ~CSTOPB;
+			else if(n ==2)
+				ts.c_cflag |= CSTOPB;
+			else
+				error(Ebadarg);
+			break;
+		case 'P':
+		case 'p':
+			if(*(f[i]+1) == 'o')
+				ts.c_cflag |= PARENB|PARODD;
+			else if(*(f[i]+1) == 'e') {
+				ts.c_cflag |= PARENB;
+				ts.c_cflag &= ~PARODD;
+			}
+			else
+				ts.c_cflag &= ~PARENB;
+			break;
+		case 'X':
+		case 'x':
+			if(n == 0)
+			        ts.c_iflag &= ~(IXON|IXOFF);
+			else 
+			        ts.c_iflag |= (IXON|IXOFF);
+			break;
+		case 'i':
+		case 'I':
+			/* enable fifo; ignore */
+			break;
+		default:
+		        if((xerr = wrxtra(port, &ts, f[i])) != nil)
+			        error(xerr);
+		}
+	}
+
+	osenter();
+	r = tcsetattr(fd, TCSADRAIN, &ts);
+	osleave();
+	if(r < 0)
+		goto Error;
+	eia[port].restore = 1;
+	eia[port].ts      = ts;
+}
+
+static void
+eiainit(void)
+{
+	int i, nports;
+	Dirtab *dp;
+	struct stat sb;
+
+#ifdef buildsysdev
+	buildsysdev();
+#endif
+
+	/* check to see which ports exist by trying to stat them */
+	nports = 0;
+	for (i=0; i < nelem(sysdev); i++) {
+		if(stat(sysdev[i], &sb) < 0)
+			break;
+
+		nports++;
+	}
+
+	if (!nports)
+		return;
+
+	ndir = Nqid*nports+1;
+	dp = eiadir = malloc(ndir*sizeof(Dirtab));
+	if(dp == 0)
+		panic("eiainit");
+	strcpy(dp->name, ".");
+	dp->qid.path = 0;
+	dp->qid.type = QTDIR;
+	dp->perm = DMDIR|0555;
+	dp++;
+	eia = malloc(nports*sizeof(Eia));
+	if(eia == 0)
+		panic("eiainit");
+	for(i = 0; i < nports; i++) {
+		sprint(dp->name, "%s%d", Devname, i);
+		dp->qid.path = NETQID(i, Ndataqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%s%dctl", Devname, i);
+		dp->qid.path = NETQID(i, Nctlqid);
+		dp->perm = 0660;
+		dp++;
+		sprint(dp->name, "%s%dstatus", Devname, i);
+		dp->qid.path = NETQID(i, Nstatqid);
+		dp->perm = 0660;
+		dp++;
+		eia[i].frame = eia[i].overrun = 0;
+		eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0;
+	}
+}
+
+static Chan*
+eiaattach(char *spec)
+{
+	if(eiadir == nil)
+		error(Enodev);
+
+	return devattach(Devchar, spec);
+}
+
+Walkqid*
+eiawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
+}
+
+int
+eiastat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, eiadir, ndir, devgen);
+}
+
+static void
+resxtra(int port, struct termios *ts);	/* non-POSIX extensions */
+
+static Chan*
+eiaopen(Chan *c, int mode)
+{
+	int port = NETID(c->qid.path);
+	struct termios ts;
+	int r;
+
+	c = devopen(c, mode, eiadir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Nctlqid:
+	case Ndataqid:
+	case Nstatqid:
+		if(incref(&eia[port].r) != 1)
+			break;
+
+		osenter();
+		eia[port].fd = open(sysdev[port], O_RDWR);
+		osleave();
+		if(eia[port].fd < 0)
+			oserror();
+
+		/* make port settings sane */
+		if(tcgetattr(eia[port].fd, &ts) < 0)
+			oserror();
+		ts.c_iflag = ts.c_oflag = ts.c_lflag = 0;
+		if(eia[port].restore)
+		        ts = eia[port].ts;
+		else {
+			cfsetispeed(&ts, B9600);
+			cfsetospeed(&ts, B9600);
+			ts.c_iflag |= IGNPAR;
+			ts.c_cflag &= ~CSIZE;
+			ts.c_cflag |= CS8|CREAD;
+			ts.c_cflag &= ~(PARENB|PARODD);
+			ts.c_cc[VMIN] = 1;
+			ts.c_cc[VTIME] = 0;
+		}
+		osenter();
+		r = tcsetattr(eia[port].fd, TCSANOW, &ts);
+		osleave();
+		if(r < 0)
+			oserror();
+
+		if(eia[port].restore)
+		        resxtra(port, &ts);
+		break;
+	}
+	return c;
+}
+
+static void
+eiaclose(Chan *c)
+{
+	int port = NETID(c->qid.path);
+
+	if((c->flag & COPEN) == 0)
+		return;
+
+	switch(NETTYPE(c->qid.path)) {
+	case Nctlqid:
+	case Ndataqid:
+	case Nstatqid:
+		if(decref(&eia[port].r) != 0)
+			break;
+		if(eia[port].fd >= 0) {
+			osenter();
+			close(eia[port].fd);
+			osleave();
+		}
+		break;
+	}
+
+}
+
+static long
+eiaread(Chan *c, void *buf, long n, vlong offset)
+{
+	ssize_t cnt;
+	int port = NETID(c->qid.path);
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, eiadir, ndir, devgen);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Ndataqid:
+	  	osenter(); 
+		cnt = read(eia[port].fd, buf, n);
+		osleave(); 
+		if(cnt == -1)
+			oserror();
+		return cnt;
+	case Nctlqid:
+		return readnum(offset, buf, n, port, NUMSIZE);
+	case Nstatqid:
+		return rdstat(port, buf, n, offset);
+	}
+
+	return 0;
+}
+
+static long
+eiawrite(Chan *c, void *buf, long n, vlong offset)
+{
+	ssize_t cnt;
+	char cmd[Maxctl];
+	int port = NETID(c->qid.path);
+
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	switch(NETTYPE(c->qid.path)) {
+	case Ndataqid:
+	  	osenter(); 
+		cnt = write(eia[port].fd, buf, n);
+		osleave(); 
+		if(cnt == -1)
+			oserror();
+		return cnt;
+	case Nctlqid:
+		if(n >= (long)sizeof(cmd))
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		wrctl(port, cmd);
+		return n;
+	}
+	return 0;
+}
+
+int
+eiawstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	int i;
+
+	if(strcmp(up->env->user, eve) != 0)
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	n = convM2D(dp, n, &d, nil);
+	i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
+	eiadir[i+1].perm = d.mode&0666;
+	return n;
+}
+
+Dev eiadevtab = {
+        Devchar,
+        Devname,
+
+        eiainit,
+        eiaattach,
+        eiawalk,
+        eiastat,
+        eiaopen,
+        devcreate,
+        eiaclose,
+        eiaread,
+        devbread,
+        eiawrite,
+        devbwrite,
+        devremove,
+        eiawstat
+};
--- /dev/null
+++ b/emu/port/devenv.c
@@ -1,0 +1,253 @@
+/*
+ *	devenv - environment
+ */
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+enum
+{
+	Maxenvlen=	16*1024-1,
+};
+
+
+static void envremove(Chan*);
+
+static int
+envgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Egrp *eg;
+	Evalue *e;
+
+	USED(name);
+	USED(d);
+	USED(nd);
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp);
+		return 1;
+	}
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(e = eg->entries; e != nil && s != 0; e = e->next)
+		s--;
+	if(e == nil) {
+		qunlock(&eg->l);
+		return -1;
+	}
+	/* make sure name string continues to exist after we release lock */
+	kstrcpy(up->genbuf, e->var, sizeof up->genbuf);
+	devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp);
+	qunlock(&eg->l);
+	return 1;
+}
+
+static Chan*
+envattach(char *spec)
+{
+	return devattach('e', spec);
+}
+
+static Walkqid*
+envwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, envgen);
+}
+
+static int
+envstat(Chan *c, uchar *db, int n)
+{
+	if(c->qid.type & QTDIR)
+		c->qid.vers = up->env->egrp->vers;
+	return devstat(c, db, n, 0, 0, envgen);
+}
+
+static Chan *
+envopen(Chan *c, int mode)
+{
+	Egrp *eg;
+	Evalue *e;
+	
+	if(c->qid.type & QTDIR) {
+		if(mode != OREAD)
+			error(Eperm);
+		c->mode = mode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil) {
+		qunlock(&eg->l);
+		error(Enonexist);
+	}
+	if(mode == (OWRITE|OTRUNC) && e->val) {
+		free(e->val);
+		e->val = 0;
+		e->len = 0;
+		e->qid.vers++;
+	}
+	qunlock(&eg->l);
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = mode&~OTRUNC;
+	return c;
+}
+
+static void
+envcreate(Chan *c, char *name, int mode, ulong perm)
+{
+	Egrp *eg;
+	Evalue *e, *le;
+
+	USED(perm);
+	if(c->qid.type != QTDIR)
+		error(Eperm);
+	if(strlen(name) >= sizeof(up->genbuf))
+		error("name too long");	/* needs to fit for stat */
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(le = nil, e = eg->entries; e != nil; le = e, e = e->next)
+		if(strcmp(e->var, name) == 0) {
+			qunlock(&eg->l);
+			error(Eexist);
+		}
+	e = smalloc(sizeof(Evalue));
+	e->var = smalloc(strlen(name)+1);
+	strcpy(e->var, name);
+	e->val = 0;
+	e->len = 0;
+	e->qid.path = ++eg->path;
+	if (le == nil)
+		eg->entries = e;
+	else
+		le->next = e;
+	e->qid.vers = 0;
+	c->qid = e->qid;
+	eg->vers++;
+	qunlock(&eg->l);
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = mode;
+}
+
+static void
+envclose(Chan * c)
+{
+	if(c->flag & CRCLOSE)
+		envremove(c);
+}
+
+static long
+envread(Chan *c, void *a, long n, vlong offset)
+{
+	Egrp *eg;
+	Evalue *e;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, envgen);
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil) {
+		qunlock(&eg->l);
+		error(Enonexist);
+	}
+	if(offset > e->len)	/* protects against overflow converting vlong to ulong */
+		n = 0;
+	else if(offset + n > e->len)
+		n = e->len - offset;
+	if(n <= 0)
+		n = 0;
+	else
+		memmove(a, e->val+offset, n);
+	qunlock(&eg->l);
+	return n;
+}
+
+static long
+envwrite(Chan *c, void *a, long n, vlong offset)
+{
+	char *s;
+	ulong ve;
+	Egrp *eg;
+	Evalue *e;
+
+	if(n <= 0)
+		return 0;
+	ve = offset+n;
+	if(ve > Maxenvlen)
+		error(Etoobig);
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(e = eg->entries; e != nil; e = e->next)
+		if(e->qid.path == c->qid.path)
+			break;
+	if(e == nil) {
+		qunlock(&eg->l);
+		error(Enonexist);
+	}
+	if(ve > e->len) {
+		s = smalloc(ve);
+		memmove(s, e->val, e->len);
+		if(e->val)
+			free(e->val);
+		e->val = s;
+		e->len = ve;
+	}
+	memmove(e->val+offset, a, n);
+	e->qid.vers++;
+	qunlock(&eg->l);
+	return n;
+}
+
+static void
+envremove(Chan *c)
+{
+	Egrp *eg;
+	Evalue *e, **l;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	eg = up->env->egrp;
+	qlock(&eg->l);
+	for(l = &eg->entries; *l != nil; l = &(*l)->next)
+		if((*l)->qid.path == c->qid.path)
+			break;
+	e = *l;
+	if(e == nil) {
+		qunlock(&eg->l);
+		error(Enonexist);
+	}
+	*l = e->next;
+	eg->vers++;
+	qunlock(&eg->l);
+	free(e->var);
+	if(e->val != nil)
+		free(e->val);
+	free(e);
+}
+
+Dev envdevtab = {
+	'e',
+	"env",
+
+	devinit,
+	envattach,
+	envwalk,
+	envstat,
+	envopen,
+	envcreate,
+	envclose,
+	envread,
+	devbread,
+	envwrite,
+	devbwrite,
+	envremove,
+	devwstat
+};
--- /dev/null
+++ b/emu/port/devfs-posix.c
@@ -1,0 +1,1094 @@
+/*
+ * Unix file system interface
+ */
+#define _LARGEFILE64_SOURCE	1
+#define _FILE_OFFSET_BITS 64
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<sys/fcntl.h>
+#include	<sys/socket.h>
+#include	<sys/un.h>
+#include	<utime.h>
+#include	<dirent.h>
+#include	<stdio.h>
+#define	__EXTENSIONS__
+#undef	getwd
+#include	<unistd.h>
+#include	<pwd.h>
+#include	<grp.h>
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+	int	uid;
+	int	gid;
+	int	mode;	/* Unix mode */
+	DIR*	dir;		/* open directory */
+	struct dirent*	de;	/* directory reading */
+	int	fd;		/* open files */
+	ulong	offset;	/* offset when reading directory */
+	int	eod;	/* end of directory */
+	int	issocket;
+	QLock	oq;	/* mutex for offset */
+	char*	spec;
+	Cname*	name;	/* Unix's name for file */
+	Qid	rootqid;		/* Plan 9's qid for Inferno's root */
+};
+
+#define	FS(c)	((Fsinfo*)(c)->aux)
+
+enum
+{
+	IDSHIFT	= 8,
+	NID	= 1 << IDSHIFT,
+	IDMASK	= NID - 1,
+	MAXPATH	= 1024	/* TO DO: eliminate this */
+};
+
+typedef struct User User;
+struct User
+{
+	int	id;		/* might be user or group ID */
+	int	gid;		/* if it's a user not a group, the group ID (only for setid) */
+	char*	name;
+	int	nmem;
+	int*	mem;	/* member array, if nmem != 0 */
+	User*	next;
+};
+
+char	rootdir[MAXROOT] = ROOT;
+
+static	User*	uidmap[NID];
+static	User*	gidmap[NID];
+static	QLock	idl;
+static	User*	name2user(User**, char*, User* (*get)(char*));
+static	User*	id2user(User**, int, User* (*get)(int));
+static	User*	newuid(int);
+static	User*	newgid(int);
+static	User*	newuname(char*);
+static	User*	newgname(char*);
+
+static	Qid	fsqid(struct stat *);
+static	void	fspath(Cname*, char*, char*);
+static	int	fsdirconv(Chan*, char*, char*, struct stat*, uchar*, int, int);
+static	Cname*	fswalkpath(Cname*, char*, int);
+static	char*	fslastelem(Cname*);
+static	int ingroup(int id, int gid);
+static	void	fsperm(Chan*, int);
+static	long	fsdirread(Chan*, uchar*, int, vlong);
+static	int	fsomode(int);
+static	void	fsremove(Chan*);
+static	vlong osdisksize(int);	/* defined by including file */
+
+/*
+ * make invalid symbolic links visible; less confusing, and at least you can then delete them.
+ */
+static int
+xstat(char *f, struct stat *sb)
+{
+	if(stat(f, sb) >= 0)
+		return 0;
+	/* could possibly generate ->name as rob once suggested */
+	return lstat(f, sb);
+}
+
+static void
+fsfree(Chan *c)
+{
+	cnameclose(FS(c)->name);
+	free(FS(c));
+}
+
+Chan*
+fsattach(char *spec)
+{
+	Chan *c;
+	struct stat st;
+	static int devno;
+	static Lock l;
+
+	if(!emptystr(spec) && strcmp(spec, "*") != 0)
+		error(Ebadspec);
+	if(stat(rootdir, &st) < 0)
+		oserror();
+	if(!S_ISDIR(st.st_mode))
+		error(Enotdir);
+
+	c = devattach('U', spec);
+	c->qid = fsqid(&st);
+	c->aux = smalloc(sizeof(Fsinfo));
+	FS(c)->dir = nil;
+	FS(c)->de = nil;
+	FS(c)->fd = -1;
+	FS(c)->issocket = 0;
+	FS(c)->gid = st.st_gid;
+	FS(c)->uid = st.st_uid;
+	FS(c)->mode = st.st_mode;
+	lock(&l);
+	c->dev = devno++;
+	unlock(&l);
+	if(!emptystr(spec)){
+		FS(c)->spec = "/";
+		FS(c)->name = newcname(FS(c)->spec);
+	}else
+		FS(c)->name = newcname(rootdir);
+	FS(c)->rootqid = c->qid;
+
+	return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int j;
+	volatile int alloc;
+	Walkqid *wq;
+	struct stat st;
+	char *n;
+	Cname *next;
+	Cname *volatile current;
+	Qid rootqid;
+
+	if(nname > 0)
+		isdir(c);
+
+	alloc = 0;
+	current = nil;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone != nil)
+			cclose(wq->clone);
+		cnameclose(current);
+		free(wq);
+		return nil;
+	}
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;
+		alloc = 1;
+	}
+	wq->clone = nc;
+	rootqid = FS(c)->rootqid;
+	current = FS(c)->name;
+	if(current != nil)
+		incref(&current->r);
+	for(j = 0; j < nname; j++){
+		if(!(nc->qid.type&QTDIR)){
+			if(j==0)
+				error(Enotdir);
+			break;
+		}
+		n = name[j];
+		if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
+			next = current;
+			incref(&next->r);
+			next = addelem(current, n);
+			//print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
+			if(xstat(next->s, &st) < 0){
+				cnameclose(next);
+				if(j == 0)
+					error(Enonexist);
+				strcpy(up->env->errstr, Enonexist);
+				break;
+			}
+			nc->qid = fsqid(&st);
+			cnameclose(current);
+			current = next;
+		}
+		wq->qid[wq->nqid++] = nc->qid;
+	}
+	poperror();
+	if(wq->nqid < nname){
+		cnameclose(current);
+		if(alloc)
+			cclose(wq->clone);
+		wq->clone = nil;
+	}else if(wq->clone){
+		nc->aux = smalloc(sizeof(Fsinfo));
+		nc->type = c->type;
+		if(nname > 0) {
+			FS(nc)->gid = st.st_gid;
+			FS(nc)->uid = st.st_uid;
+			FS(nc)->mode = st.st_mode;
+			FS(nc)->issocket = S_ISSOCK(st.st_mode);
+		} else {
+			FS(nc)->gid = FS(c)->gid;
+			FS(nc)->uid = FS(c)->uid;
+			FS(nc)->mode = FS(c)->mode;
+			FS(nc)->issocket = FS(c)->issocket;
+		}
+		FS(nc)->name = current;
+		FS(nc)->spec = FS(c)->spec;
+		FS(nc)->rootqid = rootqid;
+		FS(nc)->fd = -1;
+		FS(nc)->dir = nil;
+		FS(nc)->de = nil;
+	}
+	return wq;
+}
+
+static int
+fsstat(Chan *c, uchar *dp, int n)
+{
+	struct stat st;
+	char *p;
+
+	if(FS(c)->fd >= 0){
+		if(fstat(FS(c)->fd, &st) < 0)
+			oserror();
+	}else{
+		if(xstat(FS(c)->name->s, &st) < 0)
+			oserror();
+	}
+	p = fslastelem(FS(c)->name);
+	if(*p == 0)
+		p = "/";
+	qlock(&idl);
+	n = fsdirconv(c, FS(c)->name->s, p, &st, dp, n, 0);
+	qunlock(&idl);
+	return n;
+}
+
+static int
+opensocket(char *path)
+{
+	int fd;
+	struct sockaddr_un su;
+	
+	memset(&su, 0, sizeof su);
+	su.sun_family = AF_UNIX;
+	if(strlen(path)+1 > sizeof su.sun_path)
+		error("unix socket name too long");
+	strcpy(su.sun_path, path);
+	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+		return -1;
+	if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+		return fd;
+	close(fd);
+	if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+		return -1;
+	if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+		return fd;
+	close(fd);
+	return -1;
+}		
+
+static Chan*
+fsopen(Chan *c, int mode)
+{
+	int m, isdir;
+
+	m = mode & (OTRUNC|3);
+	switch(m) {
+	case 0:
+		fsperm(c, 4);
+		break;
+	case 1:
+	case 1|16:
+		fsperm(c, 2);
+		break;
+	case 2:
+	case 0|16:
+	case 2|16:
+		fsperm(c, 4);
+		fsperm(c, 2);
+		break;
+	case 3:
+		fsperm(c, 1);
+		break;
+	default:
+		error(Ebadarg);
+	}
+
+	isdir = c->qid.type & QTDIR;
+
+	if(isdir && mode != OREAD)
+		error(Eperm);
+
+	m = fsomode(m & 3);
+	c->mode = openmode(mode);
+
+	if(isdir) {
+		FS(c)->dir = opendir(FS(c)->name->s);
+		if(FS(c)->dir == nil)
+			oserror();
+		FS(c)->eod = 0;
+	}
+	else {
+		if(!FS(c)->issocket){
+			if(mode & OTRUNC)
+				m |= O_TRUNC;
+			FS(c)->fd = open(FS(c)->name->s, m, 0666);
+		}else
+			FS(c)->fd = opensocket(FS(c)->name->s);
+		if(FS(c)->fd < 0)
+			oserror();
+	}
+
+	c->offset = 0;
+	FS(c)->offset = 0;
+	c->flag |= COPEN;
+	return c;
+}
+
+static void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+	int fd, m, o;
+	struct stat st;
+	Cname *n;
+
+	fsperm(c, 2);
+
+	m = fsomode(mode&3);
+	openmode(mode);	/* get the errors out of the way */
+
+	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+		error(Efilename);
+	n = fswalkpath(FS(c)->name, name, 1);
+	if(waserror()){
+		cnameclose(n);
+		nexterror();
+	}
+	if(perm & DMDIR) {
+		if(m)
+			error(Eperm);
+
+		perm &= ~0777 | (FS(c)->mode & 0777);
+		if(mkdir(n->s, perm) < 0)
+			oserror();
+
+		fd = open(n->s, 0);
+		if(fd < 0)
+			oserror();
+		fchmod(fd, perm);
+		fchown(fd, up->env->uid, FS(c)->gid);
+		if(fstat(fd, &st) <0){
+			close(fd);
+			oserror();
+		}
+		close(fd);
+		FS(c)->dir = opendir(n->s);
+		if(FS(c)->dir == nil)
+			oserror();
+		FS(c)->eod = 0;
+	} else {
+		o = (O_CREAT | O_EXCL) | (mode&3);
+		if(mode & OTRUNC)
+			o |= O_TRUNC;
+		perm &= ~0666 | (FS(c)->mode & 0666);
+		fd = open(n->s, o, perm);
+		if(fd < 0)
+			oserror();
+		fchmod(fd, perm);
+		fchown(fd, up->env->uid, FS(c)->gid);
+		if(fstat(fd, &st) < 0){
+			close(fd);
+			oserror();
+		}
+		FS(c)->fd = fd;
+	}
+	cnameclose(FS(c)->name);
+	FS(c)->name = n;
+	poperror();
+
+	c->qid = fsqid(&st);
+	FS(c)->gid = st.st_gid;
+	FS(c)->uid = st.st_uid;
+	FS(c)->mode = st.st_mode;
+	c->mode = openmode(mode);
+	c->offset = 0;
+	FS(c)->offset = 0;
+	FS(c)->issocket = 0;
+	c->flag |= COPEN;
+}
+
+static void
+fsclose(Chan *c)
+{
+	if((c->flag & COPEN) != 0){
+		if(c->qid.type & QTDIR)
+			closedir(FS(c)->dir);
+		else
+			close(FS(c)->fd);
+	}
+	if(c->flag & CRCLOSE) {
+		if(!waserror()) {
+			fsremove(c);
+			poperror();
+		}
+		return;
+	}
+	fsfree(c);
+}
+
+static long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+	long r;
+
+	if(c->qid.type & QTDIR){
+		qlock(&FS(c)->oq);
+		if(waserror()) {
+			qunlock(&FS(c)->oq);
+			nexterror();
+		}
+		r = fsdirread(c, va, n, offset);
+		poperror();
+		qunlock(&FS(c)->oq);
+	}else{
+		if(!FS(c)->issocket){
+			r = pread(FS(c)->fd, va, n, offset);
+			if(r >= 0)
+				return r;
+			if(errno != ESPIPE && errno != EPIPE)
+				oserror();
+		}
+		r = read(FS(c)->fd, va, n);
+		if(r < 0)
+			oserror();
+	}
+	return r;
+}
+
+static long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+	long r;
+
+	if(!FS(c)->issocket){
+		r = pwrite(FS(c)->fd, va, n, offset);
+		if(r >= 0)
+			return r;
+		if(errno != ESPIPE && errno != EPIPE)
+			oserror();
+	}
+	r = write(FS(c)->fd, va, n);
+	if(r < 0)
+		oserror();
+	return r;
+}
+
+static void
+fswchk(Cname *c)
+{
+	struct stat st;
+
+	if(stat(c->s, &st) < 0)
+		oserror();
+
+	if(st.st_uid == up->env->uid)
+		st.st_mode >>= 6;
+	else if(st.st_gid == up->env->gid || ingroup(up->env->uid, st.st_gid))
+		st.st_mode >>= 3;
+
+	if(st.st_mode & S_IWOTH)
+		return;
+
+	error(Eperm);
+}
+
+static void
+fsremove(Chan *c)
+{
+	int n;
+	Cname *volatile dir;
+
+	if(waserror()){
+		fsfree(c);
+		nexterror();
+	}
+	dir = fswalkpath(FS(c)->name, "..", 1);
+	if(waserror()){
+		cnameclose(dir);
+		nexterror();
+	}
+	fswchk(dir);
+	cnameclose(dir);
+	poperror();
+	if(c->qid.type & QTDIR)
+		n = rmdir(FS(c)->name->s);
+	else
+		n = remove(FS(c)->name->s);
+	if(n < 0)
+		oserror();
+	poperror();
+	fsfree(c);
+}
+
+static int
+fswstat(Chan *c, uchar *buf, int nb)
+{
+	Dir *d;
+	User *p;
+	Cname *volatile ph;
+	struct stat st;
+	struct utimbuf utbuf;
+	int tsync;
+
+	if(FS(c)->fd >= 0){
+		if(fstat(FS(c)->fd, &st) < 0)
+			oserror();
+	}else{
+		if(stat(FS(c)->name->s, &st) < 0)
+			oserror();
+	}
+	d = malloc(sizeof(*d)+nb);
+	if(d == nil)
+		error(Enomem);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	tsync = 1;
+	nb = convM2D(buf, nb, d, (char*)&d[1]);
+	if(nb == 0)
+		error(Eshortstat);
+	if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
+		tsync = 0;
+		validname(d->name, 0);
+		ph = fswalkpath(FS(c)->name, "..", 1);
+		if(waserror()){
+			cnameclose(ph);
+			nexterror();
+		}
+		fswchk(ph);
+		ph = fswalkpath(ph, d->name, 0);
+		if(rename(FS(c)->name->s, ph->s) < 0)
+			oserror();
+		cnameclose(FS(c)->name);
+		poperror();
+		FS(c)->name = ph;
+	}
+
+	if(d->mode != ~0 && (d->mode&0777) != (st.st_mode&0777)) {
+		tsync = 0;
+		if(up->env->uid != st.st_uid)
+			error(Eowner);
+		if(FS(c)->fd >= 0){
+			if(fchmod(FS(c)->fd, d->mode&0777) < 0)
+				oserror();
+		}else{
+			if(chmod(FS(c)->name->s, d->mode&0777) < 0)
+				oserror();
+		}
+		FS(c)->mode &= ~0777;
+		FS(c)->mode |= d->mode&0777;
+	}
+
+	if(d->atime != ~0 && d->atime != st.st_atime ||
+	   d->mtime != ~0 && d->mtime != st.st_mtime) {
+		tsync = 0;
+		if(up->env->uid != st.st_uid)
+			error(Eowner);
+		if(d->mtime != ~0)
+			utbuf.modtime = d->mtime;
+		else
+			utbuf.modtime = st.st_mtime;
+		if(d->atime != ~0)
+			utbuf.actime  = d->atime;
+		else
+			utbuf.actime = st.st_atime;
+		if(utime(FS(c)->name->s, &utbuf) < 0)	/* TO DO: futimes isn't portable */
+			oserror();
+	}
+
+	if(*d->gid){
+		tsync = 0;
+		qlock(&idl);
+		if(waserror()){
+			qunlock(&idl);
+			nexterror();
+		}
+		p = name2user(gidmap, d->gid, newgname);
+		if(p == 0)
+			error(Eunknown);
+		if(p->id != st.st_gid) {
+			if(up->env->uid != st.st_uid)
+				error(Eowner);
+			if(FS(c)->fd >= 0){
+				if(fchown(FS(c)->fd, st.st_uid, p->id) < 0)
+					oserror();
+			}else{
+				if(chown(FS(c)->name->s, st.st_uid, p->id) < 0)
+					oserror();
+			}
+			FS(c)->gid = p->id;
+		}
+		poperror();
+		qunlock(&idl);
+	}
+
+	if(d->length != ~(uvlong)0){
+		tsync = 0;
+		if(FS(c)->fd >= 0){
+			fsperm(c, 2);
+			if(ftruncate(FS(c)->fd, d->length) < 0)
+				oserror();
+		}else{
+			fswchk(FS(c)->name);
+			if(truncate(FS(c)->name->s, d->length) < 0)
+				oserror();
+		}
+	}
+
+	poperror();
+	free(d);
+	if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
+		oserror();
+	return nb;
+}
+
+static Qid
+fsqid(struct stat *st)
+{
+	Qid q;
+	u16int dev;
+
+	q.type = QTFILE;
+	if(S_ISDIR(st->st_mode))
+		q.type = QTDIR;
+
+	dev = (u16int)st->st_dev;
+	if(dev & 0x8000){
+		static int aware = 1;
+		if(aware==0){
+			aware = 1;
+			fprint(2, "fs: fsqid: top-bit dev: %#4.4ux\n", dev);
+		}
+		dev ^= 0x8080;
+	}
+
+	q.path = (uvlong)dev<<48;
+	q.path ^= st->st_ino;
+	q.vers = st->st_mtime;
+
+	return q;
+}
+
+static void
+fspath(Cname *c, char *name, char *path)
+{
+	int n;
+
+	if(c->len+strlen(name) >= MAXPATH)
+		panic("fspath: name too long");
+	memmove(path, c->s, c->len);
+	n = c->len;
+	if(path[n-1] != '/')
+		path[n++] = '/';
+	strcpy(path+n, name);
+	if(isdotdot(name))
+		cleanname(path);
+/*print("->%s\n", path);*/
+}
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+	if(dup)
+		c = newcname(c->s);
+	c = addelem(c, name);
+	if(isdotdot(name))
+		cleancname(c);
+	return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+	char *p;
+
+	p = c->s + c->len;
+	while(p > c->s && p[-1] != '/')
+		p--;
+	return p;
+}
+
+static void
+fsperm(Chan *c, int mask)
+{
+	int m;
+
+	m = FS(c)->mode;
+/*
+	print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
+		m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
+*/
+	if(FS(c)->uid == up->env->uid)
+		m >>= 6;
+	else if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
+		m >>= 3;
+
+	m &= mask;
+	if(m == 0)
+		error(Eperm);
+}
+
+static int
+isdots(char *name)
+{
+	return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
+}
+
+static int
+fsdirconv(Chan *c, char *path, char *name, struct stat *s, uchar *va, int nb, int indir)
+{
+	Dir d;
+	char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
+	User *u;
+	int fd;
+
+	memset(&d, 0, sizeof(d));
+	d.name = name;
+	u = id2user(uidmap, s->st_uid, newuid);
+	if(u == nil){
+		snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
+		d.uid = uidbuf;
+	}else
+		d.uid = u->name;
+	u = id2user(gidmap, s->st_gid, newgid);
+	if(u == nil){
+		snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
+		d.gid = gidbuf;
+	}else
+		d.gid = u->name;
+	d.muid = "";
+	d.qid = fsqid(s);
+	d.mode = (d.qid.type<<24)|(s->st_mode&0777);
+	d.atime = s->st_atime;
+	d.mtime = s->st_mtime;
+	d.length = s->st_size;
+	if(d.mode&DMDIR)
+		d.length = 0;
+	else if(S_ISBLK(s->st_mode) && s->st_size == 0){
+		fd = open(path, O_RDONLY);
+		if(fd >= 0){
+			d.length = osdisksize(fd);
+			close(fd);
+		}
+	}
+	d.type = 'U';
+	d.dev = c->dev;
+	if(indir && sizeD2M(&d) > nb)
+		return -1;	/* directory reader needs to know it didn't fit */
+	return convD2M(&d, va, nb);
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+	int i;
+	long n, r;
+	struct stat st;
+	char path[MAXPATH], *ep;
+	struct dirent *de;
+	static uchar slop[8192];
+
+	i = 0;
+	fspath(FS(c)->name, "", path);
+	ep = path+strlen(path);
+	if(FS(c)->offset != offset) {
+		seekdir(FS(c)->dir, 0);
+		FS(c)->de = nil;
+		FS(c)->eod = 0;
+		for(n=0; n<offset; ) {
+			de = readdir(FS(c)->dir);
+			if(de == 0) {
+				/* EOF, so stash offset and return 0 */
+				FS(c)->offset = n;
+				FS(c)->eod = 1;
+				return 0;
+			}
+			if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+				continue;
+			strecpy(ep, path+sizeof(path), de->d_name);
+			if(xstat(path, &st) < 0) {
+				fprint(2, "dir: bad path %s\n", path);
+				continue;
+			}
+			qlock(&idl);
+			if(waserror()){
+				qunlock(&idl);
+				nexterror();
+			}
+			r = fsdirconv(c, path, de->d_name, &st, slop, sizeof(slop), 1);
+			poperror();
+			qunlock(&idl);
+			if(r <= 0) {
+				FS(c)->offset = n;
+				return 0;
+			}
+			n += r;
+		}
+		FS(c)->offset = offset;
+	}
+
+	if(FS(c)->eod)
+		return 0;
+
+	/*
+	 * Take idl on behalf of id2name.  Stalling attach, which is a
+	 * rare operation, until the readdir completes is probably
+	 * preferable to adding lock round-trips.
+	 */
+	qlock(&idl);
+	while(i < count){
+		de = FS(c)->de;
+		FS(c)->de = nil;
+		if(de == nil)
+			de = readdir(FS(c)->dir);
+		if(de == nil){
+			FS(c)->eod = 1;
+			break;
+		}
+
+		if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+			continue;
+
+		strecpy(ep, path+sizeof(path), de->d_name);
+		if(xstat(path, &st) < 0) {
+			fprint(2, "dir: bad path %s\n", path);
+			continue;
+		}
+		r = fsdirconv(c, path, de->d_name, &st, va+i, count-i, 1);
+		if(r <= 0){
+			FS(c)->de = de;
+			break;
+		}
+		i += r;
+		FS(c)->offset += r;
+	}
+	qunlock(&idl);
+	return i;
+}
+
+static int
+fsomode(int m)
+{
+	if(m < 0 || m > 3)
+		error(Ebadarg);
+	return m == 3? 0: m;
+}
+
+void
+setid(char *name, int owner)
+{
+	User *u;
+
+	if(owner && !iseve())
+		return;
+	kstrdup(&up->env->user, name);
+
+	qlock(&idl);
+	u = name2user(uidmap, name, newuname);
+	if(u == nil){
+		qunlock(&idl);
+		up->env->uid = -1;
+		up->env->gid = -1;
+		return;
+	}
+
+	up->env->uid = u->id;
+	up->env->gid = u->gid;
+	qunlock(&idl);
+}
+
+static User**
+hashuser(User** tab, int id)
+{
+	int i;
+
+	i = (id>>IDSHIFT) ^ id;
+	return &tab[i & IDMASK];
+}
+
+/*
+ * the caller of the following functions must hold QLock idl.
+ */
+
+/*
+ * we could keep separate maps of user and group names to Users to
+ * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
+ */
+static User*
+name2user(User **tab, char *name, User* (*get)(char*))
+{
+	int i;
+	User *u, **h;
+	static User *prevu;
+	static User **prevtab;
+
+	if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
+		return prevu;	/* it's often the one we've just seen */
+
+	for(i=0; i<NID; i++)
+		for(u = tab[i]; u != nil; u = u->next)
+			if(strcmp(name, u->name) == 0) {
+				prevtab = tab;
+				prevu = u;
+				return u;
+			}
+
+	u = get(name);
+	if(u == nil)
+		return nil;
+	h = hashuser(tab, u->id);
+	u->next = *h;
+	*h = u;
+	prevtab = tab;
+	prevu = u;
+	return u;
+}
+
+static void
+freeuser(User *u)
+{
+	if(u != nil){
+		free(u->name);
+		free(u->mem);
+		free(u);
+	}
+}
+
+static User*
+newuser(int id, int gid, char *name, int nmem)
+{
+	User *u;
+
+	u = malloc(sizeof(*u));
+	if(u == nil)
+		return nil;
+	u->name = strdup(name);
+	if(u->name == nil){
+		free(u);
+		return nil;
+	}
+	u->nmem = nmem;
+	if(nmem){
+		u->mem = malloc(nmem*sizeof(*u->mem));
+		if(u->mem == nil){
+			free(u->name);
+			free(u);
+			return nil;
+		}
+	}else
+		u->mem = nil;
+	u->id = id;
+	u->gid = gid;
+	u->next = nil;
+	return u;
+}
+
+static User*
+newuname(char *name)
+{
+	struct passwd *p;
+
+	p = getpwnam(name);
+	if(p == nil)
+		return nil;
+	return newuser(p->pw_uid, p->pw_gid, name, 0);
+}
+
+static User*
+newuid(int id)
+{
+	struct passwd *p;
+
+	p = getpwuid(id);
+	if(p == nil)
+		return nil;
+	return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
+}
+
+static User*
+newgroup(struct group *g)
+{
+	User *u, *gm;
+	int n, o;
+
+	if(g == nil)
+		return nil;
+	for(n=0; g->gr_mem[n] != nil; n++)
+		;
+	u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
+	if(u == nil)
+		return nil;
+	o = 0;
+	for(n=0; g->gr_mem[n] != nil; n++){
+		gm = name2user(uidmap, g->gr_mem[n], newuname);
+		if(gm != nil)
+			u->mem[o++] = gm->id;
+		/* ignore names that don't map to IDs */
+	}
+	u->nmem = o;
+	return u;
+}
+
+static User*
+newgid(int id)
+{
+	return newgroup(getgrgid(id));
+}
+
+static User*
+newgname(char *name)
+{
+	return newgroup(getgrnam(name));
+}
+
+static User*
+id2user(User **tab, int id, User* (*get)(int))
+{
+	User *u, **h;
+
+	h = hashuser(tab, id);
+	for(u = *h; u != nil; u = u->next)
+		if(u->id == id)
+			return u;
+	u = get(id);
+	if(u == nil)
+		return nil;
+	u->next = *h;
+	*h = u;
+	return u;
+}
+
+static int
+ingroup(int id, int gid)
+{
+	int i;
+	User *g;
+
+	g = id2user(gidmap, gid, newgid);
+	if(g == nil || g->mem == nil)
+		return 0;
+	for(i = 0; i < g->nmem; i++)
+		if(g->mem[i] == id)
+			return 1;
+	return 0;
+}
+
+Dev fsdevtab = {
+	'U',
+	"fs",
+
+	devinit,
+	fsattach,
+	fswalk,
+	fsstat,
+	fsopen,
+	fscreate,
+	fsclose,
+	fsread,
+	devbread,
+	fswrite,
+	devbwrite,
+	fsremove,
+	fswstat
+};
--- /dev/null
+++ b/emu/port/devindir.c
@@ -1,0 +1,35 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+static Chan *
+indirattach(char *spec)
+{
+	char *p;
+	Dev *d;
+
+	if(*spec == 0)
+		error(Ebadspec);
+	p = strrchr(spec, '!');
+	if(p == nil)
+		p = "";
+	else
+		*p++ = 0;
+	d = devbyname(spec);
+	if(d == nil || d->dc == '*'){
+		snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec);
+		error(up->env->errstr);
+	}
+	if(up->env->pgrp->nodevs &&
+	   (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0'))
+		error(Enoattach);
+	return d->attach(p);
+}
+
+Dev indirdevtab = {
+	'*',
+	"indir",
+
+	devinit,
+	indirattach,
+};
--- /dev/null
+++ b/emu/port/devip.c
@@ -1,0 +1,1119 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"ip.h"
+
+enum
+{
+	Qtopdir		= 1,	/* top level directory */
+	Qtopbase,
+	Qarp=	Qtopbase,
+/*	Qiproute, */
+/*	Qipselftab,	*/
+	Qndb,
+
+	Qprotodir,		/* directory for a protocol */
+	Qprotobase,
+	Qclone=	Qprotobase,
+	Qstats,
+
+	Qconvdir,		/* directory for a conversation */
+	Qconvbase,
+	Qctl=	Qconvbase,
+	Qdata,
+	Qlisten,
+	Qlocal,
+	Qremote,
+	Qstatus,
+
+	Logtype=	5,
+	Masktype=	(1<<Logtype)-1,
+	Logconv=	12,
+	Maskconv=	(1<<Logconv)-1,
+	Shiftconv=	Logtype,
+	Logproto=	8,
+	Maskproto=	(1<<Logproto)-1,
+	Shiftproto=	Logtype + Logconv,
+
+	Statelen = 256,
+
+	Nfs=	1,
+
+	Maxproto	= 4,
+	MAXCONV		= 4096
+};
+#define TYPE(x) 	( ((ulong)(x).path) & Masktype )
+#define CONV(x) 	( (((ulong)(x).path) >> Shiftconv) & Maskconv )
+#define PROTO(x) 	( (((ulong)(x).path) >> Shiftproto) & Maskproto )
+#define QID(p, c, y) 	( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) )
+
+enum
+{
+	Idle=		0,
+	Announcing=	1,
+	Announced=	2,
+	Connecting=	3,
+	Connected=	4,
+	Hungup=	5,
+};
+
+struct Conv
+{
+	QLock	l;
+
+	int	x;	/* conversation index */
+	Proto*	p;
+
+	uchar	laddr[IPaddrlen];	/* local IP address */
+	uchar	raddr[IPaddrlen];	/* remote IP address */
+	int	restricted;	/* remote port is restricted */
+	ushort	lport;	/* local port number */
+	ushort	rport;	/* remote port number */
+
+	char*	owner;	/* protections */
+	int	perm;
+	int	inuse;	/* opens of listen/data/ctl */
+	int	state;
+
+	/* udp specific */
+	int	headers;	/* data src/dst headers in udp */
+
+	char	cerr[ERRMAX];
+
+	QLock	listenq;
+
+	void*	ptcl;	/* protocol specific stuff */
+
+	int	sfd;
+	QLock	wlock;	/* prevent data from being split by concurrent writes */
+};
+
+struct Proto
+{
+	QLock	l;
+	int	x;
+	int	ipproto;
+	int	stype;
+	char*	name;
+	int	maxconv;
+	Fs*	f;	/* file system this proto is part of */
+	Conv**	conv;	/* array of conversations */
+	int	pctlsize;	/* size of per protocol ctl block */
+	int	nc;	/* number of conversations */
+	int	ac;
+	Qid	qid;	/* qid for protocol directory */
+	/* port allocation isn't done here when hosted */
+
+	void*	priv;
+};
+
+/*
+ * one per IP protocol stack
+ */
+struct Fs
+{
+	RWlock	l;
+	int	dev;
+
+	int	np;
+	Proto*	p[Maxproto+1];	/* list of supported protocols */
+	Proto*	t2p[256];	/* vector of all protocols */
+
+	char	ndb[1024];	/* an ndb entry for this interface */
+	int	ndbvers;
+	long	ndbmtime;
+};
+
+static	Fs	*ipfs[Nfs];	/* attached fs's */
+static	char	network[] = "network";
+static	char* ipstates[] = {
+	"Closed",	/* Idle */
+	"Announcing",
+	"Announced",
+	"Connecting",
+	"Established",	/* Connected */
+	"Closed",	/* Hungup */
+};
+
+static	Conv*	protoclone(Proto*, char*, int);
+static	Conv*	newconv(Proto*, Conv **);
+static	void	setladdr(Conv*);
+
+static int
+ip3gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+	char *p;
+
+	cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
+	if(cv->owner == nil)
+		kstrdup(&cv->owner, eve);
+	mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE);
+
+	switch(i) {
+	default:
+		return -1;
+	case Qctl:
+		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qdata:
+		devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qlisten:
+		devdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
+		return 1;
+	case Qlocal:
+		p = "local";
+		break;
+	case Qremote:
+		p = "remote";
+		break;
+	case Qstatus:
+		p = "status";
+		break;
+	}
+	devdir(c, q, p, 0, cv->owner, 0444, dp);
+	return 1;
+}
+
+static int
+ip2gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+
+	switch(i) {
+	case Qclone:
+		mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE);
+		devdir(c, q, "clone", 0, network, 0666, dp);
+		return 1;
+	case Qstats:
+		mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE);
+		devdir(c, q, "stats", 0, network, 0444, dp);
+		return 1;
+	}	
+	return -1;
+}
+
+static int
+ip1gen(Chan *c, int i, Dir *dp)
+{
+	Qid q;
+	char *p;
+	int prot;
+	int len = 0;
+	Fs *f;
+	extern ulong	kerndate;
+
+	f = ipfs[c->dev];
+
+	prot = 0664;
+	mkqid(&q, QID(0, 0, i), 0, QTFILE);
+	switch(i) {
+	default:
+		return -1;
+	case Qarp:
+		p = "arp";
+		break;
+	case Qndb:
+		p = "ndb";
+		len = strlen(ipfs[c->dev]->ndb);
+		break;
+/*	case Qiproute:
+		p = "iproute";
+		break;
+	case Qipselftab:
+		p = "ipselftab";
+		prot = 0444;
+		break;
+	case Qiprouter:
+		p = "iprouter";
+		break;
+	case Qlog:
+		p = "log";
+		break;
+*/
+	}
+	devdir(c, q, p, len, network, prot, dp);
+	if(i == Qndb && f->ndbmtime > kerndate)
+		dp->mtime = f->ndbmtime;
+	return 1;
+}
+
+static int
+ipgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp)
+{
+	Qid q;
+	Conv *cv;
+	Fs *f;
+
+	USED(name);
+	USED(tab);
+	USED(x);
+	f = ipfs[c->dev];
+
+	switch(TYPE(c->qid)) {
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#I%lud", c->dev);
+			devdir(c, q, up->genbuf, 0, network, 0555, dp);
+			return 1;
+		}
+		if(s < f->np) {
+/*			if(f->p[s]->connect == nil)
+				return 0;	/* protocol with no user interface */
+			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+			return 1;
+		}
+		s -= f->np;
+		return ip1gen(c, s+Qtopbase, dp);
+	case Qarp:
+	case Qndb:
+/*	case Qiproute:
+	case Qiprouter:
+	case Qipselftab:	*/
+		return ip1gen(c, TYPE(c->qid), dp);
+	case Qprotodir:
+		if(s == DEVDOTDOT){
+			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
+			sprint(up->genbuf, "#I%lud", c->dev);
+			devdir(c, q, up->genbuf, 0, network, 0555, dp);
+			return 1;
+		}
+		if(s < f->p[PROTO(c->qid)]->ac) {
+			cv = f->p[PROTO(c->qid)]->conv[s];
+			sprint(up->genbuf, "%d", s);
+			mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR);
+			devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp);
+			return 1;
+		}
+		s -= f->p[PROTO(c->qid)]->ac;
+		return ip2gen(c, s+Qprotobase, dp);
+	case Qclone:
+	case Qstats:
+		return ip2gen(c, TYPE(c->qid), dp);
+	case Qconvdir:
+		if(s == DEVDOTDOT){
+			s = PROTO(c->qid);
+			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
+			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
+			return 1;
+		}
+		return ip3gen(c, s+Qconvbase, dp);
+	case Qctl:
+	case Qdata:
+	case Qlisten:
+	case Qlocal:
+	case Qremote:
+	case Qstatus:
+		return ip3gen(c, TYPE(c->qid), dp);
+	}
+	return -1;
+}
+
+static void
+newproto(char *name, int type, int maxconv)
+{
+	Proto *p;
+
+	p = smalloc(sizeof(*p));
+	p->name = name;
+	p->stype = type;
+	p->ipproto = type+1;	/* temporary */
+	p->nc = maxconv;
+	if(Fsproto(ipfs[0], p))
+		panic("can't newproto %s", name);
+}
+
+void
+ipinit(void)
+{
+	ipfs[0] = malloc(sizeof(Fs));
+	if(ipfs[0] == nil)
+		panic("no memory for IP stack");
+
+	newproto("udp", S_UDP, 64);
+	newproto("tcp", S_TCP, 2048);
+
+	fmtinstall('i', eipfmt);
+	fmtinstall('I', eipfmt);
+	fmtinstall('E', eipfmt);
+	fmtinstall('V', eipfmt);
+	fmtinstall('M', eipfmt);
+}
+
+Chan *
+ipattach(char *spec)
+{
+	Chan *c;
+
+	if(atoi(spec) != 0)
+		error("bad specification");
+
+	c = devattach('I', spec);
+	mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
+	c->dev = 0;
+
+	return c;
+}
+
+static Walkqid*
+ipwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, ipgen);
+}
+
+static int
+ipstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, ipgen);
+}
+
+static int m2p[] = {
+	4,
+	2,
+	6,
+};
+
+static Chan *
+ipopen(Chan *c, int omode)
+{
+	Conv *cv, *nc;
+	Proto *p;
+	uchar raddr[IPaddrlen];
+	ushort rport;
+	int perm, sfd;
+	Fs *f;
+
+	perm = m2p[omode&3];
+
+	f = ipfs[c->dev];
+
+	switch(TYPE(c->qid)) {
+	default:
+		break;
+	case Qtopdir:
+	case Qprotodir:
+	case Qconvdir:
+	case Qstatus:
+	case Qremote:
+	case Qlocal:
+	case Qstats:
+	/* case Qipselftab: */
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qndb:
+		if(omode & (OWRITE|OTRUNC) && !iseve())
+			error(Eperm);
+		if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){
+			f->ndb[0] = 0;
+			f->ndbvers++;
+		}
+		break;
+	case Qclone:
+		p = f->p[PROTO(c->qid)];
+		cv = protoclone(p, up->env->user, -1);
+		if(cv == 0)
+			error(Enodev);
+		mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE);
+		break;
+	case Qdata:
+	case Qctl:
+		p = f->p[PROTO(c->qid)];
+		qlock(&p->l);
+		cv = p->conv[CONV(c->qid)];
+		qlock(&cv->l);
+		if(waserror()){
+			qunlock(&cv->l);
+			qunlock(&p->l);
+			nexterror();
+		}
+		if((perm & (cv->perm>>6)) != perm) {
+			if(strcmp(up->env->user, cv->owner) != 0)
+				error(Eperm);
+			if((perm & cv->perm) != perm)
+				error(Eperm);
+		}
+		cv->inuse++;
+		if(cv->inuse == 1) {
+			kstrdup(&cv->owner, up->env->user);
+			cv->perm = 0660;
+			if(cv->sfd < 0)
+				cv->sfd = so_socket(p->stype);
+		}
+		poperror();
+		qunlock(&cv->l);
+		qunlock(&p->l);
+		break;
+	case Qlisten:
+		p = f->p[PROTO(c->qid)];
+		cv = p->conv[CONV(c->qid)];
+		if((perm & (cv->perm>>6)) != perm){
+			if(strcmp(up->env->user, cv->owner) != 0)
+				error(Eperm);
+			if((perm & cv->perm) != perm)
+				error(Eperm);
+		}
+
+		if(cv->state != Announced)
+			error("not announced");
+
+		qlock(&cv->listenq);
+		if(waserror()){
+			qunlock(&cv->listenq);
+			nexterror();
+		}
+
+		sfd = so_accept(cv->sfd, raddr, &rport);
+
+		nc = protoclone(p, up->env->user, sfd);
+		if(nc == 0) {
+			so_close(sfd);
+			error(Enodev);
+		}
+		memmove(nc->raddr, raddr, IPaddrlen);
+		nc->rport = rport;
+		setladdr(nc);
+		nc->state = Connected;
+		mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE);
+
+		poperror();
+		qunlock(&cv->listenq);
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+closeconv(Conv *cv)
+{
+	int fd;
+
+	qlock(&cv->l);
+
+	if(--cv->inuse > 0) {
+		qunlock(&cv->l);
+		return;
+	}
+
+	if(waserror()){
+		qunlock(&cv->l);
+		return;
+	}
+	kstrdup(&cv->owner, network);
+	cv->perm = 0660;
+	/* cv->p->close(cv); */
+	cv->state = Idle;
+	cv->restricted = 0;
+	fd = cv->sfd;
+	cv->sfd = -1;
+	if(fd >= 0)
+		so_close(fd);
+	poperror();
+	qunlock(&cv->l);
+}
+
+static void
+ipclose(Chan *c)
+{
+	Fs *f;
+
+	f = ipfs[c->dev];
+	switch(TYPE(c->qid)) {
+	case Qdata:
+	case Qctl:
+		if(c->flag & COPEN)
+			closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
+		break;
+	}
+}
+
+static long
+ipread(Chan *ch, void *a, long n, vlong off)
+{
+	int r;
+	Conv *c;
+	Proto *x;
+	char *p, *s;
+	Fs *f;
+	ulong offset = off;
+
+	f = ipfs[ch->dev];
+
+	p = a;
+	switch(TYPE(ch->qid)) {
+	default:
+		error(Eperm);
+	case Qprotodir:
+	case Qtopdir:
+	case Qconvdir:
+		return devdirread(ch, a, n, 0, 0, ipgen);
+	case Qarp:
+		error(Eperm);	/* TO DO */
+	case Qndb:
+		return readstr(off, a, n, f->ndb);
+	case Qctl:
+		sprint(up->genbuf, "%lud", CONV(ch->qid));
+		return readstr(offset, p, n, up->genbuf);
+	case Qremote:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		sprint(up->genbuf, "%I!%d\n", c->raddr, c->rport);
+		return readstr(offset, p, n, up->genbuf);
+	case Qlocal:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		sprint(up->genbuf, "%I!%d\n", c->laddr, c->lport);
+		return readstr(offset, p, n, up->genbuf);
+	case Qstatus:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		s = smalloc(Statelen);
+		if(waserror()){
+			free(s);
+			nexterror();
+		}
+		snprint(s, Statelen, "%s\n", ipstates[c->state]);
+		n = readstr(offset, p, n, s);
+		poperror();
+		free(s);
+		return n;
+	case Qdata:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		if(c->sfd < 0)
+			error(Ehungup);
+		if(c->headers) {
+			if(n < c->headers)
+				error(Ebadarg);
+			p = a;
+			r = so_recv(c->sfd, p + c->headers, n - c->headers, p, c->headers);
+			if(r >= 0)
+				r += c->headers;
+		} else
+			r = so_recv(c->sfd, a, n, nil, 0);
+		if(r < 0)
+			oserror();
+		return r;
+	case Qstats:
+		error("stats not implemented");
+		return n;
+	}
+}
+
+static void
+setladdr(Conv *c)
+{
+	/* TO DO: this can't be right for hosts with several addresses before connect/accept */
+	so_getsockname(c->sfd, c->laddr, &c->lport);
+}
+
+/*
+ * pick a local port and set it
+ */
+static void
+setlport(Conv *c)
+{
+	uchar laddr[IPaddrlen];
+	ushort p;
+
+	so_bind(c->sfd, c->restricted, c->laddr, c->lport);
+	if(c->lport == 0 || ipcmp(c->laddr, IPnoaddr) == 0){
+		so_getsockname(c->sfd, laddr, &p);
+		if(c->lport == 0)
+			c->lport = p;
+		if(ipcmp(c->laddr, IPnoaddr) == 0)
+			memmove(c->laddr, laddr, sizeof laddr);
+	}
+}
+
+static int
+portno(char *p)
+{
+	long n;
+	char *e;
+
+	n = strtoul(p, &e, 0);
+	if(p == e)
+		error("non-numeric port number");
+	return n;
+}
+
+/*
+ *  set a local address and port from a string of the form
+ *	[address!]port[!r]
+ */
+static void
+setladdrport(Conv *c, char *str, int announcing)
+{
+	char *p;
+	int lport;
+
+	/*
+	 *  ignore restricted part if it exists.  it's
+	 *  meaningless on local ports.
+	 */
+	p = strchr(str, '!');
+	if(p != nil){
+		*p++ = 0;
+		if(strcmp(p, "r") == 0)
+			p = nil;
+	}
+
+	c->lport = 0;
+	if(p == nil){
+		if(announcing)
+			ipmove(c->laddr, IPnoaddr);
+		else if(0)
+			setladdr(c);
+		p = str;
+	} else {
+		if(strcmp(str, "*") == 0)
+			ipmove(c->laddr, IPnoaddr);
+		else if(parseip(c->laddr, str) == 0)
+			error("invalid IP address");
+	}
+
+	if(announcing && strcmp(p, "*") == 0){
+		if(!iseve())
+			error(Eperm);
+		c->lport = 0;
+		setlport(c);
+		return;
+	}
+
+	lport = portno(p);
+	if(lport <= 0)
+		c->lport = 0;
+	else
+		c->lport = lport;
+
+	setlport(c);
+}
+
+static char*
+setraddrport(Conv *c, char *str)
+{
+	char *p;
+
+	p = strchr(str, '!');
+	if(p == nil)
+		return "malformed address";
+	*p++ = 0;
+	if(parseip(c->raddr, str) == 0)
+		return "invalid IP address";
+	c->rport = portno(p);
+	p = strchr(p, '!');
+	if(p){
+		if(strstr(p, "!r") != nil)
+			c->restricted = 1;
+	}
+	return nil;
+}
+
+static void
+connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	char *p;
+
+	USED(x);
+	if(c->state != Idle)
+		error(Econinuse);
+	c->state = Connecting;
+	c->cerr[0] = '\0';
+	switch(cb->nf) {
+	default:
+		error("bad args to connect");
+	case 2:
+		p = setraddrport(c, cb->f[1]);
+		if(p != nil)
+			error(p);
+		break;
+	case 3:
+		p = setraddrport(c, cb->f[1]);
+		if(p != nil)
+			error(p);
+		c->lport = portno(cb->f[2]);
+		setlport(c);
+		break;
+	}
+	qunlock(&c->l);
+	if(waserror()){
+		qlock(&c->l);
+		c->state = Connected;	/* sic */
+		nexterror();
+	}
+	/* p = x->connect(c, cb->f, cb->nf); */
+	so_connect(c->sfd, c->raddr, c->rport);
+	qlock(&c->l);
+	poperror();
+	setladdr(c);
+	c->state = Connected;
+}
+
+static void
+announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	if(c->state != Idle)
+		error(Econinuse);
+	c->state = Announcing;
+	c->cerr[0] = '\0';
+	ipmove(c->raddr, IPnoaddr);
+	c->rport = 0;
+	switch(cb->nf){
+	default:
+		error("bad args to announce");
+	case 2:
+		setladdrport(c, cb->f[1], 1);
+		break;
+	}
+	USED(x);
+	/* p = x->announce(c, cb->f, cb->nf); */
+	if(c->p->stype != S_UDP){
+		qunlock(&c->l);
+		if(waserror()){
+			c->state = Announced;	/* sic */
+			qlock(&c->l);
+			nexterror();
+		}
+		so_listen(c->sfd);
+		qlock(&c->l);
+		poperror();
+	}
+	c->state = Announced;
+}
+
+static void
+bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
+{
+	USED(x);
+	switch(cb->nf){
+	default:
+		error("bad args to bind");
+	case 2:
+		setladdrport(c, cb->f[1], 0);
+		break;
+	}
+}
+
+static long
+ipwrite(Chan *ch, void *a, long n, vlong off)
+{
+	Conv *c;
+	Proto *x;
+	char *p;
+	Cmdbuf *cb;
+	Fs *f;
+
+	f = ipfs[ch->dev];
+
+	switch(TYPE(ch->qid)) {
+	default:
+		error(Eperm);
+	case Qdata:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		if(c->sfd < 0)
+			error(Ehungup);
+		qlock(&c->wlock);
+		if(waserror()){
+			qunlock(&c->wlock);
+			nexterror();
+		}
+		if(c->headers) {
+			if(n < c->headers)
+				error(Eshort);
+			p = a;
+			n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers);
+			if(n >= 0)
+				n += c->headers;
+		} else
+			n = so_send(c->sfd, a, n, nil, 0);
+		poperror();
+		qunlock(&c->wlock);
+		if(n < 0)
+			oserror();
+		break;
+	case Qarp:
+		return arpwrite(a, n);
+	case Qndb:
+		if(off > strlen(f->ndb))
+			error(Eio);
+		if(off+n >= sizeof(f->ndb)-1)
+			error(Eio);
+		memmove(f->ndb+off, a, n);
+		f->ndb[off+n] = 0;
+		f->ndbvers++;
+		f->ndbmtime = seconds();
+		break;
+	case Qctl:
+		x = f->p[PROTO(ch->qid)];
+		c = x->conv[CONV(ch->qid)];
+		cb = parsecmd(a, n);
+		qlock(&c->l);
+		if(waserror()){
+			qunlock(&c->l);
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error("short control request");
+		if(strcmp(cb->f[0], "connect") == 0)
+			connectctlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "announce") == 0)
+			announcectlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "bind") == 0)
+			bindctlmsg(x, c, cb);
+		else if(strcmp(cb->f[0], "ttl") == 0){
+			/* ignored */
+		} else if(strcmp(cb->f[0], "tos") == 0){
+			/* ignored */
+		} else if(strcmp(cb->f[0], "ignoreadvice") == 0){
+			/* ignored */
+		} else if(strcmp(cb->f[0], "headers4") == 0){
+			if(c->p->stype != S_UDP)
+				error(Enoctl);
+			c->headers = OUdphdrlenv4;
+		} else if(strcmp(cb->f[0], "oldheaders") == 0){
+			if(c->p->stype != S_UDP)
+				error(Enoctl);
+			c->headers = OUdphdrlen;
+		} else if(strcmp(cb->f[0], "headers") == 0){
+			if(c->p->stype != S_UDP)
+				error(Enoctl);
+			c->headers = Udphdrlen;
+		} else if(strcmp(cb->f[0], "hangup") == 0){
+			if(c->p->stype != S_TCP)
+				error(Enoctl);
+			qunlock(&c->l);
+			if(waserror()){
+				qlock(&c->l);
+				nexterror();
+			}
+			/* TO DO: check fd status if socket close/hangup interrupted */
+			if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0)
+				oserror();
+			qlock(&c->l);
+			poperror();
+			c->sfd = -1;
+			c->state = Hungup;
+		} else if(strcmp(cb->f[0], "keepalive") == 0){
+			if(c->p->stype != S_TCP)
+				error(Enoctl);
+			if(c->sfd < 0)
+				error("not connected");
+			so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0);
+		} else
+			error(Enoctl);
+		poperror();
+		qunlock(&c->l);
+		free(cb);
+		break;
+	}
+	return n;
+}
+
+static int
+ipwstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	Conv *cv;
+	Proto *p;
+	Fs *f;
+
+	f = ipfs[c->dev];
+	switch(TYPE(c->qid)) {
+	default:
+		error(Eperm);
+		break;
+	case Qctl:
+	case Qdata:
+		break;
+	}
+
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	p = f->p[PROTO(c->qid)];
+	cv = p->conv[CONV(c->qid)];
+	if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
+		error(Eperm);
+	if(!emptystr(d->uid))
+		kstrdup(&cv->owner, d->uid);
+	if(d->mode != ~0UL)
+		cv->perm = d->mode & 0777;
+	poperror();
+	free(d);
+	return n;
+}
+
+static Conv*
+protoclone(Proto *p, char *user, int nfd)
+{
+	Conv *c, **pp, **ep, **np;
+	int maxconv;
+
+	c = 0;
+	qlock(&p->l);
+	if(waserror()) {
+		qunlock(&p->l);
+		nexterror();
+	}
+	ep = &p->conv[p->nc];
+	for(pp = p->conv; pp < ep; pp++) {
+		c = *pp;
+		if(c == 0) {
+			c = newconv(p, pp);
+			break;
+		}
+		if(canqlock(&c->l)){
+			if(c->inuse == 0)
+				break;
+			qunlock(&c->l);
+		}
+	}
+	if(pp >= ep) {
+		if(p->nc >= MAXCONV) {
+			qunlock(&p->l);
+			poperror();
+			return 0;
+		}
+		maxconv = 2 * p->nc;
+		if(maxconv > MAXCONV)
+			maxconv = MAXCONV;
+		np = realloc(p->conv, sizeof(Conv*) * maxconv);
+		if(np == nil)
+			error(Enomem);
+		p->conv = np;
+		pp = &p->conv[p->nc];
+		memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc));
+		p->nc = maxconv;
+		c = newconv(p, pp);
+	}
+
+	c->inuse = 1;
+	kstrdup(&c->owner, user);
+	c->perm = 0660;
+	c->state = Idle;
+	ipmove(c->laddr, IPnoaddr);
+	ipmove(c->raddr, IPnoaddr);
+	c->lport = 0;
+	c->rport = 0;
+	c->restricted = 0;
+	c->headers = 0;
+	c->sfd = nfd;
+	if(nfd == -1)
+		c->sfd = so_socket(p->stype);
+
+	qunlock(&c->l);
+	qunlock(&p->l);
+	poperror();
+	return c;
+}
+
+static Conv*
+newconv(Proto *p, Conv **pp)
+{
+	Conv *c;
+
+	*pp = c = malloc(sizeof(Conv));
+	if(c == 0)
+		error(Enomem);
+	qlock(&c->l);
+	c->inuse = 1;
+	c->p = p;
+	c->x = pp - p->conv;
+	p->ac++;
+	return c;
+}
+
+int
+arpwrite(char *s, int len)
+{
+	int n;
+	char *f[4], buf[256];
+
+	if(len >= sizeof(buf))
+		len = sizeof(buf)-1;
+	memmove(buf, s, len);
+	buf[len] = 0;
+	if(len > 0 && buf[len-1] == '\n')
+		buf[len-1] = 0;
+
+	n = getfields(buf, f, 4, 1, " ");
+	if(strcmp(f[0], "add") == 0) {
+		if(n == 3) {
+			arpadd(f[1], f[2], n);
+			return len;
+		}
+	}
+	error("bad arp request");
+
+	return len;
+}
+
+Dev ipdevtab = {
+	'I',
+	"ip",
+
+	ipinit,
+	ipattach,
+	ipwalk,
+	ipstat,
+	ipopen,
+	devcreate,
+	ipclose,
+	ipread,
+	devbread,
+	ipwrite,
+	devbwrite,
+	devremove,
+	ipwstat
+};
+
+int
+Fsproto(Fs *f, Proto *p)
+{
+	if(f->np >= Maxproto)
+		return -1;
+
+	p->f = f;
+
+	if(p->ipproto > 0){
+		if(f->t2p[p->ipproto] != nil)
+			return -1;
+		f->t2p[p->ipproto] = p;
+	}
+
+	p->qid.type = QTDIR;
+	p->qid.path = QID(f->np, 0, Qprotodir);
+	p->conv = malloc(sizeof(Conv*)*(p->nc+1));
+	if(p->conv == nil)
+		panic("Fsproto");
+
+	p->x = f->np;
+	f->p[f->np++] = p;
+
+	return 0;
+}
+
+/*
+ *  return true if this protocol is
+ *  built in
+ */
+int
+Fsbuiltinproto(Fs* f, uchar proto)
+{
+	return f->t2p[proto] != nil;
+}
--- /dev/null
+++ b/emu/port/devlogfs.c
@@ -1,0 +1,1522 @@
+#ifndef EMU
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#else
+#include	"error.h"
+#endif
+#include	<dat.h>
+#include	<fns.h>
+#include	<kernel.h>
+#include	<logfs.h>
+#include	<nandfs.h>
+
+#ifndef EMU
+#define Sleep sleep
+#define Wakeup wakeup
+#endif
+
+#ifndef offsetof
+#define offsetof(T,X) ((ulong)&(((T*)0)->X))
+#endif
+
+typedef struct Devlogfs Devlogfs;
+typedef struct DevlogfsSession DevlogfsSession;
+
+//#define CALLTRACE
+
+enum {
+	DEVLOGFSDEBUG = 0,
+	DEVLOGFSIODEBUG = 0,
+	DEVLOGFSBAD = 1,
+};
+
+enum {
+	Qdir,
+	Qctl,
+	Qusers,
+	Qdump,
+	Qfs,
+	Qfsboot,
+	Qend,
+};
+
+typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState;
+
+struct Devlogfs {
+	QLock qlock;
+	Ref ref;
+	int instance;
+	int trace;	/* (debugging) trace of read/write actions */
+	int nand;
+	char *name;
+	char *device;
+	char *filename[Qend - Qfs];
+	LogfsLowLevel *ll;
+	Chan *flash, *flashctl;
+	QLock bootqlock;
+	int logfstrace;
+	LogfsBoot *lb;
+	/* stuff for server */
+	ulong openflags;
+	Fcall in;
+	Fcall out;
+	int reading;
+	DevlogfsServerState state;
+	Rendez readrendez;
+	Rendez writerendez;
+	uint readcount;
+	ulong readbufsize;
+	uchar *readbuf;
+	uchar *readp;
+	LogfsServer *server;
+	Devlogfs *next;
+};
+
+#define MAXMSIZE 8192
+
+static struct {
+	RWlock rwlock;		/* rlock when walking, wlock when changing */
+	QLock configqlock;		/* serialises addition of new configurations */
+	Devlogfs *head;
+	char *defname;
+} devlogfslist;
+
+static LogfsIdentityStore *is;
+
+#ifndef EMU
+char Eunknown[] = "unknown user or group id";
+#endif
+
+static	void	devlogfsfree(Devlogfs*);
+
+#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; }
+#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend)
+#define MKPATH(instance, qid) ((instance << 4) | qid)
+
+#define PREFIX "logfs"
+
+static char *devlogfsprefix = PREFIX;
+static char *devlogfsctlname = PREFIX "ctl";
+static char *devlogfsusersname = PREFIX "users";
+static char *devlogfsdumpname = PREFIX "dump";
+static char *devlogfsbootsuffix = "boot";
+static char *devlogfs9pversion = "9P2000";
+
+enum {
+	Toshiba = 0x98,
+	Samsung = 0xec,
+};
+
+static struct {
+	uchar manufacturer;
+	uchar device;
+} nandtab[] = {
+	{ 0,	0xe6 },
+	{ 0,	0xea },
+	{ 0,	0xe3 },
+	{ 0,	0xe5 },
+	{ 0,	0x73 },
+	{ 0,	0x75 },
+	{ 0,	0x76 },
+};
+
+static void
+errorany(char *errmsg)
+{
+	if (errmsg)
+		error(errmsg);
+}
+
+static void *
+emalloc(ulong size)
+{
+	void *p;
+	p = logfsrealloc(nil, size);
+	if (p == nil)
+		error(Enomem);
+	return p;
+}
+
+static char *
+estrdup(char *q)
+{
+	void *p;
+	if (q == nil)
+		return nil;
+	p = logfsrealloc(nil, strlen(q) + 1);
+	if (p == nil)
+		error(Enomem);
+	return strcpy(p, q);
+}
+
+static char *
+estrconcat(char *a, ...)
+{
+	va_list l;
+	char *p, *r;
+	int t;
+
+	t = strlen(a);
+	va_start(l, a);
+	while ((p = va_arg(l, char *)) != nil)
+		t += strlen(p);
+
+	r = logfsrealloc(nil, t + 1);
+	if (r == nil)
+		error(Enomem);
+
+	strcpy(r, a);
+	va_start(l, a);
+	while ((p = va_arg(l, char *)) != nil)
+		strcat(r, p);
+
+	va_end(l);
+
+	return r;
+}
+
+static int
+gen(Chan *c, int i, Dir *dp, int lockit)
+{
+	Devlogfs *l;
+	long size;
+	Qid qid;
+	qid.vers = 0;
+	qid.type = 0;
+
+	if (i + Qctl < Qfs) {
+		switch (i + Qctl) {
+		case Qctl:
+			qid.path = Qctl;
+			devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp);
+			return 1;
+		case Qusers:
+			qid.path = Qusers;
+			devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp);
+			return 1;
+		case Qdump:
+			qid.path = Qdump;
+			devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp);
+			return 1;
+		}
+	}
+
+	i -= Qfs - Qctl;
+
+	if (lockit)
+		rlock(&devlogfslist.rwlock);
+
+	if (waserror()) {
+		if (lockit)
+			runlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+
+	for (l = devlogfslist.head; l; l = l->next) {
+		if (i < Qend - Qfs)
+			break;
+		i -= Qend - Qfs;
+	}
+
+	if (l == nil) {
+		poperror();
+		if (lockit)
+			runlock(&devlogfslist.rwlock);
+		return -1;
+	}
+
+	switch (Qfs + i) {
+	case Qfsboot:
+		size = l->lb ? logfsbootgetsize(l->lb) : 0;
+		break;
+	default:
+		size = 0;
+		break;
+	}
+	/* perhaps the user id should come from the underlying file */
+	qid.path = MKPATH(l->instance, Qfs + i);
+	devdir(c, qid, l->filename[i], size, eve, 0666, dp);
+
+	poperror();
+	if (lockit)
+		runlock(&devlogfslist.rwlock);
+
+	return 1;
+}
+
+static int
+devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	USED(n);
+	USED(tab);
+	USED(ntab);
+	return gen(c, i, dp, 1);
+}
+
+static int
+devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	USED(n);
+	USED(tab);
+	USED(ntab);
+	return gen(c, i, dp, 0);
+}
+
+/* called under lock */
+static Devlogfs *
+devlogfsfind(int instance)
+{
+	Devlogfs *l;
+
+	for (l = devlogfslist.head; l; l = l->next)
+		if (l->instance == instance)
+			break;
+	return l;
+}
+
+static Devlogfs *
+devlogfsget(int instance)
+{
+	Devlogfs *l;
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (l->instance == instance)
+			break;
+	if (l)
+		incref(&l->ref);
+	runlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Devlogfs *
+devlogfsfindbyname(char *name)
+{
+	Devlogfs *l;
+
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, name) == 0)
+			break;
+	runlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Devlogfs *
+devlogfssetdefname(char *name)
+{
+	Devlogfs *l;
+	char *searchname;
+	wlock(&devlogfslist.rwlock);
+	if (waserror()) {
+		wunlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+	if (name == nil)
+		searchname = devlogfslist.defname;
+	else
+		searchname = name;
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, searchname) == 0)
+			break;
+	if (l == nil) {
+		logfsfreemem(devlogfslist.defname);
+		devlogfslist.defname = nil;
+	}
+	else if (name) {
+		if (devlogfslist.defname) {
+			logfsfreemem(devlogfslist.defname);
+			devlogfslist.defname = nil;
+		}
+		devlogfslist.defname = estrdup(name);
+	}
+	poperror();
+	wunlock(&devlogfslist.rwlock);
+	return l;
+}
+
+static Chan *
+devlogfskopen(char *name, char *suffix, int mode)
+{
+	Chan *c;
+	char *fn;
+	int fd;
+
+	fn = estrconcat(name, suffix, 0);
+	fd = kopen(fn, mode);
+	logfsfreemem(fn);
+	if (fd < 0)
+		error(up->env->errstr);
+	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+	kclose(fd);
+	return c;
+}
+
+static char *
+xread(void *a, void *buf, long nbytes, ulong offset)
+{
+	Devlogfs *l = a;
+	long rv;
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes);
+	l->flash->offset = offset;
+	rv = kchanio(l->flash, buf, nbytes, OREAD);
+	if (rv < 0) {
+		print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	if (rv != nbytes) {
+		print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+		return "short read";
+	}
+	return nil;
+}
+
+static char *
+xwrite(void *a, void *buf, long nbytes, ulong offset)
+{
+	Devlogfs *l = a;
+	long rv;
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes);
+	l->flash->offset = offset;
+	rv = kchanio(l->flash, buf, nbytes, OWRITE);
+	if (rv < 0) {
+		print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	if (rv != nbytes) {
+		print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
+		return "short write";
+	}
+	return nil;
+}
+
+static char *
+xerase(void *a, long address)
+{
+	Devlogfs *l = a;
+	char cmd[40];
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: erase(0x%lux)\n", l->device, address);
+	snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address);
+	if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) {
+		print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr);
+		return up->env->errstr;
+	}
+	return nil;
+}
+
+static char *
+xsync(void *a)
+{
+	Devlogfs *l = a;
+	uchar statbuf[STATFIXLEN];
+
+	if (DEVLOGFSIODEBUG || l->trace)
+		print("devlogfs: %s: sync()\n", l->device);
+	memset(statbuf, 0xff, sizeof(statbuf));
+	memset(statbuf + STATFIXLEN - 8, 0x00, 8);
+	PBIT16(statbuf, sizeof(statbuf) - BIT16SZ);
+	if (kwstat(l->device, statbuf, sizeof(statbuf)) < 0)
+		return up->env->errstr;
+	return nil;
+}
+
+//#define LEAKHUNT
+#ifdef LEAKHUNT
+#define MAXLIVE 2000
+typedef struct Live {
+	void *p;
+	int freed;
+	ulong callerpc;
+} Live;
+
+static Live livemem[MAXLIVE];
+
+static void
+leakalloc(void *p, ulong callerpc)
+{
+	int x;
+	int use = -1;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p == p) {
+			if (!livemem[x].freed)
+				print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc);
+//			else
+//				print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+			livemem[x].freed = 0;
+			livemem[x].callerpc = callerpc;
+			return;
+		}
+		else if (use < 0 && livemem[x].p == 0)
+			use = x;
+	}
+	if (use < 0)
+		panic("leakalloc: too many live entries");
+	livemem[use].p = p;
+	livemem[use].freed = 0;
+	livemem[use].callerpc = callerpc;
+}
+
+static void
+leakaudit(void)
+{
+	int x;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p && !livemem[x].freed)
+			print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc);
+	}
+}
+
+static void
+leakfree(void *p, ulong callerpc)
+{
+	int x;
+	if (p == nil)
+		return;
+	for (x = 0; x < MAXLIVE; x++) {
+		if (livemem[x].p == p) {
+			if (livemem[x].freed)
+				print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n",
+					p, callerpc, livemem[x].callerpc);
+			livemem[x].freed = 1;
+			livemem[x].callerpc = callerpc;
+			return;
+		}
+	}
+	print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc);
+	leakaudit();
+}
+
+static void
+leakrealloc(void *newp, void *oldp, ulong callerpc)
+{
+	leakfree(oldp, callerpc);
+	leakalloc(newp, callerpc);
+}
+#endif
+
+
+#ifdef LEAKHUNT
+static void *_realloc(void *p, ulong size, ulong callerpc)
+#else
+void *
+logfsrealloc(void *p, ulong size)
+#endif
+{
+	void *q;
+	ulong osize;
+	if (waserror()) {
+		print("wobbly thrown in memory allocator: %s\n", up->env->errstr);
+		nexterror();
+	}
+	if (p == nil) {
+		q = smalloc(size);
+		poperror();
+#ifdef LEAKHUNT
+		leakrealloc(q, nil, callerpc);
+#endif
+		return q;
+	}
+	q = realloc(p, size);
+	if (q) {
+		poperror();
+#ifdef LEAKHUNT
+		leakrealloc(q, p, callerpc);
+#endif
+		return q;
+	}
+	q = smalloc(size);
+	osize = msize(p);
+	if (osize > size)
+		osize = size;
+	memmove(q, p, osize);
+	free(p);
+	poperror();
+#ifdef LEAKHUNT
+	leakrealloc(q, p, callerpc);
+#endif
+	return q;
+}
+
+#ifdef LEAKHUNT
+void *
+logfsrealloc(void *p, ulong size)
+{
+	return _realloc(p, size, getcallerpc(&p));
+}
+
+void *
+nandfsrealloc(void *p, ulong size)
+{
+	return _realloc(p, size, getcallerpc(&p));
+}
+#else
+void *
+nandfsrealloc(void *p, ulong size)
+{
+	return logfsrealloc(p, size);
+}
+#endif
+
+void
+logfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+	leakfree(p, getcallerpc(&p));
+#endif
+	free(p);
+}
+
+void
+nandfsfreemem(void *p)
+{
+#ifdef LEAKHUNT
+	leakfree(p, getcallerpc(&p));
+#endif
+	free(p);
+}
+
+static Devlogfs *
+devlogfsconfig(char *name, char *device)
+{
+	Devlogfs *newl, *l;
+	int i;
+	int n;
+	char buf[100], *fields[8];
+	long rawblocksize, rawsize;
+
+	newl = nil;
+
+	qlock(&devlogfslist.configqlock);
+
+	if (waserror()) {
+		qunlock(&devlogfslist.configqlock);
+		devlogfsfree(newl);
+		nexterror();
+	}
+
+	rlock(&devlogfslist.rwlock);
+	for (l = devlogfslist.head; l; l = l->next)
+		if (strcmp(l->name, name) == 0) {
+			runlock(&devlogfslist.rwlock);
+			error(Einuse);
+		}
+
+	/* horrid n^2 solution to finding a unique instance number */
+
+	for (i = 0;; i++) {
+		for (l = devlogfslist.head; l; l = l->next)
+			if (l->instance == i)
+				break;
+		if (l == nil)
+			break;
+	}
+	runlock(&devlogfslist.rwlock);
+
+	newl = emalloc(sizeof(Devlogfs));
+	newl->instance = i;
+	newl->name = estrdup(name);
+	newl->device = estrdup(device);
+	newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil);
+	newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil);
+	newl->flash = devlogfskopen(device, nil, ORDWR);
+	newl->flashctl = devlogfskopen(device, "ctl", ORDWR);
+	newl->flashctl->offset = 0;
+	if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) {
+		print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr);
+		error(up->env->errstr);
+	}
+
+	if (n >= sizeof(buf))
+		n = sizeof(buf) - 1;
+	buf[n] = 0;
+	n = getfields(buf, fields, nelem(fields), 1, " \t\n");
+	newl->nand = 0;
+	if (n >= 2) {
+		/* detect NAND devices, and learn parameters from there */
+		ulong manufacturer = strtoul(fields[0], nil, 16);
+		ulong device = strtoul(fields[1], nil, 16);
+		int d;
+
+		for (d = 0; d < sizeof(nandtab) / sizeof(nandtab[0]); d++) {
+			if ((nandtab[d].manufacturer == manufacturer
+				&& nandtab[d].device == device)
+				|| (nandtab[d].manufacturer == 0
+					&& (manufacturer == Toshiba || manufacturer == Samsung)
+					&& nandtab[d].device == device))
+			{
+				if (DEVLOGFSDEBUG)
+					print("devlogfsconfig: nand device detected\n");
+				newl->nand = 1;
+				break;
+			}
+		}
+	}
+	if (n < 4)
+		error("unknown erase size");
+	rawblocksize = strtol(fields[5], nil, 0);
+	rawsize = strtol(fields[4], nil, 0)-strtol(fields[3], nil, 0);
+	if (newl->nand == 0)
+		error("only NAND supported at the moment");
+	errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll));
+	wlock(&devlogfslist.rwlock);
+	newl->next = devlogfslist.head;
+	devlogfslist.head = newl;
+	logfsfreemem(devlogfslist.defname);
+	devlogfslist.defname = nil;
+	if (waserror()) {
+	}
+	else {
+		devlogfslist.defname = estrdup(name);
+		poperror();
+	}
+	wunlock(&devlogfslist.rwlock);
+	poperror();
+	qunlock(&devlogfslist.configqlock);
+	return newl;
+}
+
+void
+devlogfsunconfig(Devlogfs *devlogfs)
+{
+	Devlogfs **lp;
+
+	qlock(&devlogfslist.configqlock);
+
+	if (waserror()) {
+		qunlock(&devlogfslist.configqlock);
+		nexterror();
+	}
+
+	wlock(&devlogfslist.rwlock);
+
+	if (waserror()) {
+		wunlock(&devlogfslist.rwlock);
+		nexterror();
+	}
+
+	for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next)
+		;
+	if (*lp == nil) {
+		if (DEVLOGFSBAD)
+			print("devlogfsunconfig: not in list\n");
+	}
+	else
+		*lp = devlogfs->next;
+
+	poperror();
+	wunlock(&devlogfslist.rwlock);
+
+	/* now invisible to the naked eye */
+	devlogfsfree(devlogfs);
+	poperror();
+	qunlock(&devlogfslist.configqlock);
+}
+
+static void
+devlogfsllopen(Devlogfs *l)
+{
+	qlock(&l->qlock);
+	if (waserror()) {
+		qunlock(&l->qlock);
+		nexterror();
+	}
+	if (l->lb == nil)
+		errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb));
+	l->state = BootOpen;
+	poperror();
+	qunlock(&l->qlock);
+}
+
+static void
+devlogfsllformat(Devlogfs *l, long bootsize)
+{
+	qlock(&l->qlock);
+	if (waserror()) {
+		qunlock(&l->qlock);
+		nexterror();
+	}
+	if (l->lb == nil)
+		errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace));
+	poperror();
+	qunlock(&l->qlock);
+}
+
+static Chan *
+devlogfsattach(char *spec)
+{
+	Chan *c;
+#ifdef CALLTRACE
+	print("devlogfsattach(spec = %s) - start\n", spec);
+#endif
+	/* create the identity store on first attach */
+	if (is == nil)
+		errorany(logfsisnew(&is));
+	c =  devattach(0x29f, spec);
+//	c =  devattach(L'ʟ', spec);
+#ifdef CALLTRACE
+	print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c);
+#endif
+	return c;
+}
+
+static Walkqid*
+devlogfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	int instance, qid, qt, clone;
+	Walkqid *wq;
+
+#ifdef CALLTRACE
+	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n",
+		(ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+	clone = 0;
+	if(nc == nil){
+		nc = devclone(c);
+		nc->type = 0;
+		SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+		if(DATAQID(qid, qt))
+			nc->aux = devlogfsget(instance);
+		clone = 1;
+	}
+	wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen);
+	if (wq == nil || wq->nqid < nname) {
+		if(clone)
+			cclose(nc);
+	}
+	else if (clone) {
+		wq->clone = nc;
+		nc->type = c->type;
+	}
+#ifdef CALLTRACE
+	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n",
+		(ulong)c, (ulong)nc, (ulong)name, nname);
+#endif
+	return wq;
+}
+
+static int
+devlogfsstat(Chan *c, uchar *dp, int n)
+{
+#ifdef CALLTRACE
+	print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n",
+		(ulong)c, (ulong)dp, n);
+#endif
+	return devstat(c, dp, n, 0, 0, devlogfsgen);
+}
+
+static Chan*
+devlogfsopen(Chan *c, int omode)
+{
+	int instance, qid, qt;
+
+	omode = openmode(omode);
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+	print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n",
+		(ulong)c, omode, instance, qid, qt);
+#endif
+
+
+	rlock(&devlogfslist.rwlock);
+	if (waserror()) {
+		runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+		print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr);
+#endif
+		nexterror();
+	}
+
+	if (DATAQID(qid, qt)) {
+		Devlogfs *d;
+		d = devlogfsfind(instance);
+		if (d == nil)
+			error(Enodev);
+		if (strcmp(up->env->user, eve) != 0)
+			error(Eperm);
+		if (qid == Qfs && d->state != BootOpen)
+			error(Eperm);
+		if (d->server == nil) {
+			errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server));
+			d->state = NeedVersion;
+		}
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+		incref(&d->ref);
+		c->aux = d;
+	}
+	else if (qid == Qctl || qid == Qusers) {
+		if (strcmp(up->env->user, eve) != 0)
+			error(Eperm);
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+	}
+	else
+		c = devopen(c, omode, 0, 0, devlogfsgennolock);
+	poperror();
+	runlock(&devlogfslist.rwlock);
+#ifdef CALLTRACE
+	print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode);
+#endif
+	return c;
+}
+
+static void
+devlogfsclose(Chan *c)
+{
+	int instance, qid, qt;
+#ifdef CALLTRACE
+	print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c);
+#endif
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+	USED(instance);
+	if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) {
+		Devlogfs *d;
+		d = c->aux;
+		qlock(&d->qlock);
+		if (qid == Qfs && d->state == Attached) {
+			logfsserverflush(d->server);
+			logfsserverfree(&d->server);
+			d->state = BootOpen;
+		}
+		qunlock(&d->qlock);
+		decref(&d->ref);
+	}
+#ifdef CALLTRACE
+	print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c);
+#endif
+}
+
+typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write);
+
+void
+smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write)
+{
+	void *tmp = nil;
+	ulong blocks, toread;
+
+	if (waserror()) {
+		logfsfreemem(tmp);
+		nexterror();
+	}
+	if (offset % blocksize) {
+		ulong aoffset;
+		int tmpoffset;
+		int tocopy;
+
+		if (tmp == nil)
+			tmp = emalloc(blocksize);
+		aoffset = offset / blocksize;
+		aoffset *= blocksize;
+		errorany((*io)(magic, tmp, blocksize, aoffset, 0));
+		tmpoffset = offset - aoffset;
+		tocopy = blocksize - tmpoffset;
+		if (tocopy > n)
+			tocopy = n;
+		if (write) {
+			memmove((uchar *)tmp + tmpoffset, buf, tocopy);
+			errorany((*io)(magic, tmp, blocksize, aoffset, 1));
+		}
+		else
+			memmove(buf, (uchar *)tmp + tmpoffset, tocopy);
+		buf = (uchar *)buf + tocopy;
+		n -= tocopy;
+		offset = aoffset + blocksize;
+	}
+	blocks = n / blocksize;
+	toread = blocks * blocksize;
+	errorany((*io)(magic, buf, toread, offset, write));
+	buf = (uchar *)buf + toread;
+	n -= toread;
+	offset += toread;
+	if (n) {
+		if (tmp == nil)
+			tmp = emalloc(blocksize);
+		errorany((*io)(magic, tmp, blocksize, offset, 0));
+		if (write) {
+			memmove(tmp, buf, n);
+			errorany((*io)(magic, tmp, blocksize, offset, 1));
+		}
+		memmove(buf, tmp, n);
+	}
+	poperror();
+	logfsfreemem(tmp);
+}
+
+static int
+readok(void *a)
+{
+	Devlogfs *d = a;
+	return d->reading;
+}
+
+static int
+writeok(void *a)
+{
+	Devlogfs *d = a;
+	return !d->reading;
+}
+
+long
+devlogfsserverread(Devlogfs *d, void *buf, long n)
+{
+	if (d->state == Hungup)
+		error(Ehungup);
+	Sleep(&d->readrendez, readok, d);
+	if (n > d->readcount)
+		n = d->readcount;
+	memmove(buf, d->readp, n);
+	d->readp += n;
+	d->readcount -= n;
+	if (d->readcount == 0) {
+		d->reading = 0;
+		Wakeup(&d->writerendez);
+	}
+	return n;
+}
+
+static void
+reply(Devlogfs *d)
+{
+	d->readp = d->readbuf;
+	d->readcount = convS2M(&d->out, d->readp, d->readbufsize);
+//print("reply is %d bytes\n", d->readcount);
+	if (d->readcount == 0)
+		panic("logfs: reply: did not fit\n");
+	d->reading = 1;
+	Wakeup(&d->readrendez);
+}
+
+static void
+rerror(Devlogfs *d, char *ename)
+{
+	d->out.type = Rerror;
+	d->out.ename = ename;
+	reply(d);
+}
+
+static struct {
+	QLock qlock;
+	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen);
+	void *magic;
+	Devlogfs *d;
+	int line;
+} dump;
+
+static void *
+extentdumpinit(Devlogfs *d, int argc, char **argv)
+{
+	int *p;
+	ulong path;
+	ulong flashaddr, length;
+	long block;
+	int page, offset;
+	if (argc != 1)
+		error(Ebadarg);
+	path = strtoul(argv[0], 0, 0);
+	errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset));
+	p = emalloc(sizeof(ulong));
+	*p = path;
+	return p;
+}
+
+static int
+extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen)
+{
+	ulong *p = magic;
+	ulong flashaddr, length;
+	long block;
+	int page, offset;
+	USED(d);
+	errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset));
+	if (length == 0)
+		return 0;
+	return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset);
+}
+
+void
+devlogfsdumpinit(Devlogfs *d,
+	void *(*init)(Devlogfs *d, int argc, char **argv),
+	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv)
+{
+	qlock(&dump.qlock);
+	if (waserror()) {
+		qunlock(&dump.qlock);
+		nexterror();
+	}
+	if (d) {
+		if (d->state < NeedVersion)
+			error("not mounted");
+		qlock(&d->qlock);
+		if (waserror()) {
+			qunlock(&d->qlock);
+			nexterror();
+		}
+	}
+	if (dump.magic) {
+		logfsfreemem(dump.magic);
+		dump.magic = nil;
+	}
+	dump.d = d;
+	dump.magic = (*init)(d, argc, argv);
+	dump.read = read;
+	dump.line = 0;
+	if (d) {
+		poperror();
+		qunlock(&d->qlock);
+	}
+	poperror();
+	qunlock(&dump.qlock);
+}
+
+long
+devlogfsdumpread(char *buf, int buflen)
+{
+	char *tmp = nil;
+	long n;
+	qlock(&dump.qlock);
+	if (waserror()) {
+		logfsfreemem(tmp);
+		qunlock(&dump.qlock);
+		nexterror();
+	}
+	if (dump.magic == nil)
+		error(Eio);
+	tmp = emalloc(READSTR);
+	if (dump.d) {
+		if (dump.d->state < NeedVersion)
+			error("not mounted");
+		qlock(&dump.d->qlock);
+		if (waserror()) {
+			qunlock(&dump.d->qlock);
+			nexterror();
+		}
+	}
+	n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR);
+	if (n) {
+		dump.line++;
+		n = readstr(0, buf, buflen, tmp);
+	}
+	if (dump.d) {
+		poperror();
+		qunlock(&dump.d->qlock);
+	}
+	logfsfreemem(tmp);
+	poperror();
+	qunlock(&dump.qlock);
+	return n;
+}
+
+void
+devlogfsserverlogsweep(Devlogfs *d, int justone)
+{
+	int didsomething;
+	if (d->state < NeedVersion)
+		error("not mounted");
+	qlock(&d->qlock);
+	if (waserror()) {
+		qunlock(&d->qlock);
+		nexterror();
+	}
+	errorany(logfsserverlogsweep(d->server, justone, &didsomething));
+	poperror();
+	qunlock(&d->qlock);
+}
+
+void
+devlogfsserverwrite(Devlogfs *d, void *buf, long n)
+{
+	int locked = 0;
+	if (d->state == Hungup)
+		error(Ehungup);
+	Sleep(&d->writerendez, writeok, d);
+	if (convM2S(buf, n, &d->in) != n) {
+		/*
+		 * someone is writing drivel; have nothing to do with them anymore
+		 * most common cause; trying to mount authenticated
+		 */
+		d->state = Hungup;
+		error(Ehungup);
+	}
+	d->out.tag = d->in.tag;
+	d->out.fid = d->in.fid;
+	d->out.type = d->in.type + 1;
+	if (waserror()) {
+		if (locked)
+			qunlock(&d->qlock);
+		rerror(d, up->env->errstr);
+		return;
+	}
+	if (d->in.type != Tversion && d->in.type != Tattach) {
+		if (d->state != Attached)
+			error("must be attached");
+		qlock(&d->qlock);
+		locked = 1;
+	}
+	switch (d->in.type) {
+	case Tauth:
+		error("no authentication needed");
+	case Tversion: {
+		char *rversion;
+		if (d->state != NeedVersion)
+			error("unexpected Tversion");
+		 if (d->in.tag != NOTAG)
+			error("protocol botch");
+		/*
+		 * check the version string
+		 */
+		if (strcmp(d->in.version, devlogfs9pversion) != 0)
+			rversion = "unknown";
+		else
+			rversion = devlogfs9pversion;
+		/*
+		 * allocate the reply buffer
+		 */
+		d->readbufsize = d->in.msize;
+		if (d->readbufsize > MAXMSIZE)
+			d->readbufsize = MAXMSIZE;
+		d->readbuf = emalloc(d->readbufsize);
+		/*
+		 * compose the Rversion
+		 */
+		d->out.msize = d->readbufsize;
+		d->out.version = rversion;
+		d->state = NeedAttach;
+		break;
+	}
+	case Tattach:
+		if (d->state != NeedAttach)
+			error("unexpected attach");
+		if (d->in.afid != NOFID)
+			error("unexpected afid");
+		errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid));
+		d->state = Attached;
+		break;
+	case Tclunk:
+		errorany(logfsserverclunk(d->server, d->in.fid));
+		break;
+	case Tcreate:
+		errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid));
+		d->out.iounit = d->readbufsize - 11;
+		break;
+	case Tflush:
+		break;
+	case Topen:
+		errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid));
+		d->out.iounit = d->readbufsize - 11;
+		break;
+	case Tread:
+		d->out.data = (char *)d->readbuf + 11;
+		/* TODO - avoid memmove */
+		errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data,
+			d->readbufsize - 11, &d->out.count));
+		break;
+	case Tremove:
+		errorany(logfsserverremove(d->server, d->in.fid));
+		break;
+	case Tstat:
+		d->out.stat = d->readbuf + 9;
+		/* TODO - avoid memmove */
+		errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat));
+//		print("nstat %d\n", d->out.nstat);
+		break;
+	case Twalk:
+		errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid,
+			d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid));
+		break;
+	case Twrite:
+		errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data,
+			&d->out.count));
+		break;
+	case Twstat:
+		errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat));
+		break;
+	default:
+		print("devlogfsserverwrite: msg %d unimplemented\n", d->in.type);
+		error("unimplemented");
+	}
+	poperror();
+	if (locked)
+		qunlock(&d->qlock);
+	reply(d);
+}
+
+static long
+devlogfsread(Chan *c, void *buf, long n, vlong off)
+{
+	int instance, qid, qt;
+
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+	USED(instance);
+#ifdef CALLTRACE
+	print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+		(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+	if(qt & QTDIR) {
+#ifdef CALLTRACE
+		print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n",
+			(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+		return devdirread(c, buf, n, 0, 0, devlogfsgen);
+	}
+
+	if(DATAQID(qid, qt)) {
+		if (qid == Qfsboot) {
+			Devlogfs *l = c->aux;
+			qlock(&l->bootqlock);
+			if (waserror()) {
+				qunlock(&l->bootqlock);
+				nexterror();
+			}
+			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0);
+			poperror();
+			qunlock(&l->bootqlock);
+			return n;
+		}
+		else if (qid == Qfs) {
+			Devlogfs *d = c->aux;
+			return devlogfsserverread(d, buf, n);
+		}
+		error(Eio);
+	}
+
+	if (qid == Qusers) {
+		long nr;
+		errorany(logfsisusersread(is, buf, n, (ulong)off, &nr));
+		return nr;
+	}
+	else if (qid == Qdump)
+		return devlogfsdumpread(buf, n);
+
+	if (qid != Qctl)
+		error(Egreg);
+
+	return 0;
+}
+
+static long
+devlogfswrite(Chan *c, void *buf, long n, vlong off)
+{
+	char cmd[64], *realfields[6];
+	int i;
+	int instance, qid, qt;
+
+	if(n <= 0)
+		return 0;
+	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
+#ifdef CALLTRACE
+	print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
+		(ulong)c, (ulong)buf, n, instance, qid, qt);
+#endif
+	USED(instance);
+	if(DATAQID(qid, qt)){
+		if (qid == Qfsboot) {
+			Devlogfs *l = c->aux;
+			qlock(&l->bootqlock);
+			if (waserror()) {
+				qunlock(&l->bootqlock);
+				nexterror();
+			}
+			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1);
+			poperror();
+			qunlock(&l->bootqlock);
+			return n;
+		}
+		else if (qid == Qfs) {
+			Devlogfs *d = c->aux;
+			devlogfsserverwrite(d, buf, n);
+			return n;
+		}
+		error(Eio);
+	}
+	else if (qid == Qctl) {
+		Devlogfs *l = nil;
+		char **fields;
+
+		if(n > sizeof(cmd)-1)
+			n = sizeof(cmd)-1;
+		memmove(cmd, buf, n);
+		cmd[n] = 0;
+		i = getfields(cmd, realfields, 6, 1, " \t\n");
+//print("i = %d\n", i);
+		if (i <= 0)
+			error(Ebadarg);
+		fields = realfields;
+		if (i == 3 && strcmp(fields[0], "uname") == 0) {
+			switch (fields[2][0]) {
+			default:
+				errorany(logfsisgroupcreate(is, fields[1], fields[2]));
+				break;
+			case ':':
+				errorany(logfsisgroupcreate(is, fields[1], fields[2] + 1));
+				break;
+			case '%':
+				errorany(logfsisgrouprename(is, fields[1], fields[2] + 1));
+				break;
+			case '=':
+				errorany(logfsisgroupsetleader(is, fields[1], fields[2] + 1));
+				break;
+			case '+':
+				errorany(logfsisgroupaddmember(is, fields[1], fields[2] + 1));
+				break;
+			case '-':
+				errorany(logfsisgroupremovemember(is, fields[1], fields[2] + 1));
+				break;
+			}
+			i = 0;
+		}
+		if (i == 4 && strcmp(fields[0], "fsys") == 0 && strcmp(fields[2], "config") == 0) {
+			l = devlogfsconfig(fields[1], fields[3]);
+			i = 0;
+		}
+		else if (i >= 2 && strcmp(fields[0], "fsys") == 0) {
+			l = devlogfssetdefname(fields[1]);
+			if (l == nil)
+				error(Ebadarg);
+			i -= 2;
+			fields += 2;
+		}
+		if (i != 0) {
+			if (l == nil)
+				l = devlogfssetdefname(nil);
+			if (i >= 1 && strcmp(fields[0], "open") == 0) {
+				int a;
+				if (l == nil)
+					error(Ebadarg);
+				for (a = 1; a < i; a++)
+					if (fields[a][0] == '-')
+						switch (fields[a][1]) {
+						case 'P':
+							l->openflags |= LogfsOpenFlagNoPerm;
+							break;
+						case 'W':
+							l->openflags |= LogfsOpenFlagWstatAllow;
+							break;
+						default:
+							error(Ebadarg);
+						}
+				devlogfsllopen(l);
+				i = 0;
+			}
+			else if (i == 2 && strcmp(fields[0], "format") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				devlogfsllformat(l, strtol(fields[1], nil, 0));
+				i = 0;
+			}
+			else if (i >= 1 && strcmp(fields[0], "sweep") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				devlogfsserverlogsweep(l, 0);
+				i = 0;
+			}
+			else if (i >= 1 && strcmp(fields[0], "sweepone") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				devlogfsserverlogsweep(l, 1);
+				i = 0;
+			}
+			else if (i <= 2&& strcmp(fields[0], "trace") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				l->logfstrace = i > 1 ? strtol(fields[1], nil, 0) : 0;
+				if (l->server)
+					logfsservertrace(l->server, l->logfstrace);
+				if (l->lb)
+					logfsboottrace(l->lb, l->logfstrace);
+				i = 0;
+			}
+			else if (i == 1 && strcmp(fields[0], "unconfig") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				if (l->ref.ref > 0)
+					error(Einuse);
+				devlogfsunconfig(l);
+				i = 0;
+			}
+			else if (i == 2 && strcmp(fields[0], "extent") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, fields + 1);
+				i = 0;
+			}
+			else if (i >= 2 && strcmp(fields[0], "test") == 0) {
+				if (l == nil)
+					error(Ebadarg);
+				errorany(logfsservertestcmd(l->server, i - 1, fields + 1));
+				i = 0;
+			}
+#ifdef LEAKHUNT
+			else if (i == 1 && strcmp(fields[0], "leakaudit") == 0) {
+				leakaudit();
+				i = 0;
+			}
+#endif
+		}
+		if (i != 0)
+			error(Ebadarg);
+		return n;
+	}
+	error(Egreg);
+	return 0;		/* not reached */
+}
+
+static void
+devlogfsfree(Devlogfs *devlogfs)
+{
+	if (devlogfs != nil) {
+		int i;
+		logfsfreemem(devlogfs->device);
+		logfsfreemem(devlogfs->name);
+		for (i = 0; i < Qend - Qfs; i++)
+			logfsfreemem(devlogfs->filename[i]);
+		cclose(devlogfs->flash);
+		cclose(devlogfs->flashctl);
+		qlock(&devlogfs->qlock);
+		logfsserverfree(&devlogfs->server);
+		logfsbootfree(devlogfs->lb);
+		if (devlogfs->ll)
+			(*devlogfs->ll->free)(devlogfs->ll);
+		logfsfreemem(devlogfs->readbuf);
+		qunlock(&devlogfs->qlock);
+		logfsfreemem(devlogfs);
+	}
+}
+
+#ifdef EMU
+ulong
+logfsnow(void)
+{
+	extern vlong timeoffset;
+	return (timeoffset + osusectime()) / 1000000;
+}
+#endif
+
+Dev logfsdevtab = {
+	0x29f,
+//	L'ʟ',
+	"logfs",
+
+#ifndef EMU
+	devreset,
+#endif
+	devinit,
+#ifndef EMU
+	devshutdown,
+#endif
+	devlogfsattach,
+	devlogfswalk,
+	devlogfsstat,
+	devlogfsopen,
+	devcreate,
+	devlogfsclose,
+	devlogfsread,
+	devbread,
+	devlogfswrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devmem.c
@@ -1,0 +1,484 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+
+enum
+{
+	Qdir,
+	Qctl,
+	Qstate,
+	Qsum,
+	Qevent,
+	Qprof,
+	Qheap,
+	Qgc
+};
+
+static
+Dirtab memdir[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"memctl",		{Qctl},	0,			0666,
+	"memstate",	{Qstate},	0,			0444,
+	"memsum",	{Qsum},	0,			0444,
+	"memevent",	{Qevent},	0,			0444,
+	"memprof",	{Qprof},	0,			0444,
+	"memheap",	{Qheap},	0,			0444,
+	"memgc",		{Qgc},	0,			0444,
+};
+
+enum
+{
+	/* these are the top two bits of size */
+	Pflags=	3<<30,
+	  Pfree=	0<<30,
+	  Palloc=	1<<30,
+	  Paend=	2<<30,
+	  Pimmutable=	3<<30,
+
+	Npool = 3,
+	Nevent = 10000,
+	Nstate = 12000
+};
+
+/*
+ * pool snapshots
+ */
+typedef struct Pstate Pstate;
+struct Pstate
+{
+	ulong	base;
+	ulong	size;
+};
+
+static struct
+{
+	Pstate	state[3+Nstate];
+	Pstate*	lim;
+	Pstate*	ptr;
+	int	summary;
+} poolstate[Npool];
+static Ref	stateopen;
+
+/*
+ * pool/heap allocation events
+ */
+typedef struct Pevent Pevent;
+struct Pevent
+{
+	int	pool;
+	ulong	pc;
+	ulong	base;
+	ulong	size;
+};
+
+static struct
+{
+	Lock	l;
+	Ref	inuse;
+	Rendez	r;
+	int	open;
+	Pevent	events[Nevent];
+	int	rd;
+	int	wr;
+	int	full;
+	int	want;
+	ulong	lost;
+} poolevents;
+
+/*
+ * allocation profiles
+ */
+typedef struct Pprof Pprof;
+typedef struct Pbucket Pbucket;
+
+struct Pbucket
+{
+	ulong	val;
+	ulong	pool;
+	ulong	count;
+	ulong	size;
+	Pbucket*	next;
+};
+
+static struct {
+	Ref	inuse;	/* only one of these things */
+	Lock	l;
+	Pbucket	buckets[1000];
+	Pbucket	snap[1000];
+	int	used;
+	int	snapped;
+	Pbucket*	hash[128];
+	ulong	lost;
+} memprof;
+
+extern	void	(*memmonitor)(int, ulong, ulong, ulong);
+extern	ulong	gcnruns;
+extern	ulong	gcsweeps;
+extern	ulong	gcbroken;
+extern	ulong	gchalted;
+extern	ulong	gcepochs;
+extern	uvlong	gcdestroys;
+extern	uvlong	gcinspects;
+extern	uvlong	gcbusy;
+extern	uvlong	gcidle;
+extern	uvlong	gcidlepass;
+extern	uvlong	gcpartial;
+
+
+static void
+mprofreset(void)
+{
+	lock(&memprof.l);	/* need ilock in kernel */
+	memset(memprof.hash, 0, sizeof(memprof.hash));
+	memprof.used = 0;
+	memprof.lost = 0;
+	unlock(&memprof.l);
+}
+
+static void
+mprofmonitor(int pool, ulong pc, ulong base, ulong size)
+{
+	Pbucket **h0, **h, *p;
+
+	if((pool&7) == 1)
+		return;	/* ignore heap */
+	USED(base);
+	h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)];
+	lock(&memprof.l);
+	for(h = h0; (p = *h) != nil; h = &p->next)
+		if(p->val == pc && p->pool == pool){
+			p->count++;
+			p->size += size;
+			*h = p->next;
+			p->next = *h0;
+			*h0 = p;
+			unlock(&memprof.l);
+			return;
+		}
+	if(memprof.used >= nelem(memprof.buckets)){
+		memprof.lost++;
+		unlock(&memprof.l);
+		return;
+	}
+	p = &memprof.buckets[memprof.used++];
+	p->val = pc;
+	p->pool = pool;
+	p->count = 1;
+	p->size = size;
+	p->next = *h0;
+	*h0 = p;
+	unlock(&memprof.l);
+}
+
+static void
+_memmonitor(int pool, ulong pc, ulong base, ulong size)
+{
+	Pevent e;
+
+	e.pool = pool;
+	e.pc = pc;
+	e.base = base;
+	e.size = size;
+	lock(&poolevents.l);
+	if(!poolevents.full){
+		poolevents.events[poolevents.wr] = e;
+		if(++poolevents.wr == nelem(poolevents.events))
+			poolevents.wr = 0;
+		if(poolevents.wr == poolevents.rd)
+			poolevents.full = 1;
+	}else
+		poolevents.lost++;
+	if(poolevents.want){
+		poolevents.want = 0;
+		Wakeup(&poolevents.r);
+	}
+	unlock(&poolevents.l);
+}
+
+static int
+ismemdata(void *v)
+{
+	USED(v);
+	return poolevents.full || poolevents.rd != poolevents.wr;
+}
+
+static char*
+memaudit(int pno, Bhdr *b)
+{
+	Pstate *p;
+
+	if(pno >= Npool)
+		return "too many pools for memaudit";
+	if((p = poolstate[pno].ptr) == poolstate[pno].lim){
+		if(b->magic == MAGIC_E)
+			return nil;
+		p = &poolstate[pno].state[1];
+		if(b->magic == MAGIC_F)
+			p++;
+		p->base++;
+		p->size += b->size;
+		return nil;
+	}
+	poolstate[pno].ptr++;
+	p->base = (ulong)b;
+	p->size = b->size;
+	switch(b->magic){
+	case MAGIC_A:
+		p->size |= Palloc;
+		break;
+	case MAGIC_F:
+		p->size |= Pfree;
+		break;
+	case MAGIC_E:
+		p->size = b->csize | Paend;
+		break;
+	case MAGIC_I:
+		p->size |= Pimmutable;
+		break;
+	default:
+		return "bad magic number in block";
+	}
+	return nil;
+}
+
+static void
+mput4(uchar *m, ulong v)
+{
+	m[0] = v>>24;
+	m[1] = v>>16;
+	m[2] = v>>8;
+	m[3] = v;
+}
+
+static Chan*
+memattach(char *spec)
+{
+	return devattach('%', spec);
+}
+
+static Walkqid*
+memwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen);
+}
+
+static int
+memstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, memdir, nelem(memdir), devgen);
+}
+
+static Chan*
+memopen(Chan *c, int omode)
+{
+	if(memmonitor != nil && c->qid.path != Qgc)
+		error(Einuse);
+	c = devopen(c, omode, memdir, nelem(memdir), devgen);
+	switch((ulong)c->qid.path){
+	case Qevent:
+		if(incref(&poolevents.inuse) != 1){
+			decref(&poolevents.inuse);
+			c->flag &= ~COPEN;
+			error(Einuse);
+		}
+		poolevents.rd = poolevents.wr = 0;
+		poolevents.full = 0;
+		poolevents.want = 0;
+		poolevents.lost = 0;
+		memmonitor = _memmonitor;
+		poolevents.open = 1;
+		break;
+	case Qstate:
+		if(incref(&stateopen) != 1){
+			decref(&stateopen);
+			c->flag &= ~COPEN;
+			error(Einuse);
+		}
+		break;
+	case Qprof:
+		if(incref(&memprof.inuse) != 1){
+			decref(&memprof.inuse);
+			c->flag &= ~COPEN;
+			error(Einuse);
+		}
+		memmonitor = mprofmonitor;
+		break;
+	}
+	return c;
+}
+
+static void
+memclose(Chan *c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+	switch((ulong)c->qid.path) {
+	case Qevent:
+		memmonitor = nil;
+		poolevents.open = 0;
+		decref(&poolevents.inuse);
+		break;
+	case Qstate:
+		decref(&stateopen);
+		break;
+	case Qprof:
+		decref(&memprof.inuse);
+		memmonitor = nil;
+		break;
+	}
+
+}
+
+static long
+memread(Chan *c, void *va, long count, vlong offset)
+{
+	uchar *m;
+	char *e, *s;
+	int i, summary;
+	long n, nr;
+	Pstate *p;
+	Pevent pe;
+	Pbucket *b;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, count, memdir, nelem(memdir), devgen);
+
+	summary = 0;
+	switch((ulong)c->qid.path) {
+	default:
+		error(Egreg);
+	case Qctl:
+		return 0;
+	case Qsum:
+		summary = 1;
+		/* fall through */
+	case Qstate:
+		if(offset == 0){
+			for(i=0; i<Npool; i++){
+				poolstate[i].ptr = &poolstate[i].state[3];
+				poolstate[i].lim = poolstate[i].ptr + Nstate;
+				memset(poolstate[i].state, 0, sizeof(poolstate[i].state));
+				poolstate[i].summary = summary;
+			}
+			e = poolaudit(memaudit);
+			if(e != nil){
+				print("mem: %s\n", e);
+				error(e);
+			}
+		}
+		m = va;
+		nr = offset/8;
+		for(i=0; i<Npool && count >= 8; i++){
+			n = poolstate[i].ptr - poolstate[i].state;
+			poolstate[i].state[0].base = i;
+			poolstate[i].state[0].size = n;
+			if(nr >= n){
+				nr -= n;
+				continue;
+			}
+			n -= nr;
+			p = &poolstate[i].state[nr];
+			for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){
+				mput4(m, p->base);
+				mput4(m+4, p->size);
+			}
+		}
+		return m-(uchar*)va;
+	case Qevent:
+		while(!ismemdata(nil)){
+			poolevents.want = 1;
+			Sleep(&poolevents.r, ismemdata, nil);
+		}
+		m = va;
+		do{
+			if((count -= 4*4) < 0)
+				return m-(uchar*)va;
+			pe = poolevents.events[poolevents.rd];
+			mput4(m, pe.pool);
+			mput4(m+4, pe.pc);
+			mput4(m+8, pe.base);
+			mput4(m+12, pe.size);
+			m += 4*4;
+			if(++poolevents.rd >= nelem(poolevents.events))
+				poolevents.rd = 0;
+		}while(poolevents.rd != poolevents.wr);
+		poolevents.full = 0;
+		return m-(uchar*)va;
+	case Qprof:
+		if(offset == 0){
+			lock(&memprof.l);
+			memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0]));
+			memprof.snapped = memprof.used;
+			unlock(&memprof.l);
+		}
+		m = va;
+		for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){
+			b = &memprof.snap[i];
+			mput4(m, b->pool);
+			mput4(m+4, b->val);
+			mput4(m+8, b->count);
+			mput4(m+12, b->size);
+			m += 4*4;
+		}
+		return m-(uchar*)va;
+	case Qgc:
+		s = malloc(READSTR);
+		if(s == nil)
+			error(Enomem);
+		if(waserror()){
+			free(s);
+			nexterror();
+		}
+		snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n",
+			gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial);
+		count = readstr(offset, va, count, s);
+		poperror();
+		free(s);
+		return count;
+	}
+}
+
+static long
+memwrite(Chan *c, void *va, long count, vlong offset)
+{
+	USED(offset);
+	USED(count);
+	USED(va);
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	switch((ulong)c->qid.path) {
+	default:
+		error(Egreg);
+	case Qctl:
+		error(Ebadarg);
+	case Qstate:
+		error(Eperm);
+	case Qprof:
+		mprofreset();
+		break;
+	}
+	return 0;
+}
+
+Dev memdevtab = {
+	'%',
+	"mem",
+
+	devinit,
+	memattach,
+	memwalk,
+	memstat,
+	memopen,
+	devcreate,
+	memclose,
+	memread,
+	devbread,
+	memwrite,
+	devbwrite,
+	devremove,
+	devwstat
+};
--- /dev/null
+++ b/emu/port/devmnt.c
@@ -1,0 +1,1203 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+/*
+ * References are managed as follows:
+ * The channel to the server - a network connection or pipe - has one
+ * reference for every Chan open on the server.  The server channel has
+ * c->mux set to the Mnt used for muxing control to that server.  Mnts
+ * have no reference count; they go away when c goes away.
+ * Each channel derived from the mount point has mchan set to c,
+ * and increfs/decrefs mchan to manage references on the server
+ * connection.
+ */
+
+#define MAXRPC (IOHDRSZ+8192)
+
+struct Mntrpc
+{
+	Chan*	c;		/* Channel for whom we are working */
+	Mntrpc*	list;		/* Free/pending list */
+	Fcall	request;	/* Outgoing file system protocol message */
+	Fcall 	reply;		/* Incoming reply */
+	Mnt*	m;		/* Mount device during rpc */
+	Rendez	r;		/* Place to hang out */
+	uchar*	rpc;		/* I/O Data buffer */
+	uint		rpclen;	/* len of buffer */
+	Block	*b;		/* reply blocks */
+	char	done;		/* Rpc completed */
+	uvlong	stime;		/* start time for mnt statistics */
+	ulong	reqlen;		/* request length for mnt statistics */
+	ulong	replen;		/* reply length for mnt statistics */
+	Mntrpc*	flushed;	/* message this one flushes */
+};
+
+enum
+{
+	TAGSHIFT = 5,			/* ulong has to be 32 bits */
+	TAGMASK = (1<<TAGSHIFT)-1,
+	NMASK = (64*1024)>>TAGSHIFT,
+};
+
+struct Mntalloc
+{
+	Lock	l;
+	Mnt*	list;		/* Mount devices in use */
+	Mnt*	mntfree;	/* Free list */
+	Mntrpc*	rpcfree;
+	int	nrpcfree;
+	int	nrpcused;
+	ulong	id;
+	ulong	tagmask[NMASK];
+}mntalloc;
+
+void	mattach(Mnt*, Chan*, char*);
+Mnt*	mntchk(Chan*);
+void	mntdirfix(uchar*, Chan*);
+Mntrpc*	mntflushalloc(Mntrpc*, ulong);
+void	mntflushfree(Mnt*, Mntrpc*);
+void	mntfree(Mntrpc*);
+void	mntgate(Mnt*);
+void	mntpntfree(Mnt*);
+void	mntqrm(Mnt*, Mntrpc*);
+Mntrpc*	mntralloc(Chan*, ulong);
+long	mntrdwr(int, Chan*, void*, long, vlong);
+int	mntrpcread(Mnt*, Mntrpc*);
+void	mountio(Mnt*, Mntrpc*);
+void	mountmux(Mnt*, Mntrpc*);
+void	mountrpc(Mnt*, Mntrpc*);
+int	rpcattn(void*);
+Chan*	mntchan(void);
+
+char	Esbadstat[] = "invalid directory entry received from server";
+char Enoversion[] = "version not established for mount channel";
+
+
+void (*mntstats)(int, Chan*, uvlong, ulong);
+
+static void
+mntinit(void)
+{
+	mntalloc.id = 1;
+	mntalloc.tagmask[0] = 1;			/* don't allow 0 as a tag */
+	mntalloc.tagmask[NMASK-1] = 0x80000000UL;	/* don't allow NOTAG */
+	fmtinstall('F', fcallfmt);
+/*	fmtinstall('D', dirfmt); */
+/*	fmtinstall('M', dirmodefmt);  */
+
+	cinit();
+}
+
+/*
+ * Version is not multiplexed: message sent only once per connection.
+ */
+long
+mntversion(Chan *c, char *version, int msize, int returnlen)
+{
+	Fcall f;
+	uchar *msg;
+	Mnt *m;
+	char *v;
+	long k, l;
+	uvlong oo;
+	char buf[128];
+
+	qlock(&c->umqlock);	/* make sure no one else does this until we've established ourselves */
+	if(waserror()){
+		qunlock(&c->umqlock);
+		nexterror();
+	}
+
+	/* defaults */
+	if(msize == 0)
+		msize = MAXRPC;
+	if(msize > c->iounit && c->iounit != 0)
+		msize = c->iounit;
+	v = version;
+	if(v == nil || v[0] == '\0')
+		v = VERSION9P;
+
+	/* validity */
+	if(msize < 0)
+		error("bad iounit in version call");
+	if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
+		error("bad 9P version specification");
+
+	m = c->mux;
+
+	if(m != nil){
+		qunlock(&c->umqlock);
+		poperror();
+
+		strecpy(buf, buf+sizeof buf, m->version);
+		k = strlen(buf);
+		if(strncmp(buf, v, k) != 0){
+			snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
+			error(buf);
+		}
+		if(returnlen > 0){
+			if(returnlen < k)
+				error(Eshort);
+			memmove(version, buf, k);
+		}
+		return k;
+	}
+
+	f.type = Tversion;
+	f.tag = NOTAG;
+	f.msize = msize;
+	f.version = v;
+	msg = malloc(8192+IOHDRSZ);
+	if(msg == nil)
+		exhausted("version memory");
+	if(waserror()){
+		free(msg);
+		nexterror();
+	}
+	k = convS2M(&f, msg, 8192+IOHDRSZ);
+	if(k == 0)
+		error("bad fversion conversion on send");
+
+	lock(&c->l);
+	oo = c->offset;
+	c->offset += k;
+	unlock(&c->l);
+
+	l = devtab[c->type]->write(c, msg, k, oo);
+
+	if(l < k){
+		lock(&c->l);
+		c->offset -= k - l;
+		unlock(&c->l);
+		error("short write in fversion");
+	}
+
+	/* message sent; receive and decode reply */
+	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
+	if(k <= 0)
+		error("EOF receiving fversion reply");
+
+	lock(&c->l);
+	c->offset += k;
+	unlock(&c->l);
+
+	l = convM2S(msg, k, &f);
+	if(l != k)
+		error("bad fversion conversion on reply");
+	if(f.type != Rversion){
+		if(f.type == Rerror)
+			error(f.ename);
+		error("unexpected reply type in fversion");
+	}
+	if(f.msize > msize)
+		error("server tries to increase msize in fversion");
+	if(f.msize<256 || f.msize>1024*1024)
+		error("nonsense value of msize in fversion");
+	if(strncmp(f.version, v, strlen(f.version)) != 0)
+		error("bad 9P version returned from server");
+
+	/* now build Mnt associated with this connection */
+	lock(&mntalloc.l);
+	m = mntalloc.mntfree;
+	if(m != 0)
+		mntalloc.mntfree = m->list;
+	else {
+		m = malloc(sizeof(Mnt));
+		if(m == 0) {
+			unlock(&mntalloc.l);
+			exhausted("mount devices");
+		}
+	}
+	m->list = mntalloc.list;
+	mntalloc.list = m;
+	m->version = nil;
+	kstrdup(&m->version, f.version);
+	m->id = mntalloc.id++;
+	m->q = qopen(10*MAXRPC, 0, nil, nil);
+	m->msize = f.msize;
+	unlock(&mntalloc.l);
+
+	poperror();	/* msg */
+	free(msg);
+
+	lock(&m->l);
+	m->queue = 0;
+	m->rip = 0;
+
+	c->flag |= CMSG;
+	c->mux = m;
+	m->c = c;
+	unlock(&m->l);
+
+	poperror();	/* c */
+	qunlock(&c->umqlock);
+
+	k = strlen(f.version);
+	if(returnlen > 0){
+		if(returnlen < k)
+			error(Eshort);
+		memmove(version, f.version, k);
+	}
+
+	return k;
+}
+
+Chan*
+mntauth(Chan *c, char *spec)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = c->mux;
+
+	if(m == nil){
+		mntversion(c, VERSION9P, MAXRPC, 0);
+		m = c->mux;
+		if(m == nil)
+			error(Enoversion);
+	}
+
+	c = mntchan();
+	if(waserror()) {
+		/* Close must not be called since it will
+		 * call mnt recursively
+		 */
+		chanfree(c);
+		nexterror();
+	}
+
+	r = mntralloc(0, m->msize);
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = Tauth;
+	r->request.afid = c->fid;
+	r->request.uname = up->env->user;
+	r->request.aname = spec;
+	mountrpc(m, r);
+
+	c->qid = r->reply.aqid;
+	c->mchan = m->c;
+	incref(&m->c->r);
+	c->mqid = c->qid;
+	c->mode = ORDWR;
+
+	poperror();	/* r */
+	mntfree(r);
+
+	poperror();	/* c */
+
+	return c;
+
+}
+
+static Chan*
+mntattach(char *muxattach)
+{
+	Mnt *m;
+	Chan *c;
+	Mntrpc *r;
+	struct bogus{
+		Chan	*chan;
+		Chan	*authchan;
+		char	*spec;
+		int	flags;
+	}bogus;
+
+	bogus = *((struct bogus *)muxattach);
+	c = bogus.chan;
+
+	m = c->mux;
+
+	if(m == nil){
+		mntversion(c, nil, 0, 0);
+		m = c->mux;
+		if(m == nil)
+			error(Enoversion);
+	}
+
+	c = mntchan();
+	if(waserror()) {
+		/* Close must not be called since it will
+		 * call mnt recursively
+		 */
+		chanfree(c);
+		nexterror();
+	}
+
+	r = mntralloc(0, m->msize);
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = Tattach;
+	r->request.fid = c->fid;
+	if(bogus.authchan == nil)
+		r->request.afid = NOFID;
+	else
+		r->request.afid = bogus.authchan->fid;
+	r->request.uname = up->env->user;
+	r->request.aname = bogus.spec;
+	mountrpc(m, r);
+
+	c->qid = r->reply.qid;
+	c->mchan = m->c;
+	incref(&m->c->r);
+	c->mqid = c->qid;
+
+	poperror();	/* r */
+	mntfree(r);
+
+	poperror();	/* c */
+
+	if(bogus.flags&MCACHE)
+		c->flag |= CCACHE;
+	return c;
+}
+
+Chan*
+mntchan(void)
+{
+	Chan *c;
+
+	c = devattach('M', 0);
+	lock(&mntalloc.l);
+	c->dev = mntalloc.id++;
+	unlock(&mntalloc.l);
+
+	if(c->mchan)
+		panic("mntchan non-zero %p", c->mchan);
+	return c;
+}
+
+static Walkqid*
+mntwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	volatile int alloc;
+	int i;
+	Mnt *m;
+	Mntrpc *r;
+	Walkqid *wq;
+
+	if(nc != nil)
+		print("mntwalk: nc != nil\n");
+	if(nname > MAXWELEM)
+		error("devmnt: too many name elements");
+	alloc = 0;
+	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	if(waserror()){
+		if(alloc && wq->clone!=nil)
+			cclose(wq->clone);
+		free(wq);
+		return nil;
+	}
+
+	alloc = 0;
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(nc == nil){
+		nc = devclone(c);
+		/*
+		 * Until the other side accepts this fid, we can't mntclose it.
+		 * Therefore set type to 0 for now; rootclose is known to be safe.
+		 */
+		nc->type = 0;
+		alloc = 1;
+	}
+	wq->clone = nc;
+
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Twalk;
+	r->request.fid = c->fid;
+	r->request.newfid = nc->fid;
+	r->request.nwname = nname;
+	memmove(r->request.wname, name, nname*sizeof(char*));
+
+	mountrpc(m, r);
+
+	if(r->reply.nwqid > nname)
+		error("too many QIDs returned by walk");
+	if(r->reply.nwqid < nname){
+		if(alloc)
+			cclose(nc);
+		wq->clone = nil;
+		if(r->reply.nwqid == 0){
+			free(wq);
+			wq = nil;
+			goto Return;
+		}
+	}
+
+	/* move new fid onto mnt device and update its qid */
+	if(wq->clone != nil){
+		if(wq->clone != c){
+			wq->clone->type = c->type;
+			wq->clone->mchan = c->mchan;
+			incref(&c->mchan->r);
+		}
+		if(r->reply.nwqid > 0)
+			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
+	}
+	wq->nqid = r->reply.nwqid;
+	for(i=0; i<wq->nqid; i++)
+		wq->qid[i] = r->reply.wqid[i];
+
+    Return:
+	poperror();
+	mntfree(r);
+	poperror();
+	return wq;
+}
+
+static int
+mntstat(Chan *c, uchar *dp, int n)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	if(n < BIT16SZ)
+		error(Eshortstat);
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Tstat;
+	r->request.fid = c->fid;
+	mountrpc(m, r);
+
+	if(r->reply.nstat > n){
+		/* doesn't fit; just patch the count and return */
+		PBIT16((uchar*)dp, r->reply.nstat);
+		n = BIT16SZ;
+	}else{
+		n = r->reply.nstat;
+		memmove(dp, r->reply.stat, n);
+		validstat(dp, n);
+		mntdirfix(dp, c);
+	}
+	poperror();
+	mntfree(r);
+	return n;
+}
+
+static Chan*
+mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = type;
+	r->request.fid = c->fid;
+	r->request.mode = omode;
+	if(type == Tcreate){
+		r->request.perm = perm;
+		r->request.name = name;
+	}
+	mountrpc(m, r);
+
+	c->qid = r->reply.qid;
+	c->offset = 0;
+	c->mode = openmode(omode);
+	c->iounit = r->reply.iounit;
+	if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
+		c->iounit = m->msize-IOHDRSZ;
+	c->flag |= COPEN;
+	poperror();
+	mntfree(r);
+
+	if(c->flag & CCACHE)
+		copen(c);
+
+	return c;
+}
+
+static Chan*
+mntopen(Chan *c, int omode)
+{
+	return mntopencreate(Topen, c, nil, omode, 0);
+}
+
+static void
+mntcreate(Chan *c, char *name, int omode, ulong perm)
+{
+	mntopencreate(Tcreate, c, name, omode, perm);
+}
+
+static void
+mntclunk(Chan *c, int t)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()){
+		mntfree(r);
+		nexterror();
+	}
+
+	r->request.type = t;
+	r->request.fid = c->fid;
+	mountrpc(m, r);
+	mntfree(r);
+	poperror();
+}
+
+void
+muxclose(Mnt *m)
+{
+	Mntrpc *q, *r;
+
+	for(q = m->queue; q; q = r) {
+		r = q->list;
+		mntfree(q);
+	}
+	m->id = 0;
+	free(m->version);
+	m->version = nil;
+	mntpntfree(m);
+}
+
+void
+mntpntfree(Mnt *m)
+{
+	Mnt *f, **l;
+	Queue *q;
+
+	lock(&mntalloc.l);
+	l = &mntalloc.list;
+	for(f = *l; f; f = f->list) {
+		if(f == m) {
+			*l = m->list;
+			break;
+		}
+		l = &f->list;
+	}
+	m->list = mntalloc.mntfree;
+	mntalloc.mntfree = m;
+	q = m->q;
+	unlock(&mntalloc.l);
+
+	qfree(q);
+}
+
+static void
+mntclose(Chan *c)
+{
+	mntclunk(c, Tclunk);
+}
+
+static void
+mntremove(Chan *c)
+{
+	mntclunk(c, Tremove);
+}
+
+static int
+mntwstat(Chan *c, uchar *dp, int n)
+{
+	Mnt *m;
+	Mntrpc *r;
+
+	m = mntchk(c);
+	r = mntralloc(c, m->msize);
+	if(waserror()) {
+		mntfree(r);
+		nexterror();
+	}
+	r->request.type = Twstat;
+	r->request.fid = c->fid;
+	r->request.nstat = n;
+	r->request.stat = dp;
+	mountrpc(m, r);
+	poperror();
+	mntfree(r);
+	return n;
+}
+
+static long
+mntread(Chan *c, void *buf, long n, vlong off)
+{
+	uchar *p, *e;
+	int nc, cache, isdir, dirlen;
+
+	isdir = 0;
+	cache = c->flag & CCACHE;
+	if(c->qid.type & QTDIR) {
+		cache = 0;
+		isdir = 1;
+	}
+
+	p = buf;
+	if(cache) {
+		nc = cread(c, buf, n, off);
+		if(nc > 0) {
+			n -= nc;
+			if(n == 0)
+				return nc;
+			p += nc;
+			off += nc;
+		}
+		n = mntrdwr(Tread, c, p, n, off);
+		cupdate(c, p, n, off);
+		return n + nc;
+	}
+
+	n = mntrdwr(Tread, c, buf, n, off);
+	if(isdir) {
+		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
+			dirlen = BIT16SZ+GBIT16(p);
+			if(p+dirlen > e)
+				break;
+			validstat(p, dirlen);
+			mntdirfix(p, c);
+		}
+		if(p != e)
+			error(Esbadstat);
+	}
+	return n;
+}
+
+static long
+mntwrite(Chan *c, void *buf, long n, vlong off)
+{
+	return mntrdwr(Twrite, c, buf, n, off);
+}
+
+long
+mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
+{
+	Mnt *m;
+ 	Mntrpc *r;	/* TO DO: volatile struct { Mntrpc *r; } r; */
+	char *uba;
+	int cache;
+	ulong cnt, nr, nreq;
+
+	m = mntchk(c);
+	uba = buf;
+	cnt = 0;
+	cache = c->flag & CCACHE;
+	if(c->qid.type & QTDIR)
+		cache = 0;
+	for(;;) {
+		r = mntralloc(c, m->msize);
+		if(waserror()) {
+			mntfree(r);
+			nexterror();
+		}
+		r->request.type = type;
+		r->request.fid = c->fid;
+		r->request.offset = off;
+		r->request.data = uba;
+		nr = n;
+		if(nr > m->msize-IOHDRSZ)
+			nr = m->msize-IOHDRSZ;
+		r->request.count = nr;
+		mountrpc(m, r);
+		nreq = r->request.count;
+		nr = r->reply.count;
+		if(nr > nreq)
+			nr = nreq;
+
+		if(type == Tread)
+			r->b = bl2mem((uchar*)uba, r->b, nr);
+		else if(cache)
+			cwrite(c, (uchar*)uba, nr, off);
+
+		poperror();
+		mntfree(r);
+		off += nr;
+		uba += nr;
+		cnt += nr;
+		n -= nr;
+		if(nr != nreq || n == 0 || up->killed)
+			break;
+	}
+	return cnt;
+}
+
+void
+mountrpc(Mnt *m, Mntrpc *r)
+{
+	char *sn, *cn;
+	int t;
+
+	r->reply.tag = 0;
+	r->reply.type = Tmax;	/* can't ever be a valid message type */
+
+	mountio(m, r);
+
+	t = r->reply.type;
+	switch(t) {
+	case Rerror:
+		error(r->reply.ename);
+	case Rflush:
+		error(Eintr);
+	default:
+		if(t == r->request.type+1)
+			break;
+		sn = "?";
+		if(m->c->name != nil)
+			sn = m->c->name->s;
+		cn = "?";
+		if(r->c != nil && r->c->name != nil)
+			cn = r->c->name->s;
+		print("mnt: proc %s %lud: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n",
+			up->text, up->pid, sn, cn,
+			r, r->request.tag, r->request.fid, r->request.type,
+			r->reply.type, r->reply.tag);
+		error(Emountrpc);
+	}
+}
+
+void
+mountio(Mnt *m, Mntrpc *r)
+{
+	int n;
+
+	while(waserror()) {
+		if(m->rip == up)
+			mntgate(m);
+		if(strcmp(up->env->errstr, Eintr) != 0){
+			mntflushfree(m, r);
+			nexterror();
+		}
+		r = mntflushalloc(r, m->msize);
+	}
+
+	lock(&m->l);
+	r->m = m;
+	r->list = m->queue;
+	m->queue = r;
+	unlock(&m->l);
+
+	/* Transmit a file system rpc */
+	if(m->msize == 0)
+		panic("msize");
+	n = convS2M(&r->request, r->rpc, m->msize);
+	if(n < 0)
+		panic("bad message type in mountio");
+	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
+		error(Emountrpc);
+/*	r->stime = fastticks(nil); */
+	r->reqlen = n;
+
+	/* Gate readers onto the mount point one at a time */
+	for(;;) {
+		lock(&m->l);
+		if(m->rip == 0)
+			break;
+		unlock(&m->l);
+		Sleep(&r->r, rpcattn, r);
+		if(r->done){
+			poperror();
+			mntflushfree(m, r);
+			return;
+		}
+	}
+	m->rip = up;
+	unlock(&m->l);
+	while(r->done == 0) {
+		if(mntrpcread(m, r) < 0)
+			error(Emountrpc);
+		mountmux(m, r);
+	}
+	mntgate(m);
+	poperror();
+	mntflushfree(m, r);
+}
+
+static int
+doread(Mnt *m, int len)
+{
+	Block *b;
+
+	while(qlen(m->q) < len){
+		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
+		if(b == nil)
+			return -1;
+		if(blocklen(b) == 0){
+			freeblist(b);
+			return -1;
+		}
+		qaddlist(m->q, b);
+	}
+	return 0;
+}
+
+int
+mntrpcread(Mnt *m, Mntrpc *r)
+{
+	int i, t, len, hlen;
+	Block *b, **l, *nb;
+
+	r->reply.type = 0;
+	r->reply.tag = 0;
+
+	/* read at least length, type, and tag and pullup to a single block */
+	if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
+		return -1;
+	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
+
+	/* read in the rest of the message, avoid ridiculous (for now) message sizes */
+	len = GBIT32(nb->rp);
+	if(len > m->msize){
+		qdiscard(m->q, qlen(m->q));
+		return -1;
+	}
+	if(doread(m, len) < 0)
+		return -1;
+
+	/* pullup the header (i.e. everything except data) */
+	t = nb->rp[BIT32SZ];
+	switch(t){
+	case Rread:
+		hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
+		break;
+	default:
+		hlen = len;
+		break;
+	}
+	nb = pullupqueue(m->q, hlen);
+
+	if(convM2S(nb->rp, len, &r->reply) <= 0){
+		/* bad message, dump it */
+		print("mntrpcread: convM2S failed\n");
+		qdiscard(m->q, len);
+		return -1;
+	}
+
+	/* hang the data off of the fcall struct */
+	l = &r->b;
+	*l = nil;
+	do {
+		b = qremove(m->q);
+		if(hlen > 0){
+			b->rp += hlen;
+			len -= hlen;
+			hlen = 0;
+		}
+		i = BLEN(b);
+		if(i <= len){
+			len -= i;
+			*l = b;
+			l = &(b->next);
+		} else {
+			/* split block and put unused bit back */
+			nb = allocb(i-len);
+			memmove(nb->wp, b->rp+len, i-len);
+			b->wp = b->rp+len;
+			nb->wp += i-len;
+			qputback(m->q, nb);
+			*l = b;
+			return 0;
+		}
+	}while(len > 0);
+
+	return 0;
+}
+
+void
+mntgate(Mnt *m)
+{
+	Mntrpc *q;
+
+	lock(&m->l);
+	m->rip = 0;
+	for(q = m->queue; q; q = q->list) {
+		if(q->done == 0)
+		if(Wakeup(&q->r))
+			break;
+	}
+	unlock(&m->l);
+}
+
+void
+mountmux(Mnt *m, Mntrpc *r)
+{
+	Mntrpc **l, *q;
+
+	lock(&m->l);
+	l = &m->queue;
+	for(q = *l; q; q = q->list) {
+		/* look for a reply to a message */
+		if(q->request.tag == r->reply.tag) {
+			*l = q->list;
+			if(q != r) {
+				/*
+				 * Completed someone else.
+				 * Trade pointers to receive buffer.
+				 */
+				q->reply = r->reply;
+				q->b = r->b;
+				r->b = nil;
+			}
+			q->done = 1;
+			unlock(&m->l);
+			if(mntstats != nil)
+				(*mntstats)(q->request.type,
+					m->c, q->stime,
+					q->reqlen + r->replen);
+			if(q != r)
+				Wakeup(&q->r);
+			return;
+		}
+		l = &q->list;
+	}
+	unlock(&m->l);
+	if(r->reply.type == Rerror)
+		print("unexpected reply tag %ud; type %d (error %q)\n", r->reply.tag, r->reply.type, r->reply.ename);
+	else
+		print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
+}
+
+/*
+ * Create a new flush request and chain the previous
+ * requests from it
+ */
+Mntrpc*
+mntflushalloc(Mntrpc *r, ulong iounit)
+{
+	Mntrpc *fr;
+
+	fr = mntralloc(0, iounit);
+
+	fr->request.type = Tflush;
+	if(r->request.type == Tflush)
+		fr->request.oldtag = r->request.oldtag;
+	else
+		fr->request.oldtag = r->request.tag;
+	fr->flushed = r;
+
+	return fr;
+}
+
+/*
+ *  Free a chain of flushes.  Remove each unanswered
+ *  flush and the original message from the unanswered
+ *  request queue.  Mark the original message as done
+ *  and if it hasn't been answered set the reply to to
+ *  Rflush.
+ */
+void
+mntflushfree(Mnt *m, Mntrpc *r)
+{
+	Mntrpc *fr;
+
+	while(r){
+		fr = r->flushed;
+		if(!r->done){
+			r->reply.type = Rflush;
+			mntqrm(m, r);
+		}
+		if(fr)
+			mntfree(r);
+		r = fr;
+	}
+}
+
+static int
+alloctag(void)
+{
+	int i, j;
+	ulong v;
+
+	for(i = 0; i < NMASK; i++){
+		v = mntalloc.tagmask[i];
+		if(v == ~0UL)
+			continue;
+		for(j = 0; j < 1<<TAGSHIFT; j++)
+			if((v & (1<<j)) == 0){
+				mntalloc.tagmask[i] |= 1<<j;
+				return (i<<TAGSHIFT) + j;
+			}
+	}
+	/* panic("no devmnt tags left"); */
+	return NOTAG;
+}
+
+static void
+freetag(int t)
+{
+	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
+}
+
+Mntrpc*
+mntralloc(Chan *c, ulong msize)
+{
+	Mntrpc *new;
+
+	lock(&mntalloc.l);
+	new = mntalloc.rpcfree;
+	if(new == nil){
+		new = malloc(sizeof(Mntrpc));
+		if(new == nil) {
+			unlock(&mntalloc.l);
+			exhausted("mount rpc header");
+		}
+		/*
+		 * The header is split from the data buffer as
+		 * mountmux may swap the buffer with another header.
+		 */
+		new->rpc = mallocz(msize, 0);
+		if(new->rpc == nil){
+			free(new);
+			unlock(&mntalloc.l);
+			exhausted("mount rpc buffer");
+		}
+		new->rpclen = msize;
+		new->request.tag = alloctag();
+		if(new->request.tag == NOTAG){
+			free(new);
+			unlock(&mntalloc.l);
+			exhausted("rpc tags");
+		}
+	}
+	else {
+		mntalloc.rpcfree = new->list;
+		mntalloc.nrpcfree--;
+		if(new->rpclen < msize){
+			free(new->rpc);
+			new->rpc = mallocz(msize, 0);
+			if(new->rpc == nil){
+				free(new);
+				mntalloc.nrpcused--;
+				unlock(&mntalloc.l);
+				exhausted("mount rpc buffer");
+			}
+			new->rpclen = msize;
+		}
+	}
+	mntalloc.nrpcused++;
+	unlock(&mntalloc.l);
+	new->c = c;
+	new->done = 0;
+	new->flushed = nil;
+	new->b = nil;
+	return new;
+}
+
+void
+mntfree(Mntrpc *r)
+{
+	if(r->b != nil)
+		freeblist(r->b);
+	lock(&mntalloc.l);
+	if(mntalloc.nrpcfree >= 10){
+		free(r->rpc);
+		freetag(r->request.tag);
+		free(r);
+	}
+	else{
+		r->list = mntalloc.rpcfree;
+		mntalloc.rpcfree = r;
+		mntalloc.nrpcfree++;
+	}
+	mntalloc.nrpcused--;
+	unlock(&mntalloc.l);
+}
+
+void
+mntqrm(Mnt *m, Mntrpc *r)
+{
+	Mntrpc **l, *f;
+
+	lock(&m->l);
+	r->done = 1;
+
+	l = &m->queue;
+	for(f = *l; f; f = f->list) {
+		if(f == r) {
+			*l = r->list;
+			break;
+		}
+		l = &f->list;
+	}
+	unlock(&m->l);
+}
+
+Mnt*
+mntchk(Chan *c)
+{
+	Mnt *m;
+
+	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
+
+	if(c->mchan == nil)
+		panic("mntchk 1: nil mchan c %s\n", c2name(c));
+
+	m = c->mchan->mux;
+
+	if(m == nil)
+		print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
+
+	/*
+	 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
+	 */
+	if(m->id == 0 || m->id >= c->dev)
+		panic("mntchk 3: can't happen");
+
+	return m;
+}
+
+/*
+ * Rewrite channel type and dev for in-flight data to
+ * reflect local values.  These entries are known to be
+ * the first two in the Dir encoding after the count.
+ */
+void
+mntdirfix(uchar *dirbuf, Chan *c)
+{
+	uint r;
+
+	r = devtab[c->type]->dc;
+	dirbuf += BIT16SZ;	/* skip count */
+	PBIT16(dirbuf, r);
+	dirbuf += BIT16SZ;
+	PBIT32(dirbuf, c->dev);
+}
+
+int
+rpcattn(void *v)
+{
+	Mntrpc *r;
+
+	r = v;
+	return r->done || r->m->rip == 0;
+}
+
+Dev mntdevtab = {
+	'M',
+	"mnt",
+
+	mntinit,
+	mntattach,
+	mntwalk,
+	mntstat,
+	mntopen,
+	mntcreate,
+	mntclose,
+	mntread,
+	devbread,
+	mntwrite,
+	devbwrite,
+	mntremove,
+	mntwstat,
+};
--- /dev/null
+++ b/emu/port/devpipe.c
@@ -1,0 +1,460 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<interp.h>
+
+#define NETTYPE(x)	((ulong)(x)&0x1f)
+#define NETID(x)	(((ulong)(x))>>5)
+#define NETQID(i,t)	(((i)<<5)|(t))
+
+typedef struct Pipe	Pipe;
+struct Pipe
+{
+	QLock	l;
+	Pipe*	next;
+	int	ref;
+	ulong	path;
+	Queue*	q[2];
+	int	qref[2];
+	Dirtab*	pipedir;
+	char*	user;
+};
+
+static struct
+{
+	Lock	l;
+	ulong	path;
+	int	pipeqsize;	
+} pipealloc;
+
+enum
+{
+	Qdir,
+	Qdata0,
+	Qdata1
+};
+
+Dirtab pipedir[] =
+{
+	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
+	"data",		{Qdata0},	0,			0660,
+	"data1",	{Qdata1},	0,			0660,
+};
+
+static void
+freepipe(Pipe *p)
+{
+	if(p != nil){
+		free(p->user);
+		free(p->q[0]);
+		free(p->q[1]);
+		free(p->pipedir);
+		free(p);
+	}
+}
+
+static void
+pipeinit(void)
+{
+	pipealloc.pipeqsize = 32*1024;
+}
+
+/*
+ *  create a pipe, no streams are created until an open
+ */
+static Chan*
+pipeattach(char *spec)
+{
+	Pipe *p;
+	Chan *c;
+
+	c = devattach('|', spec);
+	p = malloc(sizeof(Pipe));
+	if(p == 0)
+		error(Enomem);
+	if(waserror()){
+		freepipe(p);
+		nexterror();
+	}
+	p->pipedir = malloc(sizeof(pipedir));
+	if (p->pipedir == 0)
+		error(Enomem);
+	memmove(p->pipedir, pipedir, sizeof(pipedir));
+	kstrdup(&p->user, up->env->user);
+	p->ref = 1;
+
+	p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+	if(p->q[0] == 0)
+		error(Enomem);
+	p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
+	if(p->q[1] == 0)
+		error(Enomem);
+	poperror();
+
+	lock(&pipealloc.l);
+	p->path = ++pipealloc.path;
+	unlock(&pipealloc.l);
+
+	c->qid.path = NETQID(2*p->path, Qdir);
+	c->qid.vers = 0;
+	c->qid.type = QTDIR;
+	c->aux = p;
+	c->dev = 0;
+	return c;
+}
+
+static int
+pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	int id, len;
+	Qid qid;
+	Pipe *p;
+
+	USED(name);
+	if(i == DEVDOTDOT){
+		devdir(c, c->qid, "#|", 0, eve, 0555, dp);
+		return 1;
+	}
+	i++;	/* skip . */
+	if(tab==0 || i>=ntab)
+		return -1;
+	tab += i;
+	p = c->aux;
+	switch(NETTYPE(tab->qid.path)){
+	case Qdata0:
+		len = qlen(p->q[0]);
+		break;
+	case Qdata1:
+		len = qlen(p->q[1]);
+		break;
+	default:
+		len = tab->length;
+		break;
+	}
+	id = NETID(c->qid.path);
+	qid.path = NETQID(id, tab->qid.path);
+	qid.vers = 0;
+	qid.type = QTFILE;
+	devdir(c, qid, tab->name, len, eve, tab->perm, dp);
+	return 1;
+}
+
+static Walkqid*
+pipewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+	Pipe *p;
+
+	p = c->aux;
+	wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen);
+	if(wq != nil && wq->clone != nil && wq->clone != c){
+		qlock(&p->l);
+		p->ref++;
+		if(c->flag & COPEN){
+			switch(NETTYPE(c->qid.path)){
+			case Qdata0:
+				p->qref[0]++;
+				break;
+			case Qdata1:
+				p->qref[1]++;
+				break;
+			}
+		}
+		qunlock(&p->l);
+	}
+	return wq;
+}
+
+static int
+pipestat(Chan *c, uchar *db, int n)
+{
+	Pipe *p;
+	Dir dir;
+	Dirtab *tab;
+
+	p = c->aux;
+	tab = p->pipedir;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdir:
+		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
+		break;
+	case Qdata0:
+		devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
+		break;
+	case Qdata1:
+		devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
+		break;
+	default:
+		panic("pipestat");
+	}
+	n = convD2M(&dir, db, n);
+	if(n < BIT16SZ)
+		error(Eshortstat);
+	return n;
+}
+
+/*
+ *  if the stream doesn't exist, create it
+ */
+static Chan*
+pipeopen(Chan *c, int omode)
+{
+	Pipe *p;
+
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Ebadarg);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	openmode(omode);	/* check it */
+
+	p = c->aux;
+	qlock(&p->l);
+	if(waserror()){
+		qunlock(&p->l);
+		nexterror();
+	}
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		devpermcheck(p->user, p->pipedir[1].perm, omode);
+		p->qref[0]++;
+		break;
+	case Qdata1:
+		devpermcheck(p->user, p->pipedir[2].perm, omode);
+		p->qref[1]++;
+		break;
+	}
+	poperror();
+	qunlock(&p->l);
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+static void
+pipeclose(Chan *c)
+{
+	Pipe *p;
+
+	p = c->aux;
+	qlock(&p->l);
+
+	if(c->flag & COPEN){
+		/*
+		 *  closing either side hangs up the stream
+		 */
+		switch(NETTYPE(c->qid.path)){
+		case Qdata0:
+			p->qref[0]--;
+			if(p->qref[0] == 0){
+				qhangup(p->q[1], 0);
+				qclose(p->q[0]);
+			}
+			break;
+		case Qdata1:
+			p->qref[1]--;
+			if(p->qref[1] == 0){
+				qhangup(p->q[0], 0);
+				qclose(p->q[1]);
+			}
+			break;
+		}
+	}
+
+	
+	/*
+	 *  if both sides are closed, they are reusable
+	 */
+	if(p->qref[0] == 0 && p->qref[1] == 0){
+		qreopen(p->q[0]);
+		qreopen(p->q[1]);
+	}
+
+	/*
+	 *  free the structure on last close
+	 */
+	p->ref--;
+	if(p->ref == 0){
+		qunlock(&p->l);
+		freepipe(p);
+	} else
+		qunlock(&p->l);
+}
+
+static long
+piperead(Chan *c, void *va, long n, vlong junk)
+{
+	Pipe *p;
+
+	p = c->aux;
+
+	USED(junk);
+	switch(NETTYPE(c->qid.path)){
+	case Qdir:
+		return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen);
+	case Qdata0:
+		return qread(p->q[0], va, n);
+	case Qdata1:
+		return qread(p->q[1], va, n);
+	default:
+		panic("piperead");
+	}
+	return -1;	/* not reached */
+}
+
+static Block*
+pipebread(Chan *c, long n, ulong offset)
+{
+	Pipe *p;
+
+	p = c->aux;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		return qbread(p->q[0], n);
+	case Qdata1:
+		return qbread(p->q[1], n);
+	}
+
+	return devbread(c, n, offset);
+}
+
+/*
+ *  a write to a closed pipe causes an exception to be sent to
+ *  the prog.
+ */
+static long
+pipewrite(Chan *c, void *va, long n, vlong junk)
+{
+	Pipe *p;
+	Prog *r;
+
+	USED(junk);
+	if(waserror()) {
+		/* avoid exceptions when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0) {
+			r = up->iprog;
+			if(r != nil && r->kill == nil)
+				r->kill = "write on closed pipe";
+		}
+		nexterror();
+	}
+
+	p = c->aux;
+
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		n = qwrite(p->q[1], va, n);
+		break;
+
+	case Qdata1:
+		n = qwrite(p->q[0], va, n);
+		break;
+
+	default:
+		panic("pipewrite");
+	}
+
+	poperror();
+	return n;
+}
+
+static long
+pipebwrite(Chan *c, Block *bp, ulong junk)
+{
+	long n;
+	Pipe *p;
+	Prog *r;
+
+	USED(junk);
+	if(waserror()) {
+		/* avoid exceptions when pipe is a mounted queue */
+		if((c->flag & CMSG) == 0) {
+			r = up->iprog;
+			if(r != nil && r->kill == nil)
+				r->kill = "write on closed pipe";
+		}
+		nexterror();
+	}
+
+	p = c->aux;
+	switch(NETTYPE(c->qid.path)){
+	case Qdata0:
+		n = qbwrite(p->q[1], bp);
+		break;
+
+	case Qdata1:
+		n = qbwrite(p->q[0], bp);
+		break;
+
+	default:
+		n = 0;
+		panic("pipebwrite");
+	}
+
+	poperror();
+	return n;
+}
+
+static int
+pipewstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	Pipe *p;
+	int d1;
+
+	if (c->qid.type&QTDIR)
+		error(Eperm);
+	p = c->aux;
+	if(strcmp(up->env->user, p->user) != 0)
+		error(Eperm);
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	d1 = NETTYPE(c->qid.path) == Qdata1;
+	if(!emptystr(d->name)){
+		validwstatname(d->name);
+		if(strlen(d->name) >= KNAMELEN)
+			error(Efilename);
+		if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
+			error(Eexist);
+		kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN);
+	}
+	if(d->mode != ~0UL)
+		p->pipedir[d1 + 1].perm = d->mode & 0777;
+	poperror();
+	free(d);
+	return n;
+}
+
+Dev pipedevtab = {
+	'|',
+	"pipe",
+
+	pipeinit,
+	pipeattach,
+	pipewalk,
+	pipestat,
+	pipeopen,
+	devcreate,
+	pipeclose,
+	piperead,
+	pipebread,
+	pipewrite,
+	pipebwrite,
+	devremove,
+	pipewstat,
+};
--- /dev/null
+++ b/emu/port/devpointer.c
@@ -1,0 +1,295 @@
+/*
+ * mouse or stylus
+ */
+
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#define	cursorenable()
+#define	cursordisable()
+
+enum{
+	Qdir,
+	Qpointer,
+	Qcursor
+};
+
+typedef struct Pointer Pointer;
+
+struct Pointer {
+	int	x;
+	int	y;
+	int	b;
+	ulong	msec;
+};
+
+static struct
+{
+	Pointer	v;
+	int	modify;
+	int	lastb;
+	Rendez	r;
+	Ref	ref;
+	QLock	q;
+} mouse;
+
+static
+Dirtab pointertab[]={
+	".",			{Qdir, 0, QTDIR},	0,	0555,
+	"pointer",		{Qpointer},	0,	0666,
+	"cursor",		{Qcursor},		0,	0222,
+};
+
+enum {
+	Nevent = 16	/* enough for some */
+};
+
+static struct {
+	int	rd;
+	int	wr;
+	Pointer	clicks[Nevent];
+	Rendez r;
+	int	full;
+	int	put;
+	int	get;
+} ptrq;
+
+/*
+ * called by any source of pointer data
+ */
+void
+mousetrack(int b, int x, int y, int isdelta)
+{
+	int lastb;
+	ulong msec;
+	Pointer e;
+
+	if(isdelta){
+		x += mouse.v.x;
+		y += mouse.v.y;
+	}
+	msec = osmillisec();
+	lastb = mouse.v.b;
+	mouse.v.x = x;
+	mouse.v.y = y;
+	mouse.v.b = b;
+	mouse.v.msec = msec;
+	if(!ptrq.full && lastb != b){
+		e = mouse.v;
+		ptrq.clicks[ptrq.wr] = e;
+		if(++ptrq.wr >= Nevent)
+			ptrq.wr = 0;
+		if(ptrq.wr == ptrq.rd)
+			ptrq.full = 1;
+	}
+	mouse.modify = 1;
+	ptrq.put++;
+	Wakeup(&ptrq.r);
+/*	drawactive(1);	*/
+/*	setpointer(x, y); */
+}
+
+static int
+ptrqnotempty(void *x)
+{
+	USED(x);
+	return ptrq.full || ptrq.put != ptrq.get;
+}
+
+static Pointer
+mouseconsume(void)
+{
+	Pointer e;
+
+	Sleep(&ptrq.r, ptrqnotempty, 0);
+	ptrq.full = 0;
+	ptrq.get = ptrq.put;
+	if(ptrq.rd != ptrq.wr){
+		e = ptrq.clicks[ptrq.rd];
+		if(++ptrq.rd >= Nevent)
+			ptrq.rd = 0;
+	}else
+		e = mouse.v;
+	return e;
+}
+
+Point
+mousexy(void)
+{
+	return Pt(mouse.v.x, mouse.v.y);
+}
+
+
+static Chan*
+pointerattach(char* spec)
+{
+	return devattach('m', spec);
+}
+
+static Walkqid*
+pointerwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+
+	wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen);
+	if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer)
+		incref(&mouse.ref);	/* can this happen? */
+	return wq;
+}
+
+static int
+pointerstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, pointertab, nelem(pointertab), devgen);
+}
+
+static Chan*
+pointeropen(Chan* c, int omode)
+{
+	c = devopen(c, omode, pointertab, nelem(pointertab), devgen);
+	if((ulong)c->qid.path == Qpointer){
+		if(waserror()){
+			c->flag &= ~COPEN;
+			nexterror();
+		}
+		if(!canqlock(&mouse.q))
+			error(Einuse);
+		if(incref(&mouse.ref) != 1){
+			qunlock(&mouse.q);
+			error(Einuse);
+		}
+		cursorenable();
+		qunlock(&mouse.q);
+		poperror();
+	}
+	return c;
+}
+
+static void
+pointerclose(Chan* c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+	switch((ulong)c->qid.path){
+	case Qpointer:
+		qlock(&mouse.q);
+		if(decref(&mouse.ref) == 0){
+			cursordisable();
+		}
+		qunlock(&mouse.q);
+		break;
+	}
+}
+
+static long
+pointerread(Chan* c, void* a, long n, vlong off)
+{
+	Pointer mt;
+	char buf[1+4*12+1];
+	int l;
+
+	USED(&off);
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, pointertab, nelem(pointertab), devgen);
+	case Qpointer:
+		qlock(&mouse.q);
+		if(waserror()) {
+			qunlock(&mouse.q);
+			nexterror();
+		}
+		mt = mouseconsume();
+		poperror();
+		qunlock(&mouse.q);
+		l = snprint(buf, sizeof(buf), "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec);
+		if(l < n)
+			n = l;
+		memmove(a, buf, n);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+pointerwrite(Chan* c, void* va, long n, vlong off)
+{
+	char *a = va;
+	char buf[128];
+	int b, x, y;
+	Drawcursor cur;
+
+	USED(&off);
+	switch((ulong)c->qid.path){
+	case Qpointer:
+		if(n > sizeof buf-1)
+			n = sizeof buf -1;
+		memmove(buf, va, n);
+		buf[n] = 0;
+		x = strtoul(buf+1, &a, 0);
+		if(*a == 0)
+			error(Eshort);
+		y = strtoul(a, &a, 0);
+		if(*a != 0)
+			b = strtoul(a, 0, 0);
+		else
+			b = mouse.v.b;
+		/*mousetrack(b, x, y, msec);*/
+		setpointer(x, y);
+		USED(b);
+		break;
+	case Qcursor:
+		/* TO DO: perhaps interpret data as an Image */
+		/*
+		 *  hotx[4] hoty[4] dx[4] dy[4] clr[dx/8 * dy/2] set[dx/8 * dy/2]
+		 *  dx must be a multiple of 8; dy must be a multiple of 2.
+		 */
+		if(n == 0){
+			cur.data = nil;
+			drawcursor(&cur);
+			break;
+		}
+		if(n < 8)
+			error(Eshort);
+		cur.hotx = BGLONG((uchar*)va+0*4);
+		cur.hoty = BGLONG((uchar*)va+1*4);
+		cur.minx = 0;
+		cur.miny = 0;
+		cur.maxx = BGLONG((uchar*)va+2*4);
+		cur.maxy = BGLONG((uchar*)va+3*4);
+		if(cur.maxx%8 != 0 || cur.maxy%2 != 0 || n-4*4 != (cur.maxx/8 * cur.maxy))
+			error(Ebadarg);
+		cur.data = (uchar*)va + 4*4;
+		drawcursor(&cur);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev pointerdevtab = {
+	'm',
+	"pointer",
+
+	devinit,
+	pointerattach,
+	pointerwalk,
+	pointerstat,
+	pointeropen,
+	devcreate,
+	pointerclose,
+	pointerread,
+	devbread,
+	pointerwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devprof.c
@@ -1,0 +1,817 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+#include	<isa.h>
+#include	"runt.h"
+
+extern	Pool*	imagmem;
+extern void	(*memmonitor)(int, ulong, ulong, ulong);
+
+static void	cpxec(Prog *);
+static void memprof(int, void*, ulong);
+static void memprofmi(int, ulong, ulong, ulong);
+
+extern	Inst*	pc2dispc(Inst*, Module*);
+
+static	int	interval = 100;	/* Sampling interval in milliseconds */
+
+enum
+{
+	HSIZE	= 32,
+};
+
+#define HASH(m)	((m)%HSIZE)
+
+/* cope with  multiple profilers some day */
+
+typedef struct Record Record;
+struct Record
+{
+	int	id;
+	char*	name;
+	char*	path;
+	Inst*	base;
+	int	size;
+	/*Module*	m;	*/
+	ulong	mtime;
+	Qid	qid;
+	Record*	hash;
+	Record*	link;
+	ulong	bucket[1];
+};
+
+struct
+{
+	Lock	l;
+	vlong	time;
+	Record*	hash[HSIZE];
+	Record*	list;
+} profile;
+
+typedef struct Pmod Pmod;
+struct Pmod
+{
+	char*	name;
+	Pmod*	link;
+} *pmods;
+	
+#define QSHIFT	4
+#define QID(q)		((ulong)(q).path&0xf)
+#define QPID(pid)	((pid)<<QSHIFT)
+#define PID(q)		((q).vers)
+#define PATH(q)	((ulong)(q).path&~((1<<QSHIFT)-1))
+
+enum
+{
+	Qdir,
+	Qname,
+	Qpath,
+	Qhist,
+	Qpctl,
+	Qctl,
+};
+
+Dirtab profdir[] =
+{
+	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"name",		{Qname},	0,			0444,
+	"path",		{Qpath},	0,			0444,
+	"histogram",	{Qhist},	0,			0444,
+	"pctl",		{Qpctl},	0,			0222,
+	"ctl",			{Qctl},	0,			0222,
+};
+
+enum{
+	Pnil,	/* null profiler */
+	Psam,	/* sampling profiler */
+	Pcov,	/* coverage profiler */
+	Pmem,	/* heap memory profiler */
+};
+
+enum{
+	Mnone = 0,
+	Mmain = 1,
+	Mheap = 2,
+	Mimage = 4,
+};
+
+static int profiler = Pnil;
+static int mprofiler = Mnone;
+
+static int ids;
+static int samplefn;
+
+static void sampler(void*);
+
+static Record*
+getrec(int id)
+{
+	Record *r;
+
+	for(r = profile.list; r != nil; r = r->link)
+		if(r->id == id)
+			break;
+	return r;
+}
+
+static void
+addpmod(char *m)
+{
+	Pmod *p = malloc(sizeof(Pmod));
+
+	if(p == nil)
+		return;
+	p->name = malloc(strlen(m)+1);
+	if(p->name == nil){
+		free(p);
+		return;
+	}
+	strcpy(p->name, m);
+	p->link = pmods;
+	pmods = p;
+}
+
+static void
+freepmods(void)
+{
+	Pmod *p, *np;
+
+	for(p = pmods; p != nil; p = np){
+		free(p->name);
+		np = p->link;
+		free(p);
+	}
+	pmods = nil;
+}
+
+static int
+inpmods(char *m)
+{
+	Pmod *p;
+
+	for(p = pmods; p != nil; p = p->link)
+		if(strcmp(p->name, m) == 0)
+			return 1;
+	return 0;
+}
+
+static void
+freeprof(void)
+{
+	int i;
+	Record *r, *nr;
+
+	ids = 0;
+	profiler = Pnil;
+	mprofiler = Mnone;
+	freepmods();
+	for(r = profile.list; r != nil; r = nr){
+		free(r->name);
+		free(r->path);
+		nr = r->link;
+		free(r);
+	}
+	profile.list = nil;
+	profile.time = 0;
+	for(i = 0; i < HSIZE; i++)
+		profile.hash[i] = nil;
+}
+
+static int
+profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Qid qid;
+	Record *r;
+	ulong path, perm, len;
+	Dirtab *tab;
+
+	USED(name);
+	USED(d);
+	USED(nd);
+
+	if(s == DEVDOTDOT) {
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#P", 0, eve, 0555, dp);
+		return 1;
+	}
+
+	if(c->qid.path == Qdir && c->qid.type & QTDIR) {
+		acquire();
+		if(s-- == 0){
+			tab = &profdir[Qctl];
+			mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE);
+			devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
+			release();
+			return 1;
+		}
+		r = profile.list;
+		while(s-- && r != nil)
+			r = r->link;
+		if(r == nil) {
+			release();
+			return -1;
+		}
+		sprint(up->genbuf, "%.8lux", (ulong)r->id);
+		mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR);
+		devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp);
+		release();
+		return 1;
+	}
+	if(s >= nelem(profdir)-1)
+		error(Enonexist);	/* was return -1; */
+	tab = &profdir[s];
+	path = PATH(c->qid);
+
+	acquire();
+	r = getrec(PID(c->qid));
+	if(r == nil) {
+		release();
+		error(Enonexist);	/* was return -1; */
+	}
+
+	perm = tab->perm;
+	len = tab->length;
+	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+	devdir(c, qid, tab->name, len, eve, perm, dp);
+	release();
+	return 1;
+}
+
+static Chan*
+profattach(char *spec)
+{
+	return devattach('P', spec);
+}
+
+static Walkqid*
+profwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, profgen);
+}
+
+static int
+profstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, profgen);
+}
+
+static Chan*
+profopen(Chan *c, int omode)
+{
+	int qid;
+	Record *r;
+
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = openmode(omode);
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	if(omode&OTRUNC)
+		error(Eperm);
+
+	qid = QID(c->qid);
+	if(qid == Qctl || qid == Qpctl){
+		if (omode != OWRITE)
+			error(Eperm);
+	}
+	else{
+		if(omode != OREAD)
+			error(Eperm);
+	}
+
+	if(qid != Qctl){
+		acquire();
+		r = getrec(PID(c->qid));
+		release();
+		if(r == nil)
+			error(Ethread);
+	}
+
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = openmode(omode);
+	if(QID(c->qid) == Qhist)
+		c->aux = nil;
+	return c;
+}
+
+static int
+profwstat(Chan *c, uchar *dp, int n)
+{
+	Dir d;
+	Record *r;
+
+	if(strcmp(up->env->user, eve))
+		error(Eperm);
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+	acquire();
+	r = getrec(PID(c->qid));
+	release();
+	if(r == nil)
+		error(Ethread);
+	n = convM2D(dp, n, &d, nil);
+	if(n == 0)
+		error(Eshortstat);
+	d.mode &= 0777;
+	/* TO DO: copy to c->aux->perm, once that exists */
+	return n;
+}
+
+static void
+profclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+profread(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	Record *r;
+	char *a = va;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, profgen);
+	acquire();
+	r = getrec(PID(c->qid));
+	release();
+	if(r == nil)
+		error(Ethread);
+	switch(QID(c->qid)){
+	case Qname:
+		return readstr(offset, va, n, r->name);
+	case Qpath:
+		return readstr(offset, va, n, r->path);
+	case Qhist:
+		i = (int)c->aux;
+		while(i < r->size && r->bucket[i] == 0)
+			i++;
+		if(i >= r->size)
+			return 0;
+		c->aux = (void*)(i+1);
+		if(n < 20)
+			error(Etoosmall);
+		return sprint(a, "%d %lud", i, r->bucket[i]);
+	case Qctl:
+		error(Eperm);
+	}
+	return 0;
+}
+
+static long
+profwrite(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	char *a = va;
+	char buf[128], *fields[128];
+	void	(*f)(int, ulong, ulong, ulong);
+
+	USED(va);
+	USED(n);
+	USED(offset);
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	switch(QID(c->qid)){
+	case Qctl:
+		if(n > sizeof(buf)-1)
+			n = sizeof(buf)-1;
+		memmove(buf, a, n);
+		buf[n] = 0;
+		i = getfields(buf, fields, nelem(fields), 1, " \t\n");
+		if(i > 0 && strcmp(fields[0], "module") == 0){
+			f = memmonitor;
+			memmonitor = nil;
+			freepmods();
+			while(--i > 0)
+				addpmod(fields[i]);
+			memmonitor = f;
+			return n;
+		}
+		if(i == 1){
+			if(strcmp(fields[0], "start") == 0){
+				if(profiler == Pnil) {
+					profiler = Psam;
+					if(!samplefn){
+						samplefn = 1;
+						kproc("prof", sampler, 0, 0);
+					}
+				}
+			}
+			else if(strncmp(fields[0], "startmp", 7) == 0){
+				if(profiler == Pnil){
+					profiler = Pmem;
+					for(a = &fields[0][7]; *a != '\0'; a++){
+						if(*a == '1'){
+							memmonitor = memprofmi;
+							mprofiler |= Mmain;
+						}
+						else if(*a == '2'){
+							heapmonitor = memprof;
+							mprofiler |= Mheap;
+						}
+						else if(*a == '3'){
+							memmonitor = memprofmi;
+							mprofiler |= Mimage;
+						}
+					};
+				}
+			}
+			else if(strcmp(fields[0], "stop") == 0){
+				profiler = Pnil;
+				mprofiler = Mnone;
+			}
+			else if(strcmp(fields[0], "end") == 0){
+				profiler = Pnil;
+				mprofiler = Mnone;
+				memmonitor = nil;
+				freeprof();
+				interval = 100;
+			}
+			else
+				error(Ebadarg);
+		}
+		else if (i == 2){
+			if(strcmp(fields[0], "interval") == 0)
+				interval = strtoul(fields[1], nil, 0);
+			else if(strcmp(fields[0], "startcp") == 0){
+				Prog *p;
+
+				acquire();
+				p = progpid(strtoul(fields[1], nil, 0));
+				if(p == nil){
+					release();
+					return -1;
+				}
+				if(profiler == Pnil){
+					profiler = Pcov;
+					p->xec = cpxec;
+				}
+				release();
+			}
+			else
+				error(Ebadarg);
+		}
+		else
+			error(Ebadarg);
+		return n;
+	default:
+		error(Eperm);
+	}
+	return 0;
+}
+
+static Record*
+newmodule(Module *m, int vm, int scale, int origin)
+{
+	int dsize;
+	Record *r, **l;
+
+	if(!vm)
+		acquire();
+	if((m->compiled && m->pctab == nil) || m->prog == nil) {
+		if(!vm)
+			release();
+		return nil;
+	}
+	if(m->compiled)
+		dsize = m->nprog * sizeof(r->bucket[0]);
+	else
+		dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]);
+	dsize *= scale;
+	dsize += origin;
+	r = malloc(sizeof(Record)+dsize);
+	if(r == nil) {
+		if(!vm)
+			release();
+		return nil;
+	}
+
+	r->id = ++ids;
+	if(ids == (1<<8)-1)
+		ids = 0;
+	kstrdup(&r->name, m->name);
+	kstrdup(&r->path, m->path);
+	r->base = m->prog;
+	r->size = dsize/sizeof(r->bucket[0]);
+	/* r->m = m; */
+	r->mtime = m->mtime;
+	r->qid.path = m->qid.path;
+	r->qid.vers = m->qid.vers;
+	memset(r->bucket, 0, dsize);
+	r->link = profile.list;
+	profile.list = r;
+
+	l = &profile.hash[HASH(m->mtime)];
+	r->hash = *l;
+	*l = r;
+
+	if(!vm)
+		release();
+	return r;
+}
+
+#define LIMBO(m)	((m)->path[0] != '$')
+
+Module*
+limbomodule(void)
+{
+	Frame *f;
+	uchar *fp;
+	Module *m;
+
+	m = R.M->m;
+	if(LIMBO(m))
+		return m;
+	for(fp = R.FP ; fp != nil; fp = f->fp){
+		f = (Frame*)fp;
+		if(f->mr != nil){
+			m = f->mr->m;
+			if(LIMBO(m))
+				return m;
+		}
+	}
+	return nil;
+}
+	
+static Record*
+mlook(Module *m, int limbo, int vm, int scale, int origin)
+{
+	Record *r;
+	void	(*f)(int, ulong, ulong, ulong);
+
+	if(limbo)
+		m = limbomodule();
+	if(m == nil)
+		return nil;
+	for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){
+		if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){
+			r->base = m->prog;
+			return r;
+		}
+	}
+	if(pmods == nil || inpmods(m->name) || inpmods(m->path)){
+		f = memmonitor;
+		memmonitor = nil;	/* prevent monitoring of our memory usage */
+		r = newmodule(m, vm, scale, origin);
+		memmonitor = f;
+		return r;
+	}
+	return nil;
+}
+
+static void
+sampler(void* a)
+{
+	int i;
+	Module *m;
+	Record *r;
+	Inst *p;
+
+	USED(a);
+	for(;;) {
+		osmillisleep(interval);
+		if(profiler != Psam)
+			break;
+		lock(&profile.l);
+		profile.time += interval;
+		if(R.M == H || (m = R.M->m) == nil){
+			unlock(&profile.l);
+			continue;
+		}
+		p = R.PC;
+		r = mlook(m, 0, 0, 1, 0);
+		if(r == nil){
+			unlock(&profile.l);
+			continue;
+		}
+		if(m->compiled && m->pctab != nil)
+			p = pc2dispc(p, m);
+		if((i = p-r->base) >= 0 && i < r->size)
+			r->bucket[i]++;
+		unlock(&profile.l);
+	}
+	samplefn = 0;
+	pexit("", 0);
+}
+
+/*
+ *	coverage profiling
+ */
+
+static void
+cpxec(Prog *p)
+{
+	int op, i;
+	Module *m;
+	Record *r;
+	Prog *n;
+
+	R = p->R;
+	R.MP = R.M->MP;
+	R.IC = p->quanta;
+
+	if(p->kill != nil){
+		char *m;
+		m = p->kill;
+		p->kill = nil;
+		error(m);
+	}
+
+	if(R.M->compiled)
+		comvec();
+	else{
+		m = R.M->m;
+		r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+		do{
+			dec[R.PC->add]();
+			op = R.PC->op;
+			if(r != nil){
+				i = R.PC-r->base;
+				if(i >= 0 && i < r->size)
+					r->bucket[i]++;
+			}
+			R.PC++;
+			optab[op]();
+			if(op == ISPAWN || op == IMSPAWN){
+				n = delruntail(Pdebug);	/* any state will do */
+				n->xec = cpxec;
+				addrun(n);
+			}
+			if(m != R.M->m){
+				m = R.M->m;
+				r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+			}
+		}while(--R.IC != 0);
+	}
+
+	p->R = R;
+}
+
+/* memory profiling */
+
+enum{
+	Mhalloc,
+	Mhfree,
+	Mgcfree,
+	Mmfree,
+	Mmalloc,
+	Mifree,
+	Mialloc,
+};
+
+static void
+memprof(int c, void *v, ulong n)
+{
+	int i, j, k;
+	ulong kk, *b;
+	Module *m;
+	Record *r;
+	Inst *p;
+	Heap *h;
+
+	USED(v);
+	USED(n);
+	if(profiler != Pmem){
+		memmonitor = nil;
+		heapmonitor = nil;
+		return;
+	}
+	lock(&profile.l);
+	m = nil;
+	if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){
+		unlock(&profile.l);
+		return;
+	}
+	h = v;
+	if(c == Mhalloc || c == Mmalloc || c == Mialloc){
+		p = R.PC;
+		if(m->compiled && m->pctab != nil)
+			p = pc2dispc(p, m);
+		if((r = mlook(m, 1, 1, 2, 2)) == nil){
+			unlock(&profile.l);
+			return;
+		}
+		i = p-r->base;
+		k = (r->id<<24) | i;
+		if(c == Mhalloc){
+			h->hprof = k;
+			j = hmsize(h)-sizeof(Heap);
+		}
+		else if(c == Mmalloc){
+			setmalloctag(v, k);
+			j = msize(v);
+		}
+		else{
+			((ulong*)v)[1] = k;
+			j = poolmsize(imagmem, v)-sizeof(ulong);
+		}
+	}
+	else{
+		if(c == Mmfree)
+			k = getmalloctag(v);
+		else if(c == Mifree)
+			k = ((ulong*)v)[1];
+		else
+			k = h->hprof;
+		if((r = getrec(k>>24)) == nil){
+			unlock(&profile.l);
+			return;
+		}
+		i = k&0xffffff;
+		if(c == Mmfree)
+			j = msize(v);
+		else if(c == Mifree)
+			j = poolmsize(imagmem, v)-sizeof(ulong);
+		else
+			j = hmsize(h)-sizeof(Heap);
+		j = -j;
+	}
+	i = 2*(i+1);
+	b = r->bucket;
+	if(i >= 0 && i < r->size){
+		if(0){
+			if(c == 1){
+				b[0] -= j;
+				b[i] -= j;
+			}
+			else if(c == 2){
+				b[1] -= j;
+				b[i+1] -= j;
+			}
+		}
+		else{
+			b[0] += j;
+			if((int)b[0] < 0)
+				b[0] = 0;
+			b[i] += j;
+			if((int)b[i] < 0)
+				b[i] = 0;
+			if(j > 0){
+				if((kk = b[0]) > b[1])
+					b[1] = kk;
+				if((kk = b[i]) > b[i+1])
+					b[i+1] = kk;
+			}
+		}
+	}
+	unlock(&profile.l);
+}
+
+/* main and image memory */
+static void
+memprofmi(int c, ulong pc, ulong v, ulong n)
+{
+	USED(pc);
+
+	if(c&2){
+		if(!(mprofiler&Mimage))
+			return;
+	}
+	else{
+		if(!(mprofiler&Mmain))
+			return;
+	}
+	switch(c){
+	case 0:
+		c = Mmalloc;
+		break;
+	case 2:
+		c = Mialloc;
+		break;
+	case 0 | 1<<8:
+		c = Mmfree;
+		break;
+	case 2 | 1<<8:
+		c = Mifree;
+		break;
+	default:
+		print("bad profile code %d\n", c);
+	}
+	memprof(c, (void*)v, n);
+}
+
+Dev profdevtab = {
+	'P',
+	"prof",
+
+	devinit,
+	profattach,
+	profwalk,
+	profstat,
+	profopen,
+	devcreate,
+	profclose,
+	profread,
+	devbread,
+	profwrite,
+	devbwrite,
+	devremove,
+	profwstat
+};
--- /dev/null
+++ b/emu/port/devprog.c
@@ -1,0 +1,1505 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<interp.h>
+#include	<isa.h>
+#include	"runt.h"
+
+/*
+ * Enable the heap device for environments that allow debugging =>
+ * Must be 1 for a production environment.
+ */
+int	SECURE = 0;
+
+enum
+{
+	Qdir,
+	Qctl,
+	Qdbgctl,
+	Qheap,
+	Qns,
+	Qnsgrp,
+	Qpgrp,
+	Qstack,
+	Qstatus,
+	Qtext,
+	Qwait,
+	Qfd,
+	Qexception,
+};
+
+/*
+ * must be in same order as enum
+ */
+Dirtab progdir[] =
+{
+	"ctl",		{Qctl},		0,			0200,
+	"dbgctl",	{Qdbgctl},	0,			0600,
+	"heap",		{Qheap},	0,			0600,
+	"ns",		{Qns},		0,			0400,
+	"nsgrp",	{Qnsgrp},	0,			0444,
+	"pgrp",		{Qpgrp},	0,			0444,
+	"stack",	{Qstack},	0,			0400,
+	"status",	{Qstatus},	0,			0444,
+	"text",		{Qtext},	0,			0000,
+	"wait",		{Qwait},	0,			0400,
+	"fd",		{Qfd},		0,			0400,
+	"exception",	{Qexception},	0,	0400,
+};
+
+enum
+{
+	CMkill,
+	CMkillgrp,
+	CMrestricted,
+	CMexceptions,
+	CMprivate
+};
+
+static
+Cmdtab progcmd[] = {
+	CMkill,	"kill",	1,
+	CMkillgrp,	"killgrp",	1,
+	CMrestricted, "restricted", 1,
+	CMexceptions, "exceptions", 2,
+	CMprivate, "private",	1,
+};
+
+enum
+{
+	CDstep,
+	CDtoret,
+	CDcont,
+	CDstart,
+	CDstop,
+	CDunstop,
+	CDmaim,
+	CDbpt
+};
+
+static
+Cmdtab progdbgcmd[] = {
+	CDstep,	"step",	0,	/* known below to be first, to cope with stepN */
+	CDtoret,	"toret",	1,
+	CDcont,	"cont",	1,
+	CDstart,	"start",	1,
+	CDstop,	"stop",	1,
+	CDunstop,	"unstop",	1,
+	CDmaim,	"maim",	1,
+	CDbpt,	"bpt",	4,
+};
+
+typedef struct Heapqry Heapqry;
+struct Heapqry
+{
+	char	fmt;
+	ulong	addr;
+	ulong	module;
+	int	count;
+};
+
+typedef struct Bpt	Bpt;
+
+struct Bpt
+{
+	Bpt	*next;
+	int	pc;
+	char	*file;
+	char	path[1];
+};
+
+typedef struct Progctl Progctl;
+struct Progctl
+{
+	Rendez	r;
+	int	ref;
+	Proc	*debugger;	/* waiting for dbgxec */
+	char	*msg;		/* reply from dbgxec */
+	int	step;		/* instructions to try */
+	int	stop;		/* stop running the program */
+	Bpt*	bpts;		/* active breakpoints */
+	Queue*	q;		/* status queue */
+};
+
+#define	QSHIFT		4		/* location in qid of pid */
+#define	QID(q)		(((ulong)(q).path&0x0000000F)>>0)
+#define QPID(pid)	(((pid)<<QSHIFT))
+#define	PID(q)		((q).vers)
+#define PATH(q)		((ulong)(q).path&~((1<<QSHIFT)-1))
+
+static char *progstate[] =			/* must correspond to include/interp.h */
+{
+	"alt",				/* blocked in alt instruction */
+	"send",				/* waiting to send */
+	"recv",				/* waiting to recv */
+	"debug",			/* debugged */
+	"ready",			/* ready to be scheduled */
+	"release",			/* interpreter released */
+	"exiting",			/* exit because of kill or error */
+	"broken",			/* thread crashed */
+};
+
+static	void	dbgstep(Progctl*, Prog*, int);
+static	void	dbgstart(Prog*);
+static	void	freebpts(Bpt*);
+static	Bpt*	delbpt(Bpt*, char*, int);
+static	Bpt*	setbpt(Bpt*, char*, int);
+static	void	mntscan(Mntwalk*, Pgrp*);
+extern	Type	*Trdchan;
+extern	Type	*Twrchan;
+extern	Module*	modules;
+static  char 	Emisalign[] = "misaligned address";
+
+static int
+proggen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+	Qid qid;
+	Prog *p;
+	char *e;
+	Osenv *o;
+	ulong pid, path, perm, len;
+
+	USED(ntab);
+
+	if(s == DEVDOTDOT){
+		mkqid(&qid, Qdir, 0, QTDIR);
+		devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp);
+		return 1;
+	}
+
+	if((ulong)c->qid.path == Qdir) {
+		if(name != nil){
+			/* ignore s and use name to find pid */
+			pid = strtoul(name, &e, 0);
+			if(pid == 0 || *e != '\0')
+				return -1;
+			acquire();
+			p = progpid(pid);
+			if(p == nil){
+				release();
+				return -1;
+			}
+		}else{
+			acquire();
+			p = progn(s);
+			if(p == nil) {
+				release();
+				return -1;
+			}
+			pid = p->pid;
+		}
+		o = p->osenv;
+		sprint(up->genbuf, "%lud", pid);
+		if(name != nil && strcmp(name, up->genbuf) != 0){
+			release();
+			return -1;
+		}
+		mkqid(&qid, pid<<QSHIFT, pid, QTDIR);
+		devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp);
+		release();
+		return 1;
+	}
+
+	if(s >= nelem(progdir))
+		return -1;
+	tab = &progdir[s];
+	path = PATH(c->qid);
+
+	acquire();
+	p = progpid(PID(c->qid));
+	if(p == nil) {
+		release();
+		return -1;
+	}
+
+	o = p->osenv;
+
+	perm = tab->perm;
+	if((perm & 7) == 0)
+		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+
+	len = tab->length;
+	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+	devdir(c, qid, tab->name, len, o->user, perm, dp);
+	release();
+	return 1;
+}
+
+static Chan*
+progattach(char *spec)
+{
+	return devattach('p', spec);
+}
+
+static Walkqid*
+progwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, proggen);
+}
+
+static int
+progstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, proggen);
+}
+
+static Chan*
+progopen(Chan *c, int omode)
+{
+	Prog *p;
+	Osenv *o;
+	Progctl *ctl;
+	int perm;
+
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, 0, 0, proggen);
+
+	acquire();
+	if (waserror()) {
+		release();
+		nexterror();
+	}
+	p = progpid(PID(c->qid));
+	if(p == nil)
+		error(Ethread);
+	o = p->osenv;
+	perm = progdir[QID(c->qid)-1].perm;
+	if((perm & 7) == 0)
+		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
+	devpermcheck(o->user, perm, omode);
+	omode = openmode(omode);
+
+	switch(QID(c->qid)){
+	default:
+		error(Egreg);
+	case Qnsgrp:
+	case Qpgrp:
+	case Qtext:
+	case Qstatus:
+	case Qstack:
+	case Qctl:
+	case Qfd:
+	case Qexception:
+		break;
+	case Qwait:
+		c->aux = qopen(1024, Qmsg, nil, nil);
+		if(c->aux == nil)
+			error(Enomem);
+		o->childq = c->aux;
+		break;
+	case Qns:
+		c->aux = malloc(sizeof(Mntwalk));
+		if(c->aux == nil)
+			error(Enomem);
+		break;
+	case Qheap:
+		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+			error(Eperm);
+		c->aux = malloc(sizeof(Heapqry));
+		if(c->aux == nil)
+			error(Enomem);
+		break;
+	case Qdbgctl:
+		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
+			error(Eperm);
+		ctl = malloc(sizeof(Progctl));
+		if(ctl == nil)
+			error(Enomem);
+		ctl->q = qopen(1024, Qmsg, nil, nil);
+		if(ctl->q == nil) {
+			free(ctl);
+			error(Enomem);
+		}
+		ctl->bpts = nil;
+		ctl->ref = 1;
+		c->aux = ctl;
+		break;
+	}
+	if(p->state != Pexiting)
+		c->qid.vers = p->pid;
+
+	poperror();
+	release();
+	c->offset = 0;
+	c->mode = omode;
+	c->flag |= COPEN;
+	return c;
+}
+
+static int
+progwstat(Chan *c, uchar *db, int n)
+{
+	Dir d;
+	Prog *p;
+	char *u;
+	Osenv *o;
+
+	if(c->qid.type&QTDIR)
+		error(Eperm);
+	acquire();
+	p = progpid(PID(c->qid));
+	if(p == nil) {
+		release();
+		error(Ethread);
+	}
+
+	u = up->env->user;
+	o = p->osenv;
+	if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) {
+		release();
+		error(Eperm);
+	}
+
+	n = convM2D(db, n, &d, nil);
+	if(n == 0){
+		release();
+		error(Eshortstat);
+	}
+	if(d.mode != ~0UL)
+		o->pgrp->progmode = d.mode&0777;
+	release();
+	return n;
+}
+
+static void
+closedbgctl(Progctl *ctl, Prog *p)
+{
+	Osenv *o;
+
+	if(ctl->ref-- > 1)
+		return;
+	freebpts(ctl->bpts);
+	if(p != nil){
+		o = p->osenv;
+		if(o->debug == ctl){
+			o->debug = nil;
+			p->xec = xec;
+		}
+	}
+	qfree(ctl->q);
+	free(ctl);
+}
+
+static void
+progclose(Chan *c)
+{
+	int i;
+	Prog *f;
+	Osenv *o;
+	Progctl *ctl;
+
+	switch(QID(c->qid)) {
+	case Qns:
+	case Qheap:
+		free(c->aux);
+		break;
+	case Qdbgctl:
+		if((c->flag & COPEN) == 0)
+			return;
+		ctl = c->aux;
+		acquire();
+		closedbgctl(ctl, progpid(PID(c->qid)));
+		release();
+		break;
+	case Qwait:
+		acquire();
+		i = 0;
+		for(;;) {
+			f = progn(i++);
+			if(f == nil)
+				break;
+			o = f->osenv;
+			if(o->waitq == c->aux)
+				o->waitq = nil;
+			if(o->childq == c->aux)
+				o->childq = nil;
+		}
+		release();
+		qfree(c->aux);
+	}
+}
+
+static int
+progsize(Prog *p)
+{
+	int size;
+	Frame *f;
+	uchar *fp;
+	Modlink *m;
+
+	m = p->R.M;
+	size = 0;
+	if(m->MP != H)
+		size += hmsize(D2H(m->MP));
+	if(m->prog != nil)
+		size += msize(m->prog);
+
+	fp = p->R.FP;
+	while(fp != nil) {
+		f = (Frame*)fp;
+		fp = f->fp;
+		if(f->mr != nil) {
+			if(f->mr->MP != H)
+				size += hmsize(D2H(f->mr->MP));
+			if(f->mr->prog != nil)
+				size += msize(f->mr->prog);
+		}
+		if(f->t == nil)
+			size += msize(SEXTYPE(f));
+	}
+	return size/1024;
+}
+
+static long
+progoffset(long offset, char *va, int *np)
+{
+	if(offset > 0) {
+		offset -= *np;
+		if(offset < 0) {
+			memmove(va, va+*np+offset, -offset);
+			*np = -offset;
+		}
+		else
+			*np = 0;
+	}
+	return offset;
+}
+
+static int
+progqidwidth(Chan *c)
+{
+	char buf[32];
+
+	return sprint(buf, "%lud", c->qid.vers);
+}
+
+int
+progfdprint(Chan *c, int fd, int w, char *s, int ns)
+{
+	int n;
+
+	if(w == 0)
+		w = progqidwidth(c);
+	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
+		fd,
+		&"r w rw"[(c->mode&3)<<1],
+		devtab[c->type]->dc, c->dev,
+		c->qid.path, w, c->qid.vers, c->qid.type,
+		c->iounit, c->offset, c->name->s);
+	return n;
+}
+
+static int
+progfds(Osenv *o, char *va, int count, long offset)
+{
+	Fgrp *f;
+	Chan *c;
+	int n, i, w, ww;
+
+	f = o->fgrp;	/* f is not locked because we've acquired */
+	n = readstr(0, va, count, o->pgrp->dot->name->s);
+	n += snprint(va+n, count-n, "\n");
+	offset = progoffset(offset, va, &n);
+	/* compute width of qid.path */
+	w = 0;
+	for(i = 0; i <= f->maxfd; i++) {
+		c = f->fd[i];
+		if(c == nil)
+			continue;
+		ww = progqidwidth(c);
+		if(ww > w)
+			w = ww;
+	}
+	for(i = 0; i <= f->maxfd; i++) {
+		c = f->fd[i];
+		if(c == nil)
+			continue;
+		n += progfdprint(c, i, w, va+n, count-n);
+		offset = progoffset(offset, va, &n);
+	}
+	return n;
+}
+
+Inst *
+pc2dispc(Inst *pc, Module *mod)
+{
+	ulong l, u, m, v;
+	ulong *tab = mod->pctab;
+
+	v = (ulong)pc - (ulong)mod->prog;
+	l = 0;
+	u = mod->nprog-1;
+	while(l < u){
+		m = (l+u+1)/2;
+		if(tab[m] < v)
+			l = m;
+		else if(tab[m] > v)
+			u = m-1;
+		else
+			l = u = m;
+	}
+	if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v)
+		return &mod->prog[u];
+	return 0;
+}
+
+static int
+progstack(REG *reg, int state, char *va, int count, long offset)
+{
+	int n;
+	Frame *f;
+	Inst *pc;
+	uchar *fp;
+	Modlink *m;
+
+	n = 0;
+	m = reg->M;
+	fp = reg->FP;
+	pc = reg->PC;
+
+	/*
+	 * all states other than debug and ready block,
+	 * but interp has already advanced the PC
+	 */
+	if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog)
+		pc--;
+	if(m->compiled && m->m->pctab != nil)
+		pc = pc2dispc(pc, m->m);
+
+	while(fp != nil) {
+		f = (Frame*)fp;
+		n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n",
+				(ulong)f,		/* FP */
+				(ulong)(pc - m->prog),	/* PC in dis instructions */
+				(ulong)m->MP,		/* MP */
+				(ulong)m->prog,	/* Code for module */
+				m->compiled && m->m->pctab == nil,	/* True if native assembler: fool stack utility for now */
+				m->m->path);	/* File system path */
+
+		if(offset > 0) {
+			offset -= n;
+			if(offset < 0) {
+				memmove(va, va+n+offset, -offset);
+				n = -offset;
+			}
+			else
+				n = 0;
+		}
+
+		pc = f->lr;
+		fp = f->fp;
+		if(f->mr != nil)
+			m = f->mr;
+		if(!m->compiled)
+			pc--;
+		else if(m->m->pctab != nil)
+			pc = pc2dispc(pc, m->m)-1;
+	}
+	return n;
+}
+
+static int
+calldepth(REG *reg)
+{
+	int n;
+	uchar *fp;
+
+	n = 0;
+	for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp)
+		n++;
+	return n;
+}
+
+static int
+progheap(Heapqry *hq, char *va, int count, ulong offset)
+{
+	WORD *w;
+	void *p;
+	List *hd;
+	Array *a;
+	char *fmt, *str;
+	Module *m;
+	Modlink *ml;
+	Channel *c;
+	ulong addr;
+	String *ss;
+	union { REAL r; LONG l; WORD w[2]; } rock;
+	int i, s, n, len, signed_off;
+	Type *t;
+
+	n = 0;
+	s = 0;
+	signed_off = offset;
+	addr = hq->addr;
+	for(i = 0; i < hq->count; i++) {
+		switch(hq->fmt) {
+		case 'W':
+			if(addr & 3)
+				return -1;
+			n += snprint(va+n, count-n, "%d\n", *(WORD*)addr);
+			s = sizeof(WORD);
+			break;
+		case 'B':
+			n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr);
+			s = sizeof(BYTE);
+			break;
+		case 'V':
+			if(addr & 3)
+				return -1;
+			w = (WORD*)addr;
+			rock.w[0] = w[0];
+			rock.w[1] = w[1];
+			n += snprint(va+n, count-n, "%lld\n", rock.l);
+			s = sizeof(LONG);
+			break;
+		case 'R':
+			if(addr & 3)
+				return -1;
+			w = (WORD*)addr;
+			rock.w[0] = w[0];
+			rock.w[1] = w[1];
+			n += snprint(va+n, count-n, "%g\n", rock.r);
+			s = sizeof(REAL);
+			break;
+		case 'I':
+			if(addr & 3)
+				return -1;
+			for(m = modules; m != nil; m = m->link)
+				if(m == (Module*)hq->module)
+					break;
+			if(m == nil)
+				error(Ebadctl);
+			addr = (ulong)(m->prog+addr);
+			n += snprint(va+n, count-n, "%D\n", (Inst*)addr);
+			s = sizeof(Inst);
+			break;
+		case 'P':
+			if(addr & 3)
+				return -1;
+			p = *(void**)addr;
+			fmt = "nil\n";
+			if(p != H)
+				fmt = "%lux\n";
+			n += snprint(va+n, count-n, fmt, p);
+			s = sizeof(WORD);
+			break;
+		case 'L':
+			if(addr & 3)
+				return -1;
+			hd = *(List**)addr;
+			if(hd == H || D2H(hd)->t != &Tlist)
+				return -1;
+			n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data);
+			s = sizeof(WORD);
+			break;
+		case 'A':
+			if(addr & 3)
+				return -1;
+			a = *(Array**)addr;
+			if(a == H)
+				n += snprint(va+n, count-n, "nil\n");
+			else {
+				if(D2H(a)->t != &Tarray)
+					return -1;
+				n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data);
+			}
+			s = sizeof(WORD);
+			break;
+		case 'C':
+			if(addr & 3)
+				return -1;
+			ss = *(String**)addr;
+			if(ss == H)
+				ss = &snil;
+			else
+			if(D2H(ss)->t != &Tstring)
+				return -1;
+			n += snprint(va+n, count-n, "%d.", abs(ss->len));
+			str = string2c(ss);
+			len = strlen(str);
+			if(count-n < len)
+				len = count-n;
+			if(len > 0) {
+				memmove(va+n, str, len);
+				n += len;
+			}
+			break;
+		case 'M':
+			if(addr & 3)
+				return -1;
+			ml = *(Modlink**)addr;
+			fmt = ml == H ? "nil\n" : "%lux\n";
+			n += snprint(va+n, count-n, fmt, ml->MP);
+			s = sizeof(WORD);
+			break;
+		case 'c':
+			if(addr & 3)
+				return -1;
+			c = *(Channel**)addr;
+			if(c == H)
+				n += snprint(va+n, count-n, "nil\n");
+			else{
+				t = D2H(c)->t;
+				if(t != &Tchannel && t != Trdchan && t != Twrchan)
+					return -1;
+				if(c->buf == H)
+					n += snprint(va+n, count-n, "0.%lux\n", (ulong)c);
+				else
+					n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size);
+			}
+			break;
+			
+		}
+		addr += s;
+		if(signed_off > 0) {
+			signed_off -= n;
+			if(signed_off < 0) {
+				memmove(va, va+n+signed_off, -signed_off);
+				n = -signed_off;
+			}
+			else
+				n = 0;
+		}
+	}
+	return n;
+}
+
+WORD
+modstatus(REG *r, char *ptr, int len)
+{
+	Inst *PC;
+	Frame *f;
+
+	if(r->M->m->name[0] == '$') {
+		f = (Frame*)r->FP;
+		snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name);
+		if(f->mr->compiled)
+			return (WORD)f->lr;
+		return f->lr - f->mr->prog;
+	}
+	memmove(ptr, r->M->m->name, len);
+	if(r->M->compiled)
+		return (WORD)r->PC;
+	PC = r->PC;
+	/* should really check for blocked states */
+	if(PC > r->M->prog)
+		PC--;
+	return PC - r->M->prog;
+}
+
+static void
+int2flag(int flag, char *s)
+{
+	if(flag == 0){
+		*s = '\0';
+		return;
+	}
+	*s++ = '-';
+	if(flag & MAFTER)
+		*s++ = 'a';
+	if(flag & MBEFORE)
+		*s++ = 'b';
+	if(flag & MCREATE)
+		*s++ = 'c';
+	if(flag & MCACHE)
+		*s++ = 'C';
+	*s = '\0';
+}
+
+static char*
+progtime(ulong msec, char *buf, char *ebuf)
+{
+	int tenths, sec;
+
+	tenths = msec/100;
+	sec = tenths/10;
+	seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10);
+	return buf;
+}
+
+static long
+progread(Chan *c, void *va, long n, vlong offset)
+{
+	int i;
+	Prog *p;
+	Osenv *o;
+	Mntwalk *mw;
+	ulong grpid;
+	char *a = va;
+	Progctl *ctl;
+	char mbuf[64], timebuf[12];
+	char flag[10];
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, proggen);
+
+	switch(QID(c->qid)){
+	case Qdbgctl:
+		ctl = c->aux;
+		return qread(ctl->q, va, n);
+	case Qstatus:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil || p->state == Pexiting || p->R.M == H) {
+			release();
+			snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s",
+				PID(c->qid),
+				0,
+				eve,
+				progtime(0, timebuf, timebuf+sizeof(timebuf)),
+				progstate[Pexiting],
+				0,
+				"[$Sys]");
+			return readstr(offset, va, n, up->genbuf);
+		}
+		modstatus(&p->R, mbuf, sizeof(mbuf));
+		o = p->osenv;
+		snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s",
+			p->pid,
+			p->group!=nil? p->group->id: 0,
+			o->user,
+			progtime(p->ticks, timebuf, timebuf+sizeof(timebuf)),
+			progstate[p->state],
+			progsize(p),
+			mbuf);
+		release();
+		return readstr(offset, va, n, up->genbuf);
+	case Qwait:
+		return qread(c->aux, va, n);
+	case Qns:
+		acquire();
+		if(waserror()){
+			release();
+			nexterror();
+		}
+		p = progpid(PID(c->qid));
+		if(p == nil)
+			error(Ethread);
+		mw = c->aux;
+		if(mw->cddone){
+			poperror();
+			release();
+			return 0;
+		}
+		o = p->osenv;
+		mntscan(mw, o->pgrp);
+		if(mw->mh == 0) {
+			mw->cddone = 1;
+			i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s);
+			poperror();
+			release();
+			return i;
+		}
+		int2flag(mw->cm->mflag, flag);
+		if(strcmp(mw->cm->to->name->s, "#M") == 0){
+			i = snprint(a, n, "mount %s %s %s %s\n", flag,
+				mw->cm->to->mchan->name->s,
+				mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
+		}else
+			i = snprint(a, n, "bind %s %s %s\n", flag,
+				mw->cm->to->name->s, mw->mh->from->name->s);
+		poperror();
+		release();
+		return i;
+	case Qnsgrp:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		grpid = ((Osenv *)p->osenv)->pgrp->pgrpid;
+		release();
+		return readnum(offset, va, n, grpid, NUMSIZE);
+	case Qpgrp:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		grpid = p->group!=nil? p->group->id: 0;
+		release();
+		return readnum(offset, va, n, grpid, NUMSIZE);
+	case Qstack:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil || p->state == Pexiting) {
+			release();
+			error(Ethread);
+		}
+		if(p->state == Pready) {
+			release();
+			error(Estopped);
+		}
+		n = progstack(&p->R, p->state, va, n, offset);
+		release();
+		return n;
+	case Qheap:
+		acquire();
+		if(waserror()){
+			release();
+			nexterror();
+		}
+		n = progheap(c->aux, va, n, offset);
+		if(n == -1)
+			error(Emisalign);
+		poperror();
+		release();
+		return n;
+	case Qfd:
+		acquire();
+		if(waserror()) {
+			release();
+			nexterror();
+		}
+		p = progpid(PID(c->qid));
+		if(p == nil)
+			error(Ethread);
+		o = p->osenv;
+		n = progfds(o, va, n, offset);
+		poperror();
+		release();
+		return n;
+	case Qexception:
+		acquire();
+		p = progpid(PID(c->qid));
+		if(p == nil) {
+			release();
+			error(Ethread);
+		}
+		if(p->exstr == nil)
+			up->genbuf[0] = 0;
+		else
+			snprint(up->genbuf, sizeof(up->genbuf), p->exstr);
+		release();
+		return readstr(offset, va, n, up->genbuf);
+	}
+	error(Egreg);
+	return 0;
+}
+
+static void
+mntscan(Mntwalk *mw, Pgrp *pg)
+{
+	Mount *t;
+	Mhead *f;
+	int nxt, i;
+	ulong last, bestmid;
+
+	rlock(&pg->ns);
+
+	nxt = 0;
+	bestmid = ~0;
+
+	last = 0;
+	if(mw->mh)
+		last = mw->cm->mountid;
+
+	for(i = 0; i < MNTHASH; i++) {
+		for(f = pg->mnthash[i]; f; f = f->hash) {
+			for(t = f->mount; t; t = t->next) {
+				if(mw->mh == 0 ||
+				  (t->mountid > last && t->mountid < bestmid)) {
+					mw->cm = t;
+					mw->mh = f;
+					bestmid = mw->cm->mountid;
+					nxt = 1;
+				}
+			}
+		}
+	}
+	if(nxt == 0)
+		mw->mh = 0;
+
+	runlock(&pg->ns);
+}
+
+static long
+progwrite(Chan *c, void *va, long n, vlong offset)
+{
+	Prog *p, *f;
+	Heapqry *hq;
+	char buf[512];
+	Progctl *ctl;
+	char *b;
+	int i, pc;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	USED(offset);
+	USED(va);
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+
+	acquire();
+	if(waserror()) {
+		release();
+		nexterror();
+	}
+	p = progpid(PID(c->qid));
+	if(p == nil)
+		error(Ethread);
+
+	switch(QID(c->qid)){
+	case Qctl:
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		ct = lookupcmd(cb, progcmd, nelem(progcmd));
+		switch(ct->index){
+		case CMkillgrp:
+			killgrp(p, "killed");
+			break;
+		case CMkill:
+			killprog(p, "killed");
+			break;
+		case CMrestricted:
+			p->flags |= Prestrict;
+			break;
+		case CMexceptions:
+			if(p->group->id != p->pid)
+				error(Eperm);
+			if(strcmp(cb->f[1], "propagate") == 0)
+				p->flags |= Ppropagate;
+			else if(strcmp(cb->f[1], "notifyleader") == 0)
+				p->flags |= Pnotifyleader;
+			else
+				error(Ebadctl);
+			break;
+		case CMprivate:
+			p->group->flags |= Pprivatemem;
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+	case Qdbgctl:
+		cb = parsecmd(va, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0)
+			ct = progdbgcmd;
+		else
+			ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd));
+		switch(ct->index){
+		case CDstep:
+			if(cb->nf == 1)
+				i = strtoul(cb->f[0]+4, nil, 0);
+			else
+				i = strtoul(cb->f[1], nil, 0);
+			dbgstep(c->aux, p, i);
+			break;
+		case CDtoret:
+			f = currun();
+			i = calldepth(&p->R);
+			while(f->kill == nil) {
+				dbgstep(c->aux, p, 1024);
+				if(i > calldepth(&p->R))
+					break;
+			}
+			break;
+		case CDcont:
+			f = currun();
+			while(f->kill == nil)
+				dbgstep(c->aux, p, 1024);
+			break;
+		case CDstart:
+			dbgstart(p);
+			break;
+		case CDstop:
+			ctl = c->aux;
+			ctl->stop = 1;
+			break;
+		case CDunstop:
+			ctl = c->aux;
+			ctl->stop = 0;
+			break;
+		case CDbpt:
+			pc = strtoul(cb->f[3], nil, 10);
+			ctl = c->aux;
+			if(strcmp(cb->f[1], "set") == 0)
+				ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc);
+			else if(strcmp(cb->f[1], "del") == 0)
+				ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc);
+			else
+				error(Ebadctl);
+			break;
+		case CDmaim:
+			p->kill = "maim";
+			break;
+		}
+		poperror();
+		free(cb);
+		break;
+	case Qheap:
+		/*
+		 * Heap query:
+		 *	addr.Fn
+		 *	pc+module.In
+		 */
+		i = n;
+		if(i > sizeof(buf)-1)
+			i = sizeof(buf)-1;
+		memmove(buf, va, i);
+		buf[i] = '\0';
+		hq = c->aux;
+		hq->addr = strtoul(buf, &b, 0);
+		if(*b == '+')
+			hq->module = strtoul(b, &b, 0);
+		if(*b++ != '.')
+			error(Ebadctl);
+		hq->fmt = *b++;
+		hq->count = strtoul(b, nil, 0);
+		break;
+	default:
+		print("unknown qid in procwrite\n");
+		error(Egreg);
+	}
+	poperror();
+	release();
+	return n;
+}
+
+static Bpt*
+setbpt(Bpt *bpts, char *path, int pc)
+{
+	int n;
+	Bpt *b;
+
+	n = strlen(path);
+	b = mallocz(sizeof *b + n, 0);
+	if(b == nil)
+		return bpts;
+	b->pc = pc;
+	memmove(b->path, path, n+1);
+	b->file = b->path;
+	path = strrchr(b->path, '/');
+	if(path != nil)
+		b->file = path + 1;
+	b->next = bpts;
+	return b;
+}
+
+static Bpt*
+delbpt(Bpt *bpts, char *path, int pc)
+{
+	Bpt *b, **last;
+
+	last = &bpts;
+	for(b = bpts; b != nil; b = b->next){
+		if(b->pc == pc && strcmp(b->path, path) == 0) {
+			*last = b->next;
+			free(b);
+			break;
+		}
+		last = &b->next;
+	}
+	return bpts;
+}
+
+static void
+freebpts(Bpt *b)
+{
+	Bpt *next;
+
+	for(; b != nil; b = next){
+		next = b->next;
+		free(b);
+	}
+}
+
+static void
+telldbg(Progctl *ctl, char *msg)
+{
+	kstrcpy(ctl->msg, msg, ERRMAX);
+	ctl->debugger = nil;
+	Wakeup(&ctl->r);
+}
+
+static void
+dbgstart(Prog *p)
+{
+	Osenv *o;
+	Progctl *ctl;
+
+	o = p->osenv;
+	ctl = o->debug;
+	if(ctl != nil && ctl->debugger != nil)
+		error("prog debugged");
+	if(p->state == Pdebug)
+		addrun(p);
+	o->debug = nil;
+	p->xec = xec;
+}
+
+static int
+xecdone(void *vc)
+{
+	Progctl *ctl = vc;
+
+	return ctl->debugger == nil;
+}
+
+static void
+dbgstep(Progctl *vctl, Prog *p, int n)
+{
+	Osenv * volatile o;
+	Progctl * volatile ctl;
+	char buf[ERRMAX+20], *msg;
+
+	if(p == currun())
+		error("cannot step yourself");
+
+	if(p->state == Pbroken)
+		error("prog broken");
+
+	ctl = vctl;
+	if(ctl->debugger != nil)
+		error("prog already debugged");
+
+	o = p->osenv;
+	if(o->debug == nil) {
+		o->debug = ctl;
+		p->xec = dbgxec;
+	}else if(o->debug != ctl)
+		error("prog already debugged");
+
+	ctl->step = n;
+	if(p->state == Pdebug)
+		addrun(p);
+	ctl->debugger = up;
+	strcpy(buf, "child: ");
+	msg = buf+7;
+	ctl->msg = msg;
+
+	/*
+	 * wait for reply from dbgxec; release is okay because prog is now
+	 * debugged, cannot exit.
+	 */
+	if(waserror()){
+		acquire();
+		ctl->debugger = nil;
+		ctl->msg = nil;
+		o->debug = nil;
+		p->xec = xec;
+		nexterror();
+	}
+	release();
+	Sleep(&ctl->r, xecdone, ctl);
+	poperror();
+	acquire();
+	if(msg[0] != '\0')
+		error(buf);
+}
+
+void
+dbgexit(Prog *kid, int broken, char *estr)
+{
+	int n;
+	Osenv *e;
+	Progctl *ctl;
+	char buf[ERRMAX+20];
+
+	e = kid->osenv;
+	ctl = e->debug;
+	e->debug = nil;
+	kid->xec = xec;
+
+	if(broken)
+		n = snprint(buf, sizeof(buf), "broken: %s", estr);
+	else
+		n = snprint(buf, sizeof(buf), "exited");
+	if(ctl->debugger)
+		telldbg(ctl, buf);
+	qproduce(ctl->q, buf, n);
+}
+
+static void
+dbgaddrun(Prog *p)
+{
+	Osenv *o;
+
+	p->state = Pdebug;
+	p->addrun = nil;
+	o = p->osenv;
+	if(o->rend != nil)
+		Wakeup(o->rend);
+	o->rend = nil;
+}
+
+static int
+bdone(void *vp)
+{
+	Prog *p = vp;
+
+	return p->addrun == nil;
+}
+
+static void
+dbgblock(Prog *p)
+{
+	Osenv *o;
+	Progctl *ctl;
+
+	o = p->osenv;
+	ctl = o->debug;
+	qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state]));
+	pushrun(p);
+	p->addrun = dbgaddrun;
+	o->rend = &up->sleep;
+
+	/*
+	 * bdone(p) is safe after release because p is being debugged,
+	 * so cannot exit.
+	 */
+	if(waserror()){
+		acquire();
+		nexterror();
+	}
+	release();
+	if(o->rend != nil)
+		Sleep(o->rend, bdone, p);
+	poperror();
+	acquire();
+	if(p->kill != nil)
+		error(p->kill);
+	ctl = o->debug;
+	if(ctl != nil)
+		qproduce(ctl->q, "run", 3);
+}
+
+void
+dbgxec(Prog *p)
+{
+	Bpt *b;
+	Prog *kid;
+	Osenv *env;
+	Progctl *ctl;
+	int op, pc, n;
+	char buf[ERRMAX+10];
+	extern void (*dec[])(void);
+
+	env = p->osenv;
+	ctl = env->debug;
+	ctl->ref++;
+	if(waserror()){
+		closedbgctl(ctl, p);
+		nexterror();
+	}
+
+	R = p->R;
+	R.MP = R.M->MP;
+
+	R.IC = p->quanta;
+	if((ulong)R.IC > ctl->step)
+		R.IC = ctl->step;
+	if(ctl->stop)
+		R.IC = 0;
+
+
+	buf[0] = '\0';
+
+	if(R.IC != 0 && R.M->compiled) {
+		comvec();
+		if(p != currun())
+			dbgblock(p);
+		goto save;
+	}
+
+	while(R.IC != 0) {
+		if(0)
+			print("step: %lux: %s %4ld %D\n",
+				(ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC);
+
+		dec[R.PC->add]();
+		op = R.PC->op;
+		R.PC++;
+		optab[op]();
+
+		/*
+		 * check notification about new progs
+		 */
+		if(op == ISPAWN || op == IMSPAWN) {
+			/* pick up the kid from the end of the run queue */
+			kid = delruntail(Pdebug);
+			n = snprint(buf, sizeof buf, "new %d", kid->pid);
+			qproduce(ctl->q, buf, n);
+			buf[0] = '\0';
+		}
+		if(op == ILOAD) {
+			n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s));
+			qproduce(ctl->q, buf, n);
+			buf[0] = '\0';
+		}
+
+		/*
+		 * check for returns at big steps
+		 */
+		if(op == IRET)
+			R.IC = 1;
+
+		/*
+		 * check for blocked progs
+		 */
+		if(R.IC == 1 && p != currun())
+			dbgblock(p);
+		if(ctl->stop)
+			R.IC = 1;
+		R.IC--;
+
+		if(ctl->bpts == nil)
+			continue;
+
+		pc = R.PC - R.M->prog;
+		for(b = ctl->bpts; b != nil; b = b->next) {
+			if(pc == b->pc &&
+			  (strcmp(R.M->m->path, b->path) == 0 ||
+			   strcmp(R.M->m->path, b->file) == 0)) {
+				strcpy(buf, "breakpoint");
+				goto save;
+			}
+		}
+	}
+save:
+	if(ctl->stop)
+		strcpy(buf, "stopped");
+
+	p->R = R;
+
+	if(env->debug == nil) {
+		poperror();
+		return;
+	}
+
+	if(p == currun())
+		delrun(Pdebug);
+
+	telldbg(env->debug, buf);
+	poperror();
+	closedbgctl(env->debug, p);
+}
+
+Dev progdevtab = {
+	'p',
+	"prog",
+
+	devinit,
+	progattach,
+	progwalk,
+	progstat,
+	progopen,
+	devcreate,
+	progclose,
+	progread,
+	devbread,
+	progwrite,
+	devbwrite,
+	devremove,
+	progwstat
+};
--- /dev/null
+++ b/emu/port/devroot.c
@@ -1,0 +1,150 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+extern Rootdata rootdata[];
+extern Dirtab roottab[];
+extern int	rootmaxq;
+
+static Chan*
+rootattach(char *spec)
+{
+	int i;
+	ulong len;
+	Rootdata *r;
+
+	if(*spec)
+		error(Ebadspec);
+	for (i = 0; i < rootmaxq; i++){
+		r = &rootdata[i];
+		if (r->sizep){
+			len = *r->sizep;
+			r->size = len;
+			roottab[i].length = len;
+		}
+	}
+	return devattach('/', spec);
+}
+
+static int
+rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp)
+{
+	int p, i;
+	Rootdata *r;
+
+	if(s == DEVDOTDOT){
+		p = rootdata[c->qid.path].dotdot;
+		c->qid.path = p;
+		c->qid.type = QTDIR;
+		name = "#/";
+		if(p != 0){
+			for(i = 0; i < rootmaxq; i++)
+				if(roottab[i].qid.path == c->qid.path){
+					name = roottab[i].name;
+					break;
+				}
+		}
+		devdir(c, c->qid, name, 0, eve, 0555, dp);
+		return 1;
+	}
+	if(name != nil){
+		isdir(c);
+		r = &rootdata[(int)c->qid.path];
+		tab = r->ptr;
+		for(i=0; i<r->size; i++, tab++)
+			if(strcmp(tab->name, name) == 0){
+				devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+				return 1;
+			}
+		return -1;
+	}
+	if(s >= nd)
+		return -1;
+	tab += s;
+	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+	return 1;
+}
+
+static Walkqid*
+rootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	ulong p;
+
+	p = c->qid.path;
+	if(nname == 0)
+		p = rootdata[p].dotdot;
+	return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static int
+rootstat(Chan *c, uchar *dp, int n)
+{
+	int p;
+
+	p = rootdata[c->qid.path].dotdot;
+	return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+static Chan*
+rootopen(Chan *c, int omode)
+{
+	int p;
+
+	p = rootdata[c->qid.path].dotdot;
+	return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen);
+}
+
+/*
+ * sysremove() knows this is a nop
+ */
+static void	 
+rootclose(Chan *c)
+{
+	USED(c);
+}
+
+static long	 
+rootread(Chan *c, void *buf, long n, vlong offset)
+{
+	ulong p, len;
+	uchar *data;
+
+	p = c->qid.path;
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen);
+	len = rootdata[p].size;
+	if(offset < 0 || offset >= len)
+		return 0;
+	if(offset+n > len)
+		n = len - offset;
+	data = rootdata[p].ptr;
+	memmove(buf, data+offset, n);
+	return n;
+}
+
+static long	 
+rootwrite(Chan *c, void *a, long n, vlong off)
+{
+	USED(c); USED(a); USED(n); USED(off);
+	error(Eperm);
+	return 0;
+}
+
+Dev rootdevtab = {
+	'/',
+	"root",
+
+	devinit,
+	rootattach,
+	rootwalk,
+	rootstat,
+	rootopen,
+	devcreate,
+	rootclose,
+	rootread,
+	devbread,
+	rootwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devsign.c
@@ -1,0 +1,440 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+#include	<isa.h>
+#include	"runt.h"
+#include "mp.h"
+#include "libsec.h"
+#include "../../libkeyring/keys.h"
+
+/*
+ * experimental version of signed modules
+ */
+
+enum
+{
+	Qdir,
+	Qkey,
+	Qctl,
+
+	Maxkey = 2048
+};
+
+static Dirtab signdir[] =
+{
+	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
+	"signerkey",	{Qkey},	0,			0644,
+	"signerctl",	{Qctl},	0,			0600,
+};
+
+typedef struct Get Get;
+struct Get {
+	uchar*	p;
+	uchar*	ep;
+};
+
+#define	G32(b)	((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3])
+
+static	int	vc(Get*);
+static	int	vs(void*, int, Get*, int);
+static Signerkey* findsignerkey(Skeyset*, char*, int, char*);
+extern vlong		osusectime(void);
+
+int
+verifysigner(uchar *sign, int len, uchar *data, ulong ndata)
+{
+	Get sig;
+	int alg;
+	ulong issued, expires, now;
+	int footprint, r, n;
+	uchar buf[128], digest[SHA1dlen];
+	DigestState *ds;
+	volatile struct {mpint* b;} b;
+	volatile struct {mpint* s;} s;
+	SigAlgVec *sa;
+	Signerkey *key;
+	Skeyset *sigs;
+
+	/* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */
+	sigs = up->env->sigs;
+	if(sigs == nil)
+		return 1;	/* not enforcing signed modules */
+	sig.p = sign;
+	sig.ep = sign+len;
+	alg = vc(&sig);
+	if(alg != 2)
+		return 0;	/* we do only SHA1/RSA */
+	sa = findsigalg("rsa");
+	if(sa == nil)
+		return 0;
+	if(vs(buf, sizeof(buf), &sig, 4) < 0)
+		return 0;
+	now = osusectime()/1000000;
+	issued = G32(buf);
+	if(vs(buf, sizeof(buf), &sig, 4) < 0)
+		return 0;
+	if(issued != 0 && now < issued)
+		return 0;
+	expires = G32(buf);
+	if(expires != 0 && now >= expires)
+		return 0;
+	footprint = vc(&sig) << 8;
+	footprint |= vc(&sig);
+	if(footprint < 0)
+		return 0;
+	r = 0;
+	b.b = nil;
+	s.s = nil;
+	qlock(&sigs->l);
+	if(waserror())
+		goto out;
+	if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0)	/* owner */
+		goto out;
+	buf[n] = 0;
+	key = findsignerkey(sigs, sa->name, footprint, (char*)buf);
+	if(key == nil)
+		goto out;
+	n += snprint((char*)buf+n, NUMSIZE, " %lud", expires);
+	ds = sha1(buf, n, nil, nil);
+	sha1(data, ndata, digest, ds);
+	b.b = betomp(digest, SHA1dlen, nil);
+	if(b.b == nil)
+		goto out;
+	s.s = betomp(sig.p, sig.ep-sig.p, nil);
+	if(s.s == nil)
+		goto out;
+	r = (*sa->verify)(b.b, s.s, key->pk);
+out:
+	qunlock(&sigs->l);
+	if(b.b != nil)
+		mpfree(b.b);
+	if(s.s != nil)
+		mpfree(s.s);
+	return r;
+}
+
+int
+mustbesigned(char *path, uchar *code, ulong length, Dir *dir)
+{
+	USED(code); USED(length);
+if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type);
+	/* allow only signed modules and those in #/; already loaded modules are reloaded from cache */
+	return up->env->sigs != nil && (dir == nil || dir->type != '/');
+}
+
+static int
+vc(Get *g)
+{
+	return g->p < g->ep? *g->p++: -1;
+}
+
+static int
+vs(void *s, int lim, Get *g, int n)
+{
+	int nr;
+
+	if(n < 0){
+		if(g->p >= g->ep)
+			return -1;
+		n = *g->p++;
+		lim--;
+	}
+	if(n > lim)
+		return -1;
+	nr = g->ep - g->p;
+	if(n > nr)
+		return -1;
+	if(s != nil)
+		memmove(s, g->p, n);
+	g->p += n;
+	return n;
+}
+
+static char*
+cstring(char *str, char **strp)
+{
+	char *p, *s;
+	int n;
+
+	p = strchr(str, '\n');
+	if(p == 0)
+		p = str + strlen(str);
+	n = p - str;
+	s = malloc(n+1);
+	if(s == nil)
+		return nil;
+	memmove(s, str, n);
+	s[n] = 0;
+
+	if(strp){
+		if(*p)
+			p++;
+		*strp = p;
+	}
+
+	return s;
+}
+
+static SigAlgVec*
+cstrtoalg(char *str, char **strp)
+{
+	int n;
+	char *p, name[KNAMELEN];
+
+	p = strchr(str, '\n');
+	if(p == 0){
+		p = str + strlen(str);
+		if(strp)
+			*strp = p;
+	} else {
+		if(strp)
+			*strp = p+1;
+	}
+
+	n = p - str;
+	if(n >= sizeof(name))
+		return nil;
+	strncpy(name, str, n);
+	name[n] = 0;
+	return findsigalg(name);
+}
+
+static Signerkey*
+strtopk(char *buf)
+{
+	SigAlgVec *sa;
+	char *p;
+	Signerkey *key;
+
+	key = malloc(sizeof(*key));
+	if(key == nil)
+		return nil;
+	key->r.ref = 1;
+	sa = cstrtoalg(buf, &p);
+	if(sa == nil){
+		free(key);
+		return nil;
+	}
+	key->alg = sa;
+	key->pkfree = sa->pkfree;
+	key->owner = cstring(p, &p);
+	if(key->owner == nil){
+		free(key);
+		return nil;
+	}
+	key->pk = (*sa->str2pk)(p, &p);
+	if(key->pk == nil){
+		free(key->owner);
+		free(key);
+		return nil;
+	}
+	return key;
+}
+
+static Signerkey*
+findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner)
+{
+	int i;
+	Signerkey *key;
+
+	for(i=0; i<sigs->nkey; i++){
+		key = sigs->keys[i];
+		if(key->footprint == footprint &&
+		   strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 &&
+		   strcmp(key->owner, owner) == 0)
+			return key;
+	}
+	return nil;
+}
+
+static Chan*
+signattach(char *spec)
+{
+	return devattach(0x03A3, spec);	 /* L'Σ' */
+}
+
+static Walkqid*
+signwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen);
+}
+
+static int
+signstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, signdir, nelem(signdir), devgen);
+}
+
+static Chan*
+signopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR) {
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = openmode(omode);
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	switch((ulong)c->qid.path){
+	case Qctl:
+		if(!iseve())
+			error(Eperm);
+		break;
+
+	case Qkey:
+		if(omode != OREAD && !iseve())
+			error(Eperm);
+		break;
+	}
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+signclose(Chan *c)
+{
+	USED(c);
+}
+
+static long
+signread(Chan *c, void *va, long n, vlong offset)
+{
+	char *buf, *p;
+	SigAlgVec *sa;
+	Skeyset *sigs;
+	Signerkey *key;
+	int i;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, va, n, signdir, nelem(signdir), devgen);
+	sigs = up->env->sigs;
+	if(sigs == nil)
+		return 0;
+	switch((ulong)c->qid.path){
+	case Qkey:
+		buf = smalloc(Maxkey);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		qlock(&sigs->l);
+		if(waserror()){
+			qunlock(&sigs->l);
+			nexterror();
+		}
+		p = buf;
+		for(i=0; i<sigs->nkey; i++){
+			key = sigs->keys[i];
+			sa = key->alg;
+			p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n",
+				key->owner, sa->name, key->footprint, key->expires);
+		}
+		poperror();
+		qunlock(&sigs->l);
+		n = readstr(offset, va, n, buf);
+		poperror();
+		free(buf);
+		return n;
+
+	case Qctl:
+		return readnum(offset, va, n, sigs->nkey, NUMSIZE);
+	}
+	return 0;
+}
+
+static long
+signwrite(Chan *c, void *va, long n, vlong offset)
+{
+	char *buf;
+	Skeyset *sigs;
+	Signerkey *okey, *key;
+	int i;
+
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qkey:
+		if(n >= Maxkey)
+			error(Etoobig);
+		buf = smalloc(Maxkey);
+		if(waserror()){
+			free(buf);
+			nexterror();
+		}
+		memmove(buf, va, n);
+		buf[n] = 0;
+
+		key = strtopk(buf);
+		if(key == nil)
+			error("bad key syntax");
+		poperror();
+		free(buf);
+
+		if(waserror()){
+			freeskey(key);
+			nexterror();
+		}
+		sigs = up->env->sigs;
+		if(sigs == nil){
+			sigs = malloc(sizeof(*sigs));
+			if(sigs == nil)
+				error(Enomem);
+			sigs->r.ref = 1;
+			up->env->sigs = sigs;
+		}
+		qlock(&sigs->l);
+		if(waserror()){
+			qunlock(&sigs->l);
+			nexterror();
+		}
+		for(i=0; i<sigs->nkey; i++){
+			okey = sigs->keys[i];
+			if(strcmp(okey->owner, key->owner) == 0){
+				/* replace existing key */
+				sigs->keys[i] = key;
+				freeskey(okey);
+				break;
+			}
+		}
+		if(i >= sigs->nkey){
+			if(sigs->nkey >= nelem(sigs->keys))
+				error("too many keys");
+			sigs->keys[sigs->nkey++] = key;
+		}
+		poperror();
+		qunlock(&sigs->l);
+		poperror();	/* key */
+
+		return n;
+	case Qctl:
+		error(Ebadctl);
+		break;
+	}
+	return 0;
+}
+
+Dev signdevtab = {
+	0x03A3,	/* L'Σ',	/* U+03A3 */
+	"sign",
+
+	devinit,
+	signattach,
+	signwalk,
+	signstat,
+	signopen,
+	devcreate,
+	signclose,
+	signread,
+	devbread,
+	signwrite,
+	devbwrite,
+	devremove,
+	devwstat
+};
--- /dev/null
+++ b/emu/port/devsnarf.c
@@ -1,0 +1,162 @@
+/*
+ * host's snarf buffer
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum{
+	Qdir,
+	Qsnarf,
+
+	Maxsnarf=	100*1024
+};
+
+static
+Dirtab snarftab[]={
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"snarf",	{Qsnarf},			0,	0666,
+};
+
+static QLock snarflock;	/* easiest to synchronise all access */
+
+static Chan*
+snarfattach(char *spec)
+{
+	return devattach('^', spec);
+}
+
+static Walkqid*
+snarfwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, snarftab, nelem(snarftab), devgen);
+}
+
+static int
+snarfstat(Chan* c, uchar *db, int n)
+{
+	return devstat(c, db, n, snarftab, nelem(snarftab), devgen);
+}
+
+static Chan*
+snarfopen(Chan* c, int omode)
+{
+	c = devopen(c, omode, snarftab, nelem(snarftab), devgen);
+	if(c->qid.path == Qsnarf){
+		if(c->mode == ORDWR || c->mode == OWRITE){
+			qlock(&snarflock);
+			free(c->aux);
+			c->aux = nil;
+			qunlock(&snarflock);
+		}
+	}
+	return c;
+}
+
+static void
+snarfclose(Chan* c)
+{
+	if((c->flag & COPEN) == 0)
+		return;
+	if(c->qid.path == Qsnarf){
+		/* this must be the last reference: no need to lock */
+		if(c->mode == ORDWR || c->mode == OWRITE){
+			if(!waserror()){
+				if(c->aux != nil)
+					clipwrite(c->aux);
+				poperror();
+			}
+		}
+		free(c->aux);
+	}
+}
+
+static long
+snarfread(Chan* c, void* a, long n, vlong offset)
+{
+	void *p;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, snarftab, nelem(snarftab), devgen);
+	case Qsnarf:
+		qlock(&snarflock);
+		if(waserror()){
+			qunlock(&snarflock);
+			nexterror();
+		}
+		if(offset == 0){
+			p = c->aux;
+			c->aux = nil;
+			free(p);
+			c->aux = clipread();
+		}
+		if(c->aux != nil)
+			n = readstr(offset, a, n, c->aux);
+		else
+			n = 0;
+		poperror();
+		qunlock(&snarflock);
+		break;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+snarfwrite(Chan* c, void* va, long n, vlong offset)
+{
+	ulong l;
+	char *p;
+
+	switch((ulong)c->qid.path){
+	case Qsnarf:
+		/* append only */
+		USED(offset);	/* not */
+		qlock(&snarflock);
+		if(waserror()){
+			qunlock(&snarflock);
+			nexterror();
+		}
+		if(c->aux != nil)
+			l = strlen(c->aux);
+		else
+			l = 0;
+		if(l+n > Maxsnarf)
+			error(Etoobig);
+		c->aux = realloc(c->aux, l+n+1);
+		if((p = c->aux) == nil)
+			error(Enovmem);
+		memmove(p+l, va, n);
+		p[l+n] = 0;
+		snarftab[1].qid.vers++;
+		poperror();
+		qunlock(&snarflock);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev snarfdevtab = {
+	'^',
+	"snarf",
+
+	devinit,
+	snarfattach,
+	snarfwalk,
+	snarfstat,
+	snarfopen,
+	devcreate,
+	snarfclose,
+	snarfread,
+	devbread,
+	snarfwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/devsrv.c
@@ -1,0 +1,829 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+#include	<isa.h>
+#include	"runt.h"
+
+typedef struct SrvFile SrvFile;
+typedef struct Pending Pending;
+
+/* request pending to a server, in case a server vanishes */
+struct Pending
+{
+	Pending*	next;
+	Pending*	prev;
+	int fid;
+	Channel*	rc;
+	Channel*	wc;
+};
+
+struct SrvFile
+{
+	char*	name;
+	char*	user;
+	ulong		perm;
+	Qid		qid;
+	int		ref;
+
+	/* root directory */
+	char*	spec;
+	SrvFile*	devlist;
+	SrvFile*	entry;
+
+	/* file */
+	int		opens;
+	int		flags;
+	vlong	length;
+	Channel*	read;
+	Channel*	write;
+	SrvFile*	dir;		/* parent directory */
+	Pending	waitlist;	/* pending requests from client opens */
+};
+
+enum
+{
+	SORCLOSE	= (1<<0),
+	SRDCLOSE	= (1<<1),
+	SWRCLOSE	= (1<<2),
+	SREMOVED	= (1<<3),
+};
+
+typedef struct SrvDev SrvDev;
+struct SrvDev
+{
+	Type*	Rread;
+	Type*	Rwrite;
+	QLock	l;
+	ulong	pathgen;
+	SrvFile*	devices;
+};
+
+static SrvDev dev;
+
+void	freechan(Heap*, int);
+static void	freerdchan(Heap*, int);
+static void	freewrchan(Heap*, int);
+static void delwaiting(Pending*);
+
+Type	*Trdchan;
+Type	*Twrchan;
+
+static int
+srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
+{
+	SrvFile *f;
+
+	USED(name);
+	USED(tab);
+	USED(ntab);
+
+	if(s == DEVDOTDOT){
+		devdir(c, c->qid, "#s", 0, eve, 0555, dp);
+		return 1;
+	}
+	f = c->aux;
+	if((c->qid.type & QTDIR) == 0){
+		if(s > 0)
+			return -1;
+		devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+		return 1;
+	}
+
+	for(f = f->entry; f != nil; f = f->entry){
+		if(s-- == 0)
+			break;
+	}
+	if(f == nil)
+		return -1;
+
+	devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
+	return 1;
+}
+
+static void
+srvinit(void)
+{
+	static uchar rmap[] = Sys_Rread_map;
+	static uchar wmap[] = Sys_Rwrite_map;
+
+	Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+	Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np);
+
+	dev.pathgen = 1;
+	dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap));
+	dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap));
+}
+
+static int
+srvcanattach(SrvFile *d)
+{
+	if(strcmp(d->user, up->env->user) == 0)
+		return 1;
+
+	/*
+	 * Need write permission in other to allow attaches if
+	 * we are not the owner
+	 */
+	if(d->perm & 2)
+		return 1;
+
+	return 0;
+}
+
+static Chan*
+srvattach(char *spec)
+{
+	Chan *c;
+	SrvFile *d;
+	char srvname[16];
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+
+	if(spec[0] != '\0'){
+		for(d = dev.devices; d != nil; d = d->devlist){
+			if(strcmp(spec, d->spec) == 0){
+				if(!srvcanattach(d))
+					error(Eperm);
+				c = devattach('s', spec);
+				c->aux = d;
+				c->qid = d->qid;
+				d->ref++;
+				poperror();
+				qunlock(&dev.l);
+				return c;
+			}
+		}
+	}
+
+	d = malloc(sizeof(SrvFile));
+	if(d == nil)
+		error(Enomem);
+
+	d->ref = 1;
+	kstrdup(&d->spec, spec);
+	kstrdup(&d->user, up->env->user);
+	snprint(srvname, sizeof(srvname), "srv%ld", up->env->pgrp->pgrpid);
+	kstrdup(&d->name, srvname);
+	d->perm = DMDIR|0770;
+	mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
+
+	d->devlist = dev.devices;
+	dev.devices = d;
+
+	poperror();
+	qunlock(&dev.l);
+
+	c = devattach('s', spec);
+	c->aux = d;
+	c->qid = d->qid;
+
+	return c;
+}
+
+static Walkqid*
+srvwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	SrvFile *d, *pd;
+	Walkqid *w;
+
+	pd = c->aux;
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+
+	w = devwalk(c, nc, name, nname, nil, 0, srvgen);
+	if(w != nil && w->clone != nil){
+		if(nname != 0){
+			for(d = pd->entry; d != nil; d = d->entry)
+				if(d->qid.path == w->clone->qid.path)
+					break;
+			if(d == nil)
+				panic("srvwalk");
+			if(w->clone == c)
+				pd->ref--;
+		}else
+			d = pd;
+		w->clone->aux = d;
+		d->ref++;
+	}
+
+	poperror();
+	qunlock(&dev.l);
+	return w;
+}
+
+static int
+srvstat(Chan *c, uchar *db, int n)
+{
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	n = devstat(c, db, n, 0, 0, srvgen);
+	poperror();
+	qunlock(&dev.l);
+	return n;
+}
+
+static Chan*
+srvopen(Chan *c, int omode)
+{
+	SrvFile *sf;
+
+	openmode(omode);	/* check it */
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eisdir);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	sf = c->aux;
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	devpermcheck(sf->user, sf->perm, omode);
+	if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
+		error(Eperm);
+	if(sf->perm & DMEXCL && sf->opens != 0)
+		error(Einuse);
+	sf->opens++;
+	if(omode&ORCLOSE)
+		sf->flags |= SORCLOSE;
+	poperror();
+	qunlock(&dev.l);
+
+	c->offset = 0;
+	c->flag |= COPEN;
+	c->mode = openmode(omode);
+
+	return c;
+}
+
+static int
+srvwstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	SrvFile *sf, *f;
+
+	sf = c->aux;
+	if(strcmp(up->env->user, sf->user) != 0)
+		error(Eperm);
+
+	d = smalloc(sizeof(*d)+n);
+	if(waserror()){
+		free(d);
+		nexterror();
+	}
+	n = convM2D(dp, n, d, (char*)&d[1]);
+	if(n == 0)
+		error(Eshortstat);
+	if(!emptystr(d->name)){
+		if(sf->dir == nil)
+			error(Eperm);
+		validwstatname(d->name);
+		qlock(&dev.l);
+		for(f = sf->dir; f != nil; f = f->entry)
+			if(strcmp(f->name, d->name) == 0){
+				qunlock(&dev.l);
+				error(Eexist);
+			}
+		kstrdup(&sf->name, d->name);
+		qunlock(&dev.l);
+	}
+	if(d->mode != ~0UL)
+		sf->perm = d->mode & (DMEXCL|DMAPPEND|0777);
+	if(d->length != (vlong)-1)
+		sf->length = d->length;
+	poperror();
+	free(d);
+	return n;
+}
+
+static void
+srvputdir(SrvFile *dir)
+{
+	SrvFile **l, *d;
+
+	dir->ref--;
+	if(dir->ref != 0)
+		return;
+
+	for(l = &dev.devices; (d = *l) != nil; l = &d->devlist)
+		if(d == dir){
+			*l = d->devlist;
+			break;
+		}
+	free(dir->spec);
+	free(dir->user);
+	free(dir->name);
+	free(dir);
+}
+
+static void
+srvunblock(SrvFile *sf, int fid)
+{
+	Channel *d;
+	Sys_FileIO_read rreq;
+	Sys_FileIO_write wreq;
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+	d = sf->read;
+	if(d != H){
+		rreq.t0 = 0;
+		rreq.t1 = 0;
+		rreq.t2 = fid;
+		rreq.t3 = H;
+		csendalt(d, &rreq, d->mid.t, -1);
+	}
+
+	d = sf->write;
+	if(d != H){
+		wreq.t0 = 0;
+		wreq.t1 = H;
+		wreq.t2 = fid;
+		wreq.t3 = H;
+		csendalt(d, &wreq, d->mid.t, -1);
+	}
+	poperror();
+	release();
+}
+
+static void
+srvcancelreqs(SrvFile *sf)
+{
+	Pending *w, *ws;
+	Sys_Rread rreply;
+	Sys_Rwrite wreply;
+
+	acquire();
+	ws = &sf->waitlist;
+	while((w = ws->next) != ws){
+		delwaiting(w);
+		if(waserror() == 0){
+			if(w->rc != nil){
+				rreply.t0 = H;
+				rreply.t1 = c2string(Ehungup, strlen(Ehungup));
+				csend(w->rc, &rreply);
+			}
+			if(w->wc != nil){
+				wreply.t0 = 0;
+				wreply.t1 = c2string(Ehungup, strlen(Ehungup));
+				csend(w->wc, &wreply);
+			}
+			poperror();
+		}
+	}
+	release();
+}
+
+static void
+srvdelete(SrvFile *sf)
+{
+	SrvFile *f, **l;
+
+	if((sf->flags & SREMOVED) == 0){
+		for(l = &sf->dir->entry; (f = *l) != nil; l = &f->entry){
+			if(sf == f){
+				*l = f->entry;
+				break;
+			}
+		}
+		sf->ref--;
+		sf->flags |= SREMOVED;
+	}
+}
+
+static void
+srvchkref(SrvFile *sf)
+{
+	if(sf->ref != 0)
+		return;
+
+	if(sf->dir != nil)
+		srvputdir(sf->dir);
+
+	free(sf->user);
+	free(sf->name);
+	free(sf);
+}
+
+static void
+srvfree(SrvFile *sf, int flag)
+{
+	sf->flags |= flag;
+	if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){
+		sf->ref--;
+		srvdelete(sf);
+		/* no further requests can arrive; return error to pending requests */
+		srvcancelreqs(sf);
+		srvchkref(sf);
+	}
+}
+
+static void
+freerdchan(Heap *h, int swept)
+{
+	SrvFile *sf;
+
+	release();
+	qlock(&dev.l);
+	sf = H2D(Channel*, h)->aux;
+	sf->read = H;
+	srvfree(sf, SRDCLOSE);
+	qunlock(&dev.l);
+	acquire();
+
+	freechan(h, swept);
+}
+
+static void
+freewrchan(Heap *h, int swept)
+{
+	SrvFile *sf;
+
+	release();
+	qlock(&dev.l);
+	sf = H2D(Channel*, h)->aux;
+	sf->write = H;
+	srvfree(sf, SWRCLOSE);
+	qunlock(&dev.l);
+	acquire();
+
+	freechan(h, swept);
+}
+
+static void
+srvclunk(Chan *c, int remove)
+{
+	int opens, noperm;
+	SrvFile *sf;
+
+	sf = c->aux;
+	qlock(&dev.l);
+	if(c->qid.type & QTDIR){
+		srvputdir(sf);
+		qunlock(&dev.l);
+		if(remove)
+			error(Eperm);
+		return;
+	}
+	opens = 0;
+	if(c->flag & COPEN){
+		opens = sf->opens--;
+		if(sf->read != H || sf->write != H)
+			srvunblock(sf, c->fid);
+	}
+
+	sf->ref--;
+	if(opens == 1){
+		if(sf->flags & SORCLOSE)
+			remove = 1;
+	}
+
+	noperm = 0;
+	if(remove && strcmp(sf->dir->user, up->env->user) != 0){
+		noperm = 1;
+		remove = 0;
+	}
+	if(remove)
+		srvdelete(sf);
+	srvchkref(sf);
+	qunlock(&dev.l);
+
+	if(noperm)
+		error(Eperm);
+}
+
+static void
+srvclose(Chan *c)
+{
+	srvclunk(c, 0);
+}
+
+static void
+srvremove(Chan *c)
+{
+	srvclunk(c, 1);
+}
+
+static void
+addwaiting(SrvFile *sp, Pending *w)
+{
+	Pending *sw;
+
+	sw = &sp->waitlist;
+	w->next = sw;
+	w->prev = sw->prev;
+	sw->prev->next = w;
+	sw->prev = w;
+}
+
+static void
+delwaiting(Pending *w)
+{
+	w->next->prev = w->prev;
+	w->prev->next = w->next;
+}
+
+static long
+srvread(Chan *c, void *va, long count, vlong offset)
+{
+	int l;
+	Heap * volatile h;
+	Array *a;
+	SrvFile *sp;
+	Channel *rc;
+	Channel *rd;
+	Pending wait;
+	Sys_Rread * volatile r;
+	Sys_FileIO_read req;
+
+	if(c->qid.type & QTDIR){
+		qlock(&dev.l);
+		if(waserror()){
+			qunlock(&dev.l);
+			nexterror();
+		}
+		l = devdirread(c, va, count, 0, 0, srvgen);
+		poperror();
+		qunlock(&dev.l);
+		return l;
+	}
+
+	sp = c->aux;
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+
+	rd = sp->read;
+	if(rd == H)
+		error(Ehungup);
+
+	rc = cnewc(dev.Rread, movtmp, 1);
+	ptradd(D2H(rc));
+	if(waserror()){
+		ptrdel(D2H(rc));
+		destroy(rc);
+		nexterror();
+	}
+
+	req.t0 = offset;
+	req.t1 = count;
+	req.t2 = c->fid;
+	req.t3 = rc;
+	csend(rd, &req);
+
+	h = heap(dev.Rread);
+	r = H2D(Sys_Rread *, h);
+	ptradd(h);
+	if(waserror()){
+		ptrdel(h);
+		destroy(r);
+		nexterror();
+	}
+
+	wait.fid = c->fid;
+	wait.rc = rc;
+	wait.wc = nil;
+	addwaiting(sp, &wait);
+	if(waserror()){
+		delwaiting(&wait);
+		nexterror();
+	}
+	crecv(rc, r);
+	poperror();
+	delwaiting(&wait);
+
+	if(r->t1 != H)
+		error(string2c(r->t1));
+
+	a = r->t0;
+	l = 0;
+	if(a != H){
+		l = a->len;
+		if(l > count)
+			l = count;
+		memmove(va, a->data, l);
+	}
+
+	poperror();
+	ptrdel(h);
+	destroy(r);
+
+	poperror();
+	ptrdel(D2H(rc));
+	destroy(rc);
+
+	poperror();
+	release();
+
+	return l;
+}
+
+static long
+srvwrite(Chan *c, void *va, long count, vlong offset)
+{
+	long l;
+	Heap * volatile h;
+	SrvFile *sp;
+	Channel *wc;
+	Channel *wr;
+	Pending wait;
+	Sys_Rwrite * volatile w;
+	Sys_FileIO_write req;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	acquire();
+	if(waserror()){
+		release();
+		nexterror();
+	}
+
+	sp = c->aux;
+	wr = sp->write;
+	if(wr == H)
+		error(Ehungup);
+
+	wc = cnewc(dev.Rwrite, movtmp, 1);
+	ptradd(D2H(wc));
+	if(waserror()){
+		ptrdel(D2H(wc));
+		destroy(wc);
+		nexterror();
+	}
+
+	req.t0 = offset;
+	req.t1 = mem2array(va, count);
+	req.t2 = c->fid;
+	req.t3 = wc;
+
+	ptradd(D2H(req.t1));
+
+	if(waserror()){
+		ptrdel(D2H(req.t1));
+		destroy(req.t1);
+		nexterror();
+	}
+
+	csend(wr, &req);
+
+	poperror();
+	ptrdel(D2H(req.t1));
+	destroy(req.t1);
+
+	h = heap(dev.Rwrite);
+	w = H2D(Sys_Rwrite *, h);
+	ptradd(h);
+
+	if(waserror()){
+		ptrdel(h);
+		destroy(w);
+		nexterror();
+	}
+
+	wait.fid = c->fid;
+	wait.rc = nil;
+	wait.wc = wc;
+	addwaiting(sp, &wait);
+	if(waserror()){
+		delwaiting(&wait);
+		nexterror();
+	}
+	crecv(wc, w);
+	poperror();
+	delwaiting(&wait);
+
+	if(w->t1 != H)
+		error(string2c(w->t1));
+	poperror();
+	ptrdel(h);
+	l = w->t0;
+	destroy(w);
+
+	poperror();
+	ptrdel(D2H(wc));
+	destroy(wc);
+
+	poperror();
+	release();
+	if(l < 0)
+		l = 0;
+	return l;
+}
+
+static void
+srvretype(Channel *c, SrvFile *f, Type *t)
+{
+	Heap *h;
+
+	h = D2H(c);
+	freetype(h->t);
+	h->t = t;
+	t->ref++;
+	c->aux = f;
+}
+
+int
+srvf2c(char *dir, char *file, Sys_FileIO *io)
+{
+	SrvFile *s, *f;
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()){
+		cclose(c.c);
+		return -1;
+	}
+
+	if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
+		error(Efilename);
+
+	c.c = namec(dir, Aaccess, 0, 0);
+	if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
+		error("directory not a srv device");
+
+	s = c.c->aux;
+
+	qlock(&dev.l);
+	if(waserror()){
+		qunlock(&dev.l);
+		nexterror();
+	}
+	for(f = s->entry; f != nil; f = f->entry){
+		if(strcmp(f->name, file) == 0)
+			error(Eexist);
+	}
+
+	f = malloc(sizeof(SrvFile));
+	if(f == nil)
+		error(Enomem);
+
+	srvretype(io->read, f, Trdchan);
+	srvretype(io->write, f, Twrchan);
+	f->read = io->read;
+	f->write = io->write;
+	
+	f->waitlist.next = &f->waitlist;
+	f->waitlist.prev = &f->waitlist;
+
+	kstrdup(&f->name, file);
+	kstrdup(&f->user, up->env->user);
+	f->perm = 0666 & (~0666 | (s->perm & 0666));
+	f->length = 0;
+	f->ref = 2;
+	mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
+
+	f->entry = s->entry;
+	s->entry = f;
+	s->ref++;
+	f->dir = s;
+	poperror();
+	qunlock(&dev.l);
+
+	cclose(c.c);
+	poperror();
+
+	return 0;
+}
+
+Dev srvdevtab = {
+	's',
+	"srv",
+
+	srvinit,
+	srvattach,
+	srvwalk,
+	srvstat,
+	srvopen,
+	devcreate,
+	srvclose,
+	srvread,
+	devbread,
+	srvwrite,
+	devbwrite,
+	srvremove,
+	srvwstat
+};
--- /dev/null
+++ b/emu/port/devssl.c
@@ -1,0 +1,1441 @@
+/*
+ *  devssl - secure sockets layer
+ */
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include	"mp.h"
+#include	"libsec.h"
+
+typedef struct OneWay OneWay;
+struct OneWay
+{
+	QLock	q;
+	QLock	ctlq;
+
+	void	*state;		/* encryption state */
+	int	slen;			/* secret data length */
+	uchar	*secret;	/* secret */
+	ulong	mid;		/* message id */
+};
+
+enum
+{
+	/* connection states */
+	Sincomplete=	0,
+	Sclear=		1,
+	Sencrypting=	2,
+	Sdigesting=	4,
+	Sdigenc=	Sencrypting|Sdigesting,
+
+	/* encryption algorithms */
+	Noencryption=	0,
+	DESCBC=		1,
+	DESECB=		2,
+	RC4=		3,
+	IDEACBC=	4,
+	IDEAECB=		5
+};
+
+typedef struct Dstate Dstate;
+struct Dstate
+{
+	Chan	*c;			/* io channel */
+	uchar	state;		/* state of connection */
+	int	ref;			/* serialized by dslock for atomic destroy */
+
+	uchar	encryptalg;	/* encryption algorithm */
+	ushort	blocklen;	/* blocking length */
+
+	ushort	diglen;		/* length of digest */
+	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);	/* hash func */
+
+	/* for SSL format */
+	int	max;			/* maximum unpadded data per msg */
+	int	maxpad;			/* maximum padded data per msg */
+	
+	/* input side */
+	OneWay	in;
+	Block	*processed;
+	Block	*unprocessed;
+
+	/* output side */
+	OneWay	out;
+
+	/* protections */
+	char*	user;
+	int	perm;
+};
+
+enum
+{
+	Maxdmsg=	1<<16,
+	Maxdstate=	1<<10,
+};
+
+Lock	dslock;
+int	dshiwat;
+int	maxdstate = 20;
+Dstate** dstate;
+
+enum{
+	Qtopdir		= 1,	/* top level directory */
+	Qclonus,
+	Qconvdir,			/* directory for a conversation */
+	Qdata,
+	Qctl,
+	Qsecretin,
+	Qsecretout,
+	Qencalgs,
+	Qhashalgs
+};
+
+#define TYPE(x) 	((ulong)(x).path & 0xf)
+#define CONV(x) 	(((ulong)(x).path >> 4)&(Maxdstate-1))
+#define QID(c, y) 	(((c)<<4) | (y))
+
+static char*	encalgs;
+static char*	hashalgs;
+
+void producerand(void);
+
+static void alglistinit(void);
+static void	ensure(Dstate*, Block**, int);
+static void	consume(Block**, uchar*, int);
+static void	setsecret(OneWay*, uchar*, int);
+static Block*	encryptb(Dstate*, Block*, int);
+static Block*	decryptb(Dstate*, Block*);
+static Block*	digestb(Dstate*, Block*, int);
+static void	checkdigestb(Dstate*, Block*);
+static Chan*	buftochan(char*);
+static void	sslhangup(Dstate*);
+static void dsclone(Chan *c);
+static void	dsnew(Chan *c, Dstate **);
+
+static int
+sslgen(Chan *c, char *dname, Dirtab *d, int nd, int s, Dir *dp)
+{
+	Qid q;
+	Dstate *ds;
+	char *p, *nm;
+
+	USED(dname);
+	USED(nd);
+	USED(d);
+	q.type = QTFILE;
+	q.vers = 0;
+	if(s == DEVDOTDOT){
+		q.path = QID(0, Qtopdir);
+		q.type = QTDIR;
+		devdir(c, q, "#D", 0, eve, 0555, dp);
+		return 1;
+	}
+	switch(TYPE(c->qid)) {
+	case Qtopdir:
+		if(s < dshiwat) {
+			q.path = QID(s, Qconvdir);
+			q.type = QTDIR;
+			ds = dstate[s];
+			if(ds != 0)
+ 				nm = ds->user;
+ 			else
+ 				nm = eve;
+			snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
+			devdir(c, q, up->genbuf, 0, nm, DMDIR|0555, dp);
+			return 1;
+		}
+		if(s > dshiwat)
+			return -1;
+		/* fall through */
+	case Qclonus:
+		q.path = QID(0, Qclonus);
+		devdir(c, q, "clone", 0, eve, 0666, dp);
+		return 1;
+	case Qconvdir:
+		ds = dstate[CONV(c->qid)];
+		if(ds != 0)
+			nm = ds->user;
+		else
+			nm = eve;
+		switch(s) {
+		default:
+			return -1;
+		case 0:
+			q.path = QID(CONV(c->qid), Qctl);
+			p = "ctl";
+			break;
+		case 1:
+			q.path = QID(CONV(c->qid), Qdata);
+			p = "data";
+			break;
+		case 2:
+			q.path = QID(CONV(c->qid), Qsecretin);
+			p = "secretin";
+			break;
+		case 3:
+			q.path = QID(CONV(c->qid), Qsecretout);
+			p = "secretout";
+			break;
+		case 4:
+			q.path = QID(CONV(c->qid), Qencalgs);
+			p = "encalgs";
+			break;
+		case 5:
+			q.path = QID(CONV(c->qid), Qhashalgs);
+			p = "hashalgs";
+			break;
+		}
+		devdir(c, q, p, 0, nm, 0660, dp);
+		return 1;
+	}
+	return -1;
+}
+
+static void
+sslinit(void)
+{
+	if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0)
+		panic("sslinit");
+	alglistinit();
+}
+
+static Chan *
+sslattach(char *spec)
+{
+	Chan *c;
+
+	c = devattach('D', spec);
+	c->qid.path = QID(0, Qtopdir);
+	c->qid.vers = 0;
+	c->qid.type = QTDIR;
+	return c;
+}
+
+static Walkqid*
+sslwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, sslgen);
+}
+
+static int
+sslstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, sslgen);
+}
+
+static Chan*
+sslopen(Chan *c, int omode)
+{
+	Dstate *s, **pp;
+	int perm;
+
+	perm = 0;
+	omode &= 3;
+	switch(omode) {
+	case OREAD:
+		perm = 4;
+		break;
+	case OWRITE:
+		perm = 2;
+		break;
+	case ORDWR:
+		perm = 6;
+		break;
+	}
+
+	switch(TYPE(c->qid)) {
+	default:
+		panic("sslopen");
+	case Qtopdir:
+	case Qconvdir:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qclonus:
+		dsclone(c);
+		break;
+	case Qctl:
+	case Qdata:
+	case Qsecretin:
+	case Qsecretout:
+		if(waserror()) {
+			unlock(&dslock);
+			nexterror();
+		}
+		lock(&dslock);
+		pp = &dstate[CONV(c->qid)];
+		s = *pp;
+		if(s == 0)
+			dsnew(c, pp);
+		else {
+			if((perm & (s->perm>>6)) != perm
+			   && (strcmp(up->env->user, s->user) != 0
+			     || (perm & s->perm) != perm))
+				error(Eperm);
+
+			s->ref++;
+		}
+		unlock(&dslock);
+		poperror();
+		break;
+	case Qencalgs:
+	case Qhashalgs:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static int
+sslwstat(Chan *c, uchar *db, int n)
+{
+	Dir *dir;
+	Dstate *s;
+	int m;
+
+	s = dstate[CONV(c->qid)];
+	if(s == 0)
+		error(Ebadusefd);
+	if(strcmp(s->user, up->env->user) != 0)
+		error(Eperm);
+
+	dir = smalloc(sizeof(Dir)+n);
+	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+	if(m == 0){
+		free(dir);
+		error(Eshortstat);
+	}
+
+	if(!emptystr(dir->uid))
+		kstrdup(&s->user, dir->uid);
+	if(dir->mode != ~0UL)
+		s->perm = dir->mode;
+
+	free(dir);
+	return m;
+}
+
+static void
+sslclose(Chan *c)
+{
+	Dstate *s;
+
+	switch(TYPE(c->qid)) {
+	case Qctl:
+	case Qdata:
+	case Qsecretin:
+	case Qsecretout:
+		if((c->flag & COPEN) == 0)
+			break;
+
+		s = dstate[CONV(c->qid)];
+		if(s == 0)
+			break;
+
+		lock(&dslock);
+		if(--s->ref > 0) {
+			unlock(&dslock);
+			break;
+		}
+		dstate[CONV(c->qid)] = 0;
+		unlock(&dslock);
+
+		sslhangup(s);
+		if(s->c)
+			cclose(s->c);
+		free(s->user);
+		free(s->in.secret);
+		free(s->out.secret);
+		free(s->in.state);
+		free(s->out.state);
+		free(s);
+	}
+}
+
+/*
+ *  make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(Dstate *s, Block **l, int n)
+{
+	int sofar, i;
+	Block *b, *bl;
+
+	sofar = 0;
+	for(b = *l; b; b = b->next){
+		sofar += BLEN(b);
+		if(sofar >= n)
+			return;
+		l = &b->next;
+	}
+
+	while(sofar < n){
+		bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
+		if(bl == 0)
+			error(Ehungup);
+		*l = bl;
+		i = 0;
+		for(b = bl; b; b = b->next){
+			i += BLEN(b);
+			l = &b->next;
+		}
+		if(i == 0)
+			error(Ehungup);
+
+		sofar += i;
+	}
+}
+
+/*
+ *  copy 'n' bytes from 'l' into 'p' and free
+ *  the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{
+	Block *b;
+	int i;
+
+	for(; *l && n > 0; n -= i){
+		b = *l;
+		i = BLEN(b);
+		if(i > n)
+			i = n;
+		memmove(p, b->rp, i);
+		b->rp += i;
+		p += i;
+		if(BLEN(b) < 0)
+			panic("consume");
+		if(BLEN(b))
+			break;
+		*l = b->next;
+		freeb(b);
+	}
+}
+
+/*
+ *  remove at most n bytes from the queue, if discard is set
+ *  dump the remainder
+ */
+static Block*
+qtake(Block **l, int n, int discard)
+{
+	Block *nb, *b, *first;
+	int i;
+
+	first = *l;
+	for(b = first; b; b = b->next){
+		i = BLEN(b);
+		if(i == n){
+			if(discard){
+				freeblist(b->next);
+				*l = 0;
+			} else
+				*l = b->next;
+			b->next = 0;
+			return first;
+		} else if(i > n){
+			i -= n;
+			if(discard){
+				freeblist(b->next);
+				*l = 0;
+			} else {
+				nb = allocb(i);
+				memmove(nb->wp, b->rp+n, i);
+				nb->wp += i;
+				nb->next = b->next;
+				*l = nb;
+			}
+			b->wp -= i;
+			b->next = 0;
+			if(BLEN(b) < 0)
+				panic("qtake");
+			return first;
+		} else
+			n -= i;
+		if(BLEN(b) < 0)
+			panic("qtake");
+	}
+	*l = 0;
+	return first;
+}
+
+static Block*
+sslbread(Chan *c, long n, ulong offset)
+{
+	volatile struct { Dstate *s; } s;
+	volatile struct { int nc; } nc;
+	Block *b;
+	uchar count[3];
+	int len, pad;
+
+	USED(offset);
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslbread");
+	if(s.s->state == Sincomplete)
+		error(Ebadusefd);
+
+	nc.nc = 0;
+	if(waserror()){
+		qunlock(&s.s->in.q);
+		if(strcmp(up->env->errstr, "interrupted") == 0){
+			if(nc.nc > 0){
+				b = allocb(nc.nc);
+				memmove(b->wp, count, nc.nc);
+				b->wp += nc.nc;
+				b->next = s.s->unprocessed;
+				s.s->unprocessed = b;
+			}
+		} else
+			sslhangup(s.s);
+		nexterror();
+	}
+	qlock(&s.s->in.q);
+
+	if(s.s->processed == 0){
+		/* read in the whole message */
+		ensure(s.s, &s.s->unprocessed, 2);
+
+		consume(&s.s->unprocessed, count, 2);
+		nc.nc += 2;
+		if(count[0] & 0x80){
+			len = ((count[0] & 0x7f)<<8) | count[1];
+			ensure(s.s, &s.s->unprocessed, len);
+			pad = 0;
+		} else {
+			len = ((count[0] & 0x3f)<<8) | count[1];
+			ensure(s.s, &s.s->unprocessed, len+1);
+			consume(&s.s->unprocessed, count + nc.nc, 1);
+			pad = count[nc.nc];
+			nc.nc++;
+			if(pad > len){
+				print("pad %d buf len %d\n", pad, len);
+				error("bad pad in ssl message");
+			}
+		}
+		nc.nc = 0;
+
+		/* put extra on unprocessed queue */
+		s.s->processed = qtake(&s.s->unprocessed, len, 0);
+
+		if(waserror()){
+			qunlock(&s.s->in.ctlq);
+			nexterror();
+		}
+		qlock(&s.s->in.ctlq);
+		switch(s.s->state){
+		case Sencrypting:
+			s.s->processed = decryptb(s.s, s.s->processed);
+			break;
+		case Sdigesting:
+			s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+			if(s.s->processed == 0)
+				error("ssl message too short");
+			checkdigestb(s.s, s.s->processed);
+			s.s->processed->rp += s.s->diglen;
+			break;
+		case Sdigenc:
+			s.s->processed = decryptb(s.s, s.s->processed);
+			s.s->processed = pullupblock(s.s->processed, s.s->diglen);
+			if(s.s->processed == 0)
+				error("ssl message too short");
+			checkdigestb(s.s, s.s->processed);
+			s.s->processed->rp += s.s->diglen;
+			len -= s.s->diglen;
+			break;
+		}
+		s.s->in.mid++;
+		qunlock(&s.s->in.ctlq);
+		poperror();
+
+		/* remove pad */
+		if(pad)
+			s.s->processed = qtake(&s.s->processed, len - pad, 1);
+	}
+
+	/* return at most what was asked for */
+	b = qtake(&s.s->processed, n, 0);
+
+	qunlock(&s.s->in.q);
+	poperror();
+
+	return b;
+}
+
+static long
+sslread(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Block *b; } b;
+	Block *nb;
+	uchar *va;
+	int i;
+	char buf[128];
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, sslgen);
+
+	switch(TYPE(c->qid)) {
+	default:
+		error(Ebadusefd);
+	case Qctl:
+		sprint(buf, "%ld", CONV(c->qid));
+		return readstr(offset, a, n, buf);
+	case Qdata:
+		b.b = sslbread(c, n, offset);
+		break;
+	case Qencalgs:
+		return readstr(offset, a, n, encalgs);
+	case Qhashalgs:
+		return readstr(offset, a, n, hashalgs);
+	}
+
+	n = 0;
+	va = a;
+	for(nb = b.b; nb; nb = nb->next){
+		i = BLEN(nb);
+		memmove(va+n, nb->rp, i);
+		n += i;
+	}
+
+	freeblist(b.b);
+
+	return n;
+}
+
+/*
+ *  this algorithm doesn't have to be great since we're just
+ *  trying to obscure the block fill
+ */
+static void
+randfill(uchar *buf, int len)
+{
+	while(len-- > 0)
+		*buf++ = nrand(256);
+}
+
+/*
+ *  use SSL record format, add in count and digest or encrypt
+ */
+static long
+sslbwrite(Chan *c, Block *b, ulong offset)
+{
+	volatile struct { Dstate *s; } s;
+	volatile struct { Block *b; } bb;
+	Block *nb;
+	int h, n, m, pad, rv;
+	uchar *p;
+
+	bb.b = b;
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslbwrite");
+	if(s.s->state == Sincomplete){
+		freeb(b);
+		error(Ebadusefd);
+	}
+
+	if(waserror()){
+		qunlock(&s.s->out.q);
+		if(bb.b)
+			freeb(bb.b);
+		sslhangup(s.s);
+		nexterror();
+	}
+	qlock(&s.s->out.q);
+
+	rv = 0;
+	while(bb.b){
+		m = n = BLEN(bb.b);
+		h = s.s->diglen + 2;
+
+		/* trim to maximum block size */
+		pad = 0;
+		if(m > s.s->max){
+			m = s.s->max;
+		} else if(s.s->blocklen != 1){
+			pad = (m + s.s->diglen)%s.s->blocklen;
+			if(pad){
+				if(m > s.s->maxpad){
+					pad = 0;
+					m = s.s->maxpad;
+				} else {
+					pad = s.s->blocklen - pad;
+					h++;
+				}
+			}
+		}
+
+		rv += m;
+		if(m != n){
+			nb = allocb(m + h + pad);
+			memmove(nb->wp + h, bb.b->rp, m);
+			nb->wp += m + h;
+			bb.b->rp += m;
+		} else {
+			/* add header space */
+			nb = padblock(bb.b, h);
+			bb.b = 0;
+		}
+		m += s.s->diglen;
+
+		/* SSLv2 style count */
+		if(pad){
+			nb = padblock(nb, -pad);
+			randfill(nb->wp, pad);
+			nb->wp += pad;
+			m += pad;
+
+			p = nb->rp;
+			p[0] = (m>>8);
+			p[1] = m;
+			p[2] = pad;
+			offset = 3;
+		} else {
+			p = nb->rp;
+			p[0] = (m>>8) | 0x80;
+			p[1] = m;
+			offset = 2;
+		}
+
+		switch(s.s->state){
+		case Sencrypting:
+			nb = encryptb(s.s, nb, offset);
+			break;
+		case Sdigesting:
+			nb = digestb(s.s, nb, offset);
+			break;
+		case Sdigenc:
+			nb = digestb(s.s, nb, offset);
+			nb = encryptb(s.s, nb, offset);
+			break;
+		}
+
+		s.s->out.mid++;
+
+		m = BLEN(nb);
+		devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset);
+		s.s->c->offset += m;
+	}
+	qunlock(&s.s->out.q);
+	poperror();
+
+	return rv;
+}
+
+static void
+setsecret(OneWay *w, uchar *secret, int n)
+{
+	free(w->secret);
+	w->secret = mallocz(n, 0);
+	if(w->secret == nil)
+		error(Enomem);
+	memmove(w->secret, secret, n);
+	w->slen = n;
+}
+
+static void
+initIDEAkey(OneWay *w)
+{
+
+	free(w->state);
+	w->state = malloc(sizeof(IDEAstate));
+	if(w->state == nil)
+		error(Enomem);
+	if(w->slen >= 24)
+		setupIDEAstate(w->state, w->secret, w->secret+16);
+	else if(w->slen >= 16)
+		setupIDEAstate(w->state, w->secret, 0);
+	else
+		error("secret too short");
+}
+
+static void
+initDESkey(OneWay *w)
+{
+
+	free(w->state);
+	w->state = malloc(sizeof(DESstate));
+	if (!w->state)
+		error(Enomem);
+	if(w->slen >= 16)
+		setupDESstate(w->state, w->secret, w->secret+8);
+	else if(w->slen >= 8)
+		setupDESstate(w->state, w->secret, 0);
+	else
+		error("secret too short");
+}
+
+/*
+ *  40 bit DES is the same as 56 bit DES.  However,
+ *  16 bits of the key are masked to zero.
+ */
+static void
+initDESkey_40(OneWay *w)
+{
+	uchar key[8];
+
+
+	if(w->slen >= 8) {
+		memmove(key, w->secret, 8);
+		key[0] &= 0x0f;
+		key[2] &= 0x0f;
+		key[4] &= 0x0f;
+		key[6] &= 0x0f;
+	}
+
+	free(w->state);
+	w->state = malloc(sizeof(DESstate));
+	if (!w->state)
+		error(Enomem);
+	if(w->slen >= 16)
+		setupDESstate(w->state, key, w->secret+8);
+	else if(w->slen >= 8)
+		setupDESstate(w->state, key, 0);
+	else
+		error("secret too short");
+}
+
+static void
+initRC4key(OneWay *w)
+{
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, w->slen);
+}
+
+/*
+ *  40 bit RC4 is the same as n-bit RC4.  However,
+ *  we ignore all but the first 40 bits of the key.
+ */
+static void
+initRC4key_40(OneWay *w)
+{
+	int slen = w->slen;
+
+	if(slen > 5)
+		slen = 5;
+
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, slen);
+}
+
+/*
+ *  128 bit RC4 is the same as n-bit RC4.  However,
+ *  we ignore all but the first 128 bits of the key.
+ */
+static void
+initRC4key_128(OneWay *w)
+{
+	int slen = w->slen;
+
+	if(slen > 16)
+		slen = 16;
+
+	free(w->state);
+	w->state = malloc(sizeof(RC4state));
+	if (!w->state)
+		error(Enomem);
+	setupRC4state(w->state, w->secret, slen);
+}
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{
+	char	*name;
+	int	diglen;
+	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
+};
+
+Hashalg hashtab[] =
+{
+	{ "md4", MD4dlen, md4, },
+	{ "md5", MD5dlen, md5, },
+	{ "sha1", SHA1dlen, sha1, },
+	{ "sha", SHA1dlen, sha1, },
+	{ 0 }
+};
+
+static int
+parsehashalg(char *p, Dstate *s)
+{
+	Hashalg *ha;
+
+	for(ha = hashtab; ha->name; ha++){
+		if(strcmp(p, ha->name) == 0){
+			s->hf = ha->hf;
+			s->diglen = ha->diglen;
+			s->state &= ~Sclear;
+			s->state |= Sdigesting;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{
+	char	*name;
+	int	blocklen;
+	int	alg;
+	void	(*keyinit)(OneWay*);
+};
+
+Encalg encrypttab[] =
+{
+	{ "descbc", 8, DESCBC, initDESkey, },           /* DEPRECATED -- use des_56_cbc */
+	{ "desecb", 8, DESECB, initDESkey, },           /* DEPRECATED -- use des_56_ecb */
+	{ "des_56_cbc", 8, DESCBC, initDESkey, },
+	{ "des_56_ecb", 8, DESECB, initDESkey, },
+	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
+	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
+	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
+	{ "rc4_256", 1, RC4, initRC4key, },
+	{ "rc4_128", 1, RC4, initRC4key_128, },
+	{ "rc4_40", 1, RC4, initRC4key_40, },
+	{ "ideacbc", 8, IDEACBC, initIDEAkey, },
+	{ "ideaecb", 8, IDEAECB, initIDEAkey, },
+	{ 0 }
+};
+
+static int
+parseencryptalg(char *p, Dstate *s)
+{
+	Encalg *ea;
+
+	for(ea = encrypttab; ea->name; ea++){
+		if(strcmp(p, ea->name) == 0){
+			s->encryptalg = ea->alg;
+			s->blocklen = ea->blocklen;
+			(*ea->keyinit)(&s->in);
+			(*ea->keyinit)(&s->out);
+			s->state &= ~Sclear;
+			s->state |= Sencrypting;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void
+alglistinit(void)
+{
+	Hashalg *h;
+	Encalg *e;
+	int n;
+
+	n = 1;
+	for(e = encrypttab; e->name != nil; e++)
+		n += strlen(e->name) + 1;
+	encalgs = malloc(n);
+	if(encalgs == nil)
+		panic("sslinit");
+	n = 0;
+	for(e = encrypttab; e->name != nil; e++){
+		strcpy(encalgs+n, e->name);
+		n += strlen(e->name);
+		if(e[1].name == nil)
+			break;
+		encalgs[n++] = ' ';
+	}
+	encalgs[n] = 0;
+
+	n = 1;
+	for(h = hashtab; h->name != nil; h++)
+		n += strlen(h->name) + 1;
+	hashalgs = malloc(n);
+	if(hashalgs == nil)
+		panic("sslinit");
+	n = 0;
+	for(h = hashtab; h->name != nil; h++){
+		strcpy(hashalgs+n, h->name);
+		n += strlen(h->name);
+		if(h[1].name == nil)
+			break;
+		hashalgs[n++] = ' ';
+	}
+	hashalgs[n] = 0;
+}
+
+static long
+sslwrite(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Dstate *s; } s;
+	volatile struct { Block *b; } b;
+	int m, t;
+	char *p, *np, *e, buf[32];
+	uchar *x;
+
+	s.s = dstate[CONV(c->qid)];
+	if(s.s == 0)
+		panic("sslwrite");
+
+	t = TYPE(c->qid);
+	if(t == Qdata){
+		if(s.s->state == Sincomplete)
+			error(Ebadusefd);
+	
+		p = a;
+		e = p + n;
+		do {
+			m = e - p;
+			if(m > s.s->max)
+				m = s.s->max;
+	
+			b.b = allocb(m);
+			memmove(b.b->wp, p, m);
+			b.b->wp += m;
+
+			sslbwrite(c, b.b, offset);
+
+			p += m;
+		} while(p < e);
+		return n;
+	}
+
+	/* mutex with operations using what we're about to change */
+	if(waserror()){
+		qunlock(&s.s->in.ctlq);
+		qunlock(&s.s->out.q);
+		nexterror();
+	}
+	qlock(&s.s->in.ctlq);
+	qlock(&s.s->out.q);
+
+	switch(t){
+	default:
+		panic("sslwrite");
+	case Qsecretin:
+		setsecret(&s.s->in, a, n);
+		goto out;
+	case Qsecretout:
+		setsecret(&s.s->out, a, n);
+		goto out;
+	case Qctl:
+		break;
+	}
+
+	if(n >= sizeof(buf))
+		error(Ebadarg);
+	strncpy(buf, a, n);
+	buf[n] = 0;
+	p = strchr(buf, '\n');
+	if(p)
+		*p = 0;
+	p = strchr(buf, ' ');
+	if(p)
+		*p++ = 0;
+
+	if(strcmp(buf, "fd") == 0){
+		s.s->c = buftochan(p);
+
+		/* default is clear (msg delimiters only) */
+		s.s->state = Sclear;
+		s.s->blocklen = 1;
+		s.s->diglen = 0;
+		s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+		s.s->in.mid = 0;
+		s.s->out.mid = 0;
+	} else if(strcmp(buf, "alg") == 0 && p != 0){
+		s.s->blocklen = 1;
+		s.s->diglen = 0;
+
+		if(s.s->c == 0)
+			error("must set fd before algorithm");
+
+		if(strcmp(p, "clear") == 0){
+			s.s->state = Sclear;
+			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+			goto out;
+		}
+
+		if(s.s->in.secret && s.s->out.secret == 0)
+			setsecret(&s.s->out, s.s->in.secret, s.s->in.slen);
+		if(s.s->out.secret && s.s->in.secret == 0)
+			setsecret(&s.s->in, s.s->out.secret, s.s->out.slen);
+		if(s.s->in.secret == 0 || s.s->out.secret == 0)
+			error("algorithm but no secret");
+
+		s.s->hf = 0;
+		s.s->encryptalg = Noencryption;
+		s.s->blocklen = 1;
+
+		for(;;){
+			np = strchr(p, ' ');
+			if(np)
+				*np++ = 0;
+			else{
+				np = strchr(p, '/');
+				if(np)
+					*np++ = 0;
+			}
+			if(parsehashalg(p, s.s) < 0)
+			if(parseencryptalg(p, s.s) < 0)
+				error(Ebadarg);
+
+			if(np == 0)
+				break;
+			p = np;
+		}
+
+		if(s.s->hf == 0 && s.s->encryptalg == Noencryption)
+			error(Ebadarg);
+
+		if(s.s->blocklen != 1){
+			/* make multiple of blocklen */
+			s.s->max = (1<<15) - s.s->diglen - 1;
+			s.s->max -= s.s->max % s.s->blocklen;
+			s.s->maxpad = (1<<14) - s.s->diglen - 1;
+			s.s->maxpad -= s.s->maxpad % s.s->blocklen;
+		} else
+			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
+	} else if(strcmp(buf, "secretin") == 0 && p != 0) {
+		m = (strlen(p)*3)/2;
+		x = smalloc(m);
+		if(waserror()){
+			free(x);
+			nexterror();
+		}
+		t = dec64(x, m, p, strlen(p));
+		setsecret(&s.s->in, x, t);
+		poperror();
+		free(x);
+	} else if(strcmp(buf, "secretout") == 0 && p != 0) {
+		m = (strlen(p)*3)/2;
+		x = smalloc(m);
+		if(waserror()){
+			free(x);
+			nexterror();
+		}
+		t = dec64(x, m, p, strlen(p));
+		setsecret(&s.s->out, x, t);
+		poperror();
+		free(x);
+	} else
+		error(Ebadarg);
+
+out:
+	qunlock(&s.s->in.ctlq);
+	qunlock(&s.s->out.q);
+	poperror();
+	return n;
+}
+
+
+static Block*
+encryptb(Dstate *s, Block *b, int offset)
+{
+	uchar *p, *ep, *p2, *ip, *eip;
+	DESstate *ds;
+	IDEAstate *is;
+
+	switch(s->encryptalg){
+	case DESECB:
+		ds = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8)
+			block_cipher(ds->expanded, p, 0);
+		break;
+	case DESCBC:
+		ds = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8){
+			p2 = p;
+			ip = ds->ivec;
+			for(eip = ip+8; ip < eip; )
+				*p2++ ^= *ip++;
+			block_cipher(ds->expanded, p, 0);
+			memmove(ds->ivec, p, 8);
+		}
+		break;
+	case IDEAECB:
+		is = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8)
+			idea_cipher(is->edkey, p, 0);
+		break;
+	case IDEACBC:
+		is = s->out.state;
+		ep = b->rp + BLEN(b);
+		for(p = b->rp + offset; p < ep; p += 8){
+			p2 = p;
+			ip = is->ivec;
+			for(eip = ip+8; ip < eip; )
+				*p2++ ^= *ip++;
+			idea_cipher(is->edkey, p, 0);
+			memmove(is->ivec, p, 8);
+		}
+		break;
+	case RC4:
+		rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
+		break;
+	}
+	return b;
+}
+
+static Block*
+decryptb(Dstate *s, Block *inb)
+{
+	Block *b, **l;
+	uchar *p, *ep, *tp, *ip, *eip;
+	DESstate *ds;
+	IDEAstate *is;
+	uchar tmp[8];
+	int i;
+
+	l = &inb;
+	for(b = inb; b; b = b->next){
+		/* make sure we have a multiple of s->blocklen */
+		if(s->blocklen > 1){
+			i = BLEN(b);
+			if(i % s->blocklen){
+				*l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
+				if(b == 0)
+					error("ssl encrypted message too short");
+			}
+		}
+		l = &b->next;
+
+		/* decrypt */
+		switch(s->encryptalg){
+		case DESECB:
+			ds = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep; p += 8)
+				block_cipher(ds->expanded, p, 1);
+			break;
+		case DESCBC:
+			ds = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep;){
+				memmove(tmp, p, 8);
+				block_cipher(ds->expanded, p, 1);
+				tp = tmp;
+				ip = ds->ivec;
+				for(eip = ip+8; ip < eip; ){
+					*p++ ^= *ip;
+					*ip++ = *tp++;
+				}
+			}
+			break;
+		case IDEAECB:
+			is = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep; p += 8)
+				idea_cipher(is->edkey, p, 1);
+			break;
+		case IDEACBC:
+			is = s->in.state;
+			ep = b->rp + BLEN(b);
+			for(p = b->rp; p < ep;){
+				memmove(tmp, p, 8);
+				idea_cipher(is->edkey, p, 1);
+				tp = tmp;
+				ip = is->ivec;
+				for(eip = ip+8; ip < eip; ){
+					*p++ ^= *ip;
+					*ip++ = *tp++;
+				}
+			}
+			break;
+		case RC4:
+			rc4(s->in.state, b->rp, BLEN(b));
+			break;
+		}
+	}
+	return inb;
+}
+
+static Block*
+digestb(Dstate *s, Block *b, int offset)
+{
+	uchar *p;
+	DigestState ss;
+	uchar msgid[4];
+	ulong n, h;
+	OneWay *w;
+
+	w = &s->out;
+
+	memset(&ss, 0, sizeof(ss));
+	h = s->diglen + offset;
+	n = BLEN(b) - h;
+
+	/* hash secret + message */
+	(*s->hf)(w->secret, w->slen, 0, &ss);
+	(*s->hf)(b->rp + h, n, 0, &ss);
+
+	/* hash message id */
+	p = msgid;
+	n = w->mid;
+	*p++ = n>>24;
+	*p++ = n>>16;
+	*p++ = n>>8;
+	*p = n;
+	(*s->hf)(msgid, 4, b->rp + offset, &ss);
+
+	return b;
+}
+
+static void
+checkdigestb(Dstate *s, Block *inb)
+{
+	uchar *p;
+	DigestState ss;
+	uchar msgid[4];
+	int n, h;
+	OneWay *w;
+	uchar digest[128];
+	Block *b;
+
+	w = &s->in;
+
+	memset(&ss, 0, sizeof(ss));
+
+	/* hash secret */
+	(*s->hf)(w->secret, w->slen, 0, &ss);
+
+	/* hash message */
+	h = s->diglen;
+	for(b = inb; b; b = b->next){
+		n = BLEN(b) - h;
+		if(n < 0)
+			panic("checkdigestb");
+		(*s->hf)(b->rp + h, n, 0, &ss);
+		h = 0;
+	}
+
+	/* hash message id */
+	p = msgid;
+	n = w->mid;
+	*p++ = n>>24;
+	*p++ = n>>16;
+	*p++ = n>>8;
+	*p = n;
+	(*s->hf)(msgid, 4, digest, &ss);
+
+	/* requires pullupblock */
+	if(memcmp(digest, inb->rp, s->diglen) != 0)
+		error("bad digest");
+}
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{
+	Chan *c;
+	int fd;
+
+	if(p == 0)
+		error(Ebadarg);
+	fd = strtoul(p, 0, 0);
+	if(fd < 0)
+		error(Ebadarg);
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);	/* error check and inc ref */
+	return c;
+}
+
+/* hang up a digest connection */
+static void
+sslhangup(Dstate *s)
+{
+	qlock(&s->in.q);
+	freeblist(s->processed);
+	s->processed = 0;
+	freeblist(s->unprocessed);
+	s->unprocessed = 0;
+	s->state = Sincomplete;
+	qunlock(&s->in.q);
+}
+
+static void
+dsclone(Chan *ch)
+{
+	Dstate **pp, **ep, **np;
+	int newmax;
+
+	lock(&dslock);
+	if(waserror()) {
+		unlock(&dslock);
+		nexterror();
+	}
+	ep = &dstate[maxdstate];
+	for(pp = dstate; pp < ep; pp++) {
+		if(*pp == 0) {
+			dsnew(ch, pp);
+			break;
+		}
+	}
+	if(pp >= ep) {
+		if(maxdstate >= Maxdstate)
+			error(Enodev);
+		newmax = 2 * maxdstate;
+		if(newmax > Maxdstate)
+			newmax = Maxdstate;
+
+		np = realloc(dstate, sizeof(Dstate*) * newmax);
+		if(np == 0)
+			error(Enomem);
+		dstate = np;
+		pp = &dstate[maxdstate];
+		memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
+
+		maxdstate = newmax;
+		dsnew(ch, pp);
+	}
+	poperror();
+	unlock(&dslock);
+}
+
+static void
+dsnew(Chan *ch, Dstate **pp)
+{
+	Dstate *s;
+	int t;
+
+	*pp = s = mallocz(sizeof(*s), 1);
+	if(s == nil)
+		error(Enomem);
+	if(pp - dstate >= dshiwat)
+		dshiwat++;
+	s->state = Sincomplete;
+	s->ref = 1;
+	kstrdup(&s->user, up->env->user);
+	s->perm = 0660;
+	t = TYPE(ch->qid);
+	if(t == Qclonus)
+		t = Qctl;
+	ch->qid.path = QID(pp - dstate, t);
+	ch->qid.vers = 0;
+	ch->qid.type = QTFILE;
+}
+
+Dev ssldevtab = {
+	'D',
+	"ssl",
+
+	sslinit,
+	sslattach,
+	sslwalk,
+	sslstat,
+	sslopen,
+	devcreate,
+	sslclose,
+	sslread,
+	sslbread,
+	sslwrite,
+	sslbwrite,
+	devremove,
+	sslwstat
+};
--- /dev/null
+++ b/emu/port/devtab.c
@@ -1,0 +1,35 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+extern Dev* devtab[];
+
+long
+devtabread(Chan *c, void* buf, long n, vlong off)
+{
+	int i;
+	Dev *dev;
+	char *alloc, *e, *p;
+
+	USED(c);
+	alloc = malloc(READSTR);
+	if(alloc == nil)
+		error(Enomem);
+
+	p = alloc;
+	e = p + READSTR;
+	for(i = 0; devtab[i] != nil; i++){
+		dev = devtab[i];
+		p = seprint(p, e, "#%C %s\n", dev->dc, dev->name);
+	}
+
+	if(waserror()){
+		free(alloc);
+		nexterror();
+	}
+	n = readstr(off, buf, n, alloc);
+	free(alloc);
+	poperror();
+
+	return n;
+}
--- /dev/null
+++ b/emu/port/devtinyfs.c
@@ -1,0 +1,897 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+
+enum{
+	Qdir,
+	Qmedium,
+
+	Maxfs=		10,	/* max file systems */
+
+	Blen=		48,	/* block length */
+	Nlen=		28,	/* name length */
+	Dlen=		Blen - 4,
+
+	Tagdir=		'd',
+	Tagdata=	'D',
+	Tagend=		'e',
+	Tagfree=	'f',
+
+	Notapin=		0xffff,
+	Notabno=		0xffff,
+
+	Fcreating=	1,
+	Frmonclose=	2
+};
+
+/* representation of a Tdir on medium */
+typedef struct Mdir Mdir;
+struct Mdir {
+	uchar	type;
+	uchar	bno[2];
+	uchar	pin[2];
+	char	name[Nlen];
+	char	pad[Blen - Nlen - 6];
+	uchar	sum;
+};
+
+/* representation of a Tdata/Tend on medium */
+typedef struct Mdata Mdata;
+struct Mdata {
+	uchar	type;
+	uchar	bno[2];
+	uchar	data[Dlen];
+	uchar	sum;
+};
+
+typedef struct Tfile Tfile;
+struct Tfile {
+	int	r;
+	char	name[Nlen];
+	ushort	bno;
+	ushort	dbno;
+	ushort	pin;
+	uchar	flag;
+	ulong	length;
+
+	/* hint to avoid egregious reading */
+	ushort	fbno;
+	ulong	finger;
+};
+
+typedef struct Tfs Tfs;
+struct Tfs {
+	QLock	ql;
+	int	r;
+	Chan	*c;
+	uchar	*map;
+	int	nblocks;
+	Tfile	*f;
+	int	nf;
+	int	fsize;
+};
+
+static struct {
+	Tfs	fs[Maxfs];
+} tinyfs;
+
+#define GETS(x) ((x)[0]|((x)[1]<<8))
+#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);}
+
+#define GETL(x) (GETS(x)|(GETS(x+2)<<16))
+#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)};
+
+static uchar
+checksum(uchar *p)
+{
+	uchar *e;
+	uchar s;
+
+	s = 0;
+	for(e = p + Blen; p < e; p++)
+		s += *p;
+	return s;
+}
+
+static void
+mapclr(Tfs *fs, ulong bno)
+{
+	fs->map[bno>>3] &= ~(1<<(bno&7));
+}
+
+static void
+mapset(Tfs *fs, ulong bno)
+{
+	fs->map[bno>>3] |= 1<<(bno&7);
+}
+
+static int
+isalloced(Tfs *fs, ulong bno)
+{
+	return fs->map[bno>>3] & (1<<(bno&7));
+}
+
+static int
+mapalloc(Tfs *fs)
+{
+	int i, j, lim;
+	uchar x;
+
+	lim = (fs->nblocks + 8 - 1)/8;
+	for(i = 0; i < lim; i++){
+		x = fs->map[i];
+		if(x == 0xff)
+			continue;
+		for(j = 0; j < 8; j++)
+			if((x & (1<<j)) == 0){
+				fs->map[i] = x|(1<<j);
+				return i*8 + j;
+			}
+	}
+
+	return Notabno;
+}
+
+static Mdir*
+validdir(Tfs *fs, uchar *p)
+{
+	Mdir *md;
+	ulong x;
+
+	if(checksum(p) != 0)
+		return 0;
+	if(p[0] != Tagdir)
+		return 0;
+	md = (Mdir*)p;
+	x = GETS(md->bno);
+	if(x >= fs->nblocks)
+		return 0;
+	return md;
+}
+
+static Mdata*
+validdata(Tfs *fs, uchar *p, int *lenp)
+{
+	Mdata *md;
+	ulong x;
+
+	if(checksum(p) != 0)
+		return 0;
+	md = (Mdata*)p;
+	switch(md->type){
+	case Tagdata:
+		x = GETS(md->bno);
+		if(x >= fs->nblocks)
+			return 0;
+		if(lenp)
+			*lenp = Dlen;
+		break;
+	case Tagend:
+		x = GETS(md->bno);
+		if(x > Dlen)
+			return 0;
+		if(lenp)
+			*lenp = x;
+		break;
+	default:
+		return 0;
+	}
+	return md;
+}
+
+static Mdata*
+readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp)
+{
+	if(bno >= fs->nblocks)
+		return 0;
+	if(fs->c->dev->read(fs->c, buf, Blen, Blen*bno) != Blen)
+		error(Eio);
+	return validdata(fs, buf, lenp);
+}
+
+static void
+writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last)
+{
+	Mdata md;
+
+	if(bno >= fs->nblocks)
+		error(Eio);
+	if(len > Dlen)
+		len = Dlen;
+	if(len < 0)
+		error(Eio);
+	memset(&md, 0, sizeof(md));
+	if(last){
+		md.type = Tagend;
+		PUTS(md.bno, len);
+	} else {
+		md.type = Tagdata;
+		PUTS(md.bno, next);
+	}
+	memmove(md.data, buf, len);
+	md.sum = 0 - checksum((uchar*)&md);
+	
+	if(fs->c->dev->write(fs->c, &md, Blen, Blen*bno) != Blen)
+		error(Eio);
+}
+
+static void
+writedir(Tfs *fs, Tfile *f)
+{
+	Mdir *md;
+	uchar buf[Blen];
+
+	if(f->bno == Notabno)
+		return;
+
+	md = (Mdir*)buf;
+	memset(buf, 0, Blen);
+	md->type = Tagdir;
+	strncpy(md->name, f->name, sizeof(md->name)-1);
+	PUTS(md->bno, f->dbno);
+	PUTS(md->pin, f->pin);
+	md->sum = 0 - checksum(buf);
+	
+	if(fs->c->dev->write(fs->c, buf, Blen, Blen*f->bno) != Blen)
+		error(Eio);
+}
+
+static void
+freeblocks(Tfs *fs, ulong bno, ulong bend)
+{
+	uchar buf[Blen];
+	Mdata *md;
+
+	if(waserror())
+		return;
+
+	while(bno != bend && bno != Notabno){
+		mapclr(fs, bno);
+		if(fs->c->dev->read(fs->c, buf, Blen, Blen*bno) != Blen)
+			break;
+		md = validdata(fs, buf, 0);
+		if(md == 0)
+			break;
+		if(md->type == Tagend)
+			break;
+		bno = GETS(md->bno);
+	}
+
+	poperror();
+}
+
+static void
+freefile(Tfs *fs, Tfile *f, ulong bend)
+{
+	uchar buf[Blen];
+
+	/* remove blocks from map */
+	freeblocks(fs, f->dbno, bend);
+
+	/* change file type to free on medium */
+	if(f->bno != Notabno){
+		memset(buf, 0x55, Blen);
+		fs->c->dev->write(fs->c, buf, Blen, Blen*f->bno);
+		mapclr(fs, f->bno);
+	}
+
+	/* forget we ever knew about it */
+	memset(f, 0, sizeof(*f));
+}
+
+static void
+expand(Tfs *fs)
+{
+	Tfile *f;
+
+	fs->fsize += 8;
+	f = malloc(fs->fsize*sizeof(*f));
+	if(f == nil)
+		error(Enomem);
+
+	if(fs->f){
+		memmove(f, fs->f, fs->nf*sizeof(*f));
+		free(fs->f);
+	}
+	fs->f = f;
+}
+
+static Tfile*
+newfile(Tfs *fs, char *name)
+{
+	int i;
+	volatile struct {
+		Tfile *f;
+		Tfs *fs;
+	} rock;
+
+	/* find free entry in file table */
+	rock.f = 0;
+	rock.fs = fs;
+	for(;;) {
+		for(i = 0; i < rock.fs->fsize; i++){
+			rock.f = &rock.fs->f[i];
+			if(rock.f->name[0] == 0){
+				strncpy(rock.f->name, name, sizeof(rock.f->name)-1);
+				break;
+			}
+		}
+
+		if(i < rock.fs->fsize){
+			if(i >= rock.fs->nf)
+				rock.fs->nf = i+1;
+			break;
+		}
+
+		expand(rock.fs);
+	}
+
+	rock.f->flag = Fcreating;
+	rock.f->dbno = Notabno;
+	rock.f->bno = mapalloc(rock.fs);
+	rock.f->fbno = Notabno;
+	rock.f->r = 1;
+	rock.f->pin = Notapin;
+
+	/* write directory block */
+	if(waserror()){
+		freefile(rock.fs, rock.f, Notabno);
+		nexterror();
+	}
+	if(rock.f->bno == Notabno)
+		error("out of space");
+	writedir(rock.fs, rock.f);
+	poperror();
+	
+	return rock.f;
+}
+
+/*
+ *  Read the whole medium and build a file table and used
+ *  block bitmap.  Inconsistent files are purged.  The medium
+ *  had better be small or this could take a while.
+ */
+static void
+tfsinit(Tfs *fs)
+{
+	uchar dbuf[STATFIXLEN+32*4];
+	Dir d;
+	uchar buf[Blen];
+	ulong x, bno;
+	int n, done;
+	Tfile *f;
+	Mdir *mdir;
+	Mdata *mdata;
+
+	n = fs->c->dev->stat(fs->c, dbuf, sizeof(dbuf));
+	n = convM2D(dbuf, n, &d, nil);
+	if(n <= 0)
+		error("cannot stat tinyfs medium");
+	fs->nblocks = d.length/Blen;
+	if(fs->nblocks < 3)
+		error("tinyfs medium too small");
+
+	/* bitmap for block usage */
+	x = (fs->nblocks + 8 - 1)/8;
+	fs->map = malloc(x);
+	if(fs->map == nil)
+		error(Enomem);
+	for(bno = fs->nblocks; bno < x*8; bno++)
+		mapset(fs, bno);
+
+	/* find files */
+	for(bno = 0; bno < fs->nblocks; bno++){
+		n = fs->c->dev->read(fs->c, buf, Blen, Blen*bno);
+		if(n != Blen)
+			break;
+
+		mdir = validdir(fs, buf);
+		if(mdir == 0)
+			continue;
+
+		if(fs->nf >= fs->fsize)
+			expand(fs);
+
+		f = &fs->f[fs->nf++];
+
+		x = GETS(mdir->bno);
+		mapset(fs, bno);
+		strncpy(f->name, mdir->name, sizeof(f->name));
+		f->pin = GETS(mdir->pin);
+		f->bno = bno;
+		f->dbno = x;
+		f->fbno = Notabno;
+	}
+
+	/* follow files */
+	for(f = fs->f; f < &(fs->f[fs->nf]); f++){
+		bno = f->dbno;
+		for(done = 0; !done;) {
+			if(isalloced(fs, bno)){
+				freefile(fs, f, bno);
+				break;
+			}
+			n = fs->c->dev->read(fs->c, buf, Blen, Blen*bno);
+			if(n != Blen){
+				freefile(fs, f, bno);
+				break;
+			}
+			mdata = validdata(fs, buf, 0);
+			if(mdata == 0){
+				freefile(fs, f, bno);
+				break;
+			}
+			mapset(fs, bno);
+			switch(mdata->type){
+			case Tagdata:
+				bno = GETS(mdata->bno);
+				f->length += Dlen;
+				break;
+			case Tagend:
+				f->length += GETS(mdata->bno);
+				done = 1;
+				break;
+			}
+			if(done)
+				f->flag &= ~Fcreating;
+		}
+	}
+}
+
+/*
+ *  single directory
+ */
+static int
+tinyfsgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+	Tfs *fs;
+	Tfile *f;
+	Qid qid;
+
+	USED(name);
+	USED(ntab);
+	USED(tab);
+
+	fs = &tinyfs.fs[c->devno];
+	if(i >= fs->nf)
+		return -1;
+	qid.vers = 0;
+	if(i == DEVDOTDOT){
+		qid.type = QTDIR;
+		devdir(c, qid, ".", 0, eve, DMDIR|0555, dp);
+		return 1;
+	}
+	f = &fs->f[i];
+	if(f->name[0] == 0)
+		return 0;
+	qid.path = i+1;
+	qid.type = QTFILE;
+	devdir(c, qid, f->name, f->length, eve, 0664, dp);
+	return 1;
+}
+
+/*
+ *  specifier is the name of a device in /dev
+ */
+static Chan *
+tinyfsattach(char *spec)
+{
+	Tfs *fs;
+	Chan *c;
+	volatile struct { Chan *cc; } rock;
+	int i;
+	char buf[KNAMELEN*3];
+
+	if(*spec == 0 || strchr(spec, '/') != nil)
+		error("bad specifier");
+
+	snprint(buf, sizeof(buf), "/dev/%s", spec);
+	rock.cc = namec(buf, Aopen, ORDWR, 0);
+	if(waserror()){
+		cclose(rock.cc);
+		nexterror();
+	}
+
+	fs = 0;
+	for(i = 0; i < Maxfs; i++){
+		fs = &tinyfs.fs[i];
+		qlock(&fs->ql);
+		if(fs->r && eqchan(rock.cc, fs->c, 1))
+			break;
+		qunlock(&fs->ql);
+	}
+	if(i < Maxfs){
+		fs->r++;
+		qunlock(&fs->ql);
+		cclose(rock.cc);
+	} else {
+		for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){
+			qlock(&fs->ql);
+			if(fs->r == 0)
+				break;
+			qunlock(&fs->ql);
+		}
+		if(fs == &tinyfs.fs[Maxfs])
+			error("too many tinyfs's");
+		fs->c = rock.cc;
+		fs->r = 1;
+		fs->f = 0;
+		fs->nf = 0;
+		fs->fsize = 0;
+		tfsinit(fs);
+		qunlock(&fs->ql);
+	}
+	poperror();
+
+	c = devattach('F', spec);
+	c->devno = fs - tinyfs.fs;
+	c->qid.type = QTDIR;
+	c->qid.vers = 0;
+
+	return c;
+}
+
+static Walkqid*
+tinyfswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Tfs *fs;
+	Walkqid *wq;
+
+	fs = &tinyfs.fs[c->devno];
+
+	qlock(&fs->ql);
+	wq = devwalk(c, nc, name, nname, 0, 0, tinyfsgen);
+	if(wq != nil && (nc = wq->clone) != nil && nc->qid.path != Qdir){
+		fs = &tinyfs.fs[nc->devno];
+		fs->f[nc->qid.path-1].r++;
+	}
+	qunlock(&fs->ql);
+	return wq;
+}
+
+static int
+tinyfsstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, 0, 0, tinyfsgen);
+}
+
+static Chan *
+tinyfsopen(Chan *c, int omode)
+{
+	Tfile *f;
+	volatile struct { Tfs *fs; } rock;
+
+	rock.fs = &tinyfs.fs[c->devno];
+
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Eperm);
+	} else {
+		qlock(&rock.fs->ql);
+		if(waserror()){
+			qunlock(&rock.fs->ql);
+			nexterror();
+		}
+		switch(omode){
+		case OTRUNC|ORDWR:
+		case OTRUNC|OWRITE:
+			f = newfile(rock.fs, rock.fs->f[c->qid.path-1].name);
+			rock.fs->f[c->qid.path-1].r--;
+			c->qid.path = f - rock.fs->f;
+			break;
+		case OREAD:
+			break;
+		default:
+			error(Eperm);
+		}
+		qunlock(&rock.fs->ql);
+		poperror();
+	}
+
+	return devopen(c, omode, 0, 0, tinyfsgen);
+}
+
+static void
+tinyfscreate(Chan *c, char *name, int omode, ulong perm)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f;
+
+	USED(perm);
+
+	rock.fs = &tinyfs.fs[c->devno];
+
+	qlock(&rock.fs->ql);
+	if(waserror()){
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+	f = newfile(rock.fs, name);
+	qunlock(&rock.fs->ql);
+	poperror();
+
+	c->qid.path = (f - rock.fs->f)+1;
+	c->qid.vers = 0;
+	c->qid.type = QTFILE;
+	c->mode = openmode(omode);
+}
+
+static void
+tinyfsremove(Chan *c)
+{
+	Tfs *fs;
+	Tfile *f;
+
+	if(c->qid.path == Qdir)
+		error(Eperm);
+	fs = &tinyfs.fs[c->devno];
+	f = &fs->f[c->qid.path-1];
+	qlock(&fs->ql);
+	freefile(fs, f, Notabno);
+	qunlock(&fs->ql);
+}
+
+static void
+tinyfsclose(Chan *c)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f, *nf;
+	int i;
+
+	rock.fs = &tinyfs.fs[c->devno];
+
+	qlock(&rock.fs->ql);
+
+	/* dereference file and remove old versions */
+	if(!waserror()){
+		if(c->qid.path != Qdir){
+			f = &rock.fs->f[c->qid.path-1];
+			f->r--;
+			if(f->r == 0){
+				if(f->flag & Frmonclose)
+					freefile(rock.fs, f, Notabno);
+				else if(f->flag & Fcreating){
+					/* remove all other files with this name */
+					for(i = 0; i < rock.fs->fsize; i++){
+						nf = &rock.fs->f[i];
+						if(f == nf)
+							continue;
+						if(strcmp(nf->name, f->name) == 0){
+							if(nf->r)
+								nf->flag |= Frmonclose;
+							else
+								freefile(rock.fs, nf, Notabno);
+						}
+					}
+					f->flag &= ~Fcreating;
+				}
+			}
+		}
+		poperror();
+	}
+
+	/* dereference rock.fs and remove on zero refs */
+	rock.fs->r--;
+	if(rock.fs->r == 0){
+		if(rock.fs->f)
+			free(rock.fs->f);
+		rock.fs->f = 0;
+		rock.fs->nf = 0;
+		rock.fs->fsize = 0;
+		if(rock.fs->map)
+			free(rock.fs->map);
+		rock.fs->map = 0;
+		cclose(rock.fs->c);
+		rock.fs->c = 0;
+	}
+	qunlock(&rock.fs->ql);
+}
+
+static long
+tinyfsread(Chan *c, void *a, long n, vlong offset)
+{
+	volatile struct { Tfs *fs; } rock;
+	Tfile *f;
+	int sofar, i, off;
+	ulong bno;
+	Mdata *md;
+	uchar buf[Blen];
+	uchar *p;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, tinyfsgen);
+
+	p = a;
+	rock.fs = &tinyfs.fs[c->devno];
+	f = &rock.fs->f[c->qid.path-1];
+	if(offset >= f->length)
+		return 0;
+
+	qlock(&rock.fs->ql);
+	if(waserror()){
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+	if(n + offset >= f->length)
+		n = f->length - offset;
+
+	/* walk to starting data block */
+	if(0 && f->finger <= offset && f->fbno != Notabno){
+		sofar = f->finger;
+		bno = f->fbno;
+	} else {
+		sofar = 0;
+		bno = f->dbno;
+	}
+	for(; sofar + Dlen <= offset; sofar += Dlen){
+		md = readdata(rock.fs, bno, buf, 0);
+		if(md == 0)
+			error(Eio);
+		bno = GETS(md->bno);
+	}
+
+	/* read data */
+	off = offset%Dlen;
+	offset -= off;
+	for(sofar = 0; sofar < n; sofar += i){
+		md = readdata(rock.fs, bno, buf, &i);
+		if(md == 0)
+			error(Eio);
+
+		/* update finger for successful read */
+		f->finger = offset;
+		f->fbno = bno;
+		offset += Dlen;
+
+		i -= off;
+		if(i > n - sofar)
+			i = n - sofar;
+		memmove(p, md->data+off, i);
+		p += i;
+		bno = GETS(md->bno);
+		off = 0;
+	}
+	qunlock(&rock.fs->ql);
+	poperror();
+
+	return sofar;
+}
+
+/*
+ *  if we get a write error in this routine, blocks will
+ *  be lost.  They should be recovered next fsinit.
+ */
+static long
+tinyfswrite(Chan *c, void *a, long n, vlong offset)
+{
+	Tfile *f;
+	int last, next, i, finger, off, used;
+	ulong bno, fbno;
+	Mdata *md;
+	uchar buf[Blen];
+	uchar *p;
+	volatile struct {
+		Tfs *fs;
+		ulong dbno;
+	} rock;
+
+	if(c->qid.type & QTDIR)
+		error(Eperm);
+
+	if(n == 0)
+		return 0;
+
+	p = a;
+	rock.fs = &tinyfs.fs[c->devno];
+	f = &rock.fs->f[c->qid.path-1];
+
+	qlock(&rock.fs->ql);
+	rock.dbno = Notabno;
+	if(waserror()){
+		freeblocks(rock.fs, rock.dbno, Notabno);
+		qunlock(&rock.fs->ql);
+		nexterror();
+	}
+
+	/* files are append only, anything else is illegal */
+	if(offset != f->length)
+		error("append only");
+
+	/* write blocks backwards */
+	p += n;
+	last = offset + n;
+	fbno = Notabno;
+	finger = 0;
+	off = offset; /* so we have something signed to compare against */
+	for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){
+		bno = mapalloc(rock.fs);
+		if(bno == Notabno)
+			error("out of space");
+		i = last - next;
+		p -= i;
+		if(last == n+offset){
+			writedata(rock.fs, bno, rock.dbno, p, i, 1);
+			finger = next;	/* remember for later */
+			fbno = bno;
+		} else {
+			writedata(rock.fs, bno, rock.dbno, p, i, 0);
+		}
+		rock.dbno = bno;
+		last = next;
+	}
+
+	/* walk to last data block */
+	md = (Mdata*)buf;
+	if(0 && f->finger < offset && f->fbno != Notabno){
+		next = f->finger;
+		bno = f->fbno;
+	} else {
+		next = 0;
+		bno = f->dbno;
+	}
+
+	used = 0;
+	while(bno != Notabno){
+		md = readdata(rock.fs, bno, buf, &used);
+		if(md == 0)
+			error(Eio);
+		if(md->type == Tagend){
+			if(next + Dlen < offset)
+				panic("devtinyfs1");
+			break;
+		}
+		next += Dlen;
+		if(next > offset)
+			panic("devtinyfs1");
+		bno = GETS(md->bno);
+	}
+
+	/* point to new blocks */
+	if(offset == 0){
+		/* first block in a file */
+		f->dbno = rock.dbno;
+		writedir(rock.fs, f);
+	} else {
+		/* updating a current block */
+		i = last - offset;
+		if(i > 0){
+			p -= i;
+			memmove(md->data + used, p, i);
+			used += i;
+		}
+		writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset);
+	}
+	f->length += n;
+
+	/* update finger */
+	if(fbno != Notabno){
+		f->finger = finger;
+		f->fbno =  fbno;
+	}
+	poperror();
+	qunlock(&rock.fs->ql);
+
+	return n;
+}
+
+Dev tinyfsdevtab = {
+	'F',
+	"tinyfs",
+
+	devreset,
+	devinit,
+	devshutdown,
+	tinyfsattach,
+	tinyfswalk,
+	tinyfsstat,
+	tinyfsopen,
+	tinyfscreate,
+	tinyfsclose,
+	tinyfsread,
+	devbread,
+	tinyfswrite,
+	devbwrite,
+	tinyfsremove,
+	devwstat
+};
--- /dev/null
+++ b/emu/port/devtk.c
@@ -1,0 +1,185 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include <interp.h>
+
+#include	"draw.h"
+#include	<memdraw.h>
+#include	<memlayer.h>
+#include	<cursor.h>
+
+enum{
+	Qdir,
+	Qtkevents
+};
+
+static
+Dirtab tkdirtab[]=
+{
+	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
+	"tkevents",		{Qtkevents, 0},		0,	0600,
+};
+
+static struct {
+	QLock	l;
+	Queue*	eq;
+	Ref	inuse;
+} tkevents;
+
+static void
+tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp)
+{
+	Block *b;
+	int n;
+	char *s, *e;
+
+//fprint(2, "wiretap %p %q %q\n", top, cmd, result);
+	if(tkevents.eq == nil)
+		return;
+	n = 12;
+	if(cmd != nil)
+		n += strlen(cmd)+2+1;
+	if(result != nil)
+		n += strlen(result)+2+1;
+	if(image != nil)
+		n += 12;
+	if(rp != nil)
+		n += 4*20;
+	n++;
+	b = allocb(n);
+	if(b != nil){
+		s = (char*)b->wp;
+		e = s+n;
+		s += snprint(s, e-s, "%p", top);
+		if(cmd != nil){
+			*s++ = ' ';
+			*s++ = '[';
+			n = strlen(cmd);
+			memmove(s, cmd, n);
+			s += n;
+			*s++ = ']';
+		}
+		/* ignore result for now */
+		if(image != nil)
+			s += snprint(s, e-s, " %p", image);
+		if(rp != nil)
+			s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y);
+		b->wp = (uchar*)s;
+		release();
+		qlock(&tkevents.l);
+		if(waserror()){
+			freeb(b);
+			qunlock(&tkevents.l);
+			acquire();
+			return;
+		}
+		if(tkevents.eq != nil)
+			qbwrite(tkevents.eq, b);
+		else
+			freeb(b);
+		poperror();
+		qunlock(&tkevents.l);
+		acquire();
+	}
+}
+
+void	(*tkwiretap)(void*, char*, char*, void*, Rectangle*) = tkwiretapper;
+
+static Chan*
+tkattach(char* spec)
+{
+	return devattach(L'τ', spec);
+}
+
+static Walkqid*
+tkwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static int
+tkstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, tkdirtab, nelem(tkdirtab), devgen);
+}
+
+static Chan*
+tkopen(Chan* c, int omode)
+{
+	if(c->qid.type & QTDIR)
+		return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+	switch(c->qid.path){
+	case Qtkevents:
+		c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen);
+		qlock(&tkevents.l);
+		if(incref(&tkevents.inuse) != 1){
+			qunlock(&tkevents.l);
+			error(Einuse);
+		}
+		if(tkevents.eq == nil)
+			tkevents.eq = qopen(256*1024, 0, nil, nil);
+		else
+			qreopen(tkevents.eq);
+		qunlock(&tkevents.l);
+		break;
+	}
+	return c;
+}
+
+static void
+tkclose(Chan* c)
+{
+	if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
+		return;
+	qlock(&tkevents.l);
+	if(decref(&tkevents.inuse) == 0)
+		qclose(tkevents.eq);
+	qunlock(&tkevents.l);
+}
+
+static long
+tkread(Chan* c, void* a, long n, vlong offset)
+{
+	USED(offset);
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qtkevents:
+		return qread(tkevents.eq, a, n);
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+tkwrite(Chan *c, void* a, long n, vlong offset)
+{
+	USED(c); USED(a); USED(n); USED(offset);
+	error(Ebadusefd);
+	return 0;
+}
+
+Dev tkdevtab = {
+	L'τ',
+	"tk",
+
+//	devreset,
+	devinit,
+	tkattach,
+//	devdetach,
+	tkwalk,
+	tkstat,
+	tkopen,
+	devcreate,
+	tkclose,
+	tkread,
+	devbread,
+	tkwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/emu/port/dial.c
@@ -1,0 +1,421 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"kernel.h"
+
+typedef struct DS DS;
+
+static int	call(char*, char*, DS*);
+static int	csdial(DS*);
+static void	_dial_string_parse(char*, DS*);
+static int	nettrans(char*, char*, int na, char*, int);
+
+enum
+{
+	Maxstring=	128,
+	Maxpath=	100
+};
+
+struct DS
+{
+	char	buf[Maxstring];			/* dist string */
+	char	*netdir;
+	char	*proto;
+	char	*rem;
+	char	*local;				/* other args */
+	char	*dir;
+	int	*cfdp;
+};
+
+/*
+ *  the dialstring is of the form '[/net/]proto!dest'
+ */
+int
+kdial(char *dest, char *local, char *dir, int *cfdp)
+{
+	DS ds;
+	int rv;
+	char err[ERRMAX], alterr[ERRMAX];
+
+	ds.local = local;
+	ds.dir = dir;
+	ds.cfdp = cfdp;
+
+	_dial_string_parse(dest, &ds);
+	if(ds.netdir)
+		return csdial(&ds);
+
+	ds.netdir = "/net";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+
+	err[0] = 0;
+	kerrstr(err, sizeof err);
+	if(strstr(err, "refused") != 0){
+		kerrstr(err, sizeof err);
+		return rv;
+	}
+
+	ds.netdir = "/net.alt";
+	rv = csdial(&ds);
+	if(rv >= 0)
+		return rv;
+
+	alterr[0] = 0;
+	kerrstr(alterr, sizeof err);
+
+	if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+		kerrstr(err, sizeof err);
+	else
+		kerrstr(alterr, sizeof alterr);
+	return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+	int n, fd, rv;
+	char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+
+	/*
+	 *  open connection server
+	 */
+	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+	fd = kopen(buf, ORDWR);
+	if(fd < 0){
+		/* no connection server, don't translate */
+		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+		return call(clone, ds->rem, ds);
+	}
+
+	/*
+	 *  ask connection server to translate
+	 */
+	sprint(buf, "%s!%s", ds->proto, ds->rem);
+	if(kwrite(fd, buf, strlen(buf)) < 0){
+		kerrstr(err, sizeof err);
+		kclose(fd);
+		kwerrstr("%s (%s)", err, buf);
+		return -1;
+	}
+
+	/*
+	 *  loop through each address from the connection server till
+	 *  we get one that works.
+	 */
+	*besterr = 0;
+	strcpy(err, Egreg);
+	rv = -1;
+	kseek(fd, 0, 0);
+	while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){
+		buf[n] = 0;
+		p = strchr(buf, ' ');
+		if(p == 0)
+			continue;
+		*p++ = 0;
+		rv = call(buf, p, ds);
+		if(rv >= 0)
+			break;
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		if(strstr(err, "does not exist") == 0)
+			memmove(besterr, err, sizeof besterr);
+	}
+	kclose(fd);
+
+	if(rv < 0 && *besterr)
+		kerrstr(besterr, sizeof besterr);
+	else
+		kerrstr(err, sizeof err);
+	return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds)
+{
+	int fd, cfd, n;
+	char name[Maxpath], data[Maxpath], err[ERRMAX], *p;
+
+	cfd = kopen(clone, ORDWR);
+	if(cfd < 0){
+		kerrstr(err, sizeof err);
+		kwerrstr("%s (%s)", err, clone);
+		return -1;
+	}
+
+	/* get directory name */
+	n = kread(cfd, name, sizeof(name)-1);
+	if(n < 0){
+		kerrstr(err, sizeof err);
+		kclose(cfd);
+		kwerrstr("read %s: %s", clone, err);
+		return -1;
+	}
+	name[n] = 0;
+	for(p = name; *p == ' '; p++)
+		;
+	sprint(name, "%ld", strtoul(p, 0, 0));
+	p = strrchr(clone, '/');
+	*p = 0;
+	if(ds->dir)
+		snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name);
+	snprint(data, sizeof(data), "%s/%s/data", clone, name);
+
+	/* connect */
+	if(ds->local)
+		snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+	else
+		snprint(name, sizeof(name), "connect %s", dest);
+	if(kwrite(cfd, name, strlen(name)) < 0){
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		kclose(cfd);
+		kwerrstr("%s (%s)", err, name);
+		return -1;
+	}
+
+	/* open data connection */
+	fd = kopen(data, ORDWR);
+	if(fd < 0){
+		err[0] = 0;
+		kerrstr(err, sizeof err);
+		kwerrstr("%s (%s)", err, data);
+		kclose(cfd);
+		return -1;
+	}
+	if(ds->cfdp)
+		*ds->cfdp = cfd;
+	else
+		kclose(cfd);
+
+	return fd;
+}
+
+/*
+ *  parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+	char *p, *p2;
+
+	strncpy(ds->buf, str, Maxstring);
+	ds->buf[Maxstring-1] = 0;
+
+	p = strchr(ds->buf, '!');
+	if(p == 0) {
+		ds->netdir = 0;
+		ds->proto = "net";
+		ds->rem = ds->buf;
+	} else {
+		if(*ds->buf != '/' && *ds->buf != '#'){
+			ds->netdir = 0;
+			ds->proto = ds->buf;
+		} else {
+			for(p2 = p; *p2 != '/'; p2--)
+				;
+			*p2++ = 0;
+			ds->netdir = ds->buf;
+			ds->proto = p2;
+		}
+		*p = 0;
+		ds->rem = p + 1;
+	}
+}
+
+/*
+ *  announce a network service.
+ */
+int
+kannounce(char *addr, char *dir)
+{
+	int ctl, n, m;
+	char buf[NETPATHLEN];
+	char buf2[Maxpath];
+	char netdir[NETPATHLEN];
+	char naddr[Maxpath];
+	char *cp;
+
+	/*
+	 *  translate the address
+	 */
+	if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
+		return -1;
+
+	/*
+	 * get a control channel
+	 */
+	ctl = kopen(netdir, ORDWR);
+	if(ctl<0)
+		return -1;
+	cp = strrchr(netdir, '/');
+	*cp = 0;
+
+	/*
+	 *  find out which line we have
+	 */
+	n = sprint(buf, "%.*s/", sizeof buf, netdir);
+	m = kread(ctl, &buf[n], sizeof(buf)-n-1);
+	if(m <= 0){
+		kclose(ctl);
+		return -1;
+	}
+	buf[n+m] = 0;
+
+	/*
+	 *  make the call
+	 */
+	n = snprint(buf2, sizeof buf2, "announce %s", naddr);
+	if(kwrite(ctl, buf2, n)!=n){
+		kclose(ctl);
+		return -1;
+	}
+
+	/*
+	 *  return directory etc.
+	 */
+	if(dir)
+		strcpy(dir, buf);
+	return ctl;
+}
+
+/*
+ *  listen for an incoming call
+ */
+int
+klisten(char *dir, char *newdir)
+{
+	int ctl, n, m;
+	char buf[NETPATHLEN];
+	char *cp;
+
+	/*
+	 *  open listen, wait for a call
+	 */
+	snprint(buf, sizeof buf, "%s/listen", dir);
+	ctl = kopen(buf, ORDWR);
+	if(ctl < 0)
+		return -1;
+
+	/*
+	 *  find out which line we have
+	 */
+	strcpy(buf, dir);
+	cp = strrchr(buf, '/');
+	*++cp = 0;
+	n = cp-buf;
+	m = kread(ctl, cp, sizeof(buf) - n - 1);
+	if(m <= 0){
+		kclose(ctl);
+		return -1;
+	}
+	buf[n+m] = 0;
+
+	/*
+	 *  return directory etc.
+	 */
+	if(newdir)
+		strcpy(newdir, buf);
+	return ctl;
+
+}
+
+/*
+ *  perform the identity translation (in case we can't reach cs)
+ */
+static int
+identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
+{
+	char proto[Maxpath];
+	char *p;
+
+	USED(nf);
+
+	/* parse the protocol */
+	strncpy(proto, addr, sizeof(proto));
+	proto[sizeof(proto)-1] = 0;
+	p = strchr(proto, '!');
+	if(p)
+		*p++ = 0;
+
+	snprint(file, nf, "%s/%s/clone", netdir, proto);
+	strncpy(naddr, p, na);
+	naddr[na-1] = 0;
+
+	return 1;
+}
+
+/*
+ *  call up the connection server and get a translation
+ */
+static int
+nettrans(char *addr, char *naddr, int na, char *file, int nf)
+{
+	int i, fd;
+	char buf[Maxpath];
+	char netdir[NETPATHLEN];
+	char *p, *p2;
+	long n;
+
+	/*
+	 *  parse, get network directory
+	 */
+	p = strchr(addr, '!');
+	if(p == 0){
+		kwerrstr("bad dial string: %s", addr);
+		return -1;
+	}
+	if(*addr != '/'){
+		strcpy(netdir, "/net");
+	} else {
+		for(p2 = p; *p2 != '/'; p2--)
+			;
+		i = p2 - addr;
+		if(i == 0 || i >= sizeof(netdir)){
+			kwerrstr("bad dial string: %s", addr);
+			return -1;
+		}
+		strncpy(netdir, addr, i);
+		netdir[i] = 0;
+		addr = p2 + 1;
+	}
+
+	/*
+	 *  ask the connection server
+	 */
+	sprint(buf, "%s/cs", netdir);
+	fd = kopen(buf, ORDWR);
+	if(fd < 0)
+		return identtrans(netdir, addr, naddr, na, file, nf);
+	if(kwrite(fd, addr, strlen(addr)) < 0){
+		kclose(fd);
+		return -1;
+	}
+	kseek(fd, 0, 0);
+	n = kread(fd, buf, sizeof(buf)-1);
+	kclose(fd);
+	if(n <= 0)
+		return -1;
+	buf[n] = 0;
+
+	/*
+	 *  parse the reply
+	 */
+	p = strchr(buf, ' ');
+	if(p == 0)
+		return -1;
+	*p++ = 0;
+	strncpy(naddr, p, na);
+	naddr[na-1] = 0;
+
+	if(buf[0] == '/'){
+		p = strchr(buf+1, '/');
+		if(p == nil)
+			p = buf;
+		else
+			p++;
+	}
+	snprint(file, nf, "%s/%s", netdir, p);
+	return 0;
+}
--- /dev/null
+++ b/emu/port/dis.c
@@ -1,0 +1,1141 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	<isa.h>
+#include	<interp.h>
+#include	<kernel.h>
+#include	"error.h"
+#include	"raise.h"
+
+struct
+{
+	Lock	l;
+	Prog*	runhd;
+	Prog*	runtl;
+	Prog*	head;
+	Prog*	tail;
+	Rendez	irend;
+	int	idle;
+	int	nyield;
+	int	creating;
+	Proc*	vmq;		/* queue of procs wanting vm */
+	Proc*	vmqt;
+	Proc*	idlevmq;	/* queue of procs wanting work */
+	Atidle*	idletasks;
+} isched;
+
+int	bflag;
+int	cflag;
+uvlong	gcbusy;
+uvlong	gcidle;
+uvlong	gcidlepass;
+uvlong	gcpartial;
+int keepbroken = 1;
+extern int	vflag;
+static Prog*	proghash[64];
+
+static Progs*	delgrp(Prog*);
+static void	addgrp(Prog*, Prog*);
+void	printgrp(Prog*, char*);
+
+static Prog**
+pidlook(int pid)
+{
+	ulong h;
+	Prog **l;
+
+	h = (ulong)pid % nelem(proghash);
+	for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink)
+		;
+	return l;
+}
+
+int
+tready(void *a)
+{
+	USED(a);
+	return isched.runhd != nil || isched.vmq != nil;
+}
+
+Prog*
+progpid(int pid)
+{
+	return *pidlook(pid);
+}
+
+Prog*
+progn(int n)
+{
+	Prog *p;
+
+	for(p = isched.head; p && n--; p = p->next)
+		;
+	return p;
+}
+
+int
+nprog(void)
+{
+	int n;
+	Prog *p;
+
+	n = 0;
+	for(p = isched.head; p; p = p->next)
+		n++;
+	return n;
+}
+
+static void
+execatidle(void)
+{
+	int done;
+
+	if(tready(nil))
+		return;
+
+	gcidle++;
+	up->type = IdleGC;
+	up->iprog = nil;
+	addrun(up->prog);
+	done = gccolor+3;
+	while(gccolor < done && gcruns()) {
+		if(isched.vmq != nil || isched.runhd != isched.runtl) {
+			gcpartial++;
+			break;
+		}
+		rungc(isched.head);
+		gcidlepass++;
+		if(((ulong)gcidlepass&0xFF) == 0)
+			osyield();
+	}
+	up->type = Interp;
+	delrunq(up->prog);
+}
+
+Prog*
+newprog(Prog *p, Modlink *m)
+{
+	Heap *h;
+	Prog *n, **ph;
+	Osenv *on, *op;
+	static int pidnum;
+
+	if(p != nil){
+		if(p->group != nil)
+			p->flags |= p->group->flags & Pkilled;
+		if(p->kill != nil)
+			error(p->kill);
+		if(p->flags & Pkilled)
+			error("");
+	}
+	n = malloc(sizeof(Prog)+sizeof(Osenv));
+	if(n == 0){
+		if(p == nil)
+			panic("no memory");
+		else
+			error(exNomem);
+	}
+
+	n->pid = ++pidnum;
+	if(n->pid <= 0)
+		panic("no pids");
+	n->group = nil;
+
+	if(isched.tail != nil) {
+		n->prev = isched.tail;
+		isched.tail->next = n;
+	}
+	else {
+		isched.head = n;
+		n->prev = nil;
+	}
+	isched.tail = n;
+
+	ph = pidlook(n->pid);
+	if(*ph != nil)
+		panic("dup pid");
+	n->pidlink = nil;
+	*ph = n;
+
+	n->osenv = (Osenv*)((uchar*)n + sizeof(Prog));
+	n->xec = xec;
+	n->quanta = PQUANTA;
+	n->flags = 0;
+	n->exval = H;
+
+	h = D2H(m);
+	h->ref++;
+	Setmark(h);
+	n->R.M = m;
+	n->R.MP = m->MP;
+	if(m->MP != H)
+		Setmark(D2H(m->MP));
+	addrun(n);
+
+	if(p == nil){
+		newgrp(n);
+		return n;
+	}
+
+	addgrp(n, p);
+	n->flags = p->flags;
+	if(p->flags & Prestrict)
+		n->flags |= Prestricted;
+	memmove(n->osenv, p->osenv, sizeof(Osenv));
+	op = p->osenv;
+	on = n->osenv;
+	on->waitq = op->childq;
+	on->childq = nil;
+	on->debug = nil;
+	incref(&on->pgrp->r);
+	incref(&on->fgrp->r);
+	incref(&on->egrp->r);
+	if(on->sigs != nil)
+		incref(&on->sigs->r);
+	on->user = nil;
+	kstrdup(&on->user, op->user);
+	on->errstr = on->errbuf0;
+	on->syserrstr = on->errbuf1;
+
+	return n;
+}
+
+void
+delprog(Prog *p, char *msg)
+{
+	Osenv *o;
+	Prog **ph;
+
+	tellsomeone(p, msg);	/* call before being removed from prog list */
+
+	o = p->osenv;
+	release();
+	closepgrp(o->pgrp);
+	closefgrp(o->fgrp);
+	closeegrp(o->egrp);
+	closesigs(o->sigs);
+	acquire();
+
+	delgrp(p);
+
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		isched.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		isched.tail = p->prev;
+
+	ph = pidlook(p->pid);
+	if(*ph == nil)
+		panic("lost pid");
+	*ph = p->pidlink;
+
+	if(p == isched.runhd) {
+		isched.runhd = p->link;
+		if(p->link == nil)
+			isched.runtl = nil;
+	}
+	p->state = 0xdeadbeef;
+	free(o->user);
+	free(p->killstr);
+	free(p->exstr);
+	free(p);
+}
+
+void
+renameproguser(char *old, char *new)
+{
+	Prog *p;
+	Osenv *o;
+
+	acquire();
+	for(p = isched.head; p; p = p->next){
+		o = p->osenv;
+		if(o->user != nil && strcmp(o->user, old) == 0)
+			kstrdup(&o->user, new);
+	}
+	release();
+}
+
+void
+tellsomeone(Prog *p, char *buf)
+{
+	Osenv *o;
+
+	if(waserror())
+		return;
+	o = p->osenv;
+	if(o->childq != nil)
+		qproduce(o->childq, buf, strlen(buf));
+	if(o->waitq != nil) 
+		qproduce(o->waitq, buf, strlen(buf)); 
+	poperror();
+}
+
+static void
+swiprog(Prog *p)
+{
+	Proc *q;
+
+	lock(&procs.l);
+	for(q = procs.head; q; q = q->next) {
+		if(q->iprog == p) {
+			unlock(&procs.l);
+			swiproc(q, 1);
+			return;
+		}
+	}
+	unlock(&procs.l);
+	/*print("didn't find\n");*/
+}
+
+static Prog*
+grpleader(Prog *p)
+{
+	Progs *g;
+	Prog *l;
+
+	g = p->group;
+	if(g != nil && (l = g->head) != nil && l->pid == g->id)
+		return l;
+	return nil;
+}
+
+int
+exprog(Prog *p, char *exc)
+{
+	/* similar code to killprog but not quite */
+	switch(p->state) {
+	case Palt:
+		altdone(p->R.s, p, nil, -1);
+		break;
+	case Psend:
+		cqdelp(&p->chan->send, p);
+		break;
+	case Precv:
+		cqdelp(&p->chan->recv, p);
+		break;
+	case Pready:
+		break;
+	case Prelease:
+		swiprog(p);
+		break;
+	case Pexiting:
+	case Pbroken:
+	case Pdebug:
+		return 0;
+	default:
+		panic("exprog - bad state 0x%x\n", p->state);
+	}
+	if(p->state != Pready && p->state != Prelease)
+		addrun(p);
+	if(p->kill == nil){
+		if(p->killstr == nil){
+			p->killstr = malloc(ERRMAX);
+			if(p->killstr == nil){
+				p->kill = Enomem;
+				return 1;
+			}
+		}
+		kstrcpy(p->killstr, exc, ERRMAX);
+		p->kill = p->killstr;
+	}
+	return 1;
+}
+
+static void
+propex(Prog *p, char *estr)
+{
+	Prog *f, *nf, *pgl;
+
+	if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil)
+		return;
+	if(*estr == 0){
+		if((p->flags & Pkilled) == 0)
+			return;
+		estr = "killed";
+	}
+	pgl = grpleader(p);
+	if(pgl == nil)
+		pgl = p;
+	if(!(pgl->flags & (Ppropagate|Pnotifyleader)))
+		return;	/* exceptions are local; don't propagate */
+	for(f = p->group->head; f != nil; f = nf){
+		nf = f->grpnext;
+		if(f != p && f != pgl){
+			if(pgl->flags & Ppropagate)
+				exprog(f, estr);
+			else{
+				f->flags &= ~(Ppropagate|Pnotifyleader);	/* prevent recursion */
+				killprog(f, "killed");
+			}
+		}
+	}
+	if(p != pgl)
+		exprog(pgl, estr);
+}
+
+int
+killprog(Prog *p, char *cause)
+{
+	Osenv *env;
+	char msg[ERRMAX+2*KNAMELEN];
+
+	if(p == isched.runhd) {
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->state = Pexiting;
+		return 0;
+	}
+
+	switch(p->state) {
+	case Palt:
+		altdone(p->R.s, p, nil, -1);
+		break;
+	case Psend:
+		cqdelp(&p->chan->send, p);
+		break;
+	case Precv:
+		cqdelp(&p->chan->recv, p);
+		break;
+	case Pready:
+		delrunq(p);
+		break;
+	case Prelease:
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->state = Pexiting;
+		swiprog(p);
+		/* No break */
+	case Pexiting:
+		return 0;
+	case Pbroken:
+	case Pdebug:
+		break;
+	default:
+		panic("killprog - bad state 0x%x\n", p->state);
+	}
+
+	if(p->addrun != nil) {
+		p->kill = "";
+		p->flags |= Pkilled;
+		p->addrun(p);
+		p->addrun = nil;
+		return 0;
+	}
+
+	env = p->osenv;
+	if(env->debug != nil) {
+		p->state = Pbroken;
+		dbgexit(p, 0, cause);
+		return 0;
+	}
+
+	propex(p, "killed");
+
+	snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause);
+
+	p->state = Pexiting;
+	gclock();
+	destroystack(&p->R);
+	delprog(p, msg);
+	gcunlock();
+
+	return 1;
+}
+
+void
+newgrp(Prog *p)
+{
+	Progs *pg, *g;
+
+	if(p->group != nil && p->group->id == p->pid)
+		return;
+	g = malloc(sizeof(*g));
+	if(g == nil)
+		error(Enomem);
+	p->flags &= ~(Ppropagate|Pnotifyleader);
+	g->id = p->pid;
+	g->flags = 0;
+	if(p->group != nil)
+		g->flags |= p->group->flags&Pprivatemem;
+	g->child = nil;
+	pg = delgrp(p);
+	g->head = g->tail = p;
+	p->group = g;
+	if(pg != nil){
+		g->sib = pg->child;
+		pg->child = g;
+	}
+	g->parent = pg;
+}
+
+static void
+addgrp(Prog *n, Prog *p)
+{
+	Progs *g;
+
+	n->group = p->group;
+	if((g = n->group) != nil){
+		n->grpnext = nil;
+		if(g->head != nil){
+			n->grpprev = g->tail;
+			g->tail->grpnext = n;
+		}else{
+			n->grpprev = nil;
+			g->head = n;
+		}
+		g->tail = n;
+	}
+}
+
+static Progs*
+delgrp(Prog *p)
+{
+	Progs *g, *pg, *cg, **l;
+
+	g = p->group;
+	if(g == nil)
+		return nil;
+	if(p->grpprev)
+		p->grpprev->grpnext = p->grpnext;
+	else
+		g->head = p->grpnext;
+	if(p->grpnext)
+		p->grpnext->grpprev = p->grpprev;
+	else
+		g->tail = p->grpprev;
+	p->grpprev = p->grpnext = nil;
+	p->group = nil;
+
+	if(g->head == nil){
+		/* move up, giving subgroups of groups with no Progs to their parents */
+		do{
+			if((pg = g->parent) != nil){
+				pg = g->parent;
+				for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib)
+					;
+				*l = g->sib;
+			}
+			/* put subgroups in new parent group */
+			while((cg = g->child) != nil){
+				g->child = cg->sib;
+				cg->parent = pg;
+				if(pg != nil){
+					cg->sib = pg->child;
+					pg->child = cg;
+				}
+			}
+			free(g);
+		}while((g = pg) != nil && g->head == nil);
+	}
+	return g;
+}
+
+void
+printgrp(Prog *p, char *v)
+{
+	Progs *g;
+	Prog *q;
+
+	g = p->group;
+	print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0);
+	for(q = g->head; q != nil; q = q->grpnext)
+		print(" %d", q->pid);
+	print(" subgrp");
+	for(g = g->child; g != nil; g = g->sib)
+		print(" %d", g->id);
+	print("]\n");
+}
+
+int
+killgrp(Prog *p, char *msg)
+{
+	int i, npid, *pids;
+	Prog *f;
+	Progs *g;
+
+	/* interpreter has been acquired */
+	g = p->group;
+	if(g == nil || g->head == nil)
+		return 0;
+	while(g->flags & Pkilled){
+		release();
+		acquire();
+	}
+	npid = 0;
+	for(f = g->head; f != nil; f = f->grpnext)
+		if(f->group != g)
+			panic("killgrp");
+		else
+			npid++;
+	/* use pids not Prog* because state can change during killprog (eg, in delprog) */
+	pids = malloc(npid*sizeof(int));
+	if(pids == nil)
+		error(Enomem);
+	npid = 0;
+	for(f = g->head; f != nil; f = f->grpnext)
+		pids[npid++] = f->pid;
+	g->flags |= Pkilled;
+	if(waserror()) {
+		g->flags &= ~Pkilled;
+		free(pids);
+		nexterror();
+	}
+	for(i = 0; i < npid; i++) {
+		f = progpid(pids[i]);
+		if(f != nil && f != currun())
+			killprog(f, msg);
+	}
+	poperror();
+	g->flags &= ~Pkilled;
+	free(pids);
+	return 1;
+}
+
+char	changup[] = "channel hangup";
+
+void
+killcomm(Progq **q)
+{
+	Prog *p;
+	Progq *f;
+
+	for (f = *q; f != nil; f = *q) {
+		*q = f->next;
+		p = f->prog;
+		free(f);
+		if(p == nil)
+			return;
+		p->ptr = nil;
+		switch(p->state) {
+		case Prelease:
+			swiprog(p);
+			break;
+		case Psend:
+		case Precv:
+			p->kill = changup;
+			addrun(p);
+			break;
+		case Palt:
+			altgone(p);
+			break;
+		}
+	}
+}
+
+void
+addprog(Proc *p)
+{
+	Prog *n;
+
+	n = malloc(sizeof(Prog));
+	if(n == nil)
+		panic("no memory");
+	p->prog = n;
+	n->osenv = p->env;
+}
+
+static void
+cwakeme(Prog *p)
+{
+	Osenv *o;
+
+	p->addrun = nil;
+	o = p->osenv;
+	Wakeup(o->rend);
+}
+
+static int
+cdone(void *vp)
+{
+	Prog *p = vp;
+
+	return p->addrun == nil || p->kill != nil;
+}
+
+void
+cblock(Prog *p)
+{
+	Osenv *o;
+
+	p->addrun = cwakeme;
+	o = p->osenv;
+	o->rend = &up->sleep;
+	release();
+
+	/*
+	 * To allow cdone(p) safely after release,
+	 * p must be currun before the release.
+	 * Exits in the error case with the vm acquired.
+	 */
+	if(waserror()) {
+		acquire();
+		p->addrun = nil;
+		nexterror();
+	}
+	Sleep(o->rend, cdone, p);
+	if (p->kill != nil)
+		error(Eintr);
+	poperror();
+	acquire();
+}
+
+void
+addrun(Prog *p)
+{
+	if(p->addrun != 0) {
+		p->addrun(p);
+		return;
+	}
+	p->state = Pready;
+	p->link = nil;
+	if(isched.runhd == nil)
+		isched.runhd = p;
+	else
+		isched.runtl->link = p;
+
+	isched.runtl = p;
+}
+
+Prog*
+delrun(int state)
+{
+	Prog *p;
+
+	p = isched.runhd;
+	p->state = state;
+	isched.runhd = p->link;
+	if(p->link == nil)
+		isched.runtl = nil;
+
+	return p;
+}
+
+void
+delrunq(Prog *p)
+{
+	Prog *prev, *f;
+
+	prev = nil;
+	for(f = isched.runhd; f; f = f->link) {
+		if(f == p)
+			break;
+		prev = f;
+	}
+	if(f == nil)
+		return;
+	if(prev == nil)
+		isched.runhd = p->link;
+	else
+		prev->link = p->link;
+	if(p == isched.runtl)
+		isched.runtl = prev;
+}
+
+Prog*
+delruntail(int state)
+{
+	Prog *p;
+
+	p = isched.runtl;
+	delrunq(p);
+	p->state = state;
+	return p;
+}
+
+Prog*
+currun(void)
+{
+	return isched.runhd;
+}
+
+Prog*
+schedmod(Module *m)
+{
+	Heap *h;
+	Type *t;
+	Prog *p;
+	Modlink *ml;
+	Frame f, *fp;
+
+	ml = mklinkmod(m, 0);
+
+	if(m->origmp != H && m->ntype > 0) {
+		t = m->type[0];
+		h = nheap(t->size);
+		h->t = t;
+		t->ref++;
+		ml->MP = H2D(uchar*, h);
+		newmp(ml->MP, m->origmp, t);
+	}
+
+	p = newprog(nil, ml);
+	h = D2H(ml);
+	h->ref--;
+	p->R.PC = m->entry;
+	fp = &f;
+	R.s = &fp;
+	f.t = m->entryt;
+	newstack(p);
+	initmem(m->entryt, p->R.FP);
+
+	return p;
+}
+
+/*
+static char*
+m(Prog *p)
+{
+	if(p)
+		if(p->R.M)
+			if(p->R.M->m)
+				return p->R.M->m->name;
+	return "nil";
+}
+*/
+
+void
+acquire(void)
+{
+	int empty;
+	Prog *p;
+
+	lock(&isched.l);
+	if(isched.idle) {
+		isched.idle = 0;
+		unlock(&isched.l);
+	}
+	else {
+		up->qnext = nil;
+		if(isched.vmq != nil){
+			empty = 0;
+			isched.vmqt->qnext = up;
+		}else{
+			isched.vmq = up;
+			empty = 1;
+		}
+		isched.vmqt = up;
+
+		unlock(&isched.l);
+		strcpy(up->text, "acquire");
+		if(empty)
+			Wakeup(&isched.irend);
+		osblock();
+	}
+
+	if(up->type == Interp) {
+		p = up->iprog;
+		up->iprog = nil;
+		irestore(p);
+	}
+	else
+		p = up->prog;
+
+	p->state = Pready;
+	p->link = isched.runhd;
+	isched.runhd = p;
+	if(p->link == nil)
+		isched.runtl = p;
+
+	strcpy(up->text, "dis");
+}
+
+void
+release(void)
+{
+	Proc *p, **pq;
+	int f;
+
+	if(up->type == Interp)
+		up->iprog = isave();
+	else
+		delrun(Prelease);
+
+	lock(&isched.l);
+	if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) {
+		isched.idle = 1;
+		f = isched.creating;
+		isched.creating = 1;
+		unlock(&isched.l);
+		if(f == 0)
+			kproc("dis", vmachine, nil, 0);
+		return;
+	}
+	p = *pq;
+	*pq = p->qnext;
+	unlock(&isched.l);
+
+	osready(p);		/* wake up thread to run VM */
+	strcpy(up->text, "released");
+}
+
+void
+iyield(void)
+{
+	Proc *p;
+
+	lock(&isched.l);
+	p = isched.vmq;
+	if(p == nil) {
+		unlock(&isched.l);
+		return;
+	}
+	isched.nyield++;
+	isched.vmq = p->qnext;
+
+	if(up->iprog != nil)
+		panic("iyield but iprog, type %d", up->type);
+	if(up->type != Interp){
+		static int once;
+		if(!once++)
+			print("tell charles: #%p->type==%d\n", up, up->type);
+	}
+	up->qnext = isched.idlevmq;
+	isched.idlevmq = up;
+
+	unlock(&isched.l);
+	osready(p);		/* wake up acquiring kproc */
+	strcpy(up->text, "yield");
+	osblock();		/* sleep */
+	strcpy(up->text, "dis");
+}
+
+void
+startup(void)
+{
+
+	up->type = Interp;
+	up->iprog = nil;
+
+	lock(&isched.l);
+	isched.creating = 0;
+	if(isched.idle) {
+		isched.idle = 0;
+		unlock(&isched.l);
+		return;
+	}
+	up->qnext = isched.idlevmq;
+	isched.idlevmq = up;
+	unlock(&isched.l);
+
+	osblock();
+}
+
+void
+progexit(void)
+{
+	Prog *r;
+	Module *m;
+	int broken;
+	char *estr, msg[ERRMAX+2*KNAMELEN];
+
+	estr = up->env->errstr;
+	broken = 0;
+	if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0)
+		broken = 1;
+
+	r = up->iprog;
+	if(r != nil)
+		acquire();
+	else
+		r = currun();
+
+	if(*estr == '\0' && r->flags & Pkilled)
+		estr = "killed";
+
+	m = R.M->m;
+	if(broken){
+		if(cflag){	/* only works on Plan9 for now */
+			char *pc = strstr(estr, "pc=");
+
+			if(pc != nil)
+				R.PC = r->R.PC = (Inst*)strtol(pc+3, nil, 0);	/* for debugging */
+		}
+		print("[%s] Broken: \"%s\"\n", m->name, estr);
+	}
+
+	snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr);
+
+	if(up->env->debug != nil) {
+		dbgexit(r, broken, estr);
+		broken = 1;
+		/* must force it to break if in debug */
+	}else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh()))
+		broken = 0;	/* don't want them or short of memory */
+
+	if(broken){
+		tellsomeone(r, msg);
+		r = isave();
+		r->state = Pbroken;
+		return;
+	}
+
+	gclock();
+	destroystack(&R);
+	delprog(r, msg);
+	gcunlock();
+
+	if(isched.head == nil)
+		cleanexit(0);
+}
+
+void
+disfault(void *reg, char *msg)
+{
+	Prog *p;
+
+	USED(reg);
+
+	if(strncmp(msg, Eintr, 6) == 0)
+		exits(0);
+
+	if(up == nil) {
+		print("EMU: faults: %s\n", msg);
+		cleanexit(0);
+	}
+	if(up->type != Interp) {
+		print("SYS: process %s faults: %s\n", up->text, msg);
+		cleanexit(0);
+	}
+
+	if(up->iprog != nil)
+		acquire();
+
+	p = currun();
+	if(p == nil)
+		panic("Interp faults with no dis prog");
+
+	/* cause an exception in the dis prog.  As for error(), but Plan 9 needs reg*/
+	kstrcpy(up->env->errstr, msg, ERRMAX);
+	oslongjmp(reg, up->estack[--up->nerr], 1);
+}
+
+void
+vmachine(void *a)
+{
+	Prog *r;
+	Osenv *o;
+	int cycles;
+	static int gccounter;
+
+	USED(a);
+
+	startup();
+
+	while(waserror()) {
+		if(up->iprog != nil)
+			acquire();
+		if(handler(up->env->errstr) == 0) {
+			propex(currun(), up->env->errstr);
+			progexit();
+		}
+		up->env = &up->defenv;
+	}
+
+	cycles = 0;
+	for(;;) {
+		if(tready(nil) == 0) {
+			execatidle();
+			strcpy(up->text, "idle");
+			Sleep(&isched.irend, tready, 0);
+			strcpy(up->text, "dis");
+		}
+
+		if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){
+			iyield();
+			cycles = 0;
+		}
+
+		r = isched.runhd;
+		if(r != nil) {
+			o = r->osenv;
+			up->env = o;
+
+			FPrestore(&o->fpu);
+			r->xec(r);
+			FPsave(&o->fpu);
+
+			if(isched.runhd != nil)
+			if(r == isched.runhd)
+			if(isched.runhd != isched.runtl) {
+				isched.runhd = r->link;
+				r->link = nil;
+				isched.runtl->link = r;
+				isched.runtl = r;
+			}
+			up->env = &up->defenv;
+		}
+		if(isched.runhd != nil)
+		if((++gccounter&0xFF) == 0 || memlow()) {
+			gcbusy++;
+			up->type = BusyGC;
+			pushrun(up->prog);
+			rungc(isched.head);
+			up->type = Interp;
+			delrunq(up->prog);
+		}
+	}
+}
+
+void
+disinit(void *a)
+{
+	Prog *p;
+	Osenv *o;
+	Module *root;
+	char *initmod = a;
+
+	if(waserror())
+		panic("disinit error: %r");
+
+	if(vflag)
+		print("Initial Dis: \"%s\"\n", initmod);
+
+	fmtinstall('D', Dconv);
+
+	FPinit();
+	FPsave(&up->env->fpu);
+
+	opinit();
+	modinit();
+	excinit();
+
+	root = load(initmod);
+	if(root == 0) {
+		kgerrstr(up->genbuf, sizeof up->genbuf);
+		panic("loading \"%s\": %s", initmod, up->genbuf);
+	}
+
+	p = schedmod(root);
+
+	memmove(p->osenv, up->env, sizeof(Osenv));
+	o = p->osenv;
+	incref(&o->pgrp->r);
+	incref(&o->fgrp->r);
+	incref(&o->egrp->r);
+	if(o->sigs != nil)
+		incref(&o->sigs->r);
+	o->user = nil;
+	kstrdup(&o->user, up->env->user);
+	o->errstr = o->errbuf0;
+	o->syserrstr = o->errbuf1;
+
+	isched.idle = 1;
+	poperror();
+	vmachine(nil);
+}
+
+void
+pushrun(Prog *p)
+{
+	if(p->addrun != nil)
+		panic("pushrun addrun");
+	p->state = Pready;
+	p->link = isched.runhd;
+	isched.runhd = p;
+	if(p->link == nil)
+		isched.runtl = p;
+}
--- /dev/null
+++ b/emu/port/discall.c
@@ -1,0 +1,179 @@
+#include "dat.h"
+#include "fns.h"
+#include <interp.h>
+
+#define	QP(l)	(Prog**)((char*)(l)+sizeof(QLock))
+
+void*
+libqlowner(void *l)
+{
+	return *QP(l);
+}
+
+void
+libqlock(void *l)
+{
+	Prog *p;
+	QLock *q;
+
+	q = l;
+	p = currun();
+	if(p == nil)
+		abort();
+
+	if(!canqlock(q)) {
+		release();
+		qlock(q);
+		acquire();
+	}
+	*QP(l) = p;
+}
+
+void
+libqunlock(void *l)
+{
+	Prog *p;
+	QLock *q;
+
+	q = l;
+	p = currun();
+	if(p == nil)
+		abort();
+	if(*QP(l) != p)
+		abort();
+
+	*QP(l) = nil;
+	qunlock(q);
+}
+
+void*
+libqlalloc(void)
+{
+	QLock *q;
+
+	q = mallocz(sizeof(QLock)+sizeof(Prog*), 1);
+	return q;
+}
+
+void
+libqlfree(void *l)
+{
+	free(l);
+}
+
+vlong
+libseek(int fd, vlong off, int whence)
+{
+	release();
+	off = kseek(fd, off, whence);
+	acquire();
+	return off;
+}
+
+int
+libread(int fd, void *buf, int n)
+{
+	release();
+	n = kread(fd, buf, n);
+	acquire();
+	return n;
+}
+
+int
+libreadn(int fd, void *av, long n)
+{
+	char *a;
+	long m, t;
+
+	a = av;
+	t = 0;
+	release();
+	while(t < n){
+		m = kread(fd, a+t, n-t);
+		if(m <= 0){
+			if(t == 0){
+				acquire();
+				return m;
+			}
+			break;
+		}
+		t += m;
+	}
+	acquire();
+	return t;
+}
+
+int
+libwrite(int fd, void *buf, int n)
+{
+	release();
+	n = kwrite(fd, buf, n);
+	acquire();
+	return n;
+}
+
+int
+libopen(char *name, int omode)
+{
+	int fd;
+
+	release();
+	fd = kopen(name, omode);
+	acquire();
+	return fd;
+}
+
+int
+libclose(int fd)
+{
+	release();
+	fd = kclose(fd);
+	acquire();
+	return fd;
+}
+
+Dir*
+libdirfstat(int fd)
+{
+	Dir *d;
+
+	release();
+	d = kdirfstat(fd);
+	acquire();
+	return d;
+}
+
+int
+libbind(char *s, char *t, int f)
+{
+	int n;
+
+	release();
+	n = kbind(s, t, f);
+	acquire();
+	return n;
+}
+
+void
+libchanclose(void *chan)
+{
+	release();
+	cclose(chan);
+	acquire();
+}
+
+void*
+libfdtochan(int fd, int mode)
+{
+	Chan *c;
+
+	release();
+	if(waserror()) {
+		acquire();
+		return nil;
+	}
+	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
+	poperror();
+	acquire();
+	return c;
+}
--- /dev/null
+++ b/emu/port/dynld.c
@@ -1,0 +1,52 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<a.out.h>
+#include	<dynld.h>
+
+/*
+ * kernel interface to dynld, for use by devdynld.c,
+ * libinterp/dlm.c, and possibly others
+ */
+
+typedef struct Fd Fd;
+struct Fd {
+	int	fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+	return kread(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+	return kseek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+	kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+Dynobj*
+kdynloadfd(int fd, Dynsym *tab, int ntab)
+{
+	Dynobj *o;
+	Fd f;
+
+	f.fd = fd;
+	return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0);
+}
+
+int
+kdynloadable(int fd)
+{
+	Fd f;
+
+	f.fd = fd;
+	return dynloadable(&f, readfd, seekfd);
+}
--- /dev/null
+++ b/emu/port/dynldc.c
@@ -1,0 +1,65 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<a.out.h>
+#include	<dynld.h>
+
+/*
+ * channel-based kernel interface to dynld, for use by devdynld.c,
+ * libinterp/dlm.c, and possibly others
+ */
+
+static long
+readfc(void *a, void *buf, long nbytes)
+{
+	Chan *c = a;
+
+	if(waserror())
+		return -1;
+	nbytes = devtab[c->type]->read(c, buf, nbytes, c->offset);
+	poperror();
+	return nbytes;
+}
+
+static vlong
+seekfc(void *a, vlong off, int t)
+{
+	Chan *c = a;
+
+	if(c->qid.type & QTDIR || off < 0)
+		return -1;	/* won't happen */
+	switch(t){
+	case 0:
+		lock(c);
+		c->offset = off;
+		unlock(c);
+		break;
+	case 1:
+		lock(c);
+		off += c->offset;
+		c->offset = off;
+		unlock(c);
+		break;
+	case 2:
+		return -1;	/* not needed */
+	}
+	return off;
+}
+
+static void
+errfc(char *s)
+{
+	kstrcpy(up->env->errstr, s, ERRMAX);
+}
+
+Dynobj*
+kdynloadchan(Chan *c, Dynsym *tab, int ntab)
+{
+	return dynloadgen(c, readfc, seekfc, errfc, tab, ntab, 0);
+}
+
+int
+kdynloadable(Chan *c)
+{
+	return dynloadable(c, readfc, seekfc);
+}
--- /dev/null
+++ b/emu/port/env.c
@@ -1,0 +1,77 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+Egrp*
+newegrp(void)
+{
+	Egrp	*e;
+
+	e = smalloc(sizeof(Egrp));
+	if (e == nil)
+		error(Enomem);
+	e->r.ref = 1;
+	return e;
+}
+
+void
+closeegrp(Egrp *e)
+{
+	Evalue *el, *nl;
+
+	if(e == nil || decref(&e->r) != 0)
+		return;
+	for (el = e->entries; el != nil; el = nl) {
+		free(el->var);
+		if (el->val)
+			free(el->val);
+		nl = el->next;
+		free(el);
+	}
+	free(e);
+}
+
+void
+egrpcpy(Egrp *to, Egrp *from)
+{
+	Evalue *e, *ne, **last;
+
+	last = &to->entries;
+	qlock(&from->l);
+	for (e = from->entries; e != nil; e = e->next) {
+		ne = smalloc(sizeof(Evalue));
+		ne->var = smalloc(strlen(e->var)+1);
+		strcpy(ne->var, e->var);
+		if (e->val) {
+			ne->val = smalloc(e->len);
+			memmove(ne->val, e->val, e->len);
+			ne->len = e->len;
+		}
+		ne->qid.path = ++to->path;
+		*last = ne;
+		last = &ne->next;
+	}
+	qunlock(&from->l);
+}
+
+void
+ksetenv(char *var, char *val, int conf)
+{
+	Chan *c;
+	char buf[2*KNAMELEN];
+
+	USED(conf);
+	snprint(buf, sizeof(buf), "#e/%s", var);
+	if(waserror())
+		return;
+	c = namec(buf, Acreate, OWRITE, 0600);
+	poperror();
+	if(!waserror()){
+		if(!waserror()){
+			devtab[c->type]->write(c, val, strlen(val), 0);
+			poperror();
+		}
+		poperror();
+	}
+	cclose(c);
+}
--- /dev/null
+++ b/emu/port/error.c
@@ -1,0 +1,66 @@
+char Enoerror[] = "no error";
+char Emount[] = "inconsistent mount";
+char Eunmount[] = "not mounted";
+char Eunion[] = "not in union";
+char Emountrpc[] = "mount rpc error";
+char Eshutdown[] = "mounted device shut down";
+char Eowner[] = "not owner";
+char Eunknown[] = "unknown user or group id";
+char Enocreate[] = "mounted directory forbids creation";
+char Enonexist[] = "file does not exist";
+char Eexist[] = "file already exists";
+char Ebadsharp[] = "unknown device in # filename";
+char Enotdir[] = "not a directory";
+char Eisdir[] = "file is a directory";
+char Ebadchar[] = "bad character in file name";
+char Efilename[] = "file name syntax";
+char Eperm[] = "permission denied";
+char Ebadusefd[] = "inappropriate use of fd";
+char Ebadarg[] = "bad arg in system call";
+char Einuse[] = "device or object already in use";
+char Eio[] = "i/o error";
+char Etoobig[] = "read or write too large";
+char Etoosmall[] = "read or write too small";
+char Enetaddr[] = "bad network address";
+char Emsgsize[] = "message is too big for protocol";
+char Enetbusy[] = "network device is busy or allocated";
+char Enoproto[] = "network protocol not supported";
+char Enoport[] = "network port not available";
+char Enoifc[] = "bad interface or no free interface slots";
+char Enolisten[] = "not announced";
+char Ehungup[] = "i/o on hungup channel";
+char Ebadctl[] = "bad process or channel control request";
+char Enodev[] = "no free devices";
+char Enoenv[] = "no free environment resources";
+char Emuxshutdown[] = "mux server shut down";
+char Emuxbusy[] = "all mux channels busy";
+char Emuxmsg[] = "bad mux message format or mismatch";
+char Ethread[] = "thread exited";
+char Enochild[] = "no living children";
+char Eioload[] = "i/o error in demand load";
+char Enovmem[] = "out of memory: virtual memory";
+char Ebadld[] = "illegal line discipline";
+char Ebadfd[] = "fd out of range or not open";
+char Eisstream[] = "seek on a stream";
+char Ebadexec[] = "exec header invalid";
+char Etimedout[] = "connection timed out";
+char Econrefused[] = "connection refused";
+char Econinuse[] = "connection in use";
+char Enetunreach[] = "network unreachable";
+char Eintr[] = "interrupted";
+char Enomem[] = "out of memory: kernel";
+char Esfnotcached[] = "subfont not cached";
+char Esoverlap[] = "segments overlap";
+char Emouseset[] = "mouse type already set";
+char Eshort[] = "i/o count too small";
+/* char Enobitstore[] = "out of screen memory"; */
+char Egreg[] = "jim'll fix it";
+char Ebadspec[] = "bad attach specifier";
+char Estopped[] = "thread must be stopped";
+char Enoattach[] = "mount/attach disallowed";
+char Eshortstat[] = "stat buffer too small";
+char Enegoff[] = "negative i/o offset";
+char Ebadstat[] = "malformed stat buffer";
+char Ecmdargs[] = "wrong #args in control message";
+char Enofd[] = "no free file descriptors";
+char Enoctl[] = "unknown control request";
--- /dev/null
+++ b/emu/port/error.h
@@ -1,0 +1,65 @@
+extern char Enoerror[];		/* no error */
+extern char Emount[];		/* inconsistent mount */
+extern char Eunmount[];		/* not mounted */
+extern char Eunion[];		/* not in union */
+extern char Emountrpc[];	/* mount rpc error */
+extern char Eshutdown[];	/* mounted device shut down */
+extern char Eowner[];		/* not owner */
+extern char Eunknown[];		/* unknown user or group id */
+extern char Enocreate[];	/* mounted directory forbids creation */
+extern char Enonexist[];	/* file does not exist */
+extern char Eexist[];		/* file already exists */
+extern char Ebadsharp[];	/* unknown device in # filename */
+extern char Enotdir[];		/* not a directory */
+extern char Eisdir[];		/* file is a directory */
+extern char Ebadchar[];		/* bad character in file name */
+extern char Efilename[];	/* file name syntax */
+extern char Eperm[];		/* permission denied */
+extern char Ebadusefd[];	/* inappropriate use of fd */
+extern char Ebadarg[];		/* bad arg in system call */
+extern char Einuse[];		/* device or object already in use */
+extern char Eio[];		/* i/o error */
+extern char Etoobig[];		/* read or write too large */
+extern char Etoosmall[];	/* read or write too small */
+extern char Enetaddr[];		/* bad network address */
+extern char Emsgsize[];		/* message is too big for protocol */
+extern char Enetbusy[];		/* network device is busy or allocated */
+extern char Enoproto[];		/* network protocol not supported */
+extern char Enoport[];		/* network port not available */
+extern char Enoifc[];		/* bad interface or no free interface slots */
+extern char Enolisten[];	/* not announced */
+extern char Ehungup[];		/* i/o on hungup channel */
+extern char Ebadctl[];		/* bad process or channel control request */
+extern char Enodev[];		/* no free devices */
+extern char Enoenv[];		/* no free environment resources */
+extern char Ethread[];		/* thread exited */
+extern char Enochild[];		/* no living children */
+extern char Eioload[];		/* i/o error in demand load */
+extern char Enovmem[];		/* out of memory: virtual memory */
+extern char Ebadld[];		/* illegal line discipline */
+extern char Ebadfd[];		/* fd out of range or not open */
+extern char Eisstream[];	/* seek on a stream */
+extern char Ebadexec[];		/* exec header invalid */
+extern char Etimedout[];	/* connection timed out */
+extern char Econrefused[];	/* connection refused */
+extern char Econinuse[];	/* connection in use */
+extern char Enetunreach[];	/* network unreachable */
+extern char Eintr[];		/* interrupted */
+extern char Enomem[];		/* out of memory: kernel */
+extern char Esfnotcached[];	/* subfont not cached */
+extern char Esoverlap[];	/* segments overlap */
+extern char Emouseset[];	/* mouse type already set */
+extern char Eshort[];		/* i/o count too small */
+extern char Enobitstore[];	/* out of screen memory */
+extern char Egreg[];		/* jim'll fix it */
+extern char Ebadspec[];		/* bad attach specifier */
+extern char Estopped[];		/* thread must be stopped */
+extern char Enoattach[];	/* mount/attach disallowed */
+extern char Eshortstat[];	/* stat buffer too small */
+extern char Enegoff[];	/* negative i/o offset */
+extern char Ebadstat[];	/* malformed stat buffer */
+extern char Ecmdargs[];		/* wrong #args in control message */
+extern char	Enofd[];	/* no free file descriptors */
+extern char Enoctl[];	/* unknown control request */
+
+extern void	error(char*);
--- /dev/null
+++ b/emu/port/errstr.c
@@ -1,0 +1,38 @@
+#include	"dat.h"
+#include	"fns.h"
+
+/*
+ * General OS interface to errors
+ */
+void
+kwerrstr(char *fmt, ...)
+{
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	kstrcpy(up->env->errstr, buf, ERRMAX);
+}
+
+void
+kerrstr(char *err, uint size)
+{
+	char tmp[ERRMAX];
+
+	kstrcpy(tmp, up->env->errstr, sizeof(tmp));
+	kstrcpy(up->env->errstr, err, ERRMAX);
+	kstrcpy(err, tmp, size);
+}
+
+void
+kgerrstr(char *err, uint size)
+{
+	char *s;
+
+	s = "<no-up>";
+	if(up != nil)
+		s = up->env->errstr;
+	kstrcpy(err, s, size);	/* TO DO */
+}
--- /dev/null
+++ b/emu/port/exception.c
@@ -1,0 +1,214 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+#include "kernel.h"
+#include "raise.h"
+
+static int
+ematch(char *pat, char *exp)
+{
+	int l;
+
+	if(strcmp(pat, exp) == 0)
+		return 1;
+
+	l = strlen(pat);
+	if(l == 0)
+		return 0;
+	if(pat[l-1] == '*') {
+		if(l == 1)
+			return 1;
+		if(strncmp(pat, exp, l-1) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+static void
+setstr(String *s, char *p)
+{
+	if(s == H)
+		return;
+	if(s->len < 0 || s->max < 4)
+		return;
+	kstrcpy(s->Sascii, p, s->max);	/* TO DO: we are assuming they aren't runes */
+	s->len = strlen(s->Sascii);
+}
+
+static String *exstr;
+
+void
+excinit(void)
+{
+	exstr = newstring(ERRMAX);
+	poolimmutable(D2H(exstr));
+}
+
+static String*
+newestring(char *estr)
+{
+	String *s;
+
+	if(waserror()){
+		setstr(exstr, estr);
+		D2H(exstr)->ref++;
+		return exstr;
+	}
+	s = c2string(estr, strlen(estr));
+	poperror();
+	return s;
+}
+
+#define NOPC	0xffffffff
+
+#define FRTYPE(f)	((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t)
+
+/*
+ * clear up an uncalled frame
+ */
+static void
+freeframe(uchar *fp, int setsp)
+{
+	Frame *f;
+
+	f = (Frame*)fp;
+	if(f->t == nil)
+		unextend(f);
+	else if(f->t->np)
+		freeptrs(f, f->t);
+	if(setsp)
+		R.SP = fp;
+}
+
+int
+handler(char *estr)
+{
+	Prog *p;
+	Modlink *m, *mr;
+	int str, ne;
+	ulong pc, newpc;
+	long eoff;
+	uchar *fp, **eadr;
+	Frame *f;
+	Type *t, *zt;
+	Handler *h;
+	Except *e;
+	void *v;
+
+	p = currun();
+	if(*estr == 0 || p == nil)
+		return 0;
+	str = p->exval == H || D2H(p->exval)->t == &Tstring;
+	m = R.M;
+	if(m->compiled)
+		pc = (ulong)R.PC-(ulong)m->prog;
+	else
+		pc = R.PC-m->prog;
+	pc--;
+	fp = R.FP;
+
+	while(fp != nil){		/* look for a handler */
+		if((h = m->m->htab) != nil){
+			for( ; h->etab != nil; h++){
+				if(pc < h->pc1 || pc >= h->pc2)
+					continue;
+				eoff = h->eoff;
+				zt = h->t;
+				for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){
+					if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){
+						newpc = e->pc;
+						goto found;
+					}
+				}
+				newpc = e->pc;
+				if(newpc != NOPC)
+					goto found;
+			}
+		}
+		if(!str && fp != R.FP){		/* becomes a string exception in immediate caller */
+			v = p->exval;
+			p->exval = *(String**)v;
+			D2H(p->exval)->ref++;
+			destroy(v);
+			str = 1;
+			continue;
+		}
+		f = (Frame*)fp;
+		if(f->mr != nil)
+			m = f->mr;
+		if(m->compiled)
+			pc = (ulong)f->lr-(ulong)m->prog;
+		else
+			pc = f->lr-m->prog;
+		pc--;
+		fp = f->fp;
+	}
+	destroy(p->exval);
+	p->exval = H;
+	return 0;
+found:
+	{
+		int n;
+		char name[3*KNAMELEN];
+
+		pc = modstatus(&R, name, sizeof(name));
+		n = 10+1+strlen(name)+1+strlen(estr)+1;
+		p->exstr = realloc(p->exstr, n);
+		if(p->exstr != nil)
+			snprint(p->exstr, n, "%lud %s %s", pc, name, estr);
+	}
+
+	/*
+	 * there may be an uncalled frame at the top of the stack
+	 */
+	f = (Frame*)R.FP;
+	t = FRTYPE(f);
+	if(R.FP < R.EX || R.FP >= R.TS)
+		freeframe(R.EX+OA(Stkext, reg.tos.fr), 0);
+	else if(R.FP+t->size < R.SP)
+		freeframe(R.FP+t->size, 1);
+
+	m = R.M;
+	while(R.FP != fp){
+		f = (Frame*)R.FP;
+		R.PC = f->lr;
+		R.FP = f->fp;
+		R.SP = (uchar*)f;
+		mr = f->mr;
+		if(f->t == nil)
+			unextend(f);
+		else if(f->t->np)
+			freeptrs(f, f->t);
+		if(mr != nil){
+			m = mr;
+			destroy(R.M);
+			R.M = m;
+			R.MP = m->MP;
+		}
+	}
+	if(zt != nil){
+		freeptrs(fp, zt);
+		initmem(zt, fp);
+	}
+	eadr = (uchar**)(fp+eoff);
+	destroy(*eadr);
+	*eadr = H;
+	if(p->exval == H)
+		*eadr = (uchar*)newestring(estr);	/* might fail */
+	else{
+		D2H(p->exval)->ref++;
+		*eadr = p->exval;
+	}
+	if(m->compiled)
+		R.PC = (Inst*)((ulong)m->prog+newpc);
+	else
+		R.PC = m->prog+newpc;
+	memmove(&p->R, &R, sizeof(R));
+	p->kill = nil;
+	destroy(p->exval);
+	p->exval = H;
+	return 1;
+}
--- /dev/null
+++ b/emu/port/exportfs.c
@@ -1,0 +1,1219 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"kernel.h"
+
+typedef	struct Fid	Fid;
+typedef	struct Export	Export;
+typedef	struct Exq	Exq;
+
+enum
+{
+	Nfidhash	= 32,
+	MAXFDATA	= 8192,
+	MAXRPCDEF		= IOHDRSZ+MAXFDATA,	/* initial/default */
+	MAXRPCMAX	= IOHDRSZ+64*1024,	/* most every allowed */
+	MSGHDRSZ	= BIT32SZ+BIT8SZ+BIT16SZ
+};
+
+struct Export
+{
+	Lock	l;
+	Ref	r;
+	Exq*	work;
+	Lock	fidlock;
+	Fid*	fid[Nfidhash];
+	Uqidtab	uqids;
+	Chan*	io;
+	Chan*	root;
+	Pgrp*	pgrp;
+	Egrp*	egrp;
+	Fgrp*	fgrp;
+	int	async;
+	int	readonly;
+	int	uid;
+	int	gid;
+	int	msize;
+	char*	user;
+};
+
+struct Fid
+{
+	Fid*	next;
+	Fid**	last;
+	Chan*	chan;
+	int	fid;
+	int	ref;		/* fcalls using the fid; locked by Export.Lock */
+	vlong	offset;	/* last offset used (within directory) */
+	int	attached;	/* fid attached or cloned but not clunked */
+	Uqid*	qid;	/* generated qid */
+};
+
+struct Exq
+{
+	Lock	l;
+	int	busy;	/* fcall in progress */
+	int	finished;	/* will do no more work on this request or flushes */
+	Exq*	next;
+	int	shut;		/* has been noted for shutdown */
+	Exq*	flush;	/* queued flush requests */
+	Exq*	flusht;	/* tail of flush queue */
+	Export*	export;
+	Proc*	slave;
+	Fcall	in, out;
+	uchar*	buf;
+	int	bsize;
+};
+
+struct
+{
+	Lock	l;
+	QLock	qwait;
+	Rendez	rwait;
+	Exq	*head;		/* work waiting for a slave */
+	Exq	*tail;
+}exq;
+
+static void	exshutdown(Export*);
+static int	exflushed(Export*, Exq*);
+static void	exslave(void*);
+static void	exfree(Export*);
+static void	exfreeq(Exq*);
+static void	exportproc(void*);
+static void	exreply(Exq*, char*);
+static int	exisroot(Export*, Chan*);
+
+static char*	Exversion(Export*, Fcall*, Fcall*);
+static char*	Exauth(Export*, Fcall*, Fcall*);
+static char*	Exattach(Export*, Fcall*, Fcall*);
+static char*	Exclunk(Export*, Fcall*, Fcall*);
+static char*	Excreate(Export*, Fcall*, Fcall*);
+static char*	Exopen(Export*, Fcall*, Fcall*);
+static char*	Exread(Export*, Fcall*, Fcall*);
+static char*	Exremove(Export*, Fcall*, Fcall*);
+static char*	Exstat(Export*, Fcall*, Fcall*);
+static char*	Exwalk(Export*, Fcall*, Fcall*);
+static char*	Exwrite(Export*, Fcall*, Fcall*);
+static char*	Exwstat(Export*, Fcall*, Fcall*);
+
+static char	*(*fcalls[Tmax])(Export*, Fcall*, Fcall*);
+
+static char	Enofid[]   = "no such fid";
+static char	Eseekdir[] = "can't seek on a directory";
+static char	Eopen[]	= "walk of open fid";
+static char	Emode[] = "open/create -- unknown mode";
+static char	Edupfid[]	= "fid in use";
+static char	Eaccess[] = "read/write -- not open in suitable mode";
+static char	Ecount[] = "read/write -- count too big";
+int	exdebug = 0;
+
+int
+export(int fd, char *dir, int async)
+{
+	Chan *c, *dc;
+	Pgrp *pg;
+	Egrp *eg;
+	Export *fs;
+
+	if(waserror())
+		return -1;
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1);
+	poperror();
+
+	if(waserror()){
+		cclose(c);
+		return -1;
+	}
+	dc = namec(dir, Atodir, 0, 0);
+	poperror();
+
+	fs = malloc(sizeof(Export));
+	if(fs == nil){
+		cclose(c);
+		cclose(dc);
+		error(Enomem);
+	}
+
+	fs->r.ref = 1;
+	pg = up->env->pgrp;
+	fs->pgrp = pg;
+	incref(&pg->r);
+	eg = up->env->egrp;
+	fs->egrp = eg;
+	incref(&eg->r);
+	fs->fgrp = newfgrp(nil);
+	fs->uid = up->env->uid;
+	fs->gid = up->env->gid;
+	kstrdup(&fs->user, up->env->user);
+	fs->root = dc;
+	fs->io = c;
+	uqidinit(&fs->uqids);
+	fs->msize = 0;
+	c->flag |= CMSG;
+	fs->async = async;
+
+	if(async){
+		if(waserror())
+			return -1;
+		kproc("exportfs", exportproc, fs, 0);
+		poperror();
+	}else
+		exportproc(fs);
+
+	return 0;
+}
+
+static void
+exportinit(void)
+{
+	lock(&exq.l);
+	if(fcalls[Tversion] != nil) {
+		unlock(&exq.l);
+		return;
+	}
+	fcalls[Tversion] = Exversion;
+	fcalls[Tauth] = Exauth;
+	fcalls[Tattach] = Exattach;
+	fcalls[Twalk] = Exwalk;
+	fcalls[Topen] = Exopen;
+	fcalls[Tcreate] = Excreate;
+	fcalls[Tread] = Exread;
+	fcalls[Twrite] = Exwrite;
+	fcalls[Tclunk] = Exclunk;
+	fcalls[Tremove] = Exremove;
+	fcalls[Tstat] = Exstat;
+	fcalls[Twstat] = Exwstat;
+	unlock(&exq.l);
+}
+
+static int
+exisroot(Export *fs, Chan *c)
+{
+	return eqchan(fs->root, c, 1);
+}
+
+static int
+exreadn(Chan *c, void *buf, int n)
+{
+	int nr, t;
+
+	if(waserror())
+		return -1;
+	for(nr = 0; nr < n;){
+		t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0);
+		if(t <= 0)
+			break;
+		nr += t;
+	}
+	poperror();
+	return nr;
+}
+
+static int
+exreadmsg(Chan *c, void *a, uint n)
+{
+	int m, len;
+	uchar *buf;
+
+	buf = a;
+	m = exreadn(c, buf, BIT32SZ);
+	if(m < BIT32SZ){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+	len = GBIT32(buf);
+	if(len <= BIT32SZ || len > n){
+		kwerrstr("bad length in Styx message header");
+		return -1;
+	}
+	len -= BIT32SZ;
+	m = exreadn(c, buf+BIT32SZ, len);
+	if(m < len){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+	return BIT32SZ+m;
+}
+
+static void
+exportproc(void *a)
+{
+	Exq *q;
+	int async, msize;
+	int n, type;
+	Export *fs = a;
+
+	exportinit();
+
+	for(;;){
+
+		msize = fs->msize;
+		if(msize == 0)
+			msize = MAXRPCDEF;
+		for(n=0;; n++){	/* we don't use smalloc, to avoid memset */
+			q = mallocz(sizeof(*q)+msize, 0);
+			if(q != nil || n > 6000)
+				break;
+			if(n%600 == 0)
+				print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize);
+			osenter();
+			osmillisleep(100);
+			osleave();
+		}
+		if(q == nil){
+			kwerrstr("out of memory: read request");
+			n = -1;
+			break;
+		}
+		memset(q, 0, sizeof(*q));
+		q->buf = (uchar*)q + sizeof(*q);
+		q->bsize = msize;
+
+		n = exreadmsg(fs->io, q->buf, msize);	/* TO DO: avoid copy */
+		if(n <= 0)
+			break;
+		if(convM2S(q->buf, n, &q->in) != n){
+			kwerrstr("bad T-message");
+			n = -1;
+			break;
+		}
+		type = q->in.type;
+		if(type < Tversion || type >= Tmax || type&1 || type == Terror){
+			kwerrstr("invalid T-message type %d", type);
+			n = -1;
+			break;
+		}
+
+		if(exdebug)
+			print("export %ld <- %F\n", up->pid, &q->in);
+
+		q->out.type = type+1;
+		q->out.tag = q->in.tag;
+
+		q->export = fs;
+		incref(&fs->r);
+
+		if(fs->readonly){
+			switch(type){
+			case Topen:
+				if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD)
+					break;
+				/* FALL THROUGH */
+			case Tcreate:
+			case Twrite:
+			case Tremove:
+			case Twstat:
+				q->out.type = Rerror;
+				q->out.ename = "file system read only";
+				exreply(q, "exportproc");
+				exfreeq(q);
+				continue;
+			}
+		}
+
+		if(q->in.type == Tflush){
+			if(exflushed(fs, q)){
+				/* not yet started or not found (flush arrived after reply); we reply */
+				if(exdebug)
+					print("export: flush %d\n", q->in.oldtag);
+				exreply(q, "exportproc");
+				exfreeq(q);
+			}
+			continue;
+		}
+
+		lock(&exq.l);
+		if(exq.head == nil)
+			exq.head = q;
+		else
+			exq.tail->next = q;
+		q->next = nil;
+		exq.tail = q;
+		unlock(&exq.l);
+		if(exq.qwait.head == nil)
+			kproc("exslave", exslave, nil, 0);
+		Wakeup(&exq.rwait);
+	}
+
+	if(exdebug){
+		if(n < 0)
+			print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr);
+		else
+			print("exportproc %ld shut down\n", up->pid);
+	}
+
+	free(q);
+	exshutdown(fs);
+	async = fs->async;
+	exfree(fs);
+
+	if(async)
+		pexit("mount shut down", 0);
+}
+
+static int
+exflushed(Export *fs, Exq *fq)
+{
+	Exq *q, **last;
+	ulong pid;
+
+	/* not yet started? */
+	lock(&exq.l);
+	for(last = &exq.head; (q = *last) != nil; last = &q->next)
+		if(q->export == fs && q->in.tag == fq->in.oldtag){
+			*last = q->next;
+			unlock(&exq.l);
+			/* not yet started: discard, and Rflush */
+			exfreeq(q);
+			return 1;
+		}
+	unlock(&exq.l);
+
+	/* tricky case: in progress */
+	lock(&fs->l);
+	for(q = fs->work; q != nil; q = q->next)
+		if(q->in.tag == fq->in.oldtag){
+			pid = 0;
+			lock(&q->l);
+			if(q->finished){
+				/* slave replied and emptied its flush queue; we can Rflush now */
+				unlock(&q->l);
+				return 1;
+			}
+			/* append to slave's flush queue */
+			fq->next = nil;
+			if(q->flush != nil)
+				q->flusht->next = fq;
+			else
+				q->flush = fq;
+			q->flusht = fq;
+			if(q->busy){
+				pid = q->slave->pid;
+				swiproc(q->slave, 0);
+			}
+			unlock(&q->l);
+			unlock(&fs->l);
+			if(exdebug && pid)
+				print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag);
+			return 0;
+		}
+	unlock(&fs->l);
+
+	/* not found */
+	return 1;
+}
+
+static void
+exfreeq(Exq *q)
+{
+	Exq *fq;
+
+	while((fq = q->flush) != nil){
+		q->flush = fq->next;
+		exfree(fq->export);
+		free(fq);
+	}
+	exfree(q->export);
+	free(q);
+}
+
+static void
+exshutdown(Export *fs)
+{
+	Exq *q, **last;
+
+	/* work not started */
+	lock(&exq.l);
+	for(last = &exq.head; (q = *last) != nil;)
+		if(q->export == fs){
+			*last = q->next;
+			exfreeq(q);
+		}else
+			last = &q->next;
+	unlock(&exq.l);
+
+	/* tell slaves to abandon work in progress */
+	lock(&fs->l);
+	while((q = fs->work) != nil){
+		fs->work = q->next;
+		lock(&q->l);
+		q->shut = 1;
+		swiproc(q->slave, 0);	/* whether busy or not */
+		unlock(&q->l);
+	}
+	unlock(&fs->l);
+}
+
+static void
+exfreefids(Export *fs)
+{
+	Fid *f, *n;
+	int i;
+
+	for(i = 0; i < Nfidhash; i++){
+		for(f = fs->fid[i]; f != nil; f = n){
+			n = f->next;
+			f->attached = 0;
+			if(f->ref == 0) {
+				if(f->chan != nil)
+					cclose(f->chan);
+				freeuqid(&fs->uqids, f->qid);
+				free(f);
+			} else
+				print("exfreefids: busy fid\n");
+		}
+	}
+}
+
+static void
+exfree(Export *fs)
+{
+	if(exdebug)
+		print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref);
+	if(decref(&fs->r) != 0)
+		return;
+	closepgrp(fs->pgrp);
+	closeegrp(fs->egrp);
+	closefgrp(fs->fgrp);
+	cclose(fs->root);
+	cclose(fs->io);
+	exfreefids(fs);
+	free(fs->user);
+	free(fs);
+}
+
+static int
+exwork(void *a)
+{
+	USED(a);
+	return exq.head != nil;
+}
+
+static void
+exslave(void *a)
+{
+	Export *fs;
+	Exq *q, *t, *fq, **last;
+	char *err;
+
+	USED(a);
+
+	for(;;){
+		qlock(&exq.qwait);
+		if(waserror()){
+			qunlock(&exq.qwait);
+			continue;
+		}
+		Sleep(&exq.rwait, exwork, nil);
+		poperror();
+
+		lock(&exq.l);
+		q = exq.head;
+		if(q == nil) {
+			unlock(&exq.l);
+			qunlock(&exq.qwait);
+			continue;
+		}
+		exq.head = q->next;
+
+		qunlock(&exq.qwait);
+
+		/*
+		 * put the job on the work queue before it's
+		 * visible as off of the head queue, so it's always
+		 * findable for flushes and shutdown
+		 */
+		notkilled();
+		q->slave = up;
+		q->busy = 1;	/* fcall in progress: interruptible */
+		fs = q->export;
+		lock(&fs->l);
+		q->next = fs->work;
+		fs->work = q;
+		unlock(&fs->l);
+		unlock(&exq.l);
+
+		up->env->pgrp = q->export->pgrp;
+		up->env->egrp = q->export->egrp;
+		up->env->fgrp = q->export->fgrp;
+		up->env->uid = q->export->uid;
+		up->env->gid = q->export->gid;
+		kstrdup(&up->env->user, q->export->user);
+
+		if(exdebug > 1)
+			print("exslave %ld dispatch %F\n", up->pid, &q->in);
+
+		if(waserror()){
+			print("exslave %ld err %s\n", up->pid, up->env->errstr);	/* shouldn't happen */
+			err = up->env->errstr;
+		}else{
+			if(q->in.type >= Tmax || !fcalls[q->in.type]){
+				snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
+				err = up->genbuf;
+			}else{
+				switch(q->in.type){
+				case Tread:
+					q->out.data = (char*)q->buf + IOHDRSZ;
+					break;
+				case Tstat:
+					q->out.stat = q->buf + MSGHDRSZ + BIT16SZ;	/* leaves it just where we want it */
+					q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ);
+					break;
+				}
+				err = (*fcalls[q->in.type])(fs, &q->in, &q->out);
+			}
+			poperror();
+		}
+
+		/*
+		 * if the fcall completed without error we must reply,
+		 * even if a flush is pending (because the underlying server
+		 * might have changed state), unless the export has shut down completely.
+		 * must also reply to each flush in order, and only after the original reply (if sent).
+		 */
+		lock(&q->l);
+		notkilled();
+		q->busy = 0;	/* operation complete */
+		if(!q->shut){
+			if(q->flush == nil || err == nil){
+				unlock(&q->l);
+				q->out.type = q->in.type+1;
+				q->out.tag = q->in.tag;
+				if(err){
+					q->out.type = Rerror;
+					q->out.ename = err;
+				}
+				exreply(q, "exslave");
+				lock(&q->l);
+			}
+			while((fq = q->flush) != nil && !q->shut){
+				q->flush = fq->next;
+				unlock(&q->l);
+				exreply(fq, "exslave");
+				exfreeq(fq);
+				lock(&q->l);
+			}
+		}
+		q->finished = 1;	/* promise not to send any more */
+		unlock(&q->l);
+
+		lock(&fs->l);
+		for(last = &fs->work; (t = *last) != nil; last = &t->next)
+			if(t == q){
+				*last = q->next;
+				break;
+			}
+		unlock(&fs->l);
+
+		notkilled();
+		exfreeq(q);
+	}
+}
+
+static void
+exreply(Exq *q, char *who)
+{
+	Export *fs;
+	Fcall *r;
+	int n;
+
+	fs = q->export;
+	r = &q->out;
+
+	n = convS2M(r, q->buf, q->bsize);
+	if(n == 0){
+		r->type = Rerror;
+		if(fs->msize == 0)
+			r->ename = "Tversion not seen";
+		else
+			r->ename = "failed to convert R-message";
+		n = convS2M(r, q->buf, q->bsize);
+	}
+
+	if(exdebug)
+		print("%s %ld -> %F\n", who, up->pid, r);
+
+	if(!waserror()){
+		devtab[fs->io->type]->write(fs->io, q->buf, n, 0);
+		poperror();
+	}
+}
+
+static int
+exiounit(Export *fs, Chan *c)
+{
+	int iounit;
+
+	iounit = fs->msize-IOHDRSZ;
+	if(c->iounit != 0 && c->iounit < fs->msize)
+		iounit = c->iounit;
+	return iounit;
+}
+
+static Fid*
+Exmkfid(Export *fs, ulong fid)
+{
+	ulong h;
+	Fid *f, *nf;
+
+	nf = malloc(sizeof(Fid));
+	if(nf == nil)
+		return nil;
+	lock(&fs->fidlock);
+	h = fid % Nfidhash;
+	for(f = fs->fid[h]; f != nil; f = f->next){
+		if(f->fid == fid){
+			unlock(&fs->fidlock);
+			free(nf);
+			return nil;
+		}
+	}
+
+	nf->next = fs->fid[h];
+	if(nf->next != nil)
+		nf->next->last = &nf->next;
+	nf->last = &fs->fid[h];
+	fs->fid[h] = nf;
+
+	nf->fid = fid;
+	nf->ref = 1;
+	nf->attached = 1;
+	nf->offset = 0;
+	nf->chan = nil;
+	nf->qid = nil;
+	unlock(&fs->fidlock);
+	return nf;
+}
+
+static Fid*
+Exgetfid(Export *fs, ulong fid)
+{
+	Fid *f;
+	ulong h;
+
+	lock(&fs->fidlock);
+	h = fid % Nfidhash;
+	for(f = fs->fid[h]; f; f = f->next) {
+		if(f->fid == fid){
+			if(f->attached == 0)
+				break;
+			f->ref++;
+			unlock(&fs->fidlock);
+			return f;
+		}
+	}
+	unlock(&fs->fidlock);
+	return nil;
+}
+
+static void
+Exputfid(Export *fs, Fid *f)
+{
+	Chan *c;
+
+	lock(&fs->fidlock);
+	f->ref--;
+	if(f->ref == 0 && f->attached == 0){
+		c = f->chan;
+		f->chan = nil;
+		*f->last = f->next;
+		if(f->next != nil)
+			f->next->last = f->last;
+		unlock(&fs->fidlock);
+		freeuqid(&fs->uqids, f->qid);
+		free(f);
+		if(c != nil)
+			cclose(c);
+		return;
+	}
+	unlock(&fs->fidlock);
+}
+
+static Chan*
+exmount(Chan *c, Mhead **mp, int doname)
+{
+	struct {Chan *nc;} nc;
+	Cname *oname;
+
+	nc.nc = nil;
+	if((c->flag & COPEN) == 0 && findmount(&nc.nc, mp, c->type, c->dev, c->qid)){
+		if(waserror()){
+			cclose(nc.nc);
+			nexterror();
+		}
+		nc.nc = cunique(nc.nc);
+		poperror();
+		if(doname){
+			oname = c->name;
+			incref(&oname->r);
+			cnameclose(nc.nc->name);
+			nc.nc->name = oname;
+		}
+		return nc.nc;
+	}
+	incref(&c->r);
+	return c;
+}
+
+static char*
+Exversion(Export *fs, Fcall *t, Fcall *r)
+{
+	char *p;
+	static char version[] = VERSION9P;
+	int iounit;
+
+	r->msize = t->msize;
+	if(r->msize > MAXRPCMAX)
+		r->msize = MAXRPCMAX;
+	iounit = fs->io->iounit;
+	if(iounit != 0 && iounit > 64 && iounit < r->msize)
+		r->msize = iounit;
+	if(r->msize < 64)
+		return "message size too small";
+	if(0)
+		print("msgsize=%d\n", r->msize);
+	if((p = strchr(t->version, '.')) != nil)
+		*p = 0;
+	if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){
+		r->version = version;
+		fs->msize = r->msize;
+	}else
+		r->version = "unknown";
+	return nil;
+}
+
+static char*
+Exauth(Export *fs, Fcall *t, Fcall *r)
+{
+	USED(fs);
+	USED(t);
+	USED(r);
+	return "authentication not required";
+}
+
+static char*
+Exattach(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+
+	f = Exmkfid(fs, t->fid);
+	if(f == nil)
+		return Edupfid;
+	if(waserror()){
+		f->attached = 0;
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	f->chan = cclone(fs->root);
+	f->qid = uqidalloc(&fs->uqids, f->chan);
+	poperror();
+	r->qid = mkuqid(f->chan, f->qid);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exclunk(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	f->attached = 0;
+	Exputfid(fs, f);
+	return nil;
+}
+
+static int
+safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
+{
+	int r;
+
+	/* walk can raise error */
+	if(waserror())
+		return -1;
+	r = walk(cp, names, nnames, nomount, nerror);
+	poperror();
+	return r;
+}
+
+static char*
+Exwalk(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f, *nf;
+	Chan *c;
+	char *name;
+	Uqid *qid;
+	int i;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Eopen;
+	}
+	if(waserror())
+		return up->env->errstr;
+	c = cclone(f->chan);
+	poperror();
+	qid = f->qid;
+	incref(&qid->r);
+	r->nwqid = 0;
+	if(t->nwname > 0){
+		for(i=0; i<t->nwname; i++){
+			name = t->wname[i];
+			if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){
+				if(safewalk(&c, &name, 1, 0, nil) < 0){
+					/* leave the original state on error */
+					cclose(c);
+					freeuqid(&fs->uqids, qid);
+					Exputfid(fs, f);
+					if(i == 0)
+						return up->env->errstr;
+					return nil;
+				}
+				freeuqid(&fs->uqids, qid);
+				qid = uqidalloc(&fs->uqids, c);
+			}
+			r->wqid[r->nwqid++] = mkuqid(c, qid);
+		}
+	}
+
+	if(t->newfid != t->fid){
+		nf = Exmkfid(fs, t->newfid);
+		if(nf == nil){
+			cclose(c);
+			freeuqid(&fs->uqids, qid);
+			Exputfid(fs, f);
+			return Edupfid;
+		}
+		nf->chan = c;
+		nf->qid = qid;
+		Exputfid(fs, nf);
+	}else{
+		cclose(f->chan);
+		f->chan = c;
+		freeuqid(&fs->uqids, f->qid);
+		f->qid = qid;
+	}
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exopen(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	Uqid *qid;
+	Mhead *m;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Emode;
+	}
+	m = nil;
+	c = exmount(f->chan, &m, 1);
+	if(waserror()){
+		cclose(c);
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+
+	/* only save the mount head if it's a multiple element union */
+	if(m && m->mount && m->mount->next)
+		c->umh = m;
+	else
+		putmhead(m);
+
+	c = devtab[c->type]->open(c, t->mode);
+	if(t->mode & ORCLOSE)
+		c->flag |= CRCLOSE;
+
+	qid = uqidalloc(&fs->uqids, c);
+	poperror();
+	freeuqid(&fs->uqids, f->qid);
+	cclose(f->chan);
+	f->chan = c;
+	f->qid = qid;
+	f->offset = 0;
+	r->qid = mkuqid(c, f->qid);
+	r->iounit = exiounit(fs, c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Excreate(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	volatile struct {Chan *c;} c, dc;
+	Cname *oname;
+	Uqid *qid;
+	Mhead *m;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(f->chan->flag & COPEN){
+		Exputfid(fs, f);
+		return Emode;
+	}
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	validname(t->name, 0);
+	if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
+		error(Efilename);	/* underlying server should check, but stop it here */
+
+	m = nil;
+	c.c = exmount(f->chan, &m, 1);
+	if(waserror()){
+		cclose(c.c);
+		if(m != nil)
+			putmhead(m);
+		nexterror();
+	}
+	if(m != nil){
+		oname = c.c->name;
+		incref(&oname->r);
+		if(waserror()){
+			cnameclose(oname);
+			nexterror();
+		}
+		dc.c = createdir(c.c, m);
+		if(waserror()){
+			cclose(dc.c);
+			nexterror();
+		}
+		c.c = cunique(dc.c);
+		poperror();
+		cnameclose(c.c->name);
+		poperror();
+		c.c->name = oname;
+	}
+	devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm);
+	c.c->name = addelem(c.c->name, t->name);
+	if(t->mode & ORCLOSE)
+		c.c->flag |= CRCLOSE;
+	qid = uqidalloc(&fs->uqids, c.c);
+	poperror();
+	if(m != nil)
+		putmhead(m);
+
+	poperror();
+	cclose(f->chan);
+	f->chan = c.c;
+	freeuqid(&fs->uqids, f->qid);
+	f->qid = qid;
+	r->qid = mkuqid(c.c, f->qid);
+	r->iounit = exiounit(fs, c.c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exread(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	Lock *cl;
+	vlong off;
+	int dir, n, seek;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+
+	if(waserror()) {
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = f->chan;
+	if((c->flag & COPEN) == 0)
+		error(Emode);
+	if(c->mode != OREAD && c->mode != ORDWR)
+		error(Eaccess);
+	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+		error(Ecount);
+	if(t->offset < 0)
+		error(Enegoff);
+	dir = c->qid.type & QTDIR;
+	if(dir && t->offset != f->offset){
+		if(t->offset != 0)
+			error(Eseekdir);
+		f->offset = 0;
+		c->uri = 0;
+		c->dri = 0;
+	}
+
+	for(;;){
+		n = t->count;
+		seek = 0;
+		off = t->offset;
+		if(dir && f->offset != off){
+			off = f->offset;
+			n = t->offset - off;
+			if(n > MAXFDATA)
+				n = MAXFDATA;
+			seek = 1;
+		}
+		if(dir && c->umh != nil){
+			if(0)
+				print("union read %d uri %d dri %d\n", seek, c->uri, c->dri);
+			n = unionread(c, r->data, n);
+		}
+		else{
+			cl = &c->l;
+			lock(cl);
+			c->offset = off;
+			unlock(cl);
+			n = devtab[c->type]->read(c, r->data, n, off);
+			lock(cl);
+			c->offset += n;
+			unlock(cl);
+		}
+		f->offset = off + n;
+		if(n == 0 || !seek)
+			break;
+	}
+	r->count = n;
+
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exwrite(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = f->chan;
+	if((c->flag & COPEN) == 0)
+		error(Emode);
+	if(c->mode != OWRITE && c->mode != ORDWR)
+		error(Eaccess);
+	if(c->qid.type & QTDIR)
+		error(Eisdir);
+	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
+		error(Ecount);
+	if(t->offset < 0)
+		error(Enegoff);
+	r->count = devtab[c->type]->write(c, t->data, t->count, t->offset);
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exstat(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+	int n;
+
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	c = exmount(f->chan, nil, 1);
+	if(waserror()){
+		cclose(c);
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	n = devtab[c->type]->stat(c, r->stat, r->nstat);
+	if(n <= BIT16SZ)
+		error(Eshortstat);
+	r->nstat = n;
+	poperror();
+	/* TO DO: need to change qid */
+	cclose(c);
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exwstat(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	validstat(t->stat, t->nstat);	/* check name */
+
+	c = exmount(f->chan, nil, 0);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->wstat(c, t->stat, t->nstat);
+	poperror();
+
+	cclose(c);
+	poperror();
+	Exputfid(fs, f);
+	return nil;
+}
+
+static char*
+Exremove(Export *fs, Fcall *t, Fcall *r)
+{
+	Fid *f;
+	Chan *c;
+
+	USED(r);
+	f = Exgetfid(fs, t->fid);
+	if(f == nil)
+		return Enofid;
+	if(waserror()){
+		f->attached = 0;
+		Exputfid(fs, f);
+		return up->env->errstr;
+	}
+	c = exmount(f->chan, nil, 0);
+	if(waserror()){
+		c->type = 0;	/* see below */
+		cclose(c);
+		nexterror();
+	}
+	devtab[c->type]->remove(c);
+	poperror();
+	poperror();
+
+	/*
+	 * chan is already clunked by remove.
+	 * however, we need to recover the chan,
+	 * and follow sysremove's lead in making it point to root.
+	 */
+	c->type = 0;
+	cclose(c);
+
+	f->attached = 0;
+	Exputfid(fs, f);
+	return nil;
+}
--- /dev/null
+++ b/emu/port/exptab.c
@@ -1,0 +1,6 @@
+#include	"dat.h"
+#include	<dynld.h>
+
+/* dummy export table */
+
+Dynsym _exporttab[] = { 0, 0, nil };
--- /dev/null
+++ b/emu/port/file.c
@@ -1,0 +1,16 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+int
+openmode(ulong o)
+{
+	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
+		error(Ebadarg);
+	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+	if(o > OEXEC)
+		error(Ebadarg);
+	if(o == OEXEC)
+		return OREAD;
+	return o;
+}
--- /dev/null
+++ b/emu/port/fns.h
@@ -1,0 +1,251 @@
+ulong	FPcontrol(ulong,ulong);
+ulong	FPstatus(ulong,ulong);
+void		FPsave(void*);
+void		FPrestore(void*);
+void		Sleep(Rendez*, int (*)(void*), void*);
+int		Wakeup(Rendez*);
+void		FPinit(void);
+void		addprog(Proc*);
+Block*	adjustblock(Block*, int);
+Block*	allocb(int);
+Block*	bl2mem(uchar*, Block*, int);
+int	blocklen(Block*);
+char*	c2name(Chan*);
+int		canlock(Lock*);
+int		canqlock(QLock*);
+void		cclose(Chan*);
+void		chandevinit(void);
+void		chanfree(Chan*);
+Dir*		chandirstat(Chan*);
+void		cinit(void);
+char*	clipread(void);
+int		clipwrite(char*);
+void	(*coherence)(void);
+void		copen(Chan*);
+void		cmderror(Cmdbuf*, char*);
+Block*	concatblock(Block*);
+int		cread(Chan*, uchar*, int, vlong);
+void		cwrite(Chan*, uchar*, int, vlong);
+Chan*	cunique(Chan*);
+void		cupdate(Chan*, uchar*, int, vlong);
+char*	cleanname(char*);
+Chan*	cclone(Chan*);
+void		closeegrp(Egrp*);
+void		closefgrp(Fgrp*);
+void		closepgrp(Pgrp*);
+void		closesigs(Skeyset*);
+int		cmount(Chan*, Chan*, int, char*);
+Chan*	createdir(Chan*, Mhead*);
+void		cunmount(Chan*, Chan*);
+int		decref(Ref*);
+long		devbwrite(Chan*, Block*, ulong);
+void		devcreate(Chan*, char*, int, ulong);
+void		devdir(Chan*, Qid, char*, long, char*, long, Dir*);
+long		devdirread(Chan*, char*, long, Dirtab*, int, Devgen*);
+void		devinit(void);
+int		devno(int, int);
+Dev*		devbyname(char*);
+void		devpermcheck(char*, ulong, int);
+void		devremove(Chan*);
+int		devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);
+long		devtabread(Chan*, void*, long, vlong);
+int		devwstat(Chan*, uchar*, int);
+Chan*	devattach(int, char*);
+Block*	devbread(Chan*, long, ulong);
+Chan*	devclone(Chan*);
+Devgen	devgen;
+Chan*	devopen(Chan*, int, Dirtab*, int, Devgen*);
+Walkqid*	devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*);
+void		disfault(void*, char*);
+void		disinit(void*);
+int		domount(Chan**, Mhead**);
+void	drawqlock(void);
+void	drawqunlock(void);
+Fgrp*	dupfgrp(Fgrp*);
+void		egrpcpy(Egrp*, Egrp*);
+int		emptystr(char*);
+void		emuinit(void*);
+int		eqchan(Chan*, Chan*, int);
+int		eqqid(Qid, Qid);
+void		error(char*);
+void		errorf(char*, ...);
+#pragma varargck argpos errorf 1
+void		excinit(void);
+void		exhausted(char*);
+int		export(int, char*, int);
+Chan*	fdtochan(Fgrp*, int, int, int, int);
+int		findmount(Chan**, Mhead**, int, int, Qid);
+void		freeb(Block*);
+void		freeblist(Block*);
+void		freeskey(Signerkey*);
+ulong	getcallerpc(void*);
+ulong	getFPcontrol(void);
+ulong	getFPstatus(void);
+void		gkbdputc(Queue*, int);
+int		incref(Ref*);
+int		iprint(char*, ...);
+void		isdir(Chan*);
+int		isdotdot(char*);
+int		iseve(void);
+int		kannounce(char*, char*);
+int		kdial(char*, char*, char*, int*);
+void		kproc(char*, void (*)(void*), void*, int);
+void	kprocinit(Proc*);
+int		kfgrpclose(Fgrp*, int);
+void		ksetenv(char*, char*, int);
+void		kstrcpy(char*, char*, int);
+void		kstrdup(char**, char*);
+long		latin1(uchar*, int);
+void		libinit(char*);
+void		links(void);
+void		lock(Lock*);
+Cmdtab*		lookupcmd(Cmdbuf*, Cmdtab*, int);
+Block*	mem2bl(uchar*, int);
+int		memusehigh(void);
+int		memlow(void);
+void		mkqid(Qid*, vlong, ulong, int);
+Qid	mkuqid(Chan*, Uqid*);
+Chan*		mntauth(Chan*, char*);
+long		mntversion(Chan*, char*, int, int);
+void		mountfree(Mount*);
+void		mousetrack(int, int, int, int);
+void		muxclose(Mnt*);
+Chan*	namec(char*, int, int, ulong);
+Chan*	newchan(void);
+Cname*	newcname(char*);
+Egrp*	newegrp(void);
+Fgrp*	newfgrp(Fgrp*);
+Mount*	newmount(Mhead*, Chan*, int, char*);
+Pgrp*	newpgrp(void);
+Proc*	newproc(void);
+void		nexterror(void);
+void		nofence(void);
+void		notkilled(void);
+int		openmode(ulong);
+void		osblock(void);
+void*		oscmd(char**, int, char*, int*);
+int		oscmdwait(void*, char*, int);
+int		oscmdkill(void*);
+void		oscmdfree(void*);
+void		oserror(void);
+void		oserrstr(char*, uint);
+void		oslongjmp(void*, osjmpbuf, int);
+long		osmillisec(void);
+int		osmillisleep(ulong);
+void		osready(Proc*);
+int		limbosleep(ulong);
+vlong	osusectime(void);
+Block*	packblock(Block*);
+Block*	padblock(Block*, int);
+void		panic(char*, ...);
+Cmdbuf*		parsecmd(char*, int);
+void		pexit(char*, int);
+void		pgrpcpy(Pgrp*, Pgrp*);
+int		progfdprint(Chan*, int, int, char*, int);
+void		putenvq(char*, char*, int);
+void		putenvqv(char*, char**, int, int);
+void		putmhead(Mhead*);
+Block*	pullupblock(Block*, int);
+Block*	pullupqueue(Queue*, int);
+void		qaddlist(Queue*, Block*);
+Block*	qbread(Queue*, int);
+long		qbwrite(Queue*, Block*);
+Queue*		qbypass(void (*)(void*, Block*), void*);
+int		qcanread(Queue*);
+void		qclose(Queue*);
+int		qisclosed(Queue*);
+int		qconsume(Queue*, void*, int);
+Block*	qcopy(Queue*, int, ulong);
+int		qdiscard(Queue*, int);
+void		qflush(Queue*);
+void		qfree(Queue*);
+int		qfull(Queue*);
+Block*	qget(Queue*);
+void		qhangup(Queue*, char*);
+int		qiwrite(Queue*, void*, int);
+int		qlen(Queue*);
+void		qlock(QLock*);
+void		qnoblock(Queue*, int);
+Queue*	qopen(int, int, void (*)(void*), void*);
+int		qpass(Queue*, Block*);
+int		qproduce(Queue*, void*, int);
+void		qputback(Queue*, Block*);
+long		qread(Queue*, void*, int);
+Block*	qremove(Queue*);
+void		qreopen(Queue*);
+void		qsetlimit(Queue*, int);
+int		qstate(Queue*);
+void		qunlock(QLock*);
+int		qwindow(Queue*);
+int		qwrite(Queue*, void*, int);
+ulong	randomread(void *xp, ulong n);
+void	randominit(void);
+int		readkbd(void);
+int		readnum(ulong, char*, ulong, ulong, int);
+int		readnum_vlong(ulong, char*, ulong, vlong, int);
+int		readstr(ulong, char*, ulong, char*);
+#define	seconds()	(osusectime()/1000000)
+void		seterror(char*, ...);
+void		setid(char*, int);
+void	setpointer(int, int);
+char*	skipslash(char*);
+void		srvrtinit(void);
+void		swiproc(Proc*, int);
+Block*	trimblock(Block*, int, int);
+long		unionread(Chan*, void*, long);
+void		unlock(Lock*);
+Uqid*	uqidalloc(Uqidtab*, Chan*);
+void		uqidinit(Uqidtab*);
+void		freeuqid(Uqidtab*, Uqid*);
+void		validname(char*, int);
+void		validstat(uchar*, int);
+void		validwstatname(char*);
+void		vmachine(void*);
+int		walk(Chan**, char**, int, int, int*);
+void		cleanexit(int);
+void		oshostintr(Proc*);
+void		osenter(void);
+void		osleave(void);
+void		oslopri(void);
+void		ospause(void);
+void		osyield(void);
+void		osreboot(char*, char**);
+ulong	poolmaxsize(void);
+Pool*	poolmk(char*, int, int, int);
+void		hnputv(void*, vlong);
+void		hnputl(void*, ulong);
+void		hnputs(void*, ushort);
+vlong		nhgetv(void*);
+ulong		nhgetl(void*);
+ushort		nhgets(void*);
+void*	smalloc(size_t);
+void*	kmalloc(size_t);
+
+/* Namespace Emulation */
+int		kbind(char*, char*, int);
+int		kclose(int);
+int		kcreate(char*, int, ulong);
+int		kdup(int, int);
+int		kfstat(int, uchar*, int);
+int		kfwstat(int, uchar*, int);
+int		kmount(int, int, char*, int, char*);
+int		kunmount(char*, char*);
+int		kopen(char*, int);
+long		kread(int, void*, long);
+int		kremove(char*);
+vlong	kseek(int, vlong, int);
+int		kstat(char*, uchar*, int);
+long		kwrite(int, void*, long);
+int		kwstat(char*, uchar*, int);
+Dir*		kdirstat(char*);
+Dir*		kdirfstat(int);
+int		kdirwstat(char*, Dir*);
+int		kdirfwstat(int, Dir*);
+long		kdirread(int, Dir**);
+int		klisten(char*, char*);
+
+Cname*	addelem(Cname*, char*);
+void		cleancname(Cname*);
+void		cnameclose(Cname*);
+
+#pragma varargck argpos iprint 1
--- /dev/null
+++ b/emu/port/inferno.c
@@ -1,0 +1,1060 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include "isa.h"
+#include "runt.h"
+#include "kernel.h"
+
+/*
+ * here because Sys_FileIO is not public
+ */
+extern	int	srvf2c(char*, char*, Sys_FileIO*);
+
+/*
+ * System types connected to gc
+ */
+uchar	FDmap[] = Sys_FD_map;
+uchar	FileIOmap[] = Sys_FileIO_map;
+void	freeFD(Heap*, int);
+void	freeFileIO(Heap*, int);
+Type*	TFD;
+Type*	TFileIO;
+
+static	uchar	rmap[] = Sys_FileIO_read_map;
+static	uchar	wmap[] = Sys_FileIO_write_map;
+static	Type*	FioTread;
+static	Type*	FioTwrite;
+static	uchar	dmap[] = Sys_Dir_map;
+static	Type*	Tdir;
+
+typedef struct FD FD;
+struct FD
+{
+	Sys_FD	fd;
+	Fgrp*	grp;
+};
+
+void
+sysinit(void)
+{
+	TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap));
+	TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap));
+
+	/* Support for devsrv.c */
+	FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap));
+	FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap));
+
+	/* Support for dirread */
+	Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap));
+}
+
+void
+freeFD(Heap *h, int swept)
+{
+	FD *handle;
+
+	USED(swept);
+
+	handle = H2D(FD*, h);
+
+	release();
+	if(handle->fd.fd >= 0)
+		kfgrpclose(handle->grp, handle->fd.fd);
+	closefgrp(handle->grp);
+	acquire();
+}
+
+void
+freeFileIO(Heap *h, int swept)
+{
+	Sys_FileIO *fio;
+
+	if(swept)
+		return;
+
+	fio = H2D(Sys_FileIO*, h);
+	destroy(fio->read);
+	destroy(fio->write);
+}
+
+Sys_FD*
+mkfd(int fd)
+{
+	Heap *h;
+	Fgrp *fg;
+	FD *handle;
+
+	h = heap(TFD);
+	handle = H2D(FD*, h);
+	handle->fd.fd = fd;
+	fg = up->env->fgrp;
+	handle->grp = fg;
+	incref(&fg->r);
+	return (Sys_FD*)handle;
+}
+#define fdchk(x)	((x) == (Sys_FD*)H ? -1 : (x)->fd)
+
+void
+seterror(char *err, ...)
+{
+	char *estr;
+	va_list arg;
+
+	estr = up->env->errstr;
+	va_start(arg, err);
+	vseprint(estr, estr+ERRMAX, err, arg);
+	va_end(arg);
+}
+
+char*
+syserr(char *s, char *es, Prog *p)
+{
+	Osenv *o;
+
+	o = p->osenv;
+	kstrcpy(s, o->errstr, es - s);
+	return s + strlen(s);
+}
+
+void
+Sys_millisec(void *fp)
+{
+	F_Sys_millisec *f;
+
+	f = fp;
+	*f->ret = osmillisec();
+}
+
+void
+Sys_open(void *fp)
+{
+	int fd;
+	F_Sys_open *f;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kopen(string2c(f->s), f->mode);
+	acquire();
+	if(fd == -1)
+		return;
+
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_pipe(void *fp)
+{
+	Array *a;
+	int fd[2];
+	Sys_FD **sfd;
+	F_Sys_pipe *f;
+
+	f = fp;
+	*f->ret = -1;
+
+	a = f->fds;
+	if(a->len < 2)
+		return;
+	if(kpipe(fd) < 0)
+		return;
+
+	sfd = (Sys_FD**)a->data;
+	destroy(sfd[0]);
+	destroy(sfd[1]);
+	sfd[0] = H;
+	sfd[1] = H;
+	sfd[0] = mkfd(fd[0]);
+	sfd[1] = mkfd(fd[1]);
+	*f->ret = 0;
+}
+
+void
+Sys_fildes(void *fp)
+{
+	F_Sys_fildes *f;
+	int fd;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kdup(f->fd, -1);
+	acquire();
+	if(fd == -1)
+		return;
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_dup(void *fp)
+{
+	F_Sys_dup *f;
+
+	f = fp;
+	release();
+	*f->ret = kdup(f->old, f->new);	
+	acquire();
+}
+
+void
+Sys_create(void *fp)
+{
+	int fd;
+	F_Sys_create *f;
+
+	f = fp;
+	destroy(*f->ret);
+	*f->ret = H;
+	release();
+	fd = kcreate(string2c(f->s), f->mode, f->perm);
+	acquire();
+	if(fd == -1)
+		return;
+
+	*f->ret = mkfd(fd);
+}
+
+void
+Sys_remove(void *fp)
+{
+	F_Sys_remove *f;
+
+	f = fp;
+	release();
+	*f->ret = kremove(string2c(f->s));
+	acquire();
+}
+
+void
+Sys_seek(void *fp)
+{
+	F_Sys_seek *f;
+
+	f = fp;
+	release();
+	*f->ret = kseek(fdchk(f->fd), f->off, f->start);
+	acquire();
+}
+
+void
+Sys_unmount(void *fp)
+{
+	F_Sys_unmount *f;
+
+	f = fp;
+	release();
+	*f->ret = kunmount(string2c(f->s1), string2c(f->s2));
+	acquire();
+}
+
+void
+Sys_read(void *fp)
+{
+	int n;
+	F_Sys_read *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kread(fdchk(f->fd), f->buf->data, n);
+	acquire();
+}
+
+void
+Sys_readn(void *fp)
+{
+	int fd, m, n, t;
+	F_Sys_readn *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+	fd = fdchk(f->fd);
+
+	release();
+	for(t = 0; t < n; t += m){
+		m = kread(fd, (char*)f->buf->data+t, n-t);
+		if(m <= 0){
+			if(t == 0)
+				t = m;
+			break;
+		}
+	}
+	*f->ret = t;
+	acquire();
+}
+
+void
+Sys_pread(void *fp)
+{
+	int n;
+	F_Sys_pread *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off);
+	acquire();
+}
+
+void
+Sys_chdir(void *fp)
+{
+	F_Sys_chdir *f;
+
+	f = fp;
+	release();
+	*f->ret = kchdir(string2c(f->path));
+	acquire();
+}
+
+void
+Sys_write(void *fp)
+{
+	int n;
+	F_Sys_write *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kwrite(fdchk(f->fd), f->buf->data, n);
+	acquire();
+}
+
+void
+Sys_pwrite(void *fp)
+{
+	int n;
+	F_Sys_pwrite *f;
+
+	f = fp;
+	n = f->n;
+	if(f->buf == (Array*)H || n < 0) {
+		*f->ret = 0;
+		return;		
+	}
+	if(n > f->buf->len)
+		n = f->buf->len;
+
+	release();
+	*f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off);
+	acquire();
+}
+
+static void
+unpackdir(Dir *d, Sys_Dir *sd)
+{
+	retstr(d->name, &sd->name);
+	retstr(d->uid, &sd->uid);
+	retstr(d->gid, &sd->gid);
+	retstr(d->muid, &sd->muid);
+	sd->qid.path = d->qid.path;
+	sd->qid.vers = d->qid.vers;
+	sd->qid.qtype = d->qid.type;
+	sd->mode = d->mode;
+	sd->atime = d->atime;
+	sd->mtime = d->mtime;
+	sd->length = d->length;
+	sd->dtype = d->type;
+	sd->dev = d->dev;
+}
+
+static Dir*
+packdir(Sys_Dir *sd)
+{
+	char *nm[4], *p;
+	int i, n;
+	Dir *d;
+
+	nm[0] = string2c(sd->name);
+	nm[1] = string2c(sd->uid);
+	nm[2] = string2c(sd->gid);
+	nm[3] = string2c(sd->muid);
+	n = 0;
+	for(i=0; i<4; i++)
+		n += strlen(nm[i])+1;
+	d = smalloc(sizeof(*d)+n);
+	p = (char*)d+sizeof(*d);
+	for(i=0; i<4; i++){
+		n = strlen(nm[i])+1;
+		memmove(p, nm[i], n);
+		nm[i] = p;
+		p += n;
+	}
+	d->name = nm[0];
+	d->uid = nm[1];
+	d->gid = nm[2];
+	d->muid = nm[3];
+	d->qid.path = sd->qid.path;
+	d->qid.vers = sd->qid.vers;
+	d->qid.type = sd->qid.qtype;
+	d->mode = sd->mode;
+	d->atime = sd->atime;
+	d->mtime = sd->mtime;
+	d->length = sd->length;
+	d->type = sd->dtype;
+	d->dev = sd->dev;
+	return d;
+}
+
+void
+Sys_fstat(void *fp)
+{
+	Dir *d;
+	F_Sys_fstat *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	release();
+	d = kdirfstat(fdchk(f->fd));
+	acquire();
+	if(d == nil)
+		return;
+	if(waserror() == 0){
+		unpackdir(d, &f->ret->t1);
+		f->ret->t0 = 0;
+		poperror();
+	}
+	free(d);
+}
+
+void
+Sys_stat(void *fp)
+{
+	Dir *d;
+	F_Sys_stat *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	release();
+	d = kdirstat(string2c(f->s));
+	acquire();
+	if(d == nil)
+		return;
+	if(waserror() == 0){
+		unpackdir(d, &f->ret->t1);
+		f->ret->t0 = 0;
+		poperror();
+	}
+	free(d);
+}
+
+void
+Sys_fd2path(void *fp)
+{
+	F_Sys_fd2path *f;
+	char *s;
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	s = kfd2path(fdchk(f->fd));
+	acquire();
+	if(waserror() == 0){
+		retstr(s, f->ret);
+		poperror();
+	}
+	free(s);
+}
+
+void
+Sys_mount(void *fp)
+{
+	F_Sys_mount *f;
+
+	f = fp;
+	release();
+	*f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec));
+	acquire();
+}
+
+void
+Sys_bind(void *fp)
+{
+	F_Sys_bind *f;
+
+	f = fp;
+	release();
+	*f->ret = kbind(string2c(f->s), string2c(f->on), f->flags);
+	acquire();
+}
+
+void
+Sys_wstat(void *fp)
+{
+	Dir *d;
+	F_Sys_wstat *f;
+
+	f = fp;
+	d = packdir(&f->d);
+	release();
+	*f->ret = kdirwstat(string2c(f->s), d);
+	acquire();
+	free(d);
+}
+
+void
+Sys_fwstat(void *fp)
+{
+	Dir *d;
+	F_Sys_fwstat *f;
+
+	f = fp;
+	d = packdir(&f->d);
+	release();
+	*f->ret = kdirfwstat(fdchk(f->fd), d);
+	acquire();
+	free(d);
+}
+
+void
+Sys_print(void *fp)
+{
+	int n;
+	Prog *p;
+	Chan *c;
+	char buf[1024], *b = buf;
+	F_Sys_print *f;
+	f = fp;
+	c = up->env->fgrp->fd[1];
+	if(c == nil)
+		return;
+	p = currun();
+
+	release();
+	n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+	if (n >= sizeof(buf)-UTFmax-2)
+		n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+	*f->ret = kwrite(1, b, n);
+	if (b != buf)
+		free(b);
+	acquire();
+}
+
+void
+Sys_fprint(void *fp)
+{
+	int n;
+	Prog *p;
+	char buf[1024], *b = buf;
+	F_Sys_fprint *f;
+
+	f = fp;
+	p = currun();
+	release();
+	n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf));
+	if (n >= sizeof(buf)-UTFmax-2)
+		n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf));
+	*f->ret = kwrite(fdchk(f->fd), b, n);
+	if (b != buf)
+		free(b);
+	acquire();
+}
+
+void
+Sys_werrstr(void *fp)
+{
+	F_Sys_werrstr *f;
+
+	f = fp;
+	*f->ret = 0;
+	kstrcpy(up->env->errstr, string2c(f->s), ERRMAX);
+}
+
+void
+Sys_dial(void *fp)
+{
+	int cfd;
+	char dir[NETPATHLEN], *a, *l;
+	F_Sys_dial *f;
+
+	f = fp;
+	a = string2c(f->addr);
+	l = string2c(f->local);
+	release();
+	f->ret->t0 = kdial(a, l, dir, &cfd);
+	acquire();
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.dfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	f->ret->t1.cfd = mkfd(cfd);
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_announce(void *fp)
+{
+	char dir[NETPATHLEN], *a;
+	F_Sys_announce *f;
+
+	f = fp;
+	a = string2c(f->addr);
+	release();
+	f->ret->t0 = kannounce(a, dir);
+	acquire();
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.cfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_listen(void *fp)
+{
+	F_Sys_listen *f;
+	char dir[NETPATHLEN], *d;
+
+	f = fp;
+	d = string2c(f->c.dir);
+	release();
+	f->ret->t0 = klisten(d, dir);
+	acquire();
+
+	destroy(f->ret->t1.dfd);
+	f->ret->t1.dfd = H;
+	destroy(f->ret->t1.cfd);
+	f->ret->t1.cfd = H;
+	if(f->ret->t0 == -1)
+		return;
+
+	f->ret->t1.cfd = mkfd(f->ret->t0);
+	f->ret->t0 = 0;
+	retstr(dir, &f->ret->t1.dir);
+}
+
+void
+Sys_sleep(void *fp)
+{
+	F_Sys_sleep *f;
+
+	f = fp;
+	release();
+	if(f->period > 0){
+		if(waserror()){
+			acquire();
+			error("");
+		}
+		osenter();
+		*f->ret = limbosleep(f->period);
+		osleave();
+		poperror();
+	}
+	acquire();
+}
+
+void
+Sys_stream(void *fp)
+{
+	Prog *p;
+	uchar *buf;
+	int src, dst;
+	F_Sys_stream *f;
+	int nbytes, t, n;
+
+	f = fp;
+	buf = malloc(f->bufsiz);
+	if(buf == nil) {
+		kwerrstr(Enomem);
+		*f->ret = -1;
+		return;
+	}
+
+	src = fdchk(f->src);
+	dst = fdchk(f->dst);
+
+	p = currun();
+
+	release();
+	t = 0;
+	nbytes = 0;
+	while(p->kill == nil) {
+		n = kread(src, buf+t, f->bufsiz-t);
+		if(n <= 0)
+			break;
+		t += n;
+		if(t >= f->bufsiz) {
+			if(kwrite(dst, buf, t) != t) {
+				t = 0;
+				break;
+			}
+
+			nbytes += t;
+			t = 0;
+		}
+	}
+	if(t != 0) {
+		kwrite(dst, buf, t);
+		nbytes += t;
+	}
+	acquire();
+	free(buf);
+	*f->ret = nbytes;
+}
+
+void
+Sys_export(void *fp)
+{
+	F_Sys_export *f;
+
+	f = fp;
+	release();
+	*f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC);
+	acquire();
+}
+
+void
+Sys_file2chan(void *fp)
+{
+	int r;
+	Heap *h;
+	Channel *c;
+	Sys_FileIO *fio;
+	F_Sys_file2chan *f;
+	void *sv;
+
+	h = heap(TFileIO);
+
+	fio = H2D(Sys_FileIO*, h);
+
+	c = cnewc(FioTread, movtmp, 16);
+	fio->read = c;
+
+	c = cnewc(FioTwrite, movtmp, 16);
+	fio->write = c;
+
+	f = fp;
+	sv = *f->ret;
+	*f->ret = fio;
+	destroy(sv);
+
+	release();
+	r = srvf2c(string2c(f->dir), string2c(f->file), fio);
+	acquire();
+	if(r == -1) {
+		*f->ret = H;
+		destroy(fio);
+	}
+}
+
+enum
+{
+	/* the following pctl calls can block and must release the virtual machine */
+	BlockingPctl=	Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV
+};
+
+void
+Sys_pctl(void *fp)
+{
+	int fd;
+	Prog *p;
+	List *l;
+	Chan *c;
+	volatile struct {Pgrp *np;} np;
+	Pgrp *opg;
+	Chan *dot;
+	Osenv *o;
+	F_Sys_pctl *f;
+	Fgrp *fg, *ofg, *nfg;
+	volatile struct {Egrp *ne;} ne;
+	Egrp *oe;
+
+	f = fp;
+
+	p = currun();
+	if(f->flags & BlockingPctl)
+		release();
+
+	np.np = nil;
+	ne.ne = nil;
+	if(waserror()) {
+		closepgrp(np.np);
+		closeegrp(ne.ne);
+		if(f->flags & BlockingPctl)
+			acquire();
+		*f->ret = -1;
+		return;
+	}
+
+	o = p->osenv;
+	if(f->flags & Sys_NEWFD) {
+		ofg = o->fgrp;
+		nfg = newfgrp(ofg);
+		lock(&ofg->l);
+		/* file descriptors to preserve */
+		for(l = f->movefd; l != H; l = l->tail) {
+			fd = *(int*)l->data;
+			if(fd >= 0 && fd <= ofg->maxfd) {
+				c = ofg->fd[fd];
+				if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) {
+					incref(&c->r);
+					nfg->fd[fd] = c;
+					if(nfg->maxfd < fd)
+						nfg->maxfd = fd;
+				}
+			}
+		}
+		unlock(&ofg->l);
+		o->fgrp = nfg;
+		closefgrp(ofg);
+	}
+	else
+	if(f->flags & Sys_FORKFD) {
+		ofg = o->fgrp;
+		fg = dupfgrp(ofg);
+		/* file descriptors to close */
+		for(l = f->movefd; l != H; l = l->tail)
+			kclose(*(int*)l->data);
+		o->fgrp = fg;
+		closefgrp(ofg);
+	}
+
+	if(f->flags & Sys_NEWNS) {
+		np.np = newpgrp();
+		dot = o->pgrp->dot;
+		np.np->dot = cclone(dot);
+		np.np->slash = cclone(dot);
+		cnameclose(np.np->slash->name);
+		np.np->slash->name = newcname("/");
+		np.np->nodevs = o->pgrp->nodevs;
+		opg = o->pgrp;
+		o->pgrp = np.np;
+		np.np = nil;
+		closepgrp(opg);
+	}
+	else
+	if(f->flags & Sys_FORKNS) {
+		np.np = newpgrp();
+		pgrpcpy(np.np, o->pgrp);
+		opg = o->pgrp;
+		o->pgrp = np.np;
+		np.np = nil;
+		closepgrp(opg);
+	}
+
+	if(f->flags & Sys_NEWENV) {
+		oe = o->egrp;
+		o->egrp = newegrp();
+		closeegrp(oe);
+	}
+	else
+	if(f->flags & Sys_FORKENV) {
+		ne.ne = newegrp();
+		egrpcpy(ne.ne, o->egrp);
+		oe = o->egrp;
+		o->egrp = ne.ne;
+		ne.ne = nil;
+		closeegrp(oe);
+	}
+
+	if(f->flags & Sys_NEWPGRP)
+		newgrp(p);
+
+	if(f->flags & Sys_NODEVS)
+		o->pgrp->nodevs = 1;
+
+	poperror();
+
+	if(f->flags & BlockingPctl)
+		acquire();
+
+	*f->ret = p->pid;
+}
+
+void
+Sys_dirread(void *fp)
+{
+
+	Dir *b;
+	int i, n;
+	Heap *h;
+	uchar *d;
+	void *r;
+	F_Sys_dirread *f;
+
+	f = fp;
+	f->ret->t0 = -1;
+	r = f->ret->t1;
+	f->ret->t1 = H;
+	destroy(r);
+	release();
+	n = kdirread(fdchk(f->fd), &b);
+	acquire();
+	if(n <= 0) {
+		f->ret->t0 = n;
+		free(b);
+		return;
+	}
+	if(waserror()){
+		free(b);
+		return;
+	}
+	h = heaparray(Tdir, n);
+	poperror();
+	d = H2D(Array*, h)->data;
+	for(i = 0; i < n; i++) {
+		unpackdir(b+i, (Sys_Dir*)d);
+		d += Sys_Dir_size;
+	}
+	f->ret->t0 = n;
+	f->ret->t1 = H2D(Array*, h);
+	free(b);
+}
+
+void
+Sys_fauth(void *fp)
+{
+	int fd;
+	F_Sys_fauth *f;
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	fd = kfauth(fdchk(f->fd), string2c(f->aname));
+	acquire();
+	if(fd >= 0)
+		*f->ret = mkfd(fd);
+}
+
+void
+Sys_fversion(void *fp)
+{
+	void *r;
+	F_Sys_fversion *f;
+	int n;
+	char buf[20], *s;
+
+	f = fp;
+	f->ret->t0 = -1;
+	r = f->ret->t1;
+	f->ret->t1 = H;
+	destroy(r);
+	s = string2c(f->version);
+	n = strlen(s);
+	if(n >= sizeof(buf)-1)
+		n = sizeof(buf)-1;
+	memmove(buf, s, n);
+	buf[n] = 0;
+	release();
+	n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf));
+	acquire();
+	if(n >= 0){
+		f->ret->t0 = f->msize;
+		retnstr(buf, n, &f->ret->t1);
+	}
+}
+
+void
+Sys_iounit(void *fp)
+{
+	F_Sys_iounit *f;
+
+	f = fp;
+	release();
+	*f->ret = kiounit(fdchk(f->fd));
+	acquire();
+}
+
+void
+ccom(Progq **cl, Prog *p)
+{
+	volatile struct {Progq **cl;} vcl;
+
+	cqadd(cl, p);
+	vcl.cl = cl;
+	if(waserror()) {
+		if(p->ptr != nil) {	/* no killcomm */
+			cqdelp(vcl.cl, p);
+			p->ptr = nil;
+		}
+		nexterror();
+	}
+	cblock(p);
+	poperror();
+}
+
+void
+crecv(Channel *c, void *ip)
+{
+	Prog *p;
+	REG rsav;
+
+	if(c->send->prog == nil && c->size == 0) {
+		p = currun();
+		p->ptr = ip;
+		ccom(&c->recv, p);
+		return;
+	}
+
+	rsav = R;
+	R.s = &c;
+	R.d = ip;
+	irecv();
+	R = rsav;
+}
+
+void
+csend(Channel *c, void *ip)
+{
+ 	Prog *p;
+	REG rsav;
+
+	if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) {
+		p = currun();
+		p->ptr = ip;
+		ccom(&c->send, p);
+		return;
+	}
+
+	rsav = R;
+	R.s = ip;
+	R.d = &c;
+	isend();
+	R = rsav;
+}
--- /dev/null
+++ b/emu/port/ip.h
@@ -1,0 +1,65 @@
+enum
+{
+	IPaddrlen		= 16,	/* IPv6 */
+	IPv4addrlen	= 4,	/* IPv4 */
+	IPv4off		= 12,	/* length of IPv6 prefix for IPv4 addresses */
+	Udphdrlen	= 3*IPaddrlen+2*2,
+	OUdphdrlen	= 2*IPaddrlen+2*2,
+	OUdphdrlenv4	= 2*IPv4addrlen+2*2,
+
+	S_TCP		= 0,
+	S_UDP
+};
+
+typedef struct Fs Fs;
+typedef struct Proto	Proto;
+typedef struct Conv	Conv;
+
+extern int		so_socket(int type);
+extern void		so_connect(int, uchar*, ushort);
+extern void		so_getsockname(int, uchar*, ushort*);
+extern void		so_bind(int, int, uchar*, ushort);
+extern void		so_listen(int);
+extern int		so_accept(int, uchar*, ushort*);
+extern int		so_getservbyname(char*, char*, char*);
+extern int		so_gethostbyname(char*, char**, int);
+extern int		so_gethostbyaddr(char*, char**, int);
+extern int		so_recv(int, void*, int, void*, int);
+extern int		so_send(int, void*, int, void*, int);
+extern void		so_close(int);
+extern int		so_hangup(int, int);
+extern void		so_setsockopt(int, int, int);
+extern int		so_mustbind(int, int);
+extern void		so_keepalive(int, int);
+
+
+extern void		hnputl(void *p, unsigned long v);
+extern void		hnputs(void *p, ushort v);
+extern unsigned long	nhgetl(void *p);
+extern ushort	nhgets(void *p);
+extern unsigned long	parseip(uchar *to, char *from);
+extern int	parsemac(uchar *to, char *from, int len);
+extern char*	v4parseip(uchar*, char*);
+extern int		bipipe(int[]);
+
+extern int	isv4(uchar*);
+extern void	v4tov6(uchar *v6, uchar *v4);
+extern int	v6tov4(uchar *v4, uchar *v6);
+extern int	eipfmt(Fmt*);
+
+#define	ipmove(x, y) memmove(x, y, IPaddrlen)
+#define	ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
+
+extern uchar IPv4bcast[IPaddrlen];
+extern uchar IPv4bcastobs[IPaddrlen];
+extern uchar IPv4allsys[IPaddrlen];
+extern uchar IPv4allrouter[IPaddrlen];
+extern uchar IPnoaddr[IPaddrlen];
+extern uchar v4prefix[IPaddrlen];
+extern uchar IPallbits[IPaddrlen];
+extern uchar v6Unspecified[IPaddrlen];
+
+extern void	arpadd(char*, char*, int);
+extern int	arpwrite(char*, int);
+
+extern	int	Fsproto(Fs*, Proto*);
--- /dev/null
+++ b/emu/port/ipaux.c
@@ -1,0 +1,525 @@
+#include        "dat.h"
+#include        "fns.h"
+#include        "ip.h"
+#include        "error.h"
+
+/*
+ *  well known IP addresses
+ */
+uchar IPv4bcast[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+uchar IPv4allsys[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x01
+};
+uchar IPv4allrouter[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x02
+};
+uchar IPallbits[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+
+uchar IPnoaddr[IPaddrlen];
+
+/*
+ *  prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0, 0, 0, 0
+};
+
+/*
+ *  well known IPv6 addresses
+ */
+uchar v6Unspecified[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6loopback[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6linklocal[IPaddrlen] = {
+	0xfe, 0x80, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6linklocalmask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6linklocalprefix = 8;
+uchar v6sitelocal[IPaddrlen] = {
+	0xfe, 0xc0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6sitelocalmask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6sitelocalprefix = 6;
+uchar v6glunicast[IPaddrlen] = {
+	0x08, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6multicast[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+uchar v6multicastmask[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6multicastprefix = 1;
+uchar v6allnodesN[IPaddrlen] = {
+	0xff, 0x01, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6allnodesNmask[IPaddrlen] = {
+	0xff, 0xff, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6allnodesprefix = 2;
+uchar v6allnodesL[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+uchar v6allnodesLmask[IPaddrlen] = {
+	0xff, 0xff, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+int v6allnodesLprefix = 2;
+uchar v6allroutersN[IPaddrlen] = {
+	0xff, 0x01, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6allroutersL[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6allroutersS[IPaddrlen] = {
+	0xff, 0x05, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x02
+};
+uchar v6solicitednode[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01,
+	0xff, 0, 0, 0
+};
+uchar v6solicitednodemask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0x0, 0x0, 0x0
+};
+int v6solicitednodeprefix = 13;
+
+enum
+{
+	Isprefix= 16,
+};
+
+int
+eipfmt(Fmt *f)
+{
+	char buf[5*8];
+	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+	static char *ifmt = "%d.%d.%d.%d";
+	uchar *p, ip[16];
+	ulong *lp;
+	ushort s;
+	int i, j, n, eln, eli, m, v;
+
+	switch(f->r) {
+	case 'E':		/* Ethernet address */
+		p = va_arg(f->args, uchar*);
+		return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+	case 'I':		/* Ip address */
+		p = va_arg(f->args, uchar*);
+common:
+		if(memcmp(p, v4prefix, 12) == 0)
+			return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]);
+
+		/* find longest elision */
+		eln = eli = -1;
+		for(i = 0; i < 16; i += 2){
+			for(j = i; j < 16; j += 2)
+				if(p[j] != 0 || p[j+1] != 0)
+					break;
+			if(j > i && j - i > eln){
+				eli = i;
+				eln = j - i;
+			}
+		}
+
+		/* print with possible elision */
+		n = 0;
+		for(i = 0; i < 16; i += 2){
+			if(i == eli){
+				n += sprint(buf+n, "::");
+				i += eln;
+				if(i >= 16)
+					break;
+			} else if(i != 0)
+				n += sprint(buf+n, ":");
+			s = (p[i]<<8) + p[i+1];
+			n += sprint(buf+n, "%ux", s);
+		}
+		return fmtstrcpy(f, buf);
+
+	case 'i':		/* v6 address as 4 longs */
+		lp = va_arg(f->args, ulong*);
+		for(i = 0; i < 4; i++)
+			hnputl(ip+4*i, *lp++);
+		p = ip;
+		goto common;
+
+	case 'V':		/* v4 ip address */
+		p = va_arg(f->args, uchar*);
+		return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]);
+
+	case 'M':		/* ip mask */
+		p = va_arg(f->args, uchar*);
+
+		/* look for a prefix mask */
+		for(i = 0; i < 16; i++)
+			if(p[i] != 0xff)
+				break;
+		for(j = i+1; j < 16; j++)
+			if(p[j] != 0)
+				goto common;
+		n = 8*i;
+		if(i < IPaddrlen){
+			v = p[i];
+			for(m = 0x80; m != 0; m >>= 1){
+				if((v & m) == 0)
+					break;
+				v &= ~m;
+				n++;
+			}
+			if(v != 0)
+				goto common;
+		}
+
+		/* got one, use /xx format */
+		return fmtprint(f, "/%d", n);
+
+	}
+	return fmtstrcpy(f, "(eipfmt)");
+}
+
+#define CLASS(p) ((*(uchar*)(p))>>6)
+
+char*
+v4parseip(uchar *to, char *from)
+{
+	int i;
+	char *p;
+
+	p = from;
+	for(i = 0; i < 4 && *p; i++){
+		to[i] = strtoul(p, &p, 0);
+		if(*p == '.')
+			p++;
+	}
+	switch(CLASS(to)){
+	case 0:	/* class A - 1 uchar net */
+	case 1:
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = to[1];
+			to[1] = 0;
+		} else if(i == 2){
+			to[3] = to[1];
+			to[1] = 0;
+		}
+		break;
+	case 2:	/* class B - 2 uchar net */
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = 0;
+		}
+		break;
+	}
+	return p;
+}
+
+int
+isv4(uchar *ip)
+{
+	return memcmp(ip, v4prefix, IPv4off) == 0;
+}
+
+
+/*
+ *  the following routines are unrolled with no memset's to speed
+ *  up the usual case
+ */
+void
+v4tov6(uchar *v6, uchar *v4)
+{
+	v6[0] = 0;
+	v6[1] = 0;
+	v6[2] = 0;
+	v6[3] = 0;
+	v6[4] = 0;
+	v6[5] = 0;
+	v6[6] = 0;
+	v6[7] = 0;
+	v6[8] = 0;
+	v6[9] = 0;
+	v6[10] = 0xff;
+	v6[11] = 0xff;
+	v6[12] = v4[0];
+	v6[13] = v4[1];
+	v6[14] = v4[2];
+	v6[15] = v4[3];
+}
+
+int
+v6tov4(uchar *v4, uchar *v6)
+{
+	if(v6[0] == 0
+	&& v6[1] == 0
+	&& v6[2] == 0
+	&& v6[3] == 0
+	&& v6[4] == 0
+	&& v6[5] == 0
+	&& v6[6] == 0
+	&& v6[7] == 0
+	&& v6[8] == 0
+	&& v6[9] == 0
+	&& v6[10] == 0xff
+	&& v6[11] == 0xff)
+	{
+		v4[0] = v6[12];
+		v4[1] = v6[13];
+		v4[2] = v6[14];
+		v4[3] = v6[15];
+		return 0;
+	} else {
+		memset(v4, 0, 4);
+		return -1;
+	}
+}
+
+ulong
+parseip(uchar *to, char *from)
+{
+	int i, elipsis = 0, v4 = 1;
+	ulong x;
+	char *p, *op;
+
+	memset(to, 0, IPaddrlen);
+	p = from;
+	for(i = 0; i < 16 && *p; i+=2){
+		op = p;
+		x = strtoul(p, &p, 16);
+		if(*p == '.' || (*p == 0 && i == 0)){
+			p = v4parseip(to+i, op);
+			i += 4;
+			break;
+		} else {
+			to[i] = x>>8;
+			to[i+1] = x;
+		}
+		if(*p == ':'){
+			v4 = 0;
+			if(*++p == ':'){
+				elipsis = i+2;
+				p++;
+			}
+		}
+	}
+	if(i < 16){
+		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
+		memset(&to[elipsis], 0, 16-i);
+	}
+	if(v4){
+		to[10] = to[11] = 0xff;
+		return nhgetl(to+12);
+	} else
+		return 6;
+}
+
+/*
+ *  hack to allow ip v4 masks to be entered in the old
+ *  style
+ */
+ulong
+parseipmask(uchar *to, char *from)
+{
+	ulong x;
+	int i;
+	uchar *p;
+
+	if(*from == '/'){
+		/* as a number of prefix bits */
+		i = atoi(from+1);
+		if(i < 0)
+			i = 0;
+		if(i > 128)
+			i = 128;
+		memset(to, 0, IPaddrlen);
+		for(p = to; i >= 8; i -= 8)
+			*p++ = 0xff;
+		if(i > 0)
+			*p = ~((1<<(8-i))-1);
+		x = nhgetl(to+IPv4off);
+	} else {
+		/* as a straight bit mask */
+		x = parseip(to, from);
+		if(memcmp(to, v4prefix, IPv4off) == 0)
+			memset(to, 0xff, IPv4off);
+	}
+	return x;
+}
+
+void
+maskip(uchar *from, uchar *mask, uchar *to)
+{
+	int i;
+
+	for(i = 0; i < IPaddrlen; i++)
+		to[i] = from[i] & mask[i];
+}
+
+uchar classmask[4][16] = {
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
+};
+
+uchar*
+defmask(uchar *ip)
+{
+	if(isv4(ip))
+		return classmask[ip[IPv4off]>>6];
+	else {
+		if(ipcmp(ip, v6loopback) == 0)
+			return IPallbits;
+		else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0)
+			return v6linklocalmask;
+		else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0)
+			return v6sitelocalmask;
+		else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0)
+			return v6solicitednodemask;
+		else if(memcmp(ip, v6multicast, v6multicastprefix) == 0)
+			return v6multicastmask;
+		return IPallbits;
+	}
+}
+
+/*
+ *  parse a hex mac address
+ */
+int
+parsemac(uchar *to, char *from, int len)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	memset(to, 0, len);
+	for(i = 0; i < len; i++){
+		if(p[0] == '\0' || p[1] == '\0')
+			break;
+
+		nip[0] = p[0];
+		nip[1] = p[1];
+		nip[2] = '\0';
+		p += 2;
+
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return i;
+}
+
+void
+hnputl(void *p, unsigned long v)
+{
+	unsigned char *a;
+
+	a = p;
+	a[0] = v>>24;
+	a[1] = v>>16;
+	a[2] = v>>8;
+	a[3] = v;
+}
+
+void
+hnputs(void *p, unsigned short v)
+{
+	unsigned char *a;
+
+	a = p;
+	a[0] = v>>8;
+	a[1] = v;
+}
+
+unsigned long
+nhgetl(void *p)
+{
+	unsigned char *a;
+	a = p;
+	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
+}
+
+unsigned short
+nhgets(void *p)
+{
+	unsigned char *a;
+	a = p;
+	return (a[0]<<8)|(a[1]<<0);
+}
--- /dev/null
+++ b/emu/port/ipif-posix.c
@@ -1,0 +1,414 @@
+#ifdef sun
+#define	uint uxuint
+#define	ulong uxulong
+#define	ushort uxushort
+#endif
+#include <sys/types.h>
+#include	<sys/time.h>
+#include	<sys/socket.h>
+#include	<net/if.h>
+#include	<net/if_arp.h>
+#include	<netinet/in.h>
+#include	<netinet/tcp.h>
+#include	<arpa/inet.h>
+#include	<netdb.h>
+#include	<sys/ioctl.h>
+#undef ulong
+#undef ushort
+#undef uint
+
+#include        "dat.h"
+#include        "fns.h"
+#include        "ip.h"
+#include        "error.h"
+
+char Enotv4[] = "address not IPv4";
+
+static void
+ipw6(uchar *a, ulong w)
+{
+	memmove(a, v4prefix, IPv4off);
+	memmove(a+IPv4off, &w, IPv4addrlen);
+}
+
+int
+so_socket(int type)
+{
+	int fd, one;
+
+	switch(type) {
+	default:
+		error("bad protocol type");
+	case S_TCP:
+		type = SOCK_STREAM;
+		break;
+	case S_UDP:
+		type = SOCK_DGRAM;
+		break;
+	}
+
+	fd = socket(AF_INET, type, 0);
+	if(fd < 0)
+		oserror();
+	if(type == SOCK_DGRAM){
+		one = 1;
+		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+	}else{
+		one = 1;
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+	}
+	return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+	uchar *h = hdr;
+
+	osenter();
+	if(hdr == 0)
+		r = write(sock, va, len);
+	else {
+		memset(&sa, 0, sizeof(sa));
+		sin = (struct sockaddr_in*)&sa;
+		sin->sin_family = AF_INET;
+		switch(hdrlen){
+		case OUdphdrlenv4:
+			memmove(&sin->sin_addr, h,  4);
+			memmove(&sin->sin_port, h+8, 2);
+			break;
+		case OUdphdrlen:
+			v6tov4((uchar*)&sin->sin_addr, h);
+			memmove(&sin->sin_port, h+2*IPaddrlen, 2);	/* rport */
+			break;
+		default:
+			v6tov4((uchar*)&sin->sin_addr, h);
+			memmove(&sin->sin_port, h+3*IPaddrlen, 2);
+			break;
+		}
+		r = sendto(sock, va, len, 0, &sa, sizeof(sa));
+	}
+	osleave();
+	return r;
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	socklen_t l;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+	uchar h[Udphdrlen];
+
+	osenter();
+	if(hdr == 0)
+		r = read(sock, va, len);
+	else {
+		sin = (struct sockaddr_in*)&sa;
+		l = sizeof(sa);
+		r = recvfrom(sock, va, len, 0, &sa, &l);
+		if(r >= 0) {
+			memset(h, 0, sizeof(h));
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				memmove(h, &sin->sin_addr, IPv4addrlen);
+				memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
+				break;
+			case OUdphdrlen:
+				v4tov6(h, (uchar*)&sin->sin_addr);
+				memmove(h+2*IPaddrlen, &sin->sin_port, 2);
+				break;
+			default:
+				v4tov6(h, (uchar*)&sin->sin_addr);
+				memmove(h+3*IPaddrlen, &sin->sin_port, 2);
+				break;
+			}
+
+			/* alas there's no way to get the local addr/port correctly.  Pretend. */
+			memset(&sa, 0, sizeof(sa));
+			getsockname(sock, &sa, &l);
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
+				memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
+				break;
+			case OUdphdrlen:
+				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+				memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
+				break;
+			default:
+				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
+				v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr);	/* ifcaddr */
+				memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
+				break;
+			}
+			memmove(hdr, h, hdrlen);
+		}
+	}
+	osleave();
+	return r;
+}
+
+void
+so_close(int sock)
+{
+	close(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+	int r;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	if(!isv4(raddr))
+		error(Enotv4);
+
+	memset(&sa, 0, sizeof(sa));
+	sin = (struct sockaddr_in*)&sa;
+	sin->sin_family = AF_INET;
+	hnputs(&sin->sin_port, rport);
+	memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
+
+	osenter();
+	r = connect(fd, &sa, sizeof(sa));
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+	socklen_t len;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	len = sizeof(sa);
+	if(getsockname(fd, &sa, &len) < 0)
+		oserror();
+
+	sin = (struct sockaddr_in*)&sa;
+	if(sin->sin_family != AF_INET || len != sizeof(*sin))
+		error(Enotv4);
+
+	ipw6(laddr, sin->sin_addr.s_addr);
+	*lport = nhgets(&sin->sin_port);
+}
+
+void
+so_listen(int fd)
+{
+	int r;
+
+	osenter();
+	r = listen(fd, 256);
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+	int nfd;
+	socklen_t len;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	sin = (struct sockaddr_in*)&sa;
+
+	len = sizeof(sa);
+	osenter();
+	nfd = accept(fd, &sa, &len);
+	osleave();
+	if(nfd < 0)
+		oserror();
+
+	if(sin->sin_family != AF_INET || len != sizeof(*sin))
+		error(Enotv4);
+
+	ipw6(raddr, sin->sin_addr.s_addr);
+	*rport = nhgets(&sin->sin_port);
+	return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+	int i, one;
+	struct sockaddr sa;
+	struct sockaddr_in *sin;
+
+	sin = (struct sockaddr_in*)&sa;
+
+	one = 1;
+	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+		oserrstr(up->genbuf, sizeof(up->genbuf));
+		print("setsockopt: %s", up->genbuf);
+	}
+
+	if(su) {
+		for(i = 600; i < 1024; i++) {
+			memset(&sa, 0, sizeof(sa));
+			sin->sin_family = AF_INET;
+			memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+			hnputs(&sin->sin_port, i);
+
+			if(bind(fd, &sa, sizeof(sa)) >= 0)	
+				return;
+		}
+		oserror();
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sin->sin_family = AF_INET;
+	memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
+	hnputs(&sin->sin_port, port);
+
+	if(bind(fd, &sa, sizeof(sa)) < 0)
+		oserror();
+}
+
+int
+so_gethostbyname(char *host, char**hostv, int n)
+{
+	int i;
+	char buf[32];
+	uchar *p;
+	struct hostent *hp;
+
+	hp = gethostbyname(host);
+	if(hp == 0)
+		return 0;
+
+	for(i = 0; hp->h_addr_list[i] && i < n; i++) {
+		p = (uchar*)hp->h_addr_list[i];
+		sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+		hostv[i] = strdup(buf);
+		if(hostv[i] == 0)
+			break;
+	}
+	return i;
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+	int i;
+	struct hostent *hp;
+	unsigned long straddr;
+
+	straddr = inet_addr(addr);
+	if(straddr == -1)
+		return 0;
+
+	hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
+	if(hp == 0)
+		return 0;
+
+	hostv[0] = strdup(hp->h_name);
+	if(hostv[0] == 0)
+		return 0;
+	for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
+		hostv[i] = strdup(hp->h_aliases[i-1]);
+		if(hostv[i] == 0)
+			break;
+	}
+	return i;
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+	ushort p;
+	struct servent *s;
+
+	s = getservbyname(service, net);
+	if(s == 0)
+		return -1;
+	p = s->s_port;
+	sprint(port, "%d", nhgets(&p));	
+	return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+	int r;
+	static struct linger l = {1, 0};
+
+	osenter();
+	if(nolinger)
+		setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+	r = shutdown(fd, 2);
+	if(r >= 0)
+		r = close(fd);
+	osleave();
+	return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+#ifdef SIOCGARP
+	struct arpreq a;
+	struct sockaddr_in pa;
+	int s;
+	uchar addr[IPaddrlen];
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	memset(&a, 0, sizeof(a));
+	memset(&pa, 0, sizeof(pa));
+	pa.sin_family = AF_INET;
+	pa.sin_port = 0;
+	parseip(addr, ipaddr);
+	if(!isv4(addr)){
+		close(s);
+		error(Ebadarg);
+	}
+	memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen);
+	memmove(&a.arp_pa, &pa, sizeof(pa));
+	while(ioctl(s, SIOCGARP, &a) != -1) {
+		ioctl(s, SIOCDARP, &a);
+		memset(&a.arp_ha, 0, sizeof(a.arp_ha));
+	}
+	a.arp_ha.sa_family = AF_UNSPEC;
+	parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6);
+	a.arp_flags = ATF_PERM;
+	if(ioctl(s, SIOCSARP, &a) == -1) {
+		oserrstr(up->env->errstr, ERRMAX);
+		close(s);
+		error(up->env->errstr);
+	}
+	close(s);
+#else
+	error("arp not implemented");
+#endif
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+	return restricted || port != 0;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+	int on;
+
+	on = 1;
+	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+#ifdef TCP_KEEPIDLE
+	if(ms <= 120000)
+		ms = 120000;
+	ms /= 1000;
+	setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms));
+#endif
+}
--- /dev/null
+++ b/emu/port/ipif6-posix.c
@@ -1,0 +1,415 @@
+#ifdef sun
+#define	uint uxuint
+#define	ulong uxulong
+#define	ushort uxushort
+#endif
+#include <sys/types.h>
+#include	<sys/time.h>
+#include	<sys/socket.h>
+#include	<net/if.h>
+#include	<net/if_arp.h>
+#include	<netinet/in.h>
+#include	<netinet/tcp.h>
+#include	<arpa/inet.h>
+#include	<netdb.h>
+#include	<sys/ioctl.h>
+#undef ulong
+#undef ushort
+#undef uint
+
+#include        "dat.h"
+#include        "fns.h"
+#include        "ip.h"
+#include        "error.h"
+
+int
+so_socket(int type)
+{
+	int fd, one;
+	int v6only;
+
+	switch(type) {
+	default:
+		error("bad protocol type");
+	case S_TCP:
+		type = SOCK_STREAM;
+		break;
+	case S_UDP:
+		type = SOCK_DGRAM;
+		break;
+	}
+
+	fd = socket(AF_INET6, type, 0);
+	if(fd < 0)
+		oserror();
+
+	/* OpenBSD has v6only fixed to 1, so the following will fail */
+	v6only = 0;
+	if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, sizeof v6only) < 0)
+		oserror();
+
+	if(type == SOCK_DGRAM){
+		one = 1;
+		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
+	}else{
+		one = 1;
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
+	}
+	return fd;
+}
+
+int
+so_send(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+	char *h = hdr;
+
+	osenter();
+	if(hdr == 0)
+		r = write(sock, va, len);
+	else {
+		memset(&sa, 0, sizeof(sa));
+		sin6 = (struct sockaddr_in6*)&sa;
+		sin6->sin6_family = AF_INET6;
+		switch(hdrlen){
+		case OUdphdrlenv4:
+			v4tov6((uchar*)&sin6->sin6_addr, h);
+			memmove(&sin6->sin6_port, h+8, 2);
+			break;
+		case OUdphdrlen:
+			memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+			memmove(&sin6->sin6_port, h+2*IPaddrlen, 2);	/* rport */
+			break;
+		default:
+			memmove((uchar*)&sin6->sin6_addr, h, IPaddrlen);
+			memmove(&sin6->sin6_port, h+3*IPaddrlen, 2);
+			break;
+		}
+		r = sendto(sock, va, len, 0, (struct sockaddr*)sin6, sizeof(*sin6));
+	}
+	osleave();
+	return r;
+}
+
+int
+so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
+{
+	int r, l;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+	char h[Udphdrlen];
+
+	osenter();
+	if(hdr == 0)
+		r = read(sock, va, len);
+	else {
+		sin6 = (struct sockaddr_in6*)&sa;
+		l = sizeof(sa);
+		r = recvfrom(sock, va, len, 0, (struct sockaddr*)&sa, &l);
+		if(r >= 0) {
+			memset(h, 0, sizeof(h));
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				if(v6tov4(h, (uchar*)&sin6->sin6_addr) < 0) {
+					osleave();
+					error("OUdphdrlenv4 with IPv6 address");
+				}
+				memmove(h+2*IPv4addrlen, &sin6->sin6_port, 2);
+				break;
+			case OUdphdrlen:
+				memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen, &sin6->sin6_port, 2);
+				break;
+			default:
+				memmove(h, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+3*IPaddrlen, &sin6->sin6_port, 2);
+				break;
+			}
+
+			/* alas there's no way to get the local addr/port correctly.  Pretend. */
+			memset(&sa, 0, sizeof(sa));
+			l = sizeof(sa);
+			getsockname(sock, (struct sockaddr*)&sa, &l);
+			switch(hdrlen){
+			case OUdphdrlenv4:
+				/*
+				 * we get v6Unspecified/noaddr if local address cannot be determined.
+				 * that's reasonable for ipv4 too.
+				 */
+				if(ipcmp(v6Unspecified, (uchar*)&sin6->sin6_addr) != 0
+				&& v6tov4(h+IPv4addrlen, (uchar*)&sin6->sin6_addr) < 0) {
+					osleave();
+					error("OUdphdrlenv4 with IPv6 address");
+				}
+				memmove(h+2*IPv4addrlen+2, &sin6->sin6_port, 2);
+				break;
+			case OUdphdrlen:
+				memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen+2, &sin6->sin6_port, 2);
+				break;
+			default:
+				memmove(h+IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);
+				memmove(h+2*IPaddrlen, (uchar*)&sin6->sin6_addr, IPaddrlen);	/* ifcaddr */
+				memmove(h+3*IPaddrlen+2, &sin6->sin6_port, 2);
+				break;
+			}
+			memmove(hdr, h, hdrlen);
+		}
+	}
+	osleave();
+	return r;
+}
+
+void
+so_close(int sock)
+{
+	close(sock);
+}
+
+void
+so_connect(int fd, uchar *raddr, ushort rport)
+{
+	int r;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	memset(&sa, 0, sizeof(sa));
+	sin6 = (struct sockaddr_in6*)&sa;
+	sin6->sin6_family = AF_INET6;
+	hnputs(&sin6->sin6_port, rport);
+	memmove((uchar*)&sin6->sin6_addr, raddr, IPaddrlen);
+
+	osenter();
+	r = connect(fd, (struct sockaddr*)sin6, sizeof(*sin6));
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+void
+so_getsockname(int fd, uchar *laddr, ushort *lport)
+{
+	int len;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	len = sizeof(*sin6);
+	if(getsockname(fd, (struct sockaddr*)&sa, &len) < 0)
+		oserror();
+
+	sin6 = (struct sockaddr_in6*)&sa;
+	if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+		error("not AF_INET6");
+
+	memmove(laddr, &sin6->sin6_addr, IPaddrlen);
+	*lport = nhgets(&sin6->sin6_port);
+}
+
+void
+so_listen(int fd)
+{
+	int r;
+
+	osenter();
+	r = listen(fd, 256);
+	osleave();
+	if(r < 0)
+		oserror();
+}
+
+int
+so_accept(int fd, uchar *raddr, ushort *rport)
+{
+	int nfd, len;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6*)&sa;
+
+	len = sizeof(*sin6);
+	osenter();
+	nfd = accept(fd, (struct sockaddr*)&sa, &len);
+	osleave();
+	if(nfd < 0)
+		oserror();
+
+	if(sin6->sin6_family != AF_INET6 || len != sizeof(*sin6))
+		error("not AF_INET6");
+
+	memmove(raddr, &sin6->sin6_addr, IPaddrlen);
+	*rport = nhgets(&sin6->sin6_port);
+	return nfd;
+}
+
+void
+so_bind(int fd, int su, uchar *addr, ushort port)
+{
+	int i, one;
+	struct sockaddr_storage sa;
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6*)&sa;
+
+	one = 1;
+	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
+		oserrstr(up->genbuf, sizeof(up->genbuf));
+		print("setsockopt: %s", up->genbuf);
+	}
+
+	if(su) {
+		for(i = 600; i < 1024; i++) {
+			memset(&sa, 0, sizeof(sa));
+			sin6->sin6_family = AF_INET6;
+			memmove(&sin6->sin6_addr, addr, IPaddrlen);
+			hnputs(&sin6->sin6_port, i);
+
+			if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) >= 0)	
+				return;
+		}
+		oserror();
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sin6->sin6_family = AF_INET6;
+	memmove(&sin6->sin6_addr, addr, IPaddrlen);
+	hnputs(&sin6->sin6_port, port);
+
+	if(bind(fd, (struct sockaddr*)sin6, sizeof(*sin6)) < 0)
+		oserror();
+}
+
+static int
+resolve(char *name, char **hostv, int n, int isnumeric)
+{
+	int i;
+	struct addrinfo *res0, *r;
+	char buf[5*8];
+	uchar addr[IPaddrlen];
+	struct addrinfo hints;
+
+	memset(&hints, 0, sizeof hints);
+	hints.ai_flags = isnumeric? AI_NUMERICHOST: 0;
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if(getaddrinfo(name, nil, &hints, &res0) < 0)
+		return 0;
+	i = 0;
+	for(r = res0; r != nil && i < n; r = r->ai_next) {
+		if(r->ai_family == AF_INET)
+			v4tov6(addr, (uchar*)&((struct sockaddr_in*)r->ai_addr)->sin_addr);
+		else if(r->ai_family == AF_INET6)
+			memmove(addr, &((struct sockaddr_in6*)r->ai_addr)->sin6_addr, IPaddrlen);
+		else
+			continue;
+
+		snprint(buf, sizeof buf, "%I", addr);
+		hostv[i++] = strdup(buf);
+	}
+
+	freeaddrinfo(res0);
+	return i;
+}
+
+int
+so_gethostbyname(char *host, char **hostv, int n)
+{
+	return resolve(host, hostv, n, 0);
+}
+
+int
+so_gethostbyaddr(char *addr, char **hostv, int n)
+{
+	return resolve(addr, hostv, n, 1);
+}
+
+int
+so_getservbyname(char *service, char *net, char *port)
+{
+	ushort p;
+	struct servent *s;
+
+	s = getservbyname(service, net);
+	if(s == 0)
+		return -1;
+	p = s->s_port;
+	sprint(port, "%d", nhgets(&p));	
+	return 0;
+}
+
+int
+so_hangup(int fd, int nolinger)
+{
+	int r;
+	static struct linger l = {1, 0};
+
+	osenter();
+	if(nolinger)
+		setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
+	r = shutdown(fd, 2);
+	if(r >= 0)
+		r = close(fd);
+	osleave();
+	return r;
+}
+
+void
+arpadd(char *ipaddr, char *eaddr, int n)
+{
+#ifdef SIOCGARP
+	struct arpreq a;
+	struct sockaddr_in pa;
+	int s;
+	uchar addr[IPaddrlen];
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	memset(&a, 0, sizeof(a));
+	memset(&pa, 0, sizeof(pa));
+	pa.sin_family = AF_INET;
+	pa.sin_port = 0;
+	parseip(addr, ipaddr);
+	if(!isv4(addr)){
+		close(s);
+		error(Ebadarg);
+	}
+	memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen);
+	memmove(&a.arp_pa, &pa, sizeof(pa));
+	while(ioctl(s, SIOCGARP, &a) != -1) {
+		ioctl(s, SIOCDARP, &a);
+		memset(&a.arp_ha, 0, sizeof(a.arp_ha));
+	}
+	a.arp_ha.sa_family = AF_UNSPEC;
+	parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6);
+	a.arp_flags = ATF_PERM;
+	if(ioctl(s, SIOCSARP, &a) == -1) {
+		oserrstr(up->env->errstr, ERRMAX);
+		close(s);
+		error(up->env->errstr);
+	}
+	close(s);
+#else
+	error("arp not implemented");
+#endif
+}
+
+int
+so_mustbind(int restricted, int port)
+{
+	return restricted || port != 0;
+}
+
+void
+so_keepalive(int fd, int ms)
+{
+	int on;
+
+	on = 1;
+	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+#ifdef TCP_KEEPIDLE
+	if(ms <= 120000)
+		ms = 120000;
+	ms /= 1000;
+	setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms));
+#endif
+}
--- /dev/null
+++ b/emu/port/keysym2ucs.h
@@ -1,0 +1,9 @@
+/* $XFree86: xc/programs/xterm/keysym2ucs.h,v 1.1 1999/06/12 15:37:18 dawes Exp $ */
+/*
+ * This module converts keysym values into the corresponding ISO 10646-1
+ * (UCS, Unicode) values.
+ */
+
+#include <X11/X.h>
+
+long keysym2ucs(KeySym keysym);
--- /dev/null
+++ b/emu/port/kproc-pthreads.c
@@ -1,0 +1,235 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#undef _POSIX_C_SOURCE 
+#undef getwd
+
+#include	<unistd.h>
+#include	<signal.h>
+#include 	<pthread.h>
+#include	<limits.h>
+#include	<errno.h>
+#include	<semaphore.h>
+
+typedef struct Osdep Osdep;
+struct Osdep {
+	sem_t	sem;
+	pthread_t	self;
+};
+
+static pthread_key_t  prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+	return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+	Osenv *e;
+	Proc *p;
+	Osdep *os;
+
+	USED(t);
+
+	lock(&procs.l);
+	p = up;
+	if(p->prev)
+		p->prev->next = p->next;
+	else
+		procs.head = p->next;
+
+	if(p->next)
+		p->next->prev = p->prev;
+	else
+		procs.tail = p->prev;
+	unlock(&procs.l);
+
+	if(0)
+		print("pexit: %s: %s\n", p->text, msg);
+
+	e = p->env;
+	if(e != nil) {
+		closefgrp(e->fgrp);
+		closepgrp(e->pgrp);
+		closeegrp(e->egrp);
+		closesigs(e->sigs);
+		free(e->user);
+	}
+	free(p->prog);
+	os = p->os;
+	if(os != nil){
+		sem_destroy(&os->sem);
+		free(os);
+	}
+	free(p);
+	pthread_exit(0);
+}
+
+static void*
+tramp(void *arg)
+{
+	Proc *p;
+	Osdep *os;
+
+	p = arg;
+	os = p->os;
+	os->self = pthread_self();
+	if(pthread_setspecific(prdakey, arg))
+		panic("set specific data failed in tramp\n");
+	if(0){
+		pthread_attr_t attr;
+		memset(&attr, 0, sizeof(attr));
+		pthread_getattr_np(pthread_self(), &attr);
+		size_t s;
+		pthread_attr_getstacksize(&attr, &s);
+		print("stack size = %d\n", s);
+	}
+	p->func(p->arg);
+	pexit("{Tramp}", 0);
+	return nil;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+	pthread_t thread;
+	Proc *p;
+	Pgrp *pg;
+	Fgrp *fg;
+	Egrp *eg;
+	pthread_attr_t attr;
+	Osdep *os;
+
+	p = newproc();
+	if(p == nil)
+		panic("kproc: no memory");
+
+	os = malloc(sizeof(*os));
+	if(os == nil)
+		panic("kproc: no memory");
+	os->self = 0;	/* set by tramp */
+	sem_init(&os->sem, 0, 0);
+	p->os = os;
+
+	if(flags & KPDUPPG) {
+		pg = up->env->pgrp;
+		incref(&pg->r);
+		p->env->pgrp = pg;
+	}
+	if(flags & KPDUPFDG) {
+		fg = up->env->fgrp;
+		incref(&fg->r);
+		p->env->fgrp = fg;
+	}
+	if(flags & KPDUPENVG) {
+		eg = up->env->egrp;
+		incref(&eg->r);
+		p->env->egrp = eg;
+	}
+
+	p->env->uid = up->env->uid;
+	p->env->gid = up->env->gid;
+	kstrdup(&p->env->user, up->env->user);
+
+	strcpy(p->text, name);
+
+	p->func = func;
+	p->arg = arg;
+
+	lock(&procs.l);
+	if(procs.tail != nil) {
+		p->prev = procs.tail;
+		procs.tail->next = p;
+	} else {
+		procs.head = p;
+		p->prev = nil;
+	}
+	procs.tail = p;
+	unlock(&procs.l);
+
+	memset(&attr, 0, sizeof(attr));
+	if(pthread_attr_init(&attr) == -1)
+		panic("pthread_attr_init failed");
+	if(flags & KPX11)
+		pthread_attr_setstacksize(&attr, 512*1024);	/* could be a parameter */
+	else if(KSTACK > 0)
+		pthread_attr_setstacksize(&attr, (KSTACK < PTHREAD_STACK_MIN? PTHREAD_STACK_MIN: KSTACK)+1024);
+	pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	if(pthread_create(&thread, &attr, tramp, p))
+		panic("thr_create failed\n");
+	pthread_attr_destroy(&attr);
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+	Osdep *os;
+
+	os = p->os;
+	if(os != nil && os->self != 0)
+		pthread_kill(os->self, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+	Osdep *os;
+
+	os = up->os;
+	while(sem_wait(&os->sem))
+		{}	/* retry on signals (which shouldn't happen) */
+}
+
+void
+osready(Proc *p)
+{
+	Osdep *os;
+
+	os = p->os;
+	sem_post(&os->sem);
+}
+
+void
+kprocinit(Proc *p)
+{
+	if(pthread_key_create(&prdakey, NULL))
+		panic("key_create failed");
+	if(pthread_setspecific(prdakey, p))
+		panic("set specific thread data failed");
+}
+
+void
+osyield(void)
+{
+//	pthread_yield_np();
+	/* define pthread_yield to be sched_yield or pthread_yield_np if required */
+	pthread_yield();
+}
+
+void
+ospause(void)
+{
+	/* main just wants this thread to go away */
+	pthread_exit(0);
+}
+
+void
+oslopri(void)
+{
+	struct sched_param param;
+	int policy;
+	pthread_t self;
+
+	self = pthread_self();
+	pthread_getschedparam(self, &policy, &param);
+	param.sched_priority = sched_get_priority_min(policy);
+	pthread_setschedparam(self,  policy, &param);
+}
--- /dev/null
+++ b/emu/port/latin1.c
@@ -1,0 +1,83 @@
+#include "dat.h"
+
+/*
+ * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a
+ * prefix of latintab[j].ld only when j<i.
+ */
+static
+struct cvlist
+{
+	char	*ld;		/* must be seen before using this conversion */
+	char	*si;		/* options for last input characters */
+	char	*so;		/* the corresponding Rune for each si entry */
+} latintab[] = {
+#include "latin1.h"
+	0,	0,		0
+};
+
+/*
+ * Given 5 characters k[0]..k[4], find the rune or return -1 for failure.
+ */
+static long
+unicode(uchar *k)
+{
+	long i, c;
+
+	k++;	/* skip 'X' */
+	c = 0;
+	for(i=0; i<4; i++,k++){
+		c <<= 4;
+		if('0'<=*k && *k<='9')
+			c += *k-'0';
+		else if('a'<=*k && *k<='f')
+			c += 10 + *k-'a';
+		else if('A'<=*k && *k<='F')
+			c += 10 + *k-'A';
+		else
+			return -1;
+	}
+	return c;
+}
+
+/*
+ * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for
+ * failure, or something < -1 if n is too small.  In the latter case, the result
+ * is minus the required n.
+ */
+long
+latin1(uchar *k, int n)
+{
+	struct cvlist *l;
+	int c;
+	char* p;
+
+	if(k[0] == 'X')
+		if(n>=5)
+			return unicode(k);
+		else
+			return -5;
+	for(l=latintab; l->ld!=0; l++)
+		if(k[0] == l->ld[0]){
+			if(n == 1)
+				return -2;
+			if(l->ld[1] == 0)
+				c = k[1];
+			else if(l->ld[1] != k[1])
+				continue;
+			else if(n == 2)
+				return -3;
+			else
+				c = k[2];
+			for(p=l->si; *p!=0; p++)
+				if(*p == c) {
+					Rune r;
+					int i = p - l->si;
+					p = l->so;
+					for(; i >= 0; i--)
+						p += chartorune(&r, p);
+					return r;
+				}
+			return -1;
+		}
+	return -1;
+}
--- /dev/null
+++ b/emu/port/latin1.h
@@ -1,0 +1,99 @@
+	" ", " i",	"␣ı",
+	"!~", "-=~",	"≄≇≉",
+	"!", "!<=>?bmp",	"¡≮≠≯‽⊄∉⊅",
+	"\"*", "IUiu",	"ΪΫϊϋ",
+	"\"", "\"AEIOUYaeiouy",	"¨ÄËÏÖÜŸäëïöüÿ",
+	"$*", "fhk",	"ϕϑϰ",
+	"$", "BEFHILMRVaefglopv",	"ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ",
+	"\'\"", "Uu",	"Ǘǘ",
+	"\'", "\'ACEILNORSUYZacegilnorsuyz",	"´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź",
+	"*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz",	"∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ",
+	"+", "-O",	"±⊕",
+	",", ",ACEGIKLNORSTUacegiklnorstu",	"¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų",
+	"-*", "l",	"ƛ",
+	"-", "+-2:>DGHILOTZbdghiltuz~",	"∓­ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂",
+	".", ".CEGILOZceglz",	"·ĊĖĠİĿ⊙Żċėġŀż",
+	"/", "Oo",	"Øø",
+	"1", "234568",	"½⅓¼⅕⅙⅛",
+	"2", "-35",	"ƻ⅔⅖",
+	"3", "458",	"¾⅗⅜",
+	"4", "5",	"⅘",
+	"5", "68",	"⅚⅝",
+	"7", "8",	"⅞",
+	":", ")-=",	"☺÷≔",
+	"<!", "=~",	"≨⋦",
+	"<", "-<=>~",	"←«≤≶≲",
+	"=", ":<=>OV",	"≕⋜≡⋝⊜⇒",
+	">!", "=~",	"≩⋧",
+	">", "<=>~",	"≷≥»≳",
+	"?", "!?",	"‽¿",
+	"@\'", "\'",	"ъ",
+	"@@", "\'EKSTYZekstyz",	"ьЕКСТЫЗекстыз",
+	"@C", "Hh",	"ЧЧ",
+	"@E", "Hh",	"ЭЭ",
+	"@K", "Hh",	"ХХ",
+	"@S", "CHch",	"ЩШЩШ",
+	"@T", "Ss",	"ЦЦ",
+	"@Y", "AEOUaeou",	"ЯЕЁЮЯЕЁЮ",
+	"@Z", "Hh",	"ЖЖ",
+	"@c", "h",	"ч",
+	"@e", "h",	"э",
+	"@k", "h",	"х",
+	"@s", "ch",	"щш",
+	"@t", "s",	"ц",
+	"@y", "aeou",	"яеёю",
+	"@z", "h",	"ж",
+	"@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx",	"АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх",
+	"A", "E",	"Æ",
+	"C", "ACU",	"⋂ℂ⋃",
+	"Dv", "Zz",	"DŽDž",
+	"D", "-e",	"Ð∆",
+	"G", "-",	"Ǥ",
+	"H", "-H",	"Ħℍ",
+	"I", "-J",	"ƗIJ",
+	"L", "&-Jj|",	"⋀ŁLJLj⋁",
+	"N", "JNj",	"NJℕNj",
+	"O", "*+-./=EIcoprx",	"⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗",
+	"P", "P",	"ℙ",
+	"Q", "Q",	"ℚ",
+	"R", "R",	"ℝ",
+	"S", "123S",	"¹²³§",
+	"T", "-u",	"Ŧ⊨",
+	"V", "=",	"⇐",
+	"Y", "R",	"Ʀ",
+	"Z", "-ACSZ",	"Ƶℤ",
+	"^", "ACEGHIJOSUWYaceghijosuwy",	"ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ",
+	"_\"", "AUau",	"ǞǕǟǖ",
+	"_,", "Oo",	"Ǭǭ",
+	"_.", "Aa",	"Ǡǡ",
+	"_", "AEIOU_aeiou",	"ĀĒĪŌŪ¯āēīōū",
+	"`\"", "Uu",	"Ǜǜ",
+	"`", "AEIOUaeiou",	"ÀÈÌÒÙàèìòù",
+	"a", "ben",	"↔æ∠",
+	"b", "()+-0123456789=bknpqru",	"₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•",
+	"c", "$Oagu",	"¢©∩≅∪",
+	"dv", "z",	"dž",
+	"d", "-adegz",	"ð↓‡°†ʣ",
+	"e", "$lmns",	"€⋯—–∅",
+	"f", "a",	"∀",
+	"g", "$-r",	"¤ǥ∇",
+	"h", "-v",	"ℏƕ",
+	"i", "-bfjps",	"ɨ⊆∞ij⊇∫",
+	"l", "\"$&\'-jz|",	"“£∧‘łlj⋄∨",
+	"m", "iou",	"µ∈×",
+	"n", "jo",	"nj¬",
+	"o", "AOUaeiu",	"Å⊚Ůåœƣů",
+	"p", "Odgrt",	"℗∂¶∏∝",
+	"r", "\"\'O",	"”’®",
+	"s", "()+-0123456789=abnoprstu",	"⁽⁾⁺⁻⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑",
+	"t", "-efmsu",	"ŧ∃∴™ς⊢",
+	"u", "-AEGIOUaegiou",	"ʉĂĔĞĬŎŬ↑ĕğĭŏŭ",
+	"v\"", "Uu",	"Ǚǚ",
+	"v", "ACDEGIKLNORSTUZacdegijklnorstuz",	"ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž",
+	"w", "bknpqr",	"♗♔♘♙♕♖",
+	"x", "O",	"⊗",
+	"y", "$",	"¥",
+	"z", "-",	"ƶ",
+	"|", "Pp|",	"Þþ¦",
+	"~!", "=",	"≆",
+	"~", "-=AINOUainou~",	"≃≅ÃĨÑÕŨãĩñõũ≈",
--- /dev/null
+++ b/emu/port/lock.c
@@ -1,0 +1,142 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+void
+lock(Lock *l)
+{
+	int i;
+
+	if(_tas(&l->val) == 0)
+		return;
+	for(i=0; i<100; i++){
+		if(_tas(&l->val) == 0)
+			return;
+		osyield();
+	}
+	for(i=1;; i++){
+		if(_tas(&l->val) == 0)
+			return;
+		osmillisleep(i*10);
+		if(i > 100){
+			osyield();
+			i = 1;
+		}
+	}
+}
+
+int
+canlock(Lock *l)
+{
+	return _tas(&l->val) == 0;
+}
+
+void
+unlock(Lock *l)
+{
+	coherence();
+	l->val = 0;
+}
+
+void
+qlock(QLock *q)
+{
+	Proc *p;
+
+	lock(&q->use);
+	if(!q->locked) {
+		q->locked = 1;
+		unlock(&q->use);
+		return;
+	}
+	p = q->tail;
+	if(p == 0)
+		q->head = up;
+	else
+		p->qnext = up;
+	q->tail = up;
+	up->qnext = 0;
+	unlock(&q->use);
+	osblock();
+}
+
+int
+canqlock(QLock *q)
+{
+	if(!canlock(&q->use))
+		return 0;
+	if(q->locked){
+		unlock(&q->use);
+		return 0;
+	}
+	q->locked = 1;
+	unlock(&q->use);
+	return 1;
+}
+
+void
+qunlock(QLock *q)
+{
+	Proc *p;
+
+	lock(&q->use);
+	p = q->head;
+	if(p) {
+		q->head = p->qnext;
+		if(q->head == 0)
+			q->tail = 0;
+		unlock(&q->use);
+		osready(p);
+		return;
+	}
+	q->locked = 0;
+	unlock(&q->use);
+}
+
+void
+rlock(RWlock *l)
+{
+	qlock(&l->x);		/* wait here for writers and exclusion */
+	lock(&l->l);
+	l->readers++;
+	canqlock(&l->k);	/* block writers if we are the first reader */
+	unlock(&l->l);
+	qunlock(&l->x);
+}
+
+/* same as rlock but punts if there are any writers waiting */
+int
+canrlock(RWlock *l)
+{
+	if (!canqlock(&l->x))
+		return 0;
+	lock(&l->l);
+	l->readers++;
+	canqlock(&l->k);	/* block writers if we are the first reader */
+	unlock(&l->l);
+	qunlock(&l->x);
+	return 1;
+}
+
+void
+runlock(RWlock *l)
+{
+	lock(&l->l);
+	if(--l->readers == 0)	/* last reader out allows writers */
+		qunlock(&l->k);
+	unlock(&l->l);
+}
+
+void
+wlock(RWlock *l)
+{
+	qlock(&l->x);		/* wait here for writers and exclusion */
+	qlock(&l->k);		/* wait here for last reader */
+}
+
+void
+wunlock(RWlock *l)
+{
+	qunlock(&l->k);
+	qunlock(&l->x);
+}
--- /dev/null
+++ b/emu/port/main.c
@@ -1,0 +1,454 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+#include	"kernel.h"
+#include	"draw.h"
+#include	"version.h"
+
+int		rebootargc = 0;
+char**		rebootargv;
+static	char	*imod = "/dis/emuinit.dis";
+extern	char*	hosttype;
+char*	tkfont;	/* for libtk/utils.c */
+int	tkstylus;	/* libinterp/tk.c */
+extern	int	mflag;
+	int	dflag;
+	int vflag;
+	int	vflag;
+	Procs	procs;
+	char	*eve;
+	int	Xsize	= 640;
+	int	Ysize	= 480;
+	int	bflag = 1;
+	int	sflag;
+	int	qflag;
+	int	xtblbit;
+	ulong	displaychan;
+char *cputype;
+
+static void
+usage(void)
+{
+	fprint(2, "Usage: emu [options...] [file.dis [args...]]\n"
+		"\t-gXxY\n"
+		"\t-c[0-9]\n"
+		"\t-d file.dis\n"
+		"\t-s\n"
+		"\t-v\n"
+		"\t-p<poolname>=maxsize\n"
+		"\t-f<fontpath>\n"
+		"\t-r<rootpath>\n"
+		"\t-7\n"
+		"\t-B\n"
+		"\t-C<channel string>\n"
+		"\t-S\n");
+
+	exits("usage");
+}
+
+static void
+envusage(void)
+{
+	fprint(2, "emu: bad option in EMU environment variable (%s)\n", getenv("EMU"));
+	usage();
+}
+
+static int
+isnum(char *p)
+{
+	if (*p == 0) return 0;
+	while (*p) {
+		if (*p < '0' || *p > '9') return 0;
+		p++;
+	}
+	return 1;
+}
+
+static int
+geom(char *val)
+{
+	char *p;
+	int x, y;
+	if (val == '\0' || (*val < '0' || *val > '9')) 
+		return 0;
+	x = strtoul(val, &p, 0);
+	if(x >= 64) 
+		Xsize = x;
+	if (*p++ != 'x' || !isnum(p))
+		return 0;
+	y = strtoul(p, &p, 0);
+	if(y >= 48)
+		Ysize = y;
+	if (*p != '\0') return 0;
+	return 1;
+}
+
+static void
+poolopt(char *str)
+{
+	char *var;
+	int n;
+	ulong x;
+
+	var = str;
+	while(*str && *str != '=')
+		str++;
+	if(*str != '=' || str[1] == '\0')
+		usage();
+	*str++ = '\0';
+	n = strlen(str);
+	x = atoi(str);
+	switch(str[n - 1]){
+	case 'k':
+	case 'K':
+		x *= 1024;
+		break;
+	case 'm':
+	case 'M':
+		x *= 1024*1024;
+		break;
+	}
+	if(poolsetsize(var, x) == 0)
+		usage();
+}
+
+static void
+option(int argc, char *argv[], void (*badusage)(void))
+{
+	char *cp;
+
+	ARGBEGIN {
+	default:
+		badusage();
+	case 'g':		/* Window geometry */
+		if (geom(EARGF(badusage())) == 0)
+			badusage();
+		break;
+	case 'b':		/* jit array bounds checking (obsolete, now on by default) */
+		break;
+	case 'B':		/* suppress jit array bounds checks */
+		bflag = 0;
+		break;
+	case 'c':		/* Compile on the fly */
+		cp = EARGF(badusage());
+		if (!isnum(cp))
+			badusage();
+		cflag = atoi(cp);
+		if(cflag < 0|| cflag > 9)
+			usage();
+		break;
+	case 'I':	/* (temporary option) run without cons */
+		dflag++;
+		break;
+	case 'd':		/* run as a daemon */
+		dflag++;
+		imod = EARGF(badusage());
+		break;
+	case 's':		/* No trap handling */
+		sflag++;
+		break;
+	case 'm':		/* gc mark and sweep */
+		cp = EARGF(badusage());
+		if (!isnum(cp))
+			badusage();
+		mflag = atoi(cp);
+		if(mflag < 0|| mflag > 9)
+			usage();
+		break;
+	case 'p':		/* pool option */
+		poolopt(EARGF(badusage()));
+		break;
+	case 'f':		/* Set font path */
+		tkfont = EARGF(badusage());
+		break;
+	case 'r':		/* Set inferno root */
+		strecpy(rootdir, rootdir+sizeof(rootdir), EARGF(badusage()));
+		break;
+	case '7':		/* use 7 bit colormap in X */
+		xtblbit = 1;
+		break;
+	case 'G':		/* allow global access to file system (obsolete) */
+		break;
+	case	'C':		/* channel specification for display */
+		cp = EARGF(badusage());
+		displaychan = strtochan(cp);
+		if(displaychan == 0){
+			fprint(2, "emu: invalid channel specifier (-C): %q\n", cp);
+			exits("usage");
+		}
+		break;
+	case 'S':
+		tkstylus = 1;
+		break;
+	case 'v':
+		vflag = 1;	/* print startup messages */
+		break;
+	} ARGEND
+}
+
+static void
+savestartup(int argc, char *argv[])
+{
+	int i;
+
+	rebootargc = argc;
+	rebootargv = malloc((argc+1)*sizeof(char*));
+	if(rebootargv == nil)
+		panic("can't save startup args");
+	for(i = 0; i < argc; i++) {
+		rebootargv[i] = strdup(argv[i]);
+		if(rebootargv[i] == nil)
+			panic("can't save startup args");
+	}
+	rebootargv[i] = nil;
+}
+
+void
+putenvq(char *name, char *val, int conf)
+{
+	val = smprint("%q", val);
+	ksetenv(name, val, conf);
+	free(val);
+}
+
+void
+putenvqv(char *name, char **v, int n, int conf)
+{
+	Fmt f;
+	int i;
+	char *val;
+
+	fmtstrinit(&f);
+	for(i=0; i<n; i++)
+		fmtprint(&f, "%s%q", i?" ":"", v[i]);
+	val = fmtstrflush(&f);
+	ksetenv(name, val, conf);
+	free(val);
+}
+
+void
+nofence(void)
+{
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *opt, *p;
+	char *enva[20];
+	int envc;
+
+	if(coherence == nil)
+		coherence = nofence;
+	quotefmtinstall();
+	savestartup(argc, argv);
+	/* set default root now, so either $EMU or -r can override it later */
+	if((p = getenv("INFERNO")) != nil || (p = getenv("ROOT")) != nil)
+		strecpy(rootdir, rootdir+sizeof(rootdir), p);
+	opt = getenv("EMU");
+	if(opt != nil && *opt != '\0') {
+		enva[0] = "emu";
+		envc = tokenize(opt, &enva[1], sizeof(enva)-1) + 1;
+		enva[envc] = 0;
+		option(envc, enva, envusage);
+	}
+	option(argc, argv, usage);
+	eve = strdup("inferno");
+
+	opt = "interp";
+	if(cflag)
+		opt = "compile";
+
+	if(vflag)
+		print("Inferno %s main (pid=%d) %s\n", VERSION, getpid(), opt);
+
+	libinit(imod);
+}
+
+void
+emuinit(void *imod)
+{
+	Osenv *e;
+	char *wdir;
+
+	e = up->env;
+	e->pgrp = newpgrp();
+	e->fgrp = newfgrp(nil);
+	e->egrp = newegrp();
+	e->errstr = e->errbuf0;
+	e->syserrstr = e->errbuf1;
+	e->user = strdup("");
+
+	links();
+	chandevinit();
+
+	if(waserror())
+		panic("setting root and dot");
+
+	e->pgrp->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(e->pgrp->slash->name);
+	e->pgrp->slash->name = newcname("/");
+	e->pgrp->dot = cclone(e->pgrp->slash);
+	poperror();
+
+	strcpy(up->text, "main");
+
+	if(kopen("#c/cons", OREAD) != 0)
+		fprint(2, "failed to make fd0 from #c/cons: %r\n");
+	kopen("#c/cons", OWRITE);
+	kopen("#c/cons", OWRITE);
+
+	/* the setid cannot precede the bind of #U */
+	kbind("#U", "/", MAFTER|MCREATE);
+	setid(eve, 0);
+	kbind("#^", "/dev", MBEFORE);	/* snarf */
+	kbind("#^", "/chan", MBEFORE);
+	kbind("#m", "/dev", MBEFORE);	/* pointer */
+	kbind("#c", "/dev", MBEFORE);
+	kbind("#p", "/prog", MREPL);
+	kbind("#d", "/fd", MREPL);
+	kbind("#I", "/net", MAFTER);	/* will fail on Plan 9 */
+
+	/* BUG: we actually only need to do these on Plan 9 */
+	kbind("#U/dev", "/dev", MAFTER);
+	kbind("#U/net", "/net", MAFTER);
+	kbind("#U/net.alt", "/net.alt", MAFTER);
+
+	if(cputype != nil)
+		ksetenv("cputype", cputype, 1);
+	putenvqv("emuargs", rebootargv, rebootargc, 1);
+	putenvq("emuroot", rootdir, 1);
+	ksetenv("emuhost", hosttype, 1);
+	wdir = malloc(1024);
+	if(wdir != nil){
+		if(getwd(wdir, 1024) != nil)
+			putenvq("emuwdir", wdir, 1);
+		free(wdir);
+	}
+
+	kproc("main", disinit, imod, KPDUPFDG|KPDUPPG|KPDUPENVG);
+
+	for(;;)
+		ospause(); 
+}
+
+void
+errorf(char *fmt, ...)
+{
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	error(buf);
+}
+
+void
+error(char *err)
+{
+	if(err != up->env->errstr && up->env->errstr != nil)
+		kstrcpy(up->env->errstr, err, ERRMAX);
+//	ossetjmp(up->estack[NERR-1]);
+	nexterror();
+}
+
+void
+exhausted(char *resource)
+{
+	char buf[64];
+	int n;
+
+	n = snprint(buf, sizeof(buf), "no free %s\n", resource);
+	iprint(buf);
+	buf[n-1] = 0;
+	error(buf);
+}
+
+void
+nexterror(void)
+{
+	oslongjmp(nil, up->estack[--up->nerr], 1);
+}
+
+/* for dynamic modules - functions not macros */
+
+void*
+waserr(void)
+{
+	up->nerr++;
+	return up->estack[up->nerr-1];
+}
+
+void
+poperr(void)
+{
+	up->nerr--;
+}
+
+char*
+enverror(void)
+{
+	return up->env->errstr;
+}
+
+void
+panic(char *fmt, ...)
+{
+	va_list arg;
+	char buf[512];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "panic: %s\n", buf);
+	if(sflag)
+		abort();
+
+	cleanexit(0);
+}
+
+int
+iprint(char *fmt, ...)
+{
+
+	int n;	
+	va_list va;
+	char buf[1024];
+
+	va_start(va, fmt);
+	n = vseprint(buf, buf+sizeof buf, fmt, va) - buf;
+	va_end(va);
+
+	write(1, buf, n);
+	return 1;
+}
+
+void
+_assert(char *fmt)
+{
+	panic("assert failed: %s", fmt);
+}
+
+/*
+ * mainly for libmp
+ */
+void
+sysfatal(char *fmt, ...)
+{
+	va_list arg;
+	char buf[64];
+
+	va_start(arg, fmt);
+	vsnprint(buf, sizeof(buf), fmt, arg);
+	va_end(arg);
+	error(buf);
+}
+
+void
+oserror(void)
+{
+	oserrstr(up->env->errstr, ERRMAX);
+	error(up->env->errstr);
+}
--- /dev/null
+++ b/emu/port/master
@@ -1,0 +1,36 @@
+# do not edit; automatically generated
+
+%	mem
+*	indir
+/	root
+A	audio
+C	cmd
+D	ssl
+F	tinyfs
+I	ip
+M	mnt
+P	prof
+U	fs
+^	snarf
+a	arch
+c	cons
+d	dup
+e	env
+i	draw
+m	pointer
+p	prog
+s	srv
+|	pipe
+τ	tk
+₪	srv9
+
+α	local use
+β	local use
+γ	local use
+δ	local use
+ε	local use
+ζ	local use
+η	local use
+θ	local use
+ι	local use
+κ	local use
--- /dev/null
+++ b/emu/port/master.local
@@ -1,0 +1,10 @@
+α	local use
+β	local use
+γ	local use
+δ	local use
+ε	local use
+ζ	local use
+η	local use
+θ	local use
+ι	local use
+κ	local use
--- /dev/null
+++ b/emu/port/mkdevc
@@ -1,0 +1,99 @@
+$AWK '
+BEGIN{
+		if(ARGC < 2)
+			exit
+}
+
+/^$/{
+		next;
+}
+/^#/{
+		next;
+}
+collect && /^[^	\t]/{
+		collect = 0;
+}
+collect && section ~ "dev"{
+		dev[ndev++] = $1;
+}
+collect && section ~ "ip"{
+		ip[nip++] = $1;
+}
+collect && section ~ "link"{
+		link[nlink++] = $1;
+}
+collect && section ~ "mod"{
+		mod[nmod++] = $1;
+}
+collect && section ~ "misc"{
+		misc[nmisc++] = $1;
+}
+collect && section ~ "port"{
+		port[nport++] = $0;
+}
+collect && section ~ "code"{
+		code[ncode++] = $0;
+}
+$0 ~ /^[^ \t]/{
+		if($0 ~ "(code|dev|ip|lib|link|mod|misc|port|root)"){
+			section = $0;
+			collect = 1;
+		}
+		next;
+}
+
+END{
+		if(ARGC < 2)
+			exit "usage"
+
+		printf "#include \"dat.h\"\n"
+		printf "#include \"fns.h\"\n"
+		printf "#include \"error.h\"\n"
+		printf "#include \"interp.h\"\n\n\n"
+		printf "#include \"%s.root.h\"\n\n", ARGV[1];
+
+		nildev = 8;
+		printf "ulong ndevs = %s;\n\n", ndev+nildev
+		for(i = 0; i < ndev; i++)
+			printf "extern Dev %sdevtab;\n", dev[i];
+		printf "Dev* devtab[]={\n"
+		for(i = 0; i < ndev; i++)
+			printf "\t&%sdevtab,\n", dev[i];
+		for(i = 0; i < nildev; i++)
+			printf("\tnil,\n");
+		printf "\tnil,\n};\n\n";
+
+
+		for(i = 0; i < nlink; i++)
+			printf "extern void %slink(void);\n", link[i];
+
+		printf "void links(void){\n";
+		for(i = 0; i < nlink; i++)
+			printf "\t%slink();\n", link[i];
+		printf "}\n\n";
+
+		for(i = 0; i < nmod; i++)
+			printf "extern void %smodinit(void);\n", mod[i];
+		printf "void modinit(void){\n";
+		for(i = 0; i < nmod; i++)
+			printf "\t%smodinit();\n",mod[i];
+		printf "}\n\n";
+	
+		if(nip){
+			printf "#include \"../ip/ip.h\"\n";
+			for(i = 0; i < nip; i++)
+				printf "extern void %sinit(Fs*);\n", ip[i];
+			printf "void (*ipprotoinit[])(Fs*) = {\n";
+			for(i = 0; i < nip; i++)
+				printf "\t%sinit,\n", ip[i];
+			printf "\tnil,\n};\n\n";
+		}
+
+		for(i = 0; i < ncode; i++)
+			printf "%s\n", code[i];
+
+		printf "char* conffile = \"%s\";\n", ARGV[1];
+		printf "ulong kerndate = KERNDATE;\n";
+
+		exit
+}' $*
--- /dev/null
+++ b/emu/port/mkdevlist
@@ -1,0 +1,75 @@
+$AWK '
+BEGIN{
+		var["init"] = "INIT=";
+		var["ip"] = "IP=";
+		var["lib"] = "LIBS=";
+		var["root"] = "ROOTFILES=";
+		infernoroot = ENVIRON["ROOT"];
+}
+/^$/{		next;
+}
+/^#/{		next;
+}
+/^env/{
+		inenv = 1;
+		next;
+}
+inenv != 0 && /^[ 	]/{
+		sub("^[ 	]*", "", $0)
+		printf "%s\n", $0
+		next
+}
+
+/^(code|dev|init|ip|lib|link|mod|misc|port|root)/{
+		inenv = 0;
+		type = $1;
+		next;
+}
+/^[^ 	]/	{
+		inenv = 0;
+}
+type && /^[ 	]/{
+		if(type == "code")
+			next;
+		if(type == "root"){
+			if (NF > 1)
+				file = $2;
+			else if ($1 == "/osinit.dis")
+				next;	# handled via explicit dependency
+			else
+				file = $1;
+			if(rootfile[file] == 0){
+				var[type] = var[type] " " infernoroot file;
+				rootfile[file]++;
+			}
+			next;
+		}
+		if(type == "init" || type == "lib"){
+			var[type] = var[type] " " $1;
+			next;
+		}
+		file = $1 "'.$O'"
+		if(type == "port")
+			port[file]++;
+		else if(type == "dev")
+			obj["dev" file]++;
+		else if(type != "mod")
+			obj[file]++;
+		for(i = 2; i <= NF; i++){
+			if($i !~ "^[+=-].*")
+				obj[$i "'.$O'"]++;
+		}
+		next;
+}
+END{
+		x = ""
+		for(i in obj)
+			x = x " " i
+		printf "DEVS=%s\n", x;
+		x = ""
+		for(i in port)
+			x = x " " i
+		printf "PORT=%s\n", x
+		for(v in var)
+			printf "%s\n", var[v]
+}' $*
--- /dev/null
+++ b/emu/port/mkfile
@@ -1,0 +1,15 @@
+# If the existence of this mkfile screws something up, rename it.	-rsc
+
+master:DV:
+	{
+	echo '# do not edit; automatically generated'
+	echo
+	echo '
+		X , s/Dev (.*)devtab.*{.*\n	L?''(.*)''/DEV \1 \2\n/
+		X ,x g/^DEV/ p
+	' | sam -d ../*/dev*.c >[2]/dev/null |
+	awk '/^DEV/ { printf("%s\t%s\n", $3, $2); }' |
+	sort -u
+	echo
+	cat master.local
+	} >master
--- /dev/null
+++ b/emu/port/mkroot
@@ -1,0 +1,121 @@
+$AWK '
+BEGIN{
+		if (ARGC < 2)
+			exit "usage";
+
+		conf = ARGV[1];
+		infernoroot = ENVIRON["ROOT"];
+		init = ENVIRON["INIT"];
+		data2c = ENVIRON["DATA2C"];
+		if(data2c == "")
+			data2c = "data2c"
+		nroot = 0;
+}
+/^$/{
+		next;
+}
+/^#/{
+		next;
+}
+collect && /^[^	\t]/{
+		collect = 0;
+}
+collect && section ~ "root"{
+		dst[nroot] = $1;
+		if (NF > 1)
+			src[nroot] = infernoroot $2;
+		else if (dst[nroot] == "/osinit.dis")
+			src[nroot] = infernoroot "/dis/" init ".dis";
+		else
+			src[nroot] = infernoroot $1;
+		for(i=0; i<nroot; i++)
+			if(dst[i] == dst[nroot])
+				break;
+		if(i == nroot)
+			nroot++;
+}
+$0 ~ /^[^ \t]/{
+		if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){
+			section = $0;
+			collect = 1;
+		}
+		next;
+}
+END{
+		rootdata = conf ".root.c";
+		system("rm -f " rootdata);
+		print("/* Generated by mkroot */") >rootdata;
+		close(rootdata);
+		isdir[0] = 1;
+		dotdot[0] = 0;
+		qid = 1;
+		for (i = 0; i < nroot; i++) {
+			ncomp = split(dst[i], comp, "/");
+			if (comp[1] != "" || ncomp < 2)
+				continue;
+			q = 0;
+			for (j = 2; j <= ncomp; j++) {
+				key = q "/" comp[j];
+				if (walk[key] == 0) {
+					walk[key] = qid;
+					dotdot[qid] = q;
+					q = qid++;
+					name[q] = comp[j];
+					if (j < ncomp)
+						isdir[q] = 1;
+				}
+				else
+					q = walk[key];
+			}
+			if (system("test -d " src[i]) == 0)
+				isdir[q] = 1;
+			else {
+				if (system(data2c " root" q " <" src[i] " >>" rootdata) != 0)
+					exit 1;
+				print("extern unsigned char root" q "code[];");
+				print("extern int root" q "len;");
+			}
+		}
+
+		x = 1;
+		sort[0] = 0;
+		unsort[0] = 0;
+		for (q = 0; q < qid; q++) {
+			if (isdir[q]) {
+				nchild[q] = 0;
+				for (q2 = 1; q2 < qid; q2++) {
+					if (dotdot[q2] == q) {
+						if (nchild[q]++ == 0)
+							child0[q] = x;
+						sort[q2] = x++;
+						unsort[sort[q2]] = q2;
+					}
+				}
+			}
+		}
+
+		print("int rootmaxq = " qid ";");
+
+		print("Dirtab roottab[" qid "] = {");
+		for (oq = 0; oq < qid; oq++) {
+			q = unsort[oq];
+			if (!isdir[q])
+				print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,");
+			else
+				print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,");
+		}
+		print("};");
+
+		print("Rootdata rootdata[" qid "] = {");
+		for (oq = 0; oq < qid; oq++) {
+			q = unsort[oq];
+			if (!isdir[q])
+				print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,");
+			else if (nchild[q])
+				print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,");
+			else
+				print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,");
+		}
+		print("};");
+}
+' $1 >$1.root.h
--- /dev/null
+++ b/emu/port/parse.c
@@ -1,0 +1,111 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+/*
+ * Generous estimate of number of fields, including terminal nil pointer
+ */
+static int
+ncmdfield(char *p, int n)
+{
+	int white, nwhite;
+	char *ep;
+	int nf;
+
+	if(p == nil)
+		return 1;
+
+	nf = 0;
+	ep = p+n;
+	white = 1;	/* first text will start field */
+	while(p < ep){
+		nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0);	/* UTF is irrelevant */
+		if(white && !nwhite)	/* beginning of field */
+			nf++;
+		white = nwhite;
+	}
+	return nf+1;	/* +1 for nil */
+}
+
+/*
+ *  parse a command written to a device
+ */
+Cmdbuf*
+parsecmd(char *p, int n)
+{
+	Cmdbuf *volatile cb;
+	int nf;
+	char *sp;
+
+	nf = ncmdfield(p, n);
+
+	/* allocate Cmdbuf plus string pointers plus copy of string including \0 */
+	sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1);
+	cb = (Cmdbuf*)sp;
+	cb->f = (char**)(&cb[1]);
+	cb->buf = (char*)(&cb->f[nf]);
+
+	if(up!=nil && waserror()){
+		free(cb);
+		nexterror();
+	}
+	memmove(cb->buf, p, n);
+	if(up != nil)
+		poperror();
+
+	/* dump new line and null terminate */
+	if(n > 0 && cb->buf[n-1] == '\n')
+		n--;
+	cb->buf[n] = '\0';
+
+	cb->nf = tokenize(cb->buf, cb->f, nf-1);
+	cb->f[cb->nf] = nil;
+
+	return cb;
+}
+
+/*
+ * Reconstruct original message, for error diagnostic
+ */
+void
+cmderror(Cmdbuf *cb, char *s)
+{
+	int i;
+	char *p, *e;
+
+	p = up->genbuf;
+	e = p+ERRMAX-10;
+	p = seprint(p, e, "%s \"", s);
+	for(i=0; i<cb->nf; i++){
+		if(i > 0)
+			p = seprint(p, e, " ");
+		p = seprint(p, e, "%q", cb->f[i]);
+	}
+	strcpy(p, "\"");
+	error(up->genbuf);
+}
+
+/*
+ * Look up entry in table
+ */
+Cmdtab*
+lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
+{
+	int i;
+	Cmdtab *ct;
+
+	if(cb->nf == 0)
+		error("empty control message");
+
+	for(ct = ctab, i=0; i<nctab; i++, ct++){
+		if(strcmp(ct->cmd, "*") !=0)	/* wildcard always matches */
+		if(strcmp(ct->cmd, cb->f[0]) != 0)
+			continue;
+		if(ct->narg != 0 && ct->narg != cb->nf)
+			cmderror(cb, Ecmdargs);
+		return ct;
+	}
+
+	cmderror(cb, "unknown control message");
+	return nil;
+}
--- /dev/null
+++ b/emu/port/pgrp.c
@@ -1,0 +1,264 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+static Ref	pgrpid;
+static Ref	mountid;
+
+Pgrp*
+newpgrp(void)
+{
+	Pgrp *p;
+
+	p = malloc(sizeof(Pgrp));
+	if(p == nil)
+		error(Enomem);
+	p->r.ref = 1;
+	p->pgrpid = incref(&pgrpid);
+	p->progmode = 0644;
+	return p;
+}
+
+void
+closepgrp(Pgrp *p)
+{
+	Mhead **h, **e, *f, *next;
+
+	if(p == nil || decref(&p->r) != 0)
+		return;
+
+	wlock(&p->ns);
+	p->pgrpid = -1;
+	e = &p->mnthash[MNTHASH];
+	for(h = p->mnthash; h < e; h++) {
+		for(f = *h; f; f = next) {
+			wlock(&f->lock);
+			cclose(f->from);
+			mountfree(f->mount);
+			f->mount = nil;
+			next = f->hash;
+			wunlock(&f->lock);
+			putmhead(f);
+		}
+	}
+	wunlock(&p->ns);
+	cclose(p->dot);
+	cclose(p->slash);
+	free(p);
+}
+
+void
+pgrpinsert(Mount **order, Mount *m)
+{
+	Mount *f;
+
+	m->order = 0;
+	if(*order == 0) {
+		*order = m;
+		return;
+	}
+	for(f = *order; f; f = f->order) {
+		if(m->mountid < f->mountid) {
+			m->order = f;
+			*order = m;
+			return;
+		}
+		order = &f->order;
+	}
+	*order = m;
+}
+
+/*
+ * pgrpcpy MUST preserve the mountid allocation order of the parent group
+ */
+void
+pgrpcpy(Pgrp *to, Pgrp *from)
+{
+	int i;
+	Mount *n, *m, **link, *order;
+	Mhead *f, **tom, **l, *mh;
+
+	wlock(&from->ns);
+	if(waserror()){
+		wunlock(&from->ns);
+		nexterror();
+	}
+	order = 0;
+	tom = to->mnthash;
+	for(i = 0; i < MNTHASH; i++) {
+		l = tom++;
+		for(f = from->mnthash[i]; f; f = f->hash) {
+			rlock(&f->lock);
+			if(waserror()){
+				runlock(&f->lock);
+				nexterror();
+			}
+			mh = malloc(sizeof(Mhead));
+			if(mh == nil)
+				error(Enomem);
+			mh->from = f->from;
+			mh->r.ref = 1;
+			incref(&mh->from->r);
+			*l = mh;
+			l = &mh->hash;
+			link = &mh->mount;
+			for(m = f->mount; m; m = m->next) {
+				n = newmount(mh, m->to, m->mflag, m->spec);
+				m->copy = n;
+				pgrpinsert(&order, m);
+				*link = n;
+				link = &n->next;	
+			}
+			poperror();
+			runlock(&f->lock);
+		}
+	}
+	/*
+	 * Allocate mount ids in the same sequence as the parent group
+	 */
+	lock(&mountid.lk);
+	for(m = order; m; m = m->order)
+		m->copy->mountid = mountid.ref++;
+	unlock(&mountid.lk);
+
+	to->progmode = from->progmode;
+	to->slash = cclone(from->slash);
+	to->dot = cclone(from->dot);
+	to->nodevs = from->nodevs;
+	poperror();
+	wunlock(&from->ns);
+}
+
+Fgrp*
+newfgrp(Fgrp *old)
+{
+	Fgrp *new;
+	int n;
+
+	new = malloc(sizeof(Fgrp));
+	if(new == nil)
+		error(Enomem);
+	new->r.ref = 1;
+	n = DELTAFD;
+	if(old != nil){
+		lock(&old->l);
+		if(old->maxfd >= n)
+			n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+		new->maxfd = old->maxfd;
+		unlock(&old->l);
+	}
+	new->nfd = n;
+	new->fd = malloc(n*sizeof(Chan*));
+	if(new->fd == nil){
+		free(new);
+		error(Enomem);
+	}
+	return new;
+}
+
+Fgrp*
+dupfgrp(Fgrp *f)
+{
+	int i;
+	Chan *c;
+	Fgrp *new;
+	int n;
+
+	new = malloc(sizeof(Fgrp));
+	if(new == nil)
+		error(Enomem);
+	new->r.ref = 1;
+	lock(&f->l);
+	n = DELTAFD;
+	if(f->maxfd >= n)
+		n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
+	new->nfd = n;
+	new->fd = malloc(n*sizeof(Chan*));
+	if(new->fd == nil){
+		unlock(&f->l);
+		free(new);
+		error(Enomem);
+	}
+	new->maxfd = f->maxfd;
+	new->minfd = f->minfd;
+	for(i = 0; i <= f->maxfd; i++) {
+		if(c = f->fd[i]){
+			incref(&c->r);
+			new->fd[i] = c;
+		}
+	}
+	unlock(&f->l);
+
+	return new;
+}
+
+void
+closefgrp(Fgrp *f)
+{
+	int i;
+	Chan *c;
+
+	if(f != nil && decref(&f->r) == 0) {
+		for(i = 0; i <= f->maxfd; i++)
+			if(c = f->fd[i])
+				cclose(c);
+		free(f->fd);
+		free(f);
+	}
+}
+
+Mount*
+newmount(Mhead *mh, Chan *to, int flag, char *spec)
+{
+	Mount *m;
+
+	m = malloc(sizeof(Mount));
+	if(m == nil)
+		error(Enomem);
+	m->to = to;
+	m->head = mh;
+	incref(&to->r);
+	m->mountid = incref(&mountid);
+	m->mflag = flag;
+	if(spec != 0)
+		kstrdup(&m->spec, spec);
+
+	return m;
+}
+
+void
+mountfree(Mount *m)
+{
+	Mount *f;
+
+	while(m) {
+		f = m->next;
+		cclose(m->to);
+		m->mountid = 0;
+		free(m->spec);
+		free(m);
+		m = f;
+	}
+}
+
+void
+closesigs(Skeyset *s)
+{
+	int i;
+
+	if(s == nil || decref(&s->r) != 0)
+		return;
+	for(i=0; i<s->nkey; i++)
+		freeskey(s->keys[i]);
+	free(s);
+}
+
+void
+freeskey(Signerkey *key)
+{
+	if(key == nil || decref(&key->r) != 0)
+		return;
+	free(key->owner);
+	(*key->pkfree)(key->pk);
+	free(key);
+}
--- /dev/null
+++ b/emu/port/portmkfile
@@ -1,0 +1,170 @@
+PORTHFILES=\
+	$ROOT/$OBJDIR/include/lib9.h\
+	$ROOT/$OBJDIR/include/emu.h\
+	$ROOT/include/fcall.h\
+	$ROOT/include/interp.h\
+	$ROOT/include/draw.h\
+	../port/error.h\
+	../port/dat.h\
+	../port/fns.h\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBFILES=${LIBS:%=$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a}
+
+$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a:N:	lib%.a
+
+%.$O:	%.s
+	$AS $ASFLAGS $stem.s
+
+%.$O:	%.S$MACOSINF
+	$AS $ASFLAGS $stem.S
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:		../port/%.c
+		$CC $CFLAGS -I. ../port/$stem.c
+
+%.$O:		../ip/%.c
+		$CC $CFLAGS -I. ../ip/$stem.c
+
+&.$O:		$HFILES $PORTHFILES
+
+$INSTALLDIR/%: %
+	cp $stem $INSTALLDIR/$stem
+
+installall:V:	install-$SHELLTYPE
+all:V:		default-$SHELLTYPE
+
+# Plan 9 only (requires the compiler)
+acid:V: i$CONF.acid
+
+i$CONF.acid:	i$CONF
+	{
+		for (i in `{srclist -ec -r $ROOT/ i$CONF}) {
+			echo '//FILE: ' $i
+			$CC -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp '-DKERNDATE='$KERNDATE -a $i
+		}
+		echo 'include ("inferno");'
+	} >i$CONF.acid
+
+lib%.a:V:	$SHELLTYPE-lib%.a
+
+rc-lib%.a nt-lib%.a:VQ:
+		echo '@{builtin cd' $ROOT/lib$stem '; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+		@{builtin cd $ROOT/lib$stem; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+		echo "(cd $ROOT/lib$stem ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+		(cd $ROOT/lib$stem; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install)
+
+%-rc %-nt:V:
+		for(i in $CONFLIST)
+			mk 'CONF='$i $MKFLAGS $stem
+
+%-sh:V:
+		for i in $CONFLIST
+		do
+			mk 'CONF='$i $MKFLAGS $stem
+		done
+
+clean:V:	cleanconf-$SHELLTYPE
+		rm -f *.[$OS] *.root.[shc] errstr.h *.out *.obj
+
+cleanconf-sh:V:
+		for i in $CONFLIST $CLEANCONFLIST
+		do
+			rm -f $i.c [$OS].$i i$i i$i.* $i.ver
+		done
+
+cleanconf-rc:V:
+		for(i in $CONFLIST $CLEANCONFLIST)
+			rm -f $i.c [$OS].$i i$i i$i.* $i.ver
+
+cleanconf-nt:V:
+		for(i in $CONFLIST $CLEANCONFLIST)
+			rm -f $i.c [$OS].$i i$i i$i.* $i.ver $i.exe
+
+nuke-sh:QV:
+		for i in $LIBDIRS
+		do
+			echo "(cd $ROOT/lib$i ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)"
+			(cd $ROOT/lib$i; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke)
+		done
+
+nuke-rc nuke-nt:QV:
+		for (i in $LIBDIRS)
+		{
+			echo '@{cd $ROOT/lib$i ; mk $MKFLAGS SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}'
+			@{cd $ROOT/lib$i; mk $MKFLAGS 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke}
+		}
+
+nuke:V:		clean nuke-$SHELLTYPE
+		rm -f srv.h srvm.h
+
+$CONF.c:	../port/mkdevc $CONF
+		$SHELLNAME ../port/mkdevc $CONF > $CONF.c
+
+errstr.h:	../port/error.h
+		sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h
+
+../init/%.dis:	../init/%.b
+		cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis
+
+$ROOT/libinterp/runt.h:
+		cd $ROOT/libinterp
+		mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h
+
+INTERP=$ROOT/include/interp.h
+RUNT=$ROOT/libinterp/runt.h		# for culling dependencies
+
+alloc.$O: ../../include/pool.h\
+		$INTERP
+devcons.$O:	$ROOT/include/version.h
+devdraw.$O:	$ROOT/include/draw.h\
+		$ROOT/include/memdraw.h\
+		$ROOT/include/memlayer.h
+devmem.$O:	$INTERP
+devpipe.$O:	$INTERP
+devprof.$O:	$INTERP
+devprog.$O:	$ROOT/libinterp/runt.h\
+			$INTERP
+devsign.$O:	$INTERP
+devsign.$O: $ROOT/libkeyring/keys.h
+devsrv.$O:	$ROOT/libinterp/runt.h\
+		$INTERP
+devtk.$O:	$INTERP
+dis.$O:	$INTERP
+discall.$O:	$INTERP
+exception.$O:	$INTERP
+inferno.$O:	$ROOT/libinterp/runt.h\
+		$INTERP
+devmnt.$O: ../../include/fcall.h
+main.$O:	$ROOT/include/version.h
+main.$O:	../port/error.h\
+		$INTERP
+proc.$O:	errstr.h\
+		$INTERP
+srv.$O:	$INTERP
+devroot.$O:	errstr.h
+latin1.$O:	../port/latin1.h
+netif.$O:	../port/netif.h
+devprog.$O:	$RUNT
+devsrv.$O:	$RUNT
+exception.$O:	$RUNT
+inferno.$O:	$RUNT
+ipif-$SYSTARG.$O devip.$O:		../port/ip.h
+
+devroot.$O:	$CONF.root.h
+$CONF.$O:	$CONF.root.h
+$CONF.root.c $CONF.root.h: $CONF ../port/mkroot $ROOTFILES
+	$SHELLNAME ../port/mkroot $CONF
+
+audio.c:N:	../port/audio-tbls.c
+audio.c:N:	../port/audio.h
+
+srv.$O:			srv.h srvm.h
+srv.h srvm.h:D:		$ROOT/module/srvrunt.b $ROOT/module/srv.m
+			rm -f $alltarget
+			limbo -a -I$ROOT/module $ROOT/module/srvrunt.b >srv.h
+			limbo -t Srv -I$ROOT/module $ROOT/module/srvrunt.b >srvm.h
--- /dev/null
+++ b/emu/port/print.c
@@ -1,0 +1,23 @@
+#include "dat.h"
+#include "fns.h"
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+	lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+	unlock(&fmtl);
+}
+
+int
+_efgfmt(Fmt *f)
+{
+	USED(f);
+	return -1;
+}
--- /dev/null
+++ b/emu/port/proc.c
@@ -1,0 +1,239 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"interp.h"
+
+Proc*
+newproc(void)
+{
+	Proc *p;
+
+	p = malloc(sizeof(Proc));
+	if(p == nil)
+		return nil;
+
+	p->type = Unknown;
+	p->killed = 0;
+	p->swipend = 0;
+	p->env = &p->defenv;
+	kstrdup(&p->env->user, "*nouser");
+	p->env->errstr = p->env->errbuf0;
+	p->env->syserrstr = p->env->errbuf1;
+	addprog(p);
+
+	return p;
+}
+
+void
+Sleep(Rendez *r, int (*f)(void*), void *arg)
+{
+	lock(&up->rlock);
+	lock(&r->l);
+
+	/*
+	 * if interrupted or condition happened, never mind
+	 */
+	if(up->killed || f(arg)) {
+		unlock(&r->l);
+	}else{
+		if(r->p != nil)
+			panic("double sleep pc=0x%lux %s[%lud] %s[%lud] r=0x%lux\n", getcallerpc(&r), r->p->text, r->p->pid, up->text, up->pid, r);
+
+		r->p = up;
+		unlock(&r->l);
+		up->swipend = 0;
+		up->r = r;	/* for swiproc */
+		unlock(&up->rlock);
+
+		osblock();
+
+		lock(&up->rlock);
+		up->r = nil;
+	}
+
+	if(up->killed || up->swipend) {
+		up->killed = 0;
+		up->swipend = 0;
+		unlock(&up->rlock);
+		error(Eintr);
+	}
+	unlock(&up->rlock);
+}
+
+int
+Wakeup(Rendez *r)
+{
+	Proc *p;
+
+	lock(&r->l);
+	p = r->p;
+	if(p != nil) {
+		r->p = nil;
+		osready(p);
+	}
+	unlock(&r->l);
+	return p != nil;
+}
+
+void
+swiproc(Proc *p, int interp)
+{
+	Rendez *r;
+	
+	if(p == nil)
+		return;
+
+	/*
+	 * Pull out of emu Sleep
+	 */
+	lock(&p->rlock);
+	if(!interp)
+		p->killed = 1;
+	r = p->r;
+	if(r != nil) {
+		lock(&r->l);
+		if(r->p == p){
+			p->swipend = 1;
+			r->p = nil;
+			osready(p);
+		}
+		unlock(&r->l);
+		unlock(&p->rlock);
+		return;
+	}
+	unlock(&p->rlock);
+
+	/*
+	 * Maybe pull out of Host OS
+	 */
+	lock(&p->sysio);
+	if(p->syscall && p->intwait == 0) {
+		p->swipend = 1;
+		p->intwait = 1;
+		unlock(&p->sysio);
+		oshostintr(p);
+		return;	
+	}
+	unlock(&p->sysio);
+}
+
+void
+notkilled(void)
+{
+	lock(&up->rlock);
+	up->killed = 0;
+	unlock(&up->rlock);
+}
+
+void
+osenter(void)
+{
+	up->syscall = 1;
+}
+
+void
+osleave(void)
+{
+	int r;
+
+	lock(&up->rlock);
+	r = up->swipend;
+	up->swipend = 0;
+	unlock(&up->rlock);
+
+	lock(&up->sysio);
+	up->syscall = 0;
+	unlock(&up->sysio);
+
+	/* Cleared by the signal/note/exception handler */
+	while(up->intwait)
+		osyield();
+
+	if(r != 0)
+		error(Eintr);
+}
+
+void
+rptwakeup(void *o, void *ar)
+{
+	Rept *r;
+
+	r = ar;
+	if(r == nil)
+		return;
+	lock(&r->l);
+	r->o = o;
+	unlock(&r->l);
+	Wakeup(&r->r);
+}
+
+static int
+rptactive(void *a)
+{
+	Rept *r = a;
+	int i;
+	lock(&r->l);
+	i = r->active(r->o);
+	unlock(&r->l);
+	return i;
+}
+
+static void
+rproc(void *a)
+{
+	long now, then;
+	ulong t;
+	int i;
+	void *o;
+	Rept *r;
+
+	up->env->fgrp = newfgrp(nil);
+	r = a;
+	t = (ulong)r->t;
+
+Wait:
+	Sleep(&r->r, rptactive, r);
+	lock(&r->l);
+	o = r->o;
+	unlock(&r->l);
+	then = osmillisec();
+	for(;;){
+		osmillisleep(t);
+		now = osmillisec();
+		if(waserror())
+			break;
+		i = r->ck(o, now-then);
+		poperror();
+		if(i == -1)
+			goto Wait;
+		if(i == 0)
+			continue;
+		then = now;
+		acquire();
+		if(waserror()) {
+			release();
+			break;
+		}
+		r->f(o);
+		poperror();
+		release();
+	}
+	pexit("", 0);
+}
+
+void*
+rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*))
+{
+	Rept *r;
+
+	r = mallocz(sizeof(Rept), 1);
+	if(r == nil)
+		return nil;
+	r->t = t;
+	r->active = active;
+	r->ck = ck;
+	r->f = f;
+	r->o = o;
+	kproc(s, rproc, r, KPDUPPG);
+	return r;
+}
--- /dev/null
+++ b/emu/port/qio.c
@@ -1,0 +1,1554 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#define QDEBUG if(0)
+
+/*
+ *  IO queues
+ */
+
+struct Queue
+{
+	Lock	l;
+
+	Block*	bfirst;		/* buffer */
+	Block*	blast;
+
+	int	len;		/* bytes allocated to queue */
+	int	dlen;		/* data bytes in queue */
+	int	limit;		/* max bytes in queue */
+	int	inilim;		/* initial limit */
+	int	state;
+	int	noblock;	/* true if writes return immediately when q full */
+	int	eof;		/* number of eofs read by user */
+
+	void	(*kick)(void*);	/* restart output */
+	void	(*bypass)(void*, Block*);	/* bypass queue altogether */
+	void*	arg;		/* argument to kick */
+
+	QLock	rlock;		/* mutex for reading processes */
+	Rendez	rr;		/* process waiting to read */
+	QLock	wlock;		/* mutex for writing processes */
+	Rendez	wr;		/* process waiting to write */
+
+	char	err[ERRMAX];
+	int	want;
+};
+
+enum
+{
+	Maxatomic	= 32*1024
+};
+
+uint	qiomaxatomic = Maxatomic;
+
+void
+checkb(Block *b, char *msg)
+{
+	if(b->base > b->lim)
+		panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
+	if(b->rp < b->base)
+		panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
+	if(b->wp < b->base)
+		panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
+	if(b->rp > b->lim)
+		panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
+	if(b->wp > b->lim)
+		panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
+}
+
+void
+freeb(Block *b)
+{
+	if(b == nil)
+		return;
+
+	/*
+	 * drivers which perform non cache coherent DMA manage their own buffer 
+	 * pool of uncached buffers and provide their own free routine.
+	 */
+	if(b->free) {
+		b->free(b);
+		return;
+	}
+
+	/* poison the block in case someone is still holding onto it */
+	b->next = (void*)0xdeadbabe;
+	b->rp = (void*)0xdeadbabe;
+	b->wp = (void*)0xdeadbabe;
+	b->lim = (void*)0xdeadbabe;
+	b->base = (void*)0xdeadbabe;
+
+	free(b);
+}
+
+/* 
+ *  free a list of blocks
+ */
+void
+freeblist(Block *b)
+{
+	Block *next;
+
+	for(; b != 0; b = next){
+		next = b->next;
+		b->next = 0;
+		freeb(b);
+	}
+}
+
+ulong padblockoverhead;
+
+/*
+ *  pad a block to the front (or the back if size is negative)
+ */
+Block*
+padblock(Block *bp, int size)
+{
+	int n;
+	Block *nbp;
+
+	if(size >= 0){
+		if(bp->rp - bp->base >= size){
+			bp->rp -= size;
+			return bp;
+		}
+
+		if(bp->next)
+			panic("padblock 0x%luX", getcallerpc(&bp));
+		n = BLEN(bp);
+		padblockoverhead += n;
+		nbp = allocb(size+n);
+		nbp->rp += size;
+		nbp->wp = nbp->rp;
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+		freeb(bp);
+		nbp->rp -= size;
+	} else {
+		size = -size;
+
+		if(bp->next)
+			panic("padblock 0x%luX", getcallerpc(&bp));
+
+		if(bp->lim - bp->wp >= size)
+			return bp;
+
+		n = BLEN(bp);
+		padblockoverhead += n;
+		nbp = allocb(size+n);
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+		freeb(bp);
+	}
+	return nbp;
+}
+
+/*
+ *  return count of bytes in a string of blocks
+ */
+int
+blocklen(Block *bp)
+{
+	int len;
+
+	len = 0;
+	while(bp) {
+		len += BLEN(bp);
+		bp = bp->next;
+	}
+	return len;
+}
+
+/*
+ * return count of space in blocks
+ */
+int
+blockalloclen(Block *bp)
+{
+	int len;
+
+	len = 0;
+	while(bp) {
+		len += BALLOC(bp);
+		bp = bp->next;
+	}
+	return len;
+}
+
+/*
+ *  copy the  string of blocks into
+ *  a single block and free the string
+ */
+Block*
+concatblock(Block *bp)
+{
+	int len;
+	Block *nb, *f;
+
+	if(bp->next == 0)
+		return bp;
+
+	nb = allocb(blocklen(bp));
+	for(f = bp; f; f = f->next) {
+		len = BLEN(f);
+		memmove(nb->wp, f->rp, len);
+		nb->wp += len;
+	}
+	freeblist(bp);
+	return nb;
+}
+
+/*
+ *  make sure the first block has at least n bytes.  If we started with
+ *  less than n bytes, make sure we have exactly n bytes.  devssl.c depends
+ *  on this.
+ */
+Block*
+pullupblock(Block *bp, int n)
+{
+	int i;
+	Block *nbp;
+
+	/*
+	 *  this should almost always be true, the rest it
+	 *  just to avoid every caller checking.
+	 */
+	if(BLEN(bp) >= n)
+		return bp;
+
+	/*
+	 *  if not enough room in the first block,
+	 *  add another to the front of the list.
+	 */
+	if(bp->lim - bp->rp < n){
+		nbp = allocb(n);
+		nbp->next = bp;
+		bp = nbp;
+	}
+
+	/*
+	 *  copy bytes from the trailing blocks into the first
+	 */
+	n -= BLEN(bp);
+	while(nbp = bp->next){
+		i = BLEN(nbp);
+		if(i > n) {
+			memmove(bp->wp, nbp->rp, n);
+			bp->wp += n;
+			nbp->rp += n;
+			return bp;
+		}
+		else {
+			memmove(bp->wp, nbp->rp, i);
+			bp->wp += i;
+			bp->next = nbp->next;
+			nbp->next = 0;
+			freeb(nbp);
+			n -= i;
+			if(n == 0)
+				return bp;
+		}
+	}
+	freeblist(bp);
+	return 0;
+}
+
+/*
+ *  make sure the first block has at least n bytes
+ */
+Block*
+pullupqueue(Queue *q, int n)
+{
+	Block *b;
+
+	if(BLEN(q->bfirst) >= n)
+		return q->bfirst;
+	q->bfirst = pullupblock(q->bfirst, n);
+	for(b = q->bfirst; b != nil && b->next != nil; b = b->next)
+		;
+	q->blast = b;
+	return q->bfirst;
+}
+
+/*
+ *  trim to len bytes starting at offset
+ */
+Block *
+trimblock(Block *bp, int offset, int len)
+{
+	ulong l;
+	Block *nb, *startb;
+
+	if(blocklen(bp) < offset+len) {
+		freeblist(bp);
+		return nil;
+	}
+
+	while((l = BLEN(bp)) < offset) {
+		offset -= l;
+		nb = bp->next;
+		bp->next = nil;
+		freeb(bp);
+		bp = nb;
+	}
+
+	startb = bp;
+	bp->rp += offset;
+
+	while((l = BLEN(bp)) < len) {
+		len -= l;
+		bp = bp->next;
+	}
+
+	bp->wp -= (BLEN(bp) - len);
+
+	if(bp->next) {
+		freeblist(bp->next);
+		bp->next = nil;
+	}
+
+	return startb;
+}
+
+/*
+ *  copy 'count' bytes into a new block
+ */
+Block*
+copyblock(Block *bp, int count)
+{
+	int l;
+	Block *nbp;
+
+	nbp = allocb(count);
+	for(; count > 0 && bp != 0; bp = bp->next){
+		l = BLEN(bp);
+		if(l > count)
+			l = count;
+		memmove(nbp->wp, bp->rp, l);
+		nbp->wp += l;
+		count -= l;
+	}
+	if(count > 0){
+		memset(nbp->wp, 0, count);
+		nbp->wp += count;
+	}
+
+	return nbp;
+}
+
+Block*
+adjustblock(Block* bp, int len)
+{
+	int n;
+	Block *nbp;
+
+	if(len < 0){
+		freeb(bp);
+		return nil;
+	}
+
+	if(bp->rp+len > bp->lim){
+		nbp = copyblock(bp, len);
+		freeblist(bp);
+		QDEBUG checkb(nbp, "adjustblock 1");
+
+		return nbp;
+	}
+
+	n = BLEN(bp);
+	if(len > n)
+		memset(bp->wp, 0, len-n);
+	bp->wp = bp->rp+len;
+	QDEBUG checkb(bp, "adjustblock 2");
+
+	return bp;
+}
+
+/*
+ *  throw away up to count bytes from a
+ *  list of blocks.  Return count of bytes
+ *  thrown away.
+ */
+int
+pullblock(Block **bph, int count)
+{
+	Block *bp;
+	int n, bytes;
+
+	bytes = 0;
+	if(bph == nil)
+		return 0;
+
+	while(*bph != nil && count != 0) {
+		bp = *bph;
+		n = BLEN(bp);
+		if(count < n)
+			n = count;
+		bytes += n;
+		count -= n;
+		bp->rp += n;
+		if(BLEN(bp) == 0) {
+			*bph = bp->next;
+			bp->next = nil;
+			freeb(bp);
+		}
+	}
+	return bytes;
+}
+
+/*
+ *  allocate queues and blocks (round data base address to 64 bit boundary)
+ */
+Block*
+iallocb(int size)
+{
+	Block *b;
+	ulong addr;
+
+	b = kmalloc(sizeof(Block)+size);
+	if(b == 0)
+		return 0;
+	memset(b, 0, sizeof(Block));
+
+	addr = (ulong)b + sizeof(Block);
+	b->base = (uchar*)addr;
+	b->lim = b->base + size;
+	b->rp = b->base;
+	b->wp = b->rp;
+
+	return b;
+}
+
+
+/*
+  *  call error if iallocb fails
+ */
+Block*
+allocb(int size)
+{
+	Block *b;
+
+	b = iallocb(size);
+	if(b == 0)
+		exhausted("allocb");
+	return b;
+}
+	
+
+/*
+ *  get next block from a queue, return null if nothing there
+ */
+Block*
+qget(Queue *q)
+{
+	int dowakeup;
+	Block *b;
+
+	/* sync with qwrite */
+	lock(&q->l);
+
+	b = q->bfirst;
+	if(b == 0){
+		q->state |= Qstarve;
+		unlock(&q->l);
+		return 0;
+	}
+	q->bfirst = b->next;
+	b->next = 0;
+	q->len -= BALLOC(b);
+	q->dlen -= BLEN(b);
+	QDEBUG checkb(b, "qget");
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	unlock(&q->l);
+
+	if(dowakeup)
+		Wakeup(&q->wr);
+
+	return b;
+}
+
+/*
+ *  throw away the next 'len' bytes in the queue
+ */
+int
+qdiscard(Queue *q, int len)
+{
+	Block *b;
+	int dowakeup, n, sofar;
+
+	lock(&q->l);
+	for(sofar = 0; sofar < len; sofar += n){
+		b = q->bfirst;
+		if(b == nil)
+			break;
+		QDEBUG checkb(b, "qdiscard");
+		n = BLEN(b);
+		if(n <= len - sofar){
+			q->bfirst = b->next;
+			b->next = 0;
+			q->len -= BALLOC(b);
+			q->dlen -= BLEN(b);
+			freeb(b);
+		} else {
+			n = len - sofar;
+			b->rp += n;
+			q->dlen -= n;
+		}
+	}
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	unlock(&q->l);
+
+	if(dowakeup)
+		Wakeup(&q->wr);
+
+	return sofar;
+}
+
+/*
+ *  Interrupt level copy out of a queue, return # bytes copied.
+ */
+int
+qconsume(Queue *q, void *vp, int len)
+{
+	Block *b;
+	int n, dowakeup;
+	uchar *p = vp;
+	Block *tofree = nil;
+
+	/* sync with qwrite */
+	lock(&q->l);
+
+	for(;;) {
+		b = q->bfirst;
+		if(b == 0){
+			q->state |= Qstarve;
+			unlock(&q->l);
+			return -1;
+		}
+		QDEBUG checkb(b, "qconsume 1");
+
+		n = BLEN(b);
+		if(n > 0)
+			break;
+		q->bfirst = b->next;
+		q->len -= BALLOC(b);
+
+		/* remember to free this */
+		b->next = tofree;
+		tofree = b;
+	}
+
+	if(n < len)
+		len = n;
+	memmove(p, b->rp, len);
+	if((q->state & Qmsg) || len == n)
+		q->bfirst = b->next;
+	b->rp += len;
+	q->dlen -= len;
+
+	/* discard the block if we're done with it */
+	if((q->state & Qmsg) || len == n){
+		b->next = 0;
+		q->len -= BALLOC(b);
+		q->dlen -= BLEN(b);
+
+		/* remember to free this */
+		b->next = tofree;
+		tofree = b;
+	}
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	} else
+		dowakeup = 0;
+
+	unlock(&q->l);
+
+	if(dowakeup)
+		Wakeup(&q->wr);
+
+	if(tofree != nil)
+		freeblist(tofree);
+
+	return len;
+}
+
+int
+qpass(Queue *q, Block *b)
+{
+	int dlen, len, dowakeup;
+
+	/* sync with qread */
+	dowakeup = 0;
+	lock(&q->l);
+	if(q->len >= q->limit){
+		unlock(&q->l);
+		freeblist(b);
+		return -1;
+	}
+	if(q->state & Qclosed){
+		unlock(&q->l);
+		len = blocklen(b);
+		freeblist(b);
+		return len;
+	}
+
+	/* add buffer to queue */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	len = BALLOC(b);
+	dlen = BLEN(b);
+	QDEBUG checkb(b, "qpass");
+	while(b->next){
+		b = b->next;
+		QDEBUG checkb(b, "qpass");
+		len += BALLOC(b);
+		dlen += BLEN(b);
+	}
+	q->blast = b;
+	q->len += len;
+	q->dlen += dlen;
+
+	if(q->len >= q->limit/2)
+		q->state |= Qflow;
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+	unlock(&q->l);
+
+	if(dowakeup)
+		Wakeup(&q->rr);
+
+	return len;
+}
+
+int
+qpassnolim(Queue *q, Block *b)
+{
+	int dlen, len, dowakeup;
+
+	/* sync with qread */
+	dowakeup = 0;
+	lock(&q->l);
+
+	len = BALLOC(b);
+	if(q->state & Qclosed){
+		unlock(&q->l);
+		freeblist(b);
+		return len;
+	}
+
+	/* add buffer to queue */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	dlen = BLEN(b);
+	QDEBUG checkb(b, "qpass");
+	while(b->next){
+		b = b->next;
+		QDEBUG checkb(b, "qpass");
+		len += BALLOC(b);
+		dlen += BLEN(b);
+	}
+	q->blast = b;
+	q->len += len;
+	q->dlen += dlen;
+
+	if(q->len >= q->limit/2)
+		q->state |= Qflow;
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+	unlock(&q->l);
+
+	if(dowakeup)
+		Wakeup(&q->rr);
+
+	return len;
+}
+
+/*
+ *  if the allocated space is way out of line with the used
+ *  space, reallocate to a smaller block
+ */
+Block*
+packblock(Block *bp)
+{
+	Block **l, *nbp;
+	int n;
+
+	for(l = &bp; *l; l = &(*l)->next){
+		nbp = *l;
+		n = BLEN(nbp);
+		if((n<<2) < BALLOC(nbp)){
+			*l = allocb(n);
+			memmove((*l)->wp, nbp->rp, n);
+			(*l)->wp += n;
+			(*l)->next = nbp->next;
+			freeb(nbp);
+		}
+	}
+
+	return bp;
+}
+
+int
+qproduce(Queue *q, void *vp, int len)
+{
+	Block *b;
+	int dowakeup;
+	uchar *p = vp;
+
+	/* sync with qread */
+	dowakeup = 0;
+	lock(&q->l);
+
+	if(q->state & Qclosed){
+		unlock(&q->l);
+		return -1;
+	}
+
+	/* no waiting receivers, room in buffer? */
+	if(q->len >= q->limit){
+		q->state |= Qflow;
+		unlock(&q->l);
+		return -1;
+	}
+
+	/* save in buffer */
+	b = iallocb(len);
+	if(b == 0){
+		unlock(&q->l);
+		print("qproduce: iallocb failed\n");
+		return -1;
+	}
+	memmove(b->wp, p, len);
+	b->wp += len;
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	q->blast = b;
+	/* b->next = 0; done by allocb() */
+	q->len += BALLOC(b);
+	q->dlen += BLEN(b);
+	QDEBUG checkb(b, "qproduce");
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+
+	if(q->len >= q->limit)
+		q->state |= Qflow;
+	unlock(&q->l);
+
+
+	if(dowakeup)
+		Wakeup(&q->rr);
+
+	return len;
+}
+
+/*
+ *  copy from offset in the queue
+ */
+Block*
+qcopy(Queue *q, int len, ulong offset)
+{
+	int sofar;
+	int n;
+	Block *b, *nb;
+	uchar *p;
+
+	nb = allocb(len);
+
+	lock(&q->l);
+
+	/* go to offset */
+	b = q->bfirst;
+	for(sofar = 0; ; sofar += n){
+		if(b == nil){
+			unlock(&q->l);
+			return nb;
+		}
+		n = BLEN(b);
+		if(sofar + n > offset){
+			p = b->rp + offset - sofar;
+			n -= offset - sofar;
+			break;
+		}
+		b = b->next;
+	}
+
+	/* copy bytes from there */
+	for(sofar = 0; sofar < len;){
+		if(n > len - sofar)
+			n = len - sofar;
+		memmove(nb->wp, p, n);
+		sofar += n;
+		nb->wp += n;
+		b = b->next;
+		if(b == nil)
+			break;
+		n = BLEN(b);
+		p = b->rp;
+	}
+	unlock(&q->l);
+
+	return nb;
+}
+
+/*
+ *  called by non-interrupt code
+ */
+Queue*
+qopen(int limit, int msg, void (*kick)(void*), void *arg)
+{
+	Queue *q;
+
+	q = kmalloc(sizeof(Queue));
+	if(q == 0)
+		return 0;
+
+	q->limit = q->inilim = limit;
+	q->kick = kick;
+	q->arg = arg;
+	q->state = msg;
+	q->state |= Qstarve;
+	q->eof = 0;
+	q->noblock = 0;
+
+	return q;
+}
+
+/* open a queue to be bypassed */
+Queue*
+qbypass(void (*bypass)(void*, Block*), void *arg)
+{
+	Queue *q;
+
+	q = malloc(sizeof(Queue));
+	if(q == 0)
+		return 0;
+
+	q->limit = 0;
+	q->arg = arg;
+	q->bypass = bypass;
+	q->state = 0;
+
+	return q;
+}
+
+static int
+notempty(void *a)
+{
+	Queue *q = a;
+
+	return (q->state & Qclosed) || q->bfirst != 0;
+}
+
+/*
+ *  wait for the queue to be non-empty or closed.
+ *  called with q ilocked.
+ */
+static int
+qwait(Queue *q)
+{
+	/* wait for data */
+	for(;;){
+		if(q->bfirst != nil)
+			break;
+
+		if(q->state & Qclosed){
+			if(++q->eof > 3)
+				return -1;
+			if(*q->err && strcmp(q->err, Ehungup) != 0)
+				return -1;
+			return 0;
+		}
+
+		q->state |= Qstarve;	/* flag requesting producer to wake me */
+		unlock(&q->l);
+		Sleep(&q->rr, notempty, q);
+		lock(&q->l);
+	}
+	return 1;
+}
+
+/*
+ * add a block list to a queue
+ */
+void
+qaddlist(Queue *q, Block *b)
+{
+	/* queue the block */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	q->len += blockalloclen(b);
+	q->dlen += blocklen(b);
+	while(b->next)
+		b = b->next;
+	q->blast = b;
+}
+
+/*
+ *  called with q locked
+ */
+Block*
+qremove(Queue *q)
+{
+	Block *b;
+
+	b = q->bfirst;
+	if(b == nil)
+		return nil;
+	q->bfirst = b->next;
+	b->next = nil;
+	q->dlen -= BLEN(b);
+	q->len -= BALLOC(b);
+	QDEBUG checkb(b, "qremove");
+	return b;
+}
+
+/*
+ *  copy the contents of a string of blocks into
+ *  memory.  emptied blocks are freed.  return
+ *  pointer to first unconsumed block.
+ */
+Block*
+bl2mem(uchar *p, Block *b, int n)
+{
+	int i;
+	Block *next;
+
+	for(; b != nil; b = next){
+		i = BLEN(b);
+		if(i > n){
+			memmove(p, b->rp, n);
+			b->rp += n;
+			return b;
+		}
+		memmove(p, b->rp, i);
+		n -= i;
+		p += i;
+		b->rp += i;
+		next = b->next;
+		freeb(b);
+	}
+	return nil;
+}
+
+/*
+ *  copy the contents of memory into a string of blocks.
+ *  return nil on error.
+ */
+Block*
+mem2bl(uchar *p, int len)
+{
+	int n;
+	Block *b, *first, **l;
+
+	first = nil;
+	l = &first;
+	if(waserror()){
+		freeblist(first);
+		nexterror();
+	}
+	do {
+		n = len;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		*l = b = allocb(n);
+		setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
+		memmove(b->wp, p, n);
+		b->wp += n;
+		p += n;
+		len -= n;
+		l = &b->next;
+	} while(len > 0);
+	poperror();
+
+	return first;
+}
+
+/*
+ *  put a block back to the front of the queue
+ *  called with q ilocked
+ */
+void
+qputback(Queue *q, Block *b)
+{
+	b->next = q->bfirst;
+	if(q->bfirst == nil)
+		q->blast = b;
+	q->bfirst = b;
+	q->len += BALLOC(b);
+	q->dlen += BLEN(b);
+}
+
+/*
+ *  flow control, get producer going again
+ *  called with q locked
+ */
+static void
+qwakeup_unlock(Queue *q)
+{
+	int dowakeup = 0;
+
+	/* if writer flow controlled, restart */
+	if((q->state & Qflow) && q->len < q->limit/2){
+		q->state &= ~Qflow;
+		dowakeup = 1;
+	}
+
+	unlock(&q->l);
+
+	/* wakeup flow controlled writers */
+	if(dowakeup){
+		if(q->kick)
+			q->kick(q->arg);
+		Wakeup(&q->wr);
+	}
+}
+
+/*
+ *  get next block from a queue (up to a limit)
+ */
+Block*
+qbread(Queue *q, int len)
+{
+	Block *b, *nb;
+	int n;
+
+	qlock(&q->rlock);
+	if(waserror()){
+		qunlock(&q->rlock);
+		nexterror();
+	}
+
+	lock(&q->l);
+	switch(qwait(q)){
+	case 0:
+		/* queue closed */
+		unlock(&q->l);
+		poperror();
+		qunlock(&q->rlock);
+		return nil;
+	case -1:
+		/* multiple reads on a closed queue */
+		unlock(&q->l);
+		error(q->err);
+	}
+
+	/* if we get here, there's at least one block in the queue */
+	b = qremove(q);
+	n = BLEN(b);
+
+	/* split block if it's too big and this is not a message oriented queue */
+	nb = b;
+	if(n > len){
+		if((q->state&Qmsg) == 0){
+			n -= len;
+			b = allocb(n);
+			memmove(b->wp, nb->rp+len, n);
+			b->wp += n;
+			qputback(q, b);
+		}
+		nb->wp = nb->rp + len;
+	}
+
+	/* restart producer */
+	qwakeup_unlock(q);
+
+	poperror();
+	qunlock(&q->rlock);
+	return nb;
+}
+
+/*
+ *  read a queue.  if no data is queued, wait on its Rendez
+ */
+long
+qread(Queue *q, void *vp, int len)
+{
+	Block *b, *first, **l;
+	int m, n;
+
+	qlock(&q->rlock);
+	if(waserror()){
+		qunlock(&q->rlock);
+		nexterror();
+	}
+
+	lock(&q->l);
+again:
+	switch(qwait(q)){
+	case 0:
+		/* queue closed */
+		unlock(&q->l);
+		poperror();
+		qunlock(&q->rlock);
+		return 0;
+	case -1:
+		/* multiple reads on a closed queue */
+		unlock(&q->l);
+		error(q->err);
+	}
+
+	/* if we get here, there's at least one block in the queue */
+	if(q->state & Qcoalesce){
+		/* when coalescing, 0 length blocks just go away */
+		b = q->bfirst;
+		if(BLEN(b) <= 0){
+			freeb(qremove(q));
+			goto again;
+		}
+
+		/*  grab the first block plus as many
+		 *  following blocks as will completely
+		 *  fit in the read.
+		 */
+		n = 0;
+		l = &first;
+		m = BLEN(b);
+		for(;;) {
+			*l = qremove(q);
+			l = &b->next;
+			n += m;
+
+			b = q->bfirst;
+			if(b == nil)
+				break;
+			m = BLEN(b);
+			if(n+m > len)
+				break;
+		}
+	} else {
+		first = qremove(q);
+		n = BLEN(first);
+	}
+
+	/* copy to user space outside of the ilock */
+	unlock(&q->l);
+	b = bl2mem(vp, first, len);
+	lock(&q->l);
+
+	/* take care of any left over partial block */
+	if(b != nil){
+		n -= BLEN(b);
+		if(q->state & Qmsg)
+			freeb(b);
+		else
+			qputback(q, b);
+	}
+
+	/* restart producer */
+	qwakeup_unlock(q);
+
+	poperror();
+	qunlock(&q->rlock);
+	return n;
+}
+
+static int
+qnotfull(void *a)
+{
+	Queue *q = a;
+
+	return q->len < q->limit || (q->state & Qclosed);
+}
+
+/*
+ *  add a block to a queue obeying flow control
+ */
+long
+qbwrite(Queue *q, Block *b)
+{
+	int n, dowakeup;
+	volatile struct {Block *b;} cb;
+
+	dowakeup = 0;
+	n = BLEN(b);
+	if(q->bypass){
+		(*q->bypass)(q->arg, b);
+		return n;
+	}
+	cb.b = b;
+
+	qlock(&q->wlock);
+	if(waserror()){
+		if(cb.b != nil)
+			freeb(cb.b);
+		qunlock(&q->wlock);
+		nexterror();
+	}
+
+	lock(&q->l);
+
+	/* give up if the queue is closed */
+	if(q->state & Qclosed){
+		unlock(&q->l);
+		error(q->err);
+	}
+
+	/* if nonblocking, don't queue over the limit */
+	if(q->len >= q->limit){
+		if(q->noblock){
+			unlock(&q->l);
+			freeb(b);
+			poperror();
+			qunlock(&q->wlock);
+			return n;
+		}
+	}
+
+	/* queue the block */
+	if(q->bfirst)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+	q->blast = b;
+	b->next = 0;
+	q->len += BALLOC(b);
+	q->dlen += n;
+	QDEBUG checkb(b, "qbwrite");
+	cb.b = nil;
+
+	if(q->state & Qstarve){
+		q->state &= ~Qstarve;
+		dowakeup = 1;
+	}
+
+	unlock(&q->l);
+
+	/*  get output going again */
+	if(q->kick && (dowakeup || (q->state&Qkick)))
+		q->kick(q->arg);
+
+	if(dowakeup)
+		Wakeup(&q->rr);
+
+	/*
+	 *  flow control, wait for queue to get below the limit
+	 *  before allowing the process to continue and queue
+	 *  more.  We do this here so that postnote can only
+	 *  interrupt us after the data has been queued.  This
+	 *  means that things like 9p flushes and ssl messages
+	 *  will not be disrupted by software interrupts.
+	 *
+	 *  Note - this is moderately dangerous since a process
+	 *  that keeps getting interrupted and rewriting will
+	 *  queue infinite crud.
+	 */
+	for(;;){
+		if(q->noblock || qnotfull(q))
+			break;
+
+		lock(&q->l);
+		q->state |= Qflow;
+		unlock(&q->l);
+		Sleep(&q->wr, qnotfull, q);
+	}
+
+	qunlock(&q->wlock);
+	poperror();
+	return n;
+}
+
+/*
+ *  write to a queue.  only Maxatomic bytes at a time is atomic.
+ */
+int
+qwrite(Queue *q, void *vp, int len)
+{
+	int n, sofar;
+	Block *b;
+	uchar *p = vp;
+
+	sofar = 0;
+	do {
+		n = len-sofar;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		b = allocb(n);
+		setmalloctag(b, getcallerpc(&q));
+		if(waserror()){
+			freeb(b);
+			nexterror();
+		}
+		memmove(b->wp, p+sofar, n);
+		poperror();
+		b->wp += n;
+
+		qbwrite(q, b);
+
+		sofar += n;
+	} while(sofar < len && (q->state & Qmsg) == 0);
+
+	return len;
+}
+
+/*
+ *  used by print() to write to a queue.  Since we may be splhi or not in
+ *  a process, don't qlock.
+ */
+int
+qiwrite(Queue *q, void *vp, int len)
+{
+	int n, sofar, dowakeup;
+	Block *b;
+	uchar *p = vp;
+
+	dowakeup = 0;
+
+	sofar = 0;
+	do {
+		n = len-sofar;
+		if(n > Maxatomic)
+			n = Maxatomic;
+
+		b = iallocb(n);
+		if (b == 0) {
+			print("qiwrite: iallocb failed\n");
+			break;
+		}
+		memmove(b->wp, p+sofar, n);
+		b->wp += n;
+
+		lock(&q->l);
+
+		QDEBUG checkb(b, "qiwrite");
+		if(q->bfirst)
+			q->blast->next = b;
+		else
+			q->bfirst = b;
+		q->blast = b;
+		q->len += BALLOC(b);
+		q->dlen += n;
+
+		if(q->state & Qstarve){
+			q->state &= ~Qstarve;
+			dowakeup = 1;
+		}
+
+		unlock(&q->l);
+
+		if(dowakeup){
+			if(q->kick)
+				q->kick(q->arg);
+			Wakeup(&q->rr);
+		}
+
+		sofar += n;
+	} while(sofar < len && (q->state & Qmsg) == 0);
+
+	return sofar;
+}
+
+/*
+ *  be extremely careful when calling this,
+ *  as there is no reference accounting
+ */
+void
+qfree(Queue *q)
+{
+	qclose(q);
+	free(q);
+}
+
+/*
+ *  Mark a queue as closed.  No further IO is permitted.
+ *  All blocks are released.
+ */
+void
+qclose(Queue *q)
+{
+	Block *bfirst;
+
+	if(q == nil)
+		return;
+
+	/* mark it */
+	lock(&q->l);
+	q->state |= Qclosed;
+	q->state &= ~(Qflow|Qstarve);
+	strcpy(q->err, Ehungup);
+	bfirst = q->bfirst;
+	q->bfirst = 0;
+	q->len = 0;
+	q->dlen = 0;
+	q->noblock = 0;
+	unlock(&q->l);
+
+	/* free queued blocks */
+	freeblist(bfirst);
+
+	/* wake up readers/writers */
+	Wakeup(&q->rr);
+	Wakeup(&q->wr);
+}
+
+/*
+ *  Mark a queue as closed.  Wakeup any readers.  Don't remove queued
+ *  blocks.
+ */
+void
+qhangup(Queue *q, char *msg)
+{
+	/* mark it */
+	lock(&q->l);
+	q->state |= Qclosed;
+	if(msg == 0 || *msg == 0)
+		strcpy(q->err, Ehungup);
+	else
+		kstrcpy(q->err, msg, sizeof q->err);
+	unlock(&q->l);
+
+	/* wake up readers/writers */
+	Wakeup(&q->rr);
+	Wakeup(&q->wr);
+}
+
+/*
+ *  return non-zero if the q is hungup
+ */
+int
+qisclosed(Queue *q)
+{
+	return q->state & Qclosed;
+}
+
+/*
+ *  mark a queue as no longer hung up
+ */
+void
+qreopen(Queue *q)
+{
+	lock(&q->l);
+	q->state &= ~Qclosed;
+	q->state |= Qstarve;
+	q->eof = 0;
+	q->limit = q->inilim;
+	unlock(&q->l);
+}
+
+/*
+ *  return bytes queued
+ */
+int
+qlen(Queue *q)
+{
+	return q->dlen;
+}
+
+/*
+ * return space remaining before flow control
+ */
+int
+qwindow(Queue *q)
+{
+	int l;
+
+	l = q->limit - q->len;
+	if(l < 0)
+		l = 0;
+	return l;
+}
+
+/*
+ *  return true if we can read without blocking
+ */
+int
+qcanread(Queue *q)
+{
+	return q->bfirst!=0;
+}
+
+/*
+ *  change queue limit
+ */
+void
+qsetlimit(Queue *q, int limit)
+{
+	q->limit = limit;
+}
+
+/*
+ *  set blocking/nonblocking
+ */
+void
+qnoblock(Queue *q, int onoff)
+{
+	q->noblock = onoff;
+}
+
+/*
+ *  flush the output queue
+ */
+void
+qflush(Queue *q)
+{
+	Block *bfirst;
+
+	/* mark it */
+	lock(&q->l);
+	bfirst = q->bfirst;
+	q->bfirst = 0;
+	q->len = 0;
+	q->dlen = 0;
+	unlock(&q->l);
+
+	/* free queued blocks */
+	freeblist(bfirst);
+
+	/* wake up readers/writers */
+	Wakeup(&q->wr);
+}
+
+int
+qfull(Queue *q)
+{
+	return q->state & Qflow;
+}
+
+int
+qstate(Queue *q)
+{
+	return q->state;
+}
+
+int
+qclosed(Queue *q)
+{
+	return q->state & Qclosed;
+}
--- /dev/null
+++ b/emu/port/random.c
@@ -1,0 +1,167 @@
+#include	"dat.h"
+#include	"fns.h"
+
+static struct
+{
+	QLock	l;
+	Rendez	producer;
+	Rendez	consumer;
+	Rendez	clock;
+	ulong	randomcount;
+	uchar	buf[1024];
+	uchar	*ep;
+	uchar	*rp;
+	uchar	*wp;
+	uchar	next;
+	uchar	bits;
+	uchar	wakeme;
+	uchar	filled;
+	int	kprocstarted;
+	ulong	randn;
+	int	target;
+} rb;
+
+static int
+rbnotfull(void *v)
+{
+	int i;
+
+	USED(v);
+	i = rb.wp - rb.rp;
+	if(i < 0)
+		i += sizeof(rb.buf);
+	return i < rb.target;
+}
+
+static int
+rbnotempty(void *v)
+{
+	USED(v);
+	return rb.wp != rb.rp;
+}
+
+/*
+ *  spin counting up
+ */
+static void
+genrandom(void *v)
+{
+	USED(v);
+	oslopri();
+	for(;;){
+		for(;;)
+			if(++rb.randomcount > 65535)
+				break;
+		if(rb.filled || !rbnotfull(0))
+			Sleep(&rb.producer, rbnotfull, 0);
+	}
+}
+
+/*
+ *  produce random bits in a circular buffer
+ */
+static void
+randomclock(void *v)
+{
+	uchar *p;
+
+	USED(v);
+	for(;; osmillisleep(20)){
+		while(!rbnotfull(0)){
+			rb.filled = 1;
+			Sleep(&rb.clock, rbnotfull, 0);
+		}
+		if(rb.randomcount == 0)
+			continue;
+
+		rb.bits = (rb.bits<<2) ^ rb.randomcount;
+		rb.randomcount = 0;
+
+		rb.next++;
+		if(rb.next != 8/2)
+			continue;
+		rb.next = 0;
+
+		p = rb.wp;
+		*p ^= rb.bits;
+		if(++p == rb.ep)
+			p = rb.buf;
+		rb.wp = p;
+
+		if(rb.wakeme)
+			Wakeup(&rb.consumer);
+	}
+}
+
+void
+randominit(void)
+{
+	rb.target = 16;
+	rb.ep = rb.buf + sizeof(rb.buf);
+	rb.rp = rb.wp = rb.buf;
+}
+
+/*
+ *  consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+	uchar *e, *p, *r;
+	ulong x;
+	int i;
+
+	p = xp;
+
+if(0)print("A%ld.%d.%lux|", n, rb.target, getcallerpc(&xp));
+	if(waserror()){
+		qunlock(&rb.l);
+		nexterror();
+	}
+
+	qlock(&rb.l);
+	if(!rb.kprocstarted){
+		rb.kprocstarted = 1;
+		kproc("genrand", genrandom, 0, 0);
+		kproc("randomclock", randomclock, 0, 0);
+	}
+
+	for(e = p + n; p < e; ){
+		r = rb.rp;
+		if(r == rb.wp){
+			rb.wakeme = 1;
+			Wakeup(&rb.clock);
+			Wakeup(&rb.producer);
+			Sleep(&rb.consumer, rbnotempty, 0);
+			rb.wakeme = 0;
+			continue;
+		}
+
+		/*
+		 *  beating clocks will be predictable if
+		 *  they are synchronized.  Use a cheap pseudo
+		 *  random number generator to obscure any cycles.
+		 */
+		x = rb.randn*1103515245 ^ *r;
+		*p++ = rb.randn = x;
+
+		if(++r == rb.ep)
+			r = rb.buf;
+		rb.rp = r;
+	}
+	if(rb.filled && rb.wp == rb.rp){
+		i = 2*rb.target;
+		if(i > sizeof(rb.buf) - 1)
+			i = sizeof(rb.buf) - 1;
+		rb.target = i;
+		rb.filled = 0;
+	}
+	qunlock(&rb.l);
+	poperror();
+
+	Wakeup(&rb.clock);
+	Wakeup(&rb.producer);
+
+if(0)print("B");
+	return n;
+}
--- /dev/null
+++ b/emu/port/srv.c
@@ -1,0 +1,153 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	<interp.h>
+#include	<isa.h>
+#include	"ip.h"
+#include	"srv.h"
+#include	"srvm.h"
+
+static	QLock	dbq;
+
+void
+Srv_init(void *fp)
+{
+	USED(fp);
+}
+
+void
+Srv_iph2a(void *fp)
+{
+	Heap *hpt;
+	String *ss;
+	F_Srv_iph2a *f;
+	int i, n, nhost;
+	List **h, *l, *nl;
+	char *hostv[10];
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	qlock(&dbq);
+	if(waserror()){
+		qunlock(&dbq);
+		acquire();
+		return;
+	}
+	nhost = so_gethostbyname(string2c(f->host), hostv, nelem(hostv));
+	poperror();
+	qunlock(&dbq);
+	acquire();
+	if(nhost == 0)
+		return;
+
+	l = (List*)H;
+	h = &l;
+	for(i = 0; i < nhost; i++) {
+		n = strlen(hostv[i]);
+		ss = newstring(n);
+		memmove(ss->Sascii, hostv[i], n);
+		free(hostv[i]);
+
+		hpt = nheap(sizeof(List) + IBY2WD);
+		hpt->t = &Tlist;
+		hpt->t->ref++;
+		nl = H2D(List*, hpt);
+		nl->t = &Tptr;
+		Tptr.ref++;
+		nl->tail = (List*)H;
+		*(String**)nl->data = ss;
+
+		*h = nl;
+		h = &nl->tail;
+	}
+	*f->ret = l;
+}
+
+void
+Srv_ipa2h(void *fp)
+{
+	Heap *hpt;
+	String *ss;
+	F_Srv_ipa2h *f;
+	int i, n, naliases;
+	List **h, *l, *nl;
+	char *hostv[10];
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	qlock(&dbq);
+	if(waserror()){
+		qunlock(&dbq);
+		acquire();
+		return;
+	}
+	naliases = so_gethostbyaddr(string2c(f->addr), hostv, nelem(hostv));
+	poperror();
+	qunlock(&dbq);
+	acquire();
+	if(naliases == 0)
+		return;
+
+	l = (List*)H;
+	h = &l;
+	for(i = 0; i < naliases; i++) {
+		n = strlen(hostv[i]);
+		ss = newstring(n);
+		memmove(ss->Sascii, hostv[i], n);
+		free(hostv[i]);
+
+		hpt = nheap(sizeof(List) + IBY2WD);
+		hpt->t = &Tlist;
+		hpt->t->ref++;
+		nl = H2D(List*, hpt);
+		nl->t = &Tptr;
+		Tptr.ref++;
+		nl->tail = (List*)H;
+		*(String**)nl->data = ss;
+
+		*h = nl;
+		h = &nl->tail;
+	}
+	*f->ret = l;
+}
+
+void
+Srv_ipn2p(void *fp)
+{
+	int n;
+	char buf[16];
+	F_Srv_ipn2p *f;
+	void *r;
+
+	f = fp;
+	r = *f->ret;
+	*f->ret = H;
+	destroy(r);
+	release();
+	qlock(&dbq);
+	if(waserror()){
+		qunlock(&dbq);
+		acquire();
+		return;
+	}
+	n = so_getservbyname(string2c(f->service), string2c(f->net), buf);
+	poperror();
+	qunlock(&dbq);
+	acquire();
+	if(n >= 0)
+		retstr(buf, f->ret);
+}
+
+void
+srvmodinit(void)
+{
+	builtinmod("$Srv", Srvmodtab, Srvmodlen);
+}
--- /dev/null
+++ b/emu/port/styx.c
@@ -1,0 +1,701 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+	uint n;
+
+	if(p+BIT16SZ > ep)
+		return nil;
+	n = GBIT16(p);
+	p += BIT16SZ - 1;
+	if(p+n+1 > ep)
+		return nil;
+	/* move it down, on top of count, to make room for '\0' */
+	memmove(p, p + 1, n);
+	p[n] = '\0';
+	*s = (char*)p;
+	p += n+1;
+	return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+	if(p+QIDSZ > ep)
+		return nil;
+	q->type = GBIT8(p);
+	p += BIT8SZ;
+	q->vers = GBIT32(p);
+	p += BIT32SZ;
+	q->path = GBIT64(p);
+	p += BIT64SZ;
+	return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ *  1. message size field is incorrect
+ *  2. input buffer too short for its own data (counts too long, etc.)
+ *  3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+	uchar *p, *ep;
+	uint i, size;
+
+	p = ap;
+	ep = p + nap;
+
+	if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+		return 0;
+	size = GBIT32(p);
+	p += BIT32SZ;
+
+	if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+		return 0;
+
+	f->type = GBIT8(p);
+	p += BIT8SZ;
+	f->tag = GBIT16(p);
+	p += BIT16SZ;
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->msize = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->version);
+		break;
+
+	case Tflush:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->oldtag = GBIT16(p);
+		p += BIT16SZ;
+		break;
+
+	case Tauth:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->afid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->uname);
+		if(p == nil)
+			break;
+		p = gstring(p, ep, &f->aname);
+		if(p == nil)
+			break;
+		break;
+
+	case Tattach:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->afid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->uname);
+		if(p == nil)
+			break;
+		p = gstring(p, ep, &f->aname);
+		if(p == nil)
+			break;
+		break;
+
+	case Twalk:
+		if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->newfid = GBIT32(p);
+		p += BIT32SZ;
+		f->nwname = GBIT16(p);
+		p += BIT16SZ;
+		if(f->nwname > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwname; i++){
+			p = gstring(p, ep, &f->wname[i]);
+			if(p == nil)
+				break;
+		}
+		break;
+
+	case Topen:
+		if(p+BIT32SZ+BIT8SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->mode = GBIT8(p);
+		p += BIT8SZ;
+		break;
+
+	case Tcreate:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->name);
+		if(p == nil)
+			break;
+		if(p+BIT32SZ+BIT8SZ > ep)
+			return 0;
+		f->perm = GBIT32(p);
+		p += BIT32SZ;
+		f->mode = GBIT8(p);
+		p += BIT8SZ;
+		break;
+
+	case Tread:
+		if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->offset = GBIT64(p);
+		p += BIT64SZ;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Twrite:
+		if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->offset = GBIT64(p);
+		p += BIT64SZ;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		if(p+f->count > ep)
+			return 0;
+		f->data = (char*)p;
+		p += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Tstat:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Twstat:
+		if(p+BIT32SZ+BIT16SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->nstat = GBIT16(p);
+		p += BIT16SZ;
+		if(p+f->nstat > ep)
+			return 0;
+		f->stat = p;
+		p += f->nstat;
+		break;
+
+/*
+ */
+	case Rversion:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->msize = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->version);
+		break;
+
+	case Rerror:
+		p = gstring(p, ep, &f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		p = gqid(p, ep, &f->aqid);
+		if(p == nil)
+			break;
+		break;
+
+	case Rattach:
+		p = gqid(p, ep, &f->qid);
+		if(p == nil)
+			break;
+		break;
+
+	case Rwalk:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->nwqid = GBIT16(p);
+		p += BIT16SZ;
+		if(f->nwqid > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwqid; i++){
+			p = gqid(p, ep, &f->wqid[i]);
+			if(p == nil)
+				break;
+		}
+		break;
+
+	case Ropen:
+	case Rcreate:
+		p = gqid(p, ep, &f->qid);
+		if(p == nil)
+			break;
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->iounit = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Rread:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		if(p+f->count > ep)
+			return 0;
+		f->data = (char*)p;
+		p += f->count;
+		break;
+
+	case Rwrite:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Rclunk:
+	case Rremove:
+		break;
+
+	case Rstat:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->nstat = GBIT16(p);
+		p += BIT16SZ;
+		if(p+f->nstat > ep)
+			return 0;
+		f->stat = p;
+		p += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+
+	if(p==nil || p>ep)
+		return 0;
+	if(ap+size == p)
+		return size;
+	return 0;
+}
+
+
+
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+	uint n;
+
+	if(s == nil){
+		PBIT16(p, 0);
+		p += BIT16SZ;
+		return p;
+	}
+
+	n = strlen(s);
+	PBIT16(p, n);
+	p += BIT16SZ;
+	memmove(p, s, n);
+	p += n;
+	return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+	PBIT8(p, q->type);
+	p += BIT8SZ;
+	PBIT32(p, q->vers);
+	p += BIT32SZ;
+	PBIT64(p, q->path);
+	p += BIT64SZ;
+	return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+	if(s == nil)
+		return BIT16SZ;
+
+	return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+	uint n;
+	int i;
+
+	n = 0;
+	n += BIT32SZ;	/* size */
+	n += BIT8SZ;	/* type */
+	n += BIT16SZ;	/* tag */
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		n += BIT32SZ;
+		n += stringsz(f->version);
+		break;
+
+	case Tflush:
+		n += BIT16SZ;
+		break;
+
+	case Tauth:
+		n += BIT32SZ;
+		n += stringsz(f->uname);
+		n += stringsz(f->aname);
+		break;
+
+	case Tattach:
+		n += BIT32SZ;
+		n += BIT32SZ;
+		n += stringsz(f->uname);
+		n += stringsz(f->aname);
+		break;
+
+	case Twalk:
+		n += BIT32SZ;
+		n += BIT32SZ;
+		n += BIT16SZ;
+		for(i=0; i<f->nwname; i++)
+			n += stringsz(f->wname[i]);
+		break;
+
+	case Topen:
+		n += BIT32SZ;
+		n += BIT8SZ;
+		break;
+
+	case Tcreate:
+		n += BIT32SZ;
+		n += stringsz(f->name);
+		n += BIT32SZ;
+		n += BIT8SZ;
+		break;
+
+	case Tread:
+		n += BIT32SZ;
+		n += BIT64SZ;
+		n += BIT32SZ;
+		break;
+
+	case Twrite:
+		n += BIT32SZ;
+		n += BIT64SZ;
+		n += BIT32SZ;
+		n += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		n += BIT32SZ;
+		break;
+
+	case Tstat:
+		n += BIT32SZ;
+		break;
+
+	case Twstat:
+		n += BIT32SZ;
+		n += BIT16SZ;
+		n += f->nstat;
+		break;
+/*
+ */
+
+	case Rversion:
+		n += BIT32SZ;
+		n += stringsz(f->version);
+		break;
+
+	case Rerror:
+		n += stringsz(f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		n += QIDSZ;
+		break;
+
+	case Rattach:
+		n += QIDSZ;
+		break;
+
+	case Rwalk:
+		n += BIT16SZ;
+		n += f->nwqid*QIDSZ;
+		break;
+
+	case Ropen:
+	case Rcreate:
+		n += QIDSZ;
+		n += BIT32SZ;
+		break;
+
+	case Rread:
+		n += BIT32SZ;
+		n += f->count;
+		break;
+
+	case Rwrite:
+		n += BIT32SZ;
+		break;
+
+	case Rclunk:
+		break;
+
+	case Rremove:
+		break;
+
+	case Rstat:
+		n += BIT16SZ;
+		n += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+	return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+	uchar *p;
+	uint i, size;
+
+	size = sizeS2M(f);
+	if(size == 0)
+		return 0;
+	if(size > nap)
+		return 0;
+
+	p = (uchar*)ap;
+
+	PBIT32(p, size);
+	p += BIT32SZ;
+	PBIT8(p, f->type);
+	p += BIT8SZ;
+	PBIT16(p, f->tag);
+	p += BIT16SZ;
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		PBIT32(p, f->msize);
+		p += BIT32SZ;
+		p = pstring(p, f->version);
+		break;
+
+	case Tflush:
+		PBIT16(p, f->oldtag);
+		p += BIT16SZ;
+		break;
+
+	case Tauth:
+		PBIT32(p, f->afid);
+		p += BIT32SZ;
+		p  = pstring(p, f->uname);
+		p  = pstring(p, f->aname);
+		break;
+
+	case Tattach:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT32(p, f->afid);
+		p += BIT32SZ;
+		p  = pstring(p, f->uname);
+		p  = pstring(p, f->aname);
+		break;
+
+	case Twalk:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT32(p, f->newfid);
+		p += BIT32SZ;
+		PBIT16(p, f->nwname);
+		p += BIT16SZ;
+		if(f->nwname > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwname; i++)
+			p = pstring(p, f->wname[i]);
+		break;
+
+	case Topen:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT8(p, f->mode);
+		p += BIT8SZ;
+		break;
+
+	case Tcreate:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		p = pstring(p, f->name);
+		PBIT32(p, f->perm);
+		p += BIT32SZ;
+		PBIT8(p, f->mode);
+		p += BIT8SZ;
+		break;
+
+	case Tread:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT64(p, f->offset);
+		p += BIT64SZ;
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		break;
+
+	case Twrite:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT64(p, f->offset);
+		p += BIT64SZ;
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		memmove(p, f->data, f->count);
+		p += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		break;
+
+	case Tstat:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		break;
+
+	case Twstat:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT16(p, f->nstat);
+		p += BIT16SZ;
+		memmove(p, f->stat, f->nstat);
+		p += f->nstat;
+		break;
+/*
+ */
+
+	case Rversion:
+		PBIT32(p, f->msize);
+		p += BIT32SZ;
+		p = pstring(p, f->version);
+		break;
+
+	case Rerror:
+		p = pstring(p, f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		p = pqid(p, &f->aqid);
+		break;
+
+	case Rattach:
+		p = pqid(p, &f->qid);
+		break;
+
+	case Rwalk:
+		PBIT16(p, f->nwqid);
+		p += BIT16SZ;
+		if(f->nwqid > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwqid; i++)
+			p = pqid(p, &f->wqid[i]);
+		break;
+
+	case Ropen:
+	case Rcreate:
+		p = pqid(p, &f->qid);
+		PBIT32(p, f->iounit);
+		p += BIT32SZ;
+		break;
+
+	case Rread:
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		memmove(p, f->data, f->count);
+		p += f->count;
+		break;
+
+	case Rwrite:
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		break;
+
+	case Rclunk:
+		break;
+
+	case Rremove:
+		break;
+
+	case Rstat:
+		PBIT16(p, f->nstat);
+		p += BIT16SZ;
+		memmove(p, f->stat, f->nstat);
+		p += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+	if(size != p-ap)
+		return 0;
+	return size;
+}
--- /dev/null
+++ b/emu/port/sysfile.c
@@ -1,0 +1,1089 @@
+#include        "dat.h"
+#include        "fns.h"
+#include        "error.h"
+#include        "kernel.h"
+
+static int
+growfd(Fgrp *f, int fd)
+{
+	int n;
+	Chan **nfd, **ofd;
+
+	if(fd < f->nfd)
+		return 0;
+	n = f->nfd+DELTAFD;
+	if(n > MAXNFD)
+		n = MAXNFD;
+	if(fd >= n)
+		return -1;
+	nfd = malloc(n*sizeof(Chan*));
+	if(nfd == nil)
+		return -1;
+	ofd = f->fd;
+	memmove(nfd, ofd, f->nfd*sizeof(Chan *));
+	f->fd = nfd;
+	f->nfd = n;
+	free(ofd);
+	return 0;
+}
+
+static int
+newfd(Chan *c)
+{
+	int i;
+	Fgrp *f = up->env->fgrp;
+
+	lock(&f->l);
+	for(i=f->minfd; i<f->nfd; i++)
+		if(f->fd[i] == 0)
+			break;
+	if(i >= f->nfd && growfd(f, i) < 0){
+		unlock(&f->l);
+		exhausted("file descriptors");
+		return -1;
+	}
+	f->minfd = i + 1;
+	if(i > f->maxfd)
+		f->maxfd = i;
+	f->fd[i] = c;
+	unlock(&f->l);
+	return i;
+}
+
+Chan*
+fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
+{
+	Chan *c;
+
+	c = 0;
+	lock(&f->l);
+	if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) {
+		unlock(&f->l);
+		error(Ebadfd);
+	}
+	if(iref)
+		incref(&c->r);
+	unlock(&f->l);
+
+	if(chkmnt && (c->flag&CMSG))
+		goto bad;
+	if(mode<0 || c->mode==ORDWR)
+		return c;
+	if((mode&OTRUNC) && c->mode==OREAD)
+		goto bad;
+	if((mode&~OTRUNC) != c->mode)
+		goto bad;
+	return c;
+bad:
+	if(iref)
+		cclose(c);
+	error(Ebadusefd);
+	return nil;
+}
+
+long
+kchanio(void *vc, void *buf, int n, int mode)
+{
+	int r;
+	Chan *c;
+
+	c = vc;
+	if(waserror())
+		return -1;
+
+	if(mode == OREAD)
+		r = devtab[c->type]->read(c, buf, n, c->offset);
+	else
+		r = devtab[c->type]->write(c, buf, n, c->offset);
+
+	lock(&c->l);
+	c->offset += r;
+	unlock(&c->l);
+	poperror();
+	return r;
+}
+
+int
+openmode(ulong o)
+{
+	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
+		error(Ebadarg);
+	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+	if(o > OEXEC)
+		error(Ebadarg);
+	if(o == OEXEC)
+		return OREAD;
+	return o;
+}
+
+static void
+fdclose(Fgrp *f, int fd)
+{
+	int i;
+	Chan *c;
+
+	lock(&f->l);
+	c = f->fd[fd];
+	if(c == 0) {
+		/* can happen for users with shared fd tables */
+		unlock(&f->l);
+		return;
+	}
+	f->fd[fd] = 0;
+	if(fd == f->maxfd)
+		for(i=fd; --i>=0 && f->fd[i]==0; )
+			f->maxfd = i;
+	if(fd < f->minfd)
+		f->minfd = fd;
+	unlock(&f->l);
+	cclose(c);
+}
+
+int
+kchdir(char *path)
+{
+	Chan *c;
+	Pgrp *pg;
+
+	if(waserror())
+		return -1;
+
+	c = namec(path, Atodir, 0, 0);
+	pg = up->env->pgrp;
+	cclose(pg->dot);
+	pg->dot = c;
+	poperror();
+	return 0;
+}
+
+int
+kfgrpclose(Fgrp *f, int fd)
+{
+	if(waserror())
+		return -1;
+
+	/*
+	 * Take no reference on the chan because we don't really need the
+	 * data structure, and are calling fdtochan only for error checks.
+	 * fdclose takes care of processes racing through here.
+	 */
+	fdtochan(f, fd, -1, 0, 0);
+	fdclose(f, fd);
+	poperror();
+	return 0;
+}
+
+int
+kclose(int fd)
+{
+	return kfgrpclose(up->env->fgrp, fd);
+}
+
+int
+kcreate(char *path, int mode, ulong perm)
+{
+	int fd;
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()) {
+		cclose(c.c);
+		return -1;
+	}
+
+	openmode(mode&~OEXCL);	/* error check only; OEXCL okay here */
+	c.c = namec(path, Acreate, mode, perm);
+	fd = newfd(c.c);
+	if(fd < 0)
+		error(Enofd);
+	poperror();
+	return fd;
+}
+
+int
+kdup(int old, int new)
+{
+	int fd;
+	Chan *oc;
+	Fgrp *f = up->env->fgrp;
+	volatile struct { Chan *c; } c;
+
+	if(waserror())
+		return -1;
+
+	c.c = fdtochan(up->env->fgrp, old, -1, 0, 1);
+	if(c.c->qid.type & QTAUTH)
+		error(Eperm);
+	fd = new;
+	if(fd != -1) {
+		lock(&f->l);
+		if(fd < 0 || growfd(f, fd) < 0) {
+			unlock(&f->l);
+			cclose(c.c);
+			error(Ebadfd);
+		}
+		if(fd > f->maxfd)
+			f->maxfd = fd;
+		oc = f->fd[fd];
+		f->fd[fd] = c.c;
+		unlock(&f->l);
+		if(oc != 0)
+			cclose(oc);
+	}
+	else {
+		if(waserror()) {
+			cclose(c.c);
+			nexterror();
+		}
+		fd = newfd(c.c);
+		if(fd < 0)
+			error(Enofd);
+		poperror();
+	}
+	poperror();
+	return fd;
+}
+
+int
+kfstat(int fd, uchar *buf, int n)
+{
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()) {
+		cclose(c.c);
+		return -1;
+	}
+	c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	devtab[c.c->type]->stat(c.c, buf, n);
+	poperror();
+	cclose(c.c);
+	return n;
+}
+
+char*
+kfd2path(int fd)
+{
+	Chan *c;
+	char *s;
+
+	if(waserror())
+		return nil;
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	s = nil;
+	if(c->name != nil){
+		s = malloc(c->name->len+1);
+		if(s == nil){
+			cclose(c);
+			error(Enomem);
+		}
+		memmove(s, c->name->s, c->name->len+1);
+		cclose(c);
+	}
+	poperror();
+	return s;
+}
+
+int
+kfauth(int fd, char *aname)
+{
+	Chan *c, *ac;
+
+	if(waserror())
+		return -1;
+
+	validname(aname, 1);
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+
+	ac = mntauth(c, aname);
+
+	/* at this point ac is responsible for keeping c alive */
+	poperror();	/* c */
+	cclose(c);
+
+	if(waserror()){
+		cclose(ac);
+		nexterror();
+	}
+
+	fd = newfd(ac);
+	if(fd < 0)
+		error(Enofd);
+	poperror();	/* ac */
+
+	poperror();
+
+	return fd;
+}
+
+int
+kfversion(int fd, uint msize, char *vers, uint arglen)
+{
+	int m;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	/* check there's a NUL in the version string */
+	if(arglen==0 || memchr(vers, 0, arglen)==0)
+		error(Ebadarg);
+
+	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(waserror()){
+		cclose(c);
+		nexterror();
+	}
+
+	m = mntversion(c, vers, msize, arglen);
+
+	poperror();
+	cclose(c);
+
+	poperror();
+	return m;
+}
+
+int
+kpipe(int fd[2])
+{
+	Dev *d;
+	Fgrp *f;
+	Chan *c[2];
+	static char *names[] = {"data", "data1"};
+
+	f = up->env->fgrp;
+
+	d = devtab[devno('|', 0)];
+	c[0] = namec("#|", Atodir, 0, 0);
+	c[1] = 0;
+	fd[0] = -1;
+	fd[1] = -1;
+	if(waserror()) {
+		if(c[0] != 0)
+			cclose(c[0]);
+		if(c[1] != 0)
+			cclose(c[1]);
+		if(fd[0] >= 0)
+			f->fd[fd[0]]=0;
+		if(fd[1] >= 0)
+			f->fd[fd[1]]=0;
+		return -1;
+	}
+	c[1] = cclone(c[0]);
+	if(walk(&c[0], &names[0], 1, 1, nil) < 0)
+		error(Egreg);
+	if(walk(&c[1], &names[1], 1, 1, nil) < 0)
+		error(Egreg);
+	c[0] = d->open(c[0], ORDWR);
+	c[1] = d->open(c[1], ORDWR);
+	fd[0] = newfd(c[0]);
+	if(fd[0] < 0)
+		error(Enofd);
+	fd[1] = newfd(c[1]);
+	if(fd[1] < 0)
+		error(Enofd);
+	poperror();
+	return 0;
+}
+
+int
+kfwstat(int fd, uchar *buf, int n)
+{
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()) {
+		cclose(c.c);
+		return -1;
+	}
+	validstat(buf, n);
+	c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	n = devtab[c.c->type]->wstat(c.c, buf, n);
+	poperror();
+	cclose(c.c);
+	return n;
+}
+
+long
+bindmount(Chan *c, char *old, int flag, char *spec)
+{
+	int ret;
+	volatile struct { Chan *c; } c1;
+
+	if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER))
+		error(Ebadarg);
+
+	c1.c = namec(old, Amount, 0, 0);
+	if(waserror()){
+		cclose(c1.c);
+		nexterror();
+	}
+	ret = cmount(c, c1.c, flag, spec);
+
+	poperror();
+	cclose(c1.c);
+	return ret;
+}
+
+int
+kbind(char *new, char *old, int flags)
+{
+	long r;
+	volatile struct { Chan *c; } c0;
+
+	c0.c = nil;
+	if(waserror()) {
+		cclose(c0.c);
+		return -1;
+	}
+	c0.c = namec(new, Abind, 0, 0);
+	r = bindmount(c0.c, old, flags, "");
+	poperror();
+	cclose(c0.c);
+	return r;
+}
+
+int
+kmount(int fd, int afd, char *old, int flags, char *spec)
+{
+	long r;
+	volatile struct { Chan *c; } c0;
+	volatile struct { Chan *c; } bc;
+	volatile struct { Chan *c; } ac;
+	Mntparam mntparam;
+
+	ac.c = nil;
+	bc.c = nil;
+	c0.c = nil;
+	if(waserror()) {
+		cclose(ac.c);
+		cclose(bc.c);
+		cclose(c0.c);
+		return -1;
+	}
+	bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+	if(afd >= 0)
+		ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
+	mntparam.chan = bc.c;
+	mntparam.authchan = ac.c;
+	mntparam.spec = spec;
+	mntparam.flags = flags;
+	c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam);
+
+	r = bindmount(c0.c, old, flags, spec);
+	poperror();
+	cclose(ac.c);
+	cclose(bc.c);
+	cclose(c0.c);
+
+	return r;
+}
+
+int
+kunmount(char *old, char *new)
+{
+	volatile struct { Chan *c; } cmount;
+	volatile struct { Chan *c; } cmounted;
+
+	cmount.c = nil;
+	cmounted.c = nil;
+	if(waserror()) {
+		cclose(cmount.c);
+		cclose(cmounted.c);
+		return -1;
+	}
+
+	cmount.c = namec(new, Amount, 0, 0);
+	if(old != nil && old[0] != '\0') {
+		/*
+		 * This has to be namec(..., Aopen, ...) because
+		 * if arg[0] is something like /srv/cs or /fd/0,
+		 * opening it is the only way to get at the real
+		 * Chan underneath.
+		 */
+		cmounted.c = namec(old, Aopen, OREAD, 0);
+	}
+
+	cunmount(cmount.c, cmounted.c);
+	poperror();
+	cclose(cmount.c);
+	cclose(cmounted.c);
+	return 0;
+}
+
+int
+kopen(char *path, int mode)
+{
+	int fd;
+	volatile struct { Chan *c; } c;
+
+	if(waserror())
+		return -1;
+
+	openmode(mode);                         /* error check only */
+	c.c = namec(path, Aopen, mode, 0);
+	if(waserror()){
+		cclose(c.c);
+		nexterror();
+	}
+	fd = newfd(c.c);
+	if(fd < 0)
+		error(Enofd);
+	poperror();
+
+	poperror();
+	return fd;
+}
+
+long
+unionread(Chan *c, void *va, long n)
+{
+	int i;
+	long nr;
+	Mhead *m;
+	Mount *mount;
+
+	qlock(&c->umqlock);
+	m = c->umh;
+	rlock(&m->lock);
+	mount = m->mount;
+	/* bring mount in sync with c->uri and c->umc */
+	for(i = 0; mount != nil && i < c->uri; i++)
+		mount = mount->next;
+
+	nr = 0;
+	while(mount != nil) {
+		/* Error causes component of union to be skipped */
+		if(mount->to && !waserror()) {
+			if(c->umc == nil){
+				c->umc = cclone(mount->to);
+				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
+			}
+	
+			nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
+			if(nr < 0)
+				nr = 0;	/* dev.c can return -1 */
+			c->umc->offset += nr;
+			poperror();
+		}
+		if(nr > 0)
+			break;
+
+		/* Advance to next element */
+		c->uri++;
+		if(c->umc) {
+			cclose(c->umc);
+			c->umc = nil;
+		}
+		mount = mount->next;
+	}
+	runlock(&m->lock);
+	qunlock(&c->umqlock);
+	return nr;
+}
+
+static void
+unionrewind(Chan *c)
+{
+	qlock(&c->umqlock);
+	c->uri = 0;
+	if(c->umc){
+		cclose(c->umc);
+		c->umc = nil;
+	}
+	qunlock(&c->umqlock);
+}
+
+static long
+rread(int fd, void *va, long n, vlong *offp)
+{
+	int dir;
+	Lock *cl;
+	volatile struct { Chan *c; } c;
+	vlong off;
+
+	if(waserror())
+		return -1;
+
+	c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
+	if(waserror()){
+		cclose(c.c);
+		nexterror();
+	}
+
+	if(n < 0)
+		error(Etoosmall);
+
+	dir = c.c->qid.type & QTDIR;
+	if(dir && c.c->umh)
+		n = unionread(c.c, va, n);
+	else{
+		cl = &c.c->l;
+		if(offp == nil){
+			lock(cl);	/* lock for vlong assignment */
+			off = c.c->offset;
+			unlock(cl);
+		}else
+			off = *offp;
+		if(off < 0)
+			error(Enegoff);
+		if(off == 0){
+			if(offp == nil){
+				lock(cl);
+				c.c->offset = 0;
+				c.c->dri = 0;
+				unlock(cl);
+			}
+			unionrewind(c.c);
+		}
+		n = devtab[c.c->type]->read(c.c, va, n, off);
+		lock(cl);
+		c.c->offset += n;
+		unlock(cl);
+	}
+
+	poperror();
+	cclose(c.c);
+
+	poperror();
+	return n;
+}
+
+long
+kread(int fd, void *va, long n)
+{
+	return rread(fd, va, n, nil);
+}
+
+long
+kpread(int fd, void *va, long n, vlong off)
+{
+	return rread(fd, va, n, &off);
+}
+
+int
+kremove(char *path)
+{
+	volatile struct { Chan *c; } c;
+
+	if(waserror())
+		return -1;
+
+	c.c = namec(path, Aremove, 0, 0);
+	if(waserror()){
+		c.c->type = 0;	/* see below */
+		cclose(c.c);
+		nexterror();
+	}
+	devtab[c.c->type]->remove(c.c);
+	/*
+	 * Remove clunks the fid, but we need to recover the Chan
+	 * so fake it up.  rootclose() is known to be a nop.
+	 */
+	c.c->type = 0;
+	poperror();
+	cclose(c.c);
+
+	poperror();
+	return 0;
+}
+
+vlong
+kseek(int fd, vlong off, int whence)
+{
+	Dir *dir;
+	Chan *c;
+
+	if(waserror())
+		return -1;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
+	if(waserror()) {
+		cclose(c);
+		nexterror();
+	}
+
+	if(devtab[c->type]->dc == '|')
+		error(Eisstream);
+
+	switch(whence) {
+	case 0:
+		if(c->qid.type & QTDIR){
+			if(off != 0)
+				error(Eisdir);
+			unionrewind(c);
+		}else if(off < 0)
+			error(Enegoff);
+		lock(&c->l);	/* lock for vlong assignment */
+		c->offset = off;
+		unlock(&c->l);
+		break;
+
+	case 1:
+		if(c->qid.type & QTDIR)
+			error(Eisdir);
+		lock(&c->l);	/* lock for read/write update */
+		off += c->offset;
+		if(off < 0){
+			unlock(&c->l);
+			error(Enegoff);
+		}
+		c->offset = off;
+		unlock(&c->l);
+		break;
+
+	case 2:
+		if(c->qid.type & QTDIR)
+			error(Eisdir);
+		dir = chandirstat(c);
+		if(dir == nil)
+			error("internal error: stat error in seek");
+		off += dir->length;
+		free(dir);
+		if(off < 0)
+			error(Enegoff);
+		lock(&c->l);	/* lock for read/write update */
+		c->offset = off;
+		unlock(&c->l);
+		break;
+
+	default:
+		error(Ebadarg);
+		break;
+	}
+	poperror();
+	c->dri = 0;
+	cclose(c);
+	poperror();
+	return off;
+}
+
+void
+validstat(uchar *s, int n)
+{
+	int m;
+	char buf[64];
+
+	if(statcheck(s, n) < 0)
+		error(Ebadstat);
+	/* verify that name entry is acceptable */
+	s += STATFIXLEN - 4*BIT16SZ;	/* location of first string */
+	/*
+	 * s now points at count for first string.
+	 * if it's too long, let the server decide; this is
+	 * only for his protection anyway. otherwise
+	 * we'd have to allocate and waserror.
+	 */
+	m = GBIT16(s);
+	s += BIT16SZ;
+	if(m+1 > sizeof buf)
+		return;
+	memmove(buf, s, m);
+	buf[m] = '\0';
+	/* name could be '/' */
+	if(strcmp(buf, "/") != 0)
+		validname(buf, 0);
+}
+
+int
+kstat(char *path, uchar *buf, int n)
+{
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()){
+		cclose(c.c);
+		return -1;
+	}
+	c.c = namec(path, Aaccess, 0, 0);
+	devtab[c.c->type]->stat(c.c, buf, n);
+	poperror();
+	cclose(c.c);
+	return 0;
+}
+
+static long
+rwrite(int fd, void *va, long n, vlong *offp)
+{
+	Lock *cl;
+	volatile struct { Chan *c; } c;
+	vlong off;
+	long m;
+
+	if(waserror())
+		return -1;
+	c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
+	if(waserror()){
+		cclose(c.c);
+		nexterror();
+	}
+	if(c.c->qid.type & QTDIR)
+		error(Eisdir);
+
+	if(n < 0)
+		error(Etoosmall);
+
+	cl = &c.c->l;
+	if(offp == nil){
+		lock(cl);
+		off = c.c->offset;
+		c.c->offset += n;
+		unlock(cl);
+	}else
+		off = *offp;
+
+	if(waserror()){
+		if(offp == nil){
+			lock(cl);
+			c.c->offset -= n;
+			unlock(cl);
+		}
+		nexterror();
+	}
+	if(off < 0)
+		error(Enegoff);
+	m = devtab[c.c->type]->write(c.c, va, n, off);
+	poperror();
+
+	if(offp == nil && m < n){
+		lock(cl);
+		c.c->offset -= n - m;
+		unlock(cl);
+	}
+
+	poperror();
+	cclose(c.c);
+
+	poperror();
+	return m;
+}
+
+long
+kwrite(int fd, void *va, long n)
+{
+	return rwrite(fd, va, n, nil);
+}
+
+long
+kpwrite(int fd, void *va, long n, vlong off)
+{
+	return rwrite(fd, va, n, &off);
+}
+
+int
+kwstat(char *path, uchar *buf, int n)
+{
+	volatile struct { Chan *c; } c;
+
+	c.c = nil;
+	if(waserror()){
+		cclose(c.c);
+		return -1;
+	}
+	validstat(buf, n);
+	c.c = namec(path, Aaccess, 0, 0);
+	n = devtab[c.c->type]->wstat(c.c, buf, n);
+	poperror();
+	cclose(c.c);
+	return n;
+}
+
+enum
+{
+	DIRSIZE = STATFIXLEN + 32 * 4,
+	DIRREADLIM = 2048,	/* should handle the largest reasonable directory entry */
+};
+
+Dir*
+chandirstat(Chan *c)
+{
+	Dir *d;
+	uchar *buf;
+	int n, nd, i;
+
+	nd = DIRSIZE;
+	for(i=0; i<2; i++){	/* should work by the second try */
+		d = smalloc(sizeof(Dir) + nd);
+		buf = (uchar*)&d[1];
+		if(waserror()){
+			free(d);
+			return nil;
+		}
+		n = devtab[c->type]->stat(c, buf, nd);
+		poperror();
+		if(n < BIT16SZ){
+			free(d);
+			return nil;
+		}
+		nd = GBIT16((uchar*)buf) + BIT16SZ;	/* size needed to store whole stat buffer including count */
+		if(nd <= n){
+			convM2D(buf, n, d, (char*)&d[1]);
+			return d;
+		}
+		/* else sizeof(Dir)+nd is plenty */
+		free(d);
+	}
+	return nil;
+
+}
+
+Dir*
+kdirstat(char *name)
+{
+	volatile struct {Chan *c;} c;
+	Dir *d;
+
+	c.c = nil;
+	if(waserror()){
+		cclose(c.c);
+		return nil;
+	}
+	c.c = namec(name, Aaccess, 0, 0);
+	d = chandirstat(c.c);
+	poperror();
+	cclose(c.c);
+	return d;
+}
+
+Dir*
+kdirfstat(int fd)
+{
+	volatile struct { Chan *c; } c;
+	Dir *d;
+
+	c.c = nil;
+	if(waserror()) {
+		cclose(c.c);
+		return nil;
+	}
+	c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	d = chandirstat(c.c);
+	poperror();
+	cclose(c.c);
+	return d;
+}
+
+int
+kdirwstat(char *name, Dir *dir)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(dir);
+	buf = smalloc(r);
+	convD2M(dir, buf, r);
+	r = kwstat(name, buf, r);
+	free(buf);
+	return r < 0? r: 0;
+}
+
+int
+kdirfwstat(int fd, Dir *dir)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(dir);
+	buf = smalloc(r);
+	convD2M(dir, buf, r);
+	r = kfwstat(fd, buf, r);
+	free(buf);
+	return r < 0? r: 0;
+}
+
+static long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+	char *s;
+	long ss, i, n, nn, m;
+
+	*d = nil;
+	if(ts <= 0)
+		return ts;
+
+	/*
+	 * first find number of all stats, check they look like stats, & size all associated strings
+	 */
+	ss = 0;
+	n = 0;
+	for(i = 0; i < ts; i += m){
+		m = BIT16SZ + GBIT16(&buf[i]);
+		if(statcheck(&buf[i], m) < 0)
+			break;
+		ss += m;
+		n++;
+	}
+
+	if(i != ts)
+		error("bad directory format");
+
+	*d = malloc(n * sizeof(Dir) + ss);
+	if(*d == nil)
+		error(Enomem);
+
+	/*
+	 * then convert all buffers
+	 */
+	s = (char*)*d + n * sizeof(Dir);
+	nn = 0;
+	for(i = 0; i < ts; i += m){
+		m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+			free(*d);
+			*d = nil;
+			error("bad directory entry");
+		}
+		nn++;
+		s += m;
+	}
+
+	return nn;
+}
+
+long
+kdirread(int fd, Dir **d)
+{
+	uchar *buf;
+	long ts;
+
+	*d = nil;
+	if(waserror())
+		return -1;
+	buf = malloc(DIRREADLIM);
+	if(buf == nil)
+		error(Enomem);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+	ts = kread(fd, buf, DIRREADLIM);
+	if(ts > 0)
+		ts = dirpackage(buf, ts, d);
+
+	poperror();
+	free(buf);
+	poperror();
+	return ts;
+}
+
+int
+kiounit(int fd)
+{
+	Chan *c;
+	int n;
+
+	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
+	if(waserror()){
+		cclose(c);
+		return 0;	/* n.b. */
+	}
+	n = c->iounit;
+	poperror();
+	cclose(c);
+	return n;
+}
--- /dev/null
+++ b/emu/port/uqid.c
@@ -1,0 +1,114 @@
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+/*
+ * unique Qid path generation,
+ * for exportfs.c and various devfs-*
+ */
+
+#define	QIDMASK	(((vlong)1<<48)-1)
+
+void
+uqidinit(Uqidtab *tab)
+{
+	memset(tab, 0, sizeof(*tab));
+}
+
+static int
+uqidhash(vlong path)
+{
+	ulong p;
+	p = (ulong)path;
+	return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1);
+}
+
+static Uqid **
+uqidlook(Uqid **tab, Chan *c, vlong path)
+{
+	Uqid **hp, *q;
+
+	for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next)
+		if(q->type == c->type && q->dev == c->dev && q->oldpath == path)
+			break;
+	return hp;
+}
+
+static int
+uqidexists(Uqid **tab, vlong path)
+{
+	int i;
+	Uqid *q;
+
+	for(i=0; i<Nqidhash; i++)
+		for(q = tab[i]; q != nil; q = q->next)
+			if(q->newpath == path)
+				return 1;
+	return 0;
+}
+
+Uqid *
+uqidalloc(Uqidtab *tab, Chan *c)
+{
+	Uqid **hp, *q;
+
+	qlock(&tab->l);
+	hp = uqidlook(tab->qids, c, c->qid.path);
+	if((q = *hp) != nil){
+		incref(&q->r);
+		qunlock(&tab->l);
+		return q;
+	}
+	q = mallocz(sizeof(*q), 1);
+	if(q == nil){
+		qunlock(&tab->l);
+		error(Enomem);
+	}
+	q->r.ref = 1;
+	q->type = c->type;
+	q->dev = c->dev;
+	q->oldpath = c->qid.path;
+	q->newpath = c->qid.path;
+	while(uqidexists(tab->qids, q->newpath)){
+		if(++tab->pathgen >= (1<<16))
+			tab->pathgen = 1;
+		q->newpath = ((vlong)tab->pathgen<<48) | (q->newpath & QIDMASK);
+	}
+	q->next = nil;
+	*hp = q;
+	qunlock(&tab->l);
+	return q;
+}
+
+void
+freeuqid(Uqidtab *tab, Uqid *q)
+{
+	Uqid **hp;
+
+	if(q == nil)
+		return;
+	qlock(&tab->l);
+	if(decref(&q->r) == 0){
+		hp = &tab->qids[uqidhash(q->oldpath)];
+		for(; *hp != nil; hp = &(*hp)->next)
+			if(*hp == q){
+				*hp = q->next;
+				free(q);
+				break;
+			}
+	}
+	qunlock(&tab->l);
+}
+
+Qid
+mkuqid(Chan *c, Uqid *qid)
+{
+	Qid q;
+
+	if(qid == nil)
+		return c->qid;
+	q.path = qid->newpath;
+	q.vers = c->qid.vers;
+	q.type = c->qid.type;
+	return q;
+}
--- /dev/null
+++ b/emu/port/win-x11-old.c
@@ -1,0 +1,847 @@
+#include "dat.h"
+#include "fns.h"
+#include "cursor.h"
+#include "keyboard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+
+/*
+ * alias defs for image types to overcome name conflicts
+ */
+typedef struct ICursor		ICursor;
+typedef struct IPoint		IPoint;
+typedef struct IRectangle	IRectangle;
+typedef struct CRemapTbl	CRemapTbl;
+struct ICursor
+{
+	int	w;
+	int	h;
+	int	hotx;
+	int	hoty;
+	char	*src;
+	char	*mask;
+};
+
+struct IPoint
+{
+	int	x;
+	int	y;
+};
+
+struct IRectangle
+{
+	IPoint	min;
+	IPoint	max;
+};
+
+struct CRemapTbl
+{
+	ulong inferno[256];	/* The corresponding inferno colormap vals */
+	ulong openslot[256];	
+	Bool cellused[256];
+	int cnt;
+	int opencnt;
+};
+
+enum
+{
+	DblTime	= 300		/* double click time in msec */
+};
+
+XColor			map[256];	/* Inferno colormap array */
+XColor			map7[128];	/* Inferno colormap array */
+uchar			map7to8[128][2];
+Colormap		xcmap;		/* Default shared colormap  */
+int 			infernotox11[256]; /* Values for mapping between */
+int 			x11toinferno[256]; /* X11 and inferno */
+int				x24bitswap = 0;	/* swap endian for 24bit RGB */
+
+static	int		triedscreen;
+static	XModifierKeymap *modmap;
+static	int		keypermod;
+static	Drawable	xdrawable;
+static	Atom		wm_take_focus;
+static	void		xexpose(XEvent*);
+static	void		xmouse(XEvent*);
+static	void		xkeyboard(XEvent*);
+static	void		xmapping(XEvent*);
+static	void		xproc(void*);
+static	void		xinitscreen(int, int);
+static	void		initmap(Window);
+static	GC		creategc(Drawable);
+static 	CRemapTbl	crtbl;
+static	void		graphicscmap(XColor*);
+	int		xscreendepth;
+	Drawable	xscreenid;
+	Display*	xdisplay;
+	Display*	xkmcon;
+	Visual		*xvis;
+	GC		xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc;
+	GC		xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0;
+
+char *gkscanid = "emu_x11";
+
+
+ulong*
+attachscreen(IRectangle *r, int *ld, int *width, int *softscreen)
+{
+	extern ulong* makememones();
+
+	r->min.x = 0;
+	r->min.y = 0;
+	r->max.x = Xsize;
+	r->max.y = Ysize;
+	*ld = 3;
+	*width = Xsize/4;
+	*softscreen = 1;
+	if(!triedscreen){
+		triedscreen = 1;
+		xinitscreen(Xsize, Ysize);
+		if(kproc("xproc", xproc, nil, 0) < 0) {
+			fprint(2, "emu: win-x11 can't make X proc\n");
+			return 0;
+		}
+	}
+	return makememones();
+}
+
+void
+flushmemscreen(IRectangle r)
+{
+	if(r.min.x > r.max.x)
+		return;
+	XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy,
+		r.min.x, r.min.y,
+		r.max.x-r.min.x, r.max.y-r.min.y, r.min.x, r.min.y);
+	XFlush(xdisplay);
+}
+
+static int
+revbyte(int b)
+{
+	int r;
+
+	r = 0;
+	r |= (b&0x01) << 7;
+	r |= (b&0x02) << 5;
+	r |= (b&0x04) << 3;
+	r |= (b&0x08) << 1;
+	r |= (b&0x10) >> 1;
+	r |= (b&0x20) >> 3;
+	r |= (b&0x40) >> 5;
+	r |= (b&0x80) >> 7;
+	return r;
+}
+
+static void
+gotcursor(ICursor c)
+{
+	Cursor xc;
+	XColor fg, bg;
+	Pixmap xsrc, xmask;
+	static Cursor xcursor;
+
+	if(c.src == nil){
+		if(xcursor != 0) {
+			XFreeCursor(xdisplay, xcursor);
+			xcursor = 0;
+		}
+		XUndefineCursor(xdisplay, xdrawable);
+		XFlush(xdisplay);
+		return;
+	}
+	xsrc = XCreateBitmapFromData(xdisplay, xdrawable, c.src, c.w, c.h);
+	xmask = XCreateBitmapFromData(xdisplay, xdrawable, c.mask, c.w, c.h);
+
+	fg = map[255];
+	bg = map[0];
+	fg.pixel = infernotox11[255];
+	bg.pixel = infernotox11[0];
+	xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -c.hotx, -c.hoty);
+	if(xc != 0) {
+		XDefineCursor(xdisplay, xdrawable, xc);
+		if(xcursor != 0)
+			XFreeCursor(xdisplay, xcursor);
+		xcursor = xc;
+	}
+	XFreePixmap(xdisplay, xsrc);
+	XFreePixmap(xdisplay, xmask);
+	XFlush(xdisplay);
+	free(c.src);
+}
+
+void
+setcursor(IPoint p)
+{
+	XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, p.x, p.y);
+	XFlush(xdisplay);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	ICursor ic;
+	IRectangle ir;
+	uchar *bs, *bc;
+	int i, h, j, bpl;
+	char *src, *mask, *csrc, *cmask;
+
+	/* Set the default system cursor */
+	src = nil;
+	mask = nil;
+	if(c->data != nil){
+		h = (c->maxy-c->miny)/2;
+		ir.min.x = c->minx;
+		ir.min.y = c->miny;
+		ir.max.x = c->maxx;
+		ir.max.y = c->maxy;
+		/* passing IRectangle to Rectangle is safe */
+		bpl = bytesperline(ir, 1);
+
+		i = h*bpl;
+		src = malloc(2*i);
+		if(src == nil)
+			return;
+		mask = src + i;
+
+		csrc = src;
+		cmask = mask;
+		bc = c->data;
+		bs = c->data + h*bpl;
+		for(i = 0; i < h; i++){
+			for(j = 0; j < bpl; j++) {
+				*csrc++ = revbyte(bs[j]);
+				*cmask++ = revbyte(bs[j] | bc[j]);
+			}
+			bs += bpl;
+			bc += bpl;
+		}
+	}
+	ic.w = 8*bpl;
+	ic.h = h;
+	ic.hotx = c->hotx;
+	ic.hoty = c->hoty;
+	ic.src = src;
+	ic.mask = mask;
+
+	gotcursor(ic);
+}
+
+static void
+xproc(void *arg)
+{
+	ulong mask;
+	XEvent event;
+
+	closepgrp(up->env->pgrp);
+	closefgrp(up->env->fgrp);
+	closeegrp(up->env->egrp);
+	closesigs(up->env->sigs);
+
+	mask = 	KeyPressMask|
+		KeyReleaseMask|
+		ButtonPressMask|
+		ButtonReleaseMask|
+		PointerMotionMask|
+		Button1MotionMask|
+		Button2MotionMask|
+		Button3MotionMask|
+		ExposureMask;
+
+	XSelectInput(xkmcon, xdrawable, mask);		
+	for(;;) {
+		XWindowEvent(xkmcon, xdrawable, mask, &event);
+		switch(event.type) {
+		case KeyPress:
+		case KeyRelease:
+			xkeyboard(&event);
+			break;
+		case ButtonPress:
+		case ButtonRelease:
+		case MotionNotify:
+			xmouse(&event);
+			break;
+		case Expose:
+			xexpose(&event);
+			break;
+		case MappingNotify:
+			xmapping(&event);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static int
+shutup(Display *d, XErrorEvent *e)
+{
+	char buf[200];
+	print("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code);
+	XGetErrorText(d, e->error_code, buf, sizeof(buf));
+	print("%s\n", buf);
+	USED(d);
+	USED(e);
+	return 0;
+}
+
+static void
+xinitscreen(int xsize, int ysize)
+{
+	int i, pmid;
+	char *argv[2];
+	char *disp_val;
+	Window rootwin;
+	XWMHints hints;
+	Screen *screen;
+	XVisualInfo xvi;
+	int rootscreennum;
+	XTextProperty name;
+	XClassHint classhints;
+	XSizeHints normalhints;
+	XSetWindowAttributes attrs;
+ 
+	xscreenid = 0;
+	xdrawable = 0;
+
+	xdisplay = XOpenDisplay(NULL);
+	if(xdisplay == 0){
+		disp_val = getenv("DISPLAY");
+		if(disp_val == 0)
+			disp_val = "not set";
+		fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val);
+		cleanexit(0);
+	}
+
+	rootscreennum = DefaultScreen(xdisplay);
+	rootwin = DefaultRootWindow(xdisplay);
+
+	xscreendepth = DefaultDepth(xdisplay, rootscreennum);
+	if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi)
+	|| XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){
+		xvis = xvi.visual;
+		xscreendepth = 24;
+		xtblbit = 1;
+	}
+	else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi)
+	|| XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){
+		if(xscreendepth > 8) {
+			fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth);
+			cleanexit(0);
+		}
+		xvis = xvi.visual;
+		xscreendepth = 8;
+	}
+	else{
+		if(xscreendepth != 8){
+			fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth);
+			cleanexit(0);
+		}
+		xvis = DefaultVisual(xdisplay, rootscreennum);
+	}
+
+	screen = DefaultScreenOfDisplay(xdisplay);
+	xcmap = DefaultColormapOfScreen(screen);
+
+	if(xvis->class != StaticColor){
+		graphicscmap(map);
+		initmap(rootwin);
+	}
+
+	if(modmap = XGetModifierMapping(xdisplay))
+		keypermod = modmap->max_keypermod;
+
+	attrs.colormap = xcmap;
+	attrs.background_pixel = 0;
+	attrs.border_pixel = 0;
+	/* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
+	xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
+
+	/*
+	 * set up property as required by ICCCM
+	 */
+	name.value = "inferno";
+	name.encoding = XA_STRING;
+	name.format = 8;
+	name.nitems = strlen(name.value);
+	normalhints.flags = USSize|PMaxSize;
+	normalhints.max_width = normalhints.width = xsize;
+	normalhints.max_height = normalhints.height = ysize;
+	hints.flags = InputHint|StateHint;
+	hints.input = 1;
+	hints.initial_state = NormalState;
+	classhints.res_name = "inferno";
+	classhints.res_class = "Inferno";
+	argv[0] = "inferno";
+	argv[1] = nil;
+	XSetWMProperties(xdisplay, xdrawable,
+		&name,			/* XA_WM_NAME property for ICCCM */
+		&name,			/* XA_WM_ICON_NAME */
+		argv,			/* XA_WM_COMMAND */
+		1,			/* argc */
+		&normalhints,		/* XA_WM_NORMAL_HINTS */
+		&hints,			/* XA_WM_HINTS */
+		&classhints);		/* XA_WM_CLASS */
+
+	/*
+	 * put the window on the screen
+	 */
+	XMapWindow(xdisplay, xdrawable);
+
+	xscreenid = XCreatePixmap(xdisplay, xdrawable, xsize, ysize, xscreendepth);
+	XFlush(xdisplay);
+
+	xgcfill = creategc(xscreenid);
+	XSetFillStyle(xdisplay, xgcfill, FillSolid);
+	xgccopy = creategc(xscreenid);
+	xgcsimplesrc = creategc(xscreenid);
+	XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled);
+	xgczero = creategc(xscreenid);
+	xgcreplsrc = creategc(xscreenid);
+	XSetFillStyle(xdisplay, xgcreplsrc, FillTiled);
+
+	pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1);
+	xgcfill0 = creategc(pmid);
+	XSetFillStyle(xdisplay, xgcfill0, FillSolid);
+	xgccopy0 = creategc(pmid);
+	xgcsimplesrc0 = creategc(pmid);
+	XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled);
+	xgczero0 = creategc(pmid);
+	xgcreplsrc0 = creategc(pmid);
+	XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled);
+	XFreePixmap(xdisplay, pmid);
+
+	XSetForeground(xdisplay, xgccopy, infernotox11[0]);
+	XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize);
+
+	xkmcon = XOpenDisplay(NULL);
+	if(xkmcon == 0){
+		disp_val = getenv("DISPLAY");
+		if(disp_val == 0)
+			disp_val = "not set";
+		fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val);
+		cleanexit(0);
+	}
+}
+
+static void
+graphicscmap(XColor *map)
+{
+	int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
+
+	for(r=0; r!=4; r++) {
+		for(g = 0; g != 4; g++) {
+			for(b = 0; b!=4; b++) {
+				for(v = 0; v!=4; v++) {
+					den=r;
+					if(g > den)
+						den=g;
+					if(b > den)
+						den=b;
+					/* divide check -- pick grey shades */
+					if(den==0)
+						cr=cg=cb=v*17;
+					else {
+						num=17*(4*den+v);
+						cr=r*num/den;
+						cg=g*num/den;
+						cb=b*num/den;
+					}
+					idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
+					idx = 255 - idx;
+					map[idx].red = cr*0x0101;
+					map[idx].green = cg*0x0101;
+					map[idx].blue = cb*0x0101;
+					map[idx].pixel = idx;
+					map[idx].flags = DoRed|DoGreen|DoBlue;
+
+					v7 = v >> 1;
+					idx7 = r*32 + v7*16 + g*4 + b;
+					if((v & 1) == v7){
+						map7to8[idx7][0] = idx;
+						if(den == 0) { 		/* divide check -- pick grey shades */
+							cr = ((255.0/7.0)*v7)+0.5;
+							cg = cr;
+							cb = cr;
+						}
+						else {
+							num=17*15*(4*den+v7*2)/14;
+							cr=r*num/den;
+							cg=g*num/den;
+							cb=b*num/den;
+						}
+						map7[idx7].red = cr*0x0101;
+						map7[idx7].green = cg*0x0101;
+						map7[idx7].blue = cb*0x0101;
+						map7[idx7].pixel = idx7;
+						map7[idx7].flags = DoRed|DoGreen|DoBlue;
+					}
+					else
+						map7to8[idx7][1] = idx;
+				}
+			}
+		}
+	}
+}
+
+/*
+ * Initialize and install the Inferno colormap as a private colormap for this
+ * application.  Inferno gets the best colors here when it has the cursor focus.
+ */  
+static void 
+initmap(Window w)
+{
+	XColor c;
+	XColor xcol;
+	ulong v;
+	int i, pix, ncmaps;
+
+	if(xscreendepth <= 1)
+		return;
+
+	if(xvis->class == TrueColor || xvis->class == DirectColor) {
+	int color_order_init = 0;
+	uint pp, p;
+
+		for(i = 0; i < 256; i++) {
+			c = map[i];
+			/* find out index into colormap for our RGB */
+			if(!XAllocColor(xdisplay, xcmap, &c)) {
+				fprint(2, "emu: win-x11 can't alloc color\n");
+				cleanexit(0);
+			}
+
+			/* The pixel value returned from XGetPixel needs to
+			 * be converted to RGB so we can call rgb2cmap()
+			 * to translate between 24 bit X and our color. Unfortunately,
+			 * the return value appears to be display server endian 
+			 * dependant. Therefore, we run some heuristics to later
+			 * determine how to mask the int value correctly.
+			 * Yeah, I know we can look at xvis->byte_order but 
+			 * some displays say MSB even though they run on LSB.
+			 * Besides, this is more anal.
+			 */
+
+			p  = c.pixel;
+			pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
+			if(!color_order_init && (pp!=map[i].pixel)) {
+				/* check if endian is other way */
+				pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
+				if(pp!=map[i].pixel) {
+					fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+					cleanexit(0);
+				}
+				color_order_init = 1;
+				x24bitswap = 1;
+			}
+
+			if(color_order_init) {
+				pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
+				if(pp!=map[i].pixel) {
+					fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+					cleanexit(0);
+				}
+				/* no x11toinferno; later use rgb2cmap() */
+				infernotox11[map[i].pixel] = c.pixel;
+			}
+			else if(pp!=map[i].pixel) {
+				fprint(2, "emu: win-x11 can't convert 24bit colors\n");
+				cleanexit(0);
+			}
+			else {
+				/* no x11toinferno; later use rgb2cmap() */
+				infernotox11[map[i].pixel] = c.pixel;
+			}
+		}
+	}
+	else if(xvis->class == PseudoColor) {
+		if(xtblbit == 0){
+			xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); 
+			XStoreColors(xdisplay, xcmap, map, 256);
+			for(i = 0; i < 256; i++) {
+				infernotox11[i] = i;
+				x11toinferno[i] = i;
+			}
+		}
+		else {
+			for(i = 0; i < 128; i++) {
+				c = map7[i];
+				if(!XAllocColor(xdisplay, xcmap, &c)) {
+					fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n");
+					cleanexit(0);
+				}
+				infernotox11[map7to8[i][0]] = c.pixel;
+				infernotox11[map7to8[i][1]] = c.pixel;
+				x11toinferno[c.pixel] = map7to8[i][0];
+			}
+		}
+	}
+	else {
+		xtblbit = 0;
+		fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class);
+	}
+	return;
+}
+
+static void
+xmapping(XEvent *e)
+{
+	XMappingEvent *xe;
+
+	if(e->type != MappingNotify)
+		return;
+	xe = (XMappingEvent*)e;
+	if(modmap)
+		XFreeModifiermap(modmap);
+	modmap = XGetModifierMapping(xe->display);
+	if(modmap)
+		keypermod = modmap->max_keypermod;
+}
+
+
+/*
+ * Disable generation of GraphicsExpose/NoExpose events in the GC.
+ */
+static GC
+creategc(Drawable d)
+{
+	XGCValues gcv;
+
+	gcv.function = GXcopy;
+	gcv.graphics_exposures = False;
+	return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv);
+}
+
+static void
+xexpose(XEvent *e)
+{
+	IRectangle r;
+	XExposeEvent *xe;
+
+	if(e->type != Expose)
+		return;
+	xe = (XExposeEvent*)e;
+	r.min.x = xe->x;
+	r.min.y = xe->y;
+	r.max.x = xe->x + xe->width;
+	r.max.y = xe->y + xe->height;
+	drawqlock();
+	flushmemscreen(r);
+	drawqunlock();
+}
+
+
+static void
+xkeyboard(XEvent *e)
+{
+	int ind, n;
+	KeySym k;
+	Rune r;
+	unsigned int md;
+	char buf[1];
+
+	if(gkscanq) {
+		uchar ch = (KeyCode)e->xkey.keycode;
+		if(e->xany.type == KeyRelease)
+			ch |= 0x80;
+		qproduce(gkscanq, &ch, 1);
+		return;
+	}
+	
+	/*
+	 * I tried using XtGetActionKeysym, but it didn't seem to
+	 * do case conversion properly
+	 * (at least, with Xterminal servers and R4 intrinsics)
+	 */
+	if(e->xany.type != KeyPress)
+		return;
+
+	md = e->xkey.state;
+	ind = 0;
+	if(md & ShiftMask)
+		ind = 1;
+
+	k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+
+	/* May have to try unshifted version */
+	if(k == NoSymbol && ind == 1)
+		k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0);
+
+	if(k == XK_Multi_key || k == NoSymbol)
+		return;
+	if(k&0xFF00){
+		switch(k){
+		case XK_BackSpace:
+		case XK_Tab:
+		case XK_Escape:
+		case XK_Delete:
+		case XK_KP_0:
+		case XK_KP_1:
+		case XK_KP_2:
+		case XK_KP_3:
+		case XK_KP_4:
+		case XK_KP_5:
+		case XK_KP_6:
+		case XK_KP_7:
+		case XK_KP_8:
+		case XK_KP_9:
+		case XK_KP_Divide:
+		case XK_KP_Multiply:
+		case XK_KP_Subtract:
+		case XK_KP_Add:
+		case XK_KP_Decimal:
+			k &= 0x7F;
+			break;
+		case XK_Linefeed:
+			k = '\r';
+			break;
+		case XK_KP_Enter:
+		case XK_Return:
+			k = '\n';
+			break;
+		case XK_Alt_L:
+		case XK_Alt_R:
+		case XK_Meta_L:
+		case XK_Meta_R:
+			k = Latin;
+			break;
+		case XK_Left:
+		case XK_KP_Left:
+		  k = Left;
+		  break;
+		case XK_Down:
+		case XK_KP_Down:
+		  k = Down;
+		  break;
+		case XK_Right:
+		case XK_KP_Right:
+		  k = Right;
+		  break;
+		case XK_Up:
+		case XK_KP_Up:
+		  k = Up;
+		  break;
+		case XK_Home:
+		case XK_KP_Home:
+		  k = Home;
+		  break;
+		case XK_End:
+		case XK_KP_End:
+		  k = End;
+		  break;
+		case XK_Page_Up:
+		case XK_KP_Page_Up:
+		  k = Pgup;
+		  break;
+		case XK_Page_Down:
+		case XK_KP_Page_Down:
+		  k = Pgdown;
+		  break;
+		default:		/* not ISO-1 or tty control */
+			return;
+		}
+	}
+	/* Compensate for servers that call a minus a hyphen */
+	if(k == XK_hyphen)
+		k = XK_minus;
+	/* Do control mapping ourselves if translator doesn't */
+	if(md & ControlMask)
+		k &= 0x9f;
+	if(k == '\t' && ind)
+		k = BackTab;
+	
+	if(md & Mod1Mask)
+		k = APP|(k&0xff);
+	if(k == NoSymbol) 
+		return;
+
+	gkbdputc(gkbdq, k);
+}
+
+static void
+xmouse(XEvent *e)
+{
+	int s, dbl;
+	XButtonEvent *be;
+	XMotionEvent *me;
+	XEvent motion;
+	Pointer m;
+	static ulong lastb, lastt;
+
+	dbl = 0;
+	switch(e->type){
+	case ButtonPress:
+		be = (XButtonEvent *)e;
+		m.x = be->x;
+		m.y = be->y;
+		s = be->state;
+		if(be->button == lastb && be->time - lastt < DblTime)
+			dbl = 1;
+		lastb = be->button;
+		lastt = be->time;
+		switch(be->button){
+		case 1:
+			s |= Button1Mask;
+			break;
+		case 2:
+			s |= Button2Mask;
+			break;
+		case 3:
+			s |= Button3Mask;
+			break;
+		}
+		break;
+	case ButtonRelease:
+		be = (XButtonEvent *)e;
+		m.x = be->x;
+		m.y = be->y;
+		s = be->state;
+		switch(be->button){
+		case 1:
+			s &= ~Button1Mask;
+			break;
+		case 2:
+			s &= ~Button2Mask;
+			break;
+		case 3:
+			s &= ~Button3Mask;
+			break;
+		}
+		break;
+	case MotionNotify:
+		me = (XMotionEvent *) e;
+
+		/* remove excess MotionNotify events from queue and keep last one */
+		while(XCheckTypedWindowEvent(xkmcon, xdrawable, MotionNotify, &motion) == True)
+			me = (XMotionEvent *) &motion;
+
+		s = me->state;
+		m.x = me->x;
+		m.y = me->y;
+		break;
+	default:
+		return;
+	}
+
+	m.b = 0;
+	if(s & Button1Mask)
+		m.b |= 1;
+	if(s & Button2Mask)
+		m.b |= 2;
+	if(s & Button3Mask)
+		m.b |= 4;
+	if(dbl)
+		m.b |= 1<<4;
+
+	m.modify = 1;
+	mouseproduce(m);
+}
+
--- /dev/null
+++ b/emu/port/win-x11a.c
@@ -1,0 +1,1621 @@
+/*
+ * This implementation of the screen functions for X11 uses the
+ * portable implementation of the Inferno drawing operations (libmemdraw)
+ * to do the work, then has flushmemscreen copy the result to the X11 display.
+ * Thus it potentially supports all colour depths but with a possible
+ * performance penalty (although it tries to use the X11 shared memory extension
+ * to copy the result to the screen, which might reduce the latter).
+ *
+ *       CraigN 
+ */
+
+#define _GNU_SOURCE 1
+#define XTHREADS
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define Colormap	XColormap
+#define Cursor		XCursor
+#define Display		XDisplay
+#define Drawable	XDrawable
+#define Font		XFont
+#define GC		XGC
+#define Point		XPoint
+#define Rectangle	XRectangle
+#define Screen		XScreen
+#define Visual		XVisual
+#define Window		XWindow
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XShm.h>
+
+#include "keysym2ucs.h"
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef XDrawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+static int displaydepth;
+extern ulong displaychan;
+
+enum
+{
+	DblTime	= 300		/* double click time in msec */
+};
+
+/* screen data .... */
+static uchar*	gscreendata;
+static uchar*	xscreendata;
+
+XColor	map[256];	/* Inferno colormap array */
+XColor	mapr[256];	/* Inferno red colormap array */
+XColor	mapg[256];	/* Inferno green colormap array */
+XColor	mapb[256];	/* Inferno blue colormap array */
+XColor	map7[128];	/* Inferno colormap array */
+uchar	map7to8[128][2];
+
+/* for copy/paste, lifted from plan9ports via drawterm */
+static Atom clipboard; 
+static Atom utf8string;
+static Atom targets;
+static Atom text;
+static Atom compoundtext;
+
+static Atom cursorchange;
+
+static XColormap		xcmap;		/* Default shared colormap  */
+static int 		infernotox11[256]; /* Values for mapping between */
+static int 		infernortox11[256]; /* Values for mapping between */
+static int 		infernogtox11[256]; /* Values for mapping between */
+static int 		infernobtox11[256]; /* Values for mapping between */
+static int		triedscreen;
+static XDrawable		xdrawable;
+static void		xexpose(XEvent*);
+static void		xmouse(XEvent*);
+static void		xkeyboard(XEvent*);
+static void		xsetcursor(XEvent*);
+static void		xkbdproc(void*);
+static void		xdestroy(XEvent*);
+static void		xselect(XEvent*, XDisplay*);
+static void		xproc(void*);
+static void		xinitscreen(int, int, ulong, ulong*, int*);
+static void		initxcmap(XWindow);
+static XGC		creategc(XDrawable);
+static void		graphicsgmap(XColor*, int);
+static void		graphicscmap(XColor*);
+static void		graphicsrgbmap(XColor*, XColor*, XColor*);
+
+static int		xscreendepth;
+static	XDisplay*	xdisplay;	/* used holding draw lock */
+static	XDisplay*	xmcon;	/* used only in xproc */
+static	XDisplay*	xkbdcon;	/* used only in xkbdproc */
+static	XDisplay*	xsnarfcon;	/* used holding clip.lk */
+static XVisual		*xvis;
+static XGC		xgc;
+static XImage 		*img;
+static int              is_shm;
+
+static int putsnarf, assertsnarf;
+char *gkscanid = "emu_x11";
+
+/*
+ * The documentation for the XSHM extension implies that if the server
+ * supports XSHM but is not the local machine, the XShm calls will
+ * return False; but this turns out not to be the case.  Instead, the
+ * server throws a BadAccess error.  So, we need to catch X errors
+ * around all of our XSHM calls, sigh.
+ */
+static int shm_got_x_error = 0;
+static XErrorHandler old_handler = 0;
+static XErrorHandler old_io_handler = 0;
+
+static int
+shm_ehandler(XDisplay *dpy, XErrorEvent *error)
+{
+	shm_got_x_error = 1;
+	return 0;
+}
+
+static void
+clean_errhandlers(void)
+{
+	/* remove X11 error handler(s) */
+	if(old_handler)
+		XSetErrorHandler(old_handler); 
+	old_handler = 0;
+	if(old_io_handler)
+		XSetErrorHandler(old_io_handler); 
+	old_io_handler = 0;
+}
+
+static int
+makesharedfb(void)
+{
+	XShmSegmentInfo *shminfo;
+
+	shminfo = malloc(sizeof(XShmSegmentInfo));
+	if(shminfo == nil) {
+		fprint(2, "emu: cannot allocate XShmSegmentInfo\n");
+		cleanexit(0);
+	}
+
+	/* setup to catch X11 error(s) */
+	XSync(xdisplay, 0); 
+	shm_got_x_error = 0; 
+	if(old_handler != shm_ehandler)
+		old_handler = XSetErrorHandler(shm_ehandler);
+	if(old_io_handler != shm_ehandler)
+		old_io_handler = XSetErrorHandler(shm_ehandler);
+
+	img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 
+			      NULL, shminfo, Xsize, Ysize);
+	XSync(xdisplay, 0);
+
+	/* did we get an X11 error? if so then try without shm */
+	if(shm_got_x_error) {
+		free(shminfo);
+		shminfo = NULL;
+		clean_errhandlers();
+		return 0;
+	}
+	
+	if(img == nil) {
+		fprint(2, "emu: cannot allocate virtual screen buffer\n");
+		cleanexit(0);
+	}
+	
+	shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, IPC_CREAT|0777);
+	shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0);
+	shminfo->readOnly = True;
+
+	if(!XShmAttach(xdisplay, shminfo)) {
+		fprint(2, "emu: cannot allocate virtual screen buffer\n");
+		cleanexit(0);
+	}
+	XSync(xdisplay, 0);
+
+	/*
+	 * Delete the shared segment right now; the segment
+	 * won't actually go away until both the client and
+	 * server have deleted it.  The server will delete it
+	 * as soon as the client disconnects, so we might as
+	 * well delete our side now as later.
+	 */
+	shmctl(shminfo->shmid, IPC_RMID, 0);
+
+	/* did we get an X11 error? if so then try without shm */
+	if(shm_got_x_error) {
+		XDestroyImage(img);
+		XSync(xdisplay, 0);
+		free(shminfo);
+		shminfo = NULL;
+		clean_errhandlers();
+		return 0;
+	}
+
+	gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3));
+	if(gscreendata == nil) {
+		fprint(2, "emu: cannot allocate screen buffer (%dx%dx%d)\n", Xsize, Ysize, displaydepth);
+		cleanexit(0);
+	}
+	xscreendata = (uchar*)img->data;
+	
+	clean_errhandlers();
+	return 1;
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+	int depth;
+
+	Xsize &= ~0x3;	/* ensure multiple of 4 */
+
+	r->min.x = 0;
+	r->min.y = 0;
+	r->max.x = Xsize;
+	r->max.y = Ysize;
+
+	if(!triedscreen){
+		xinitscreen(Xsize, Ysize, displaychan, chan, d);
+		/*
+		 * moved xproc from here to end since it could cause an expose event and
+		 * hence a flushmemscreen before xscreendata is initialized
+		 */
+	}
+	else{
+		*chan = displaychan;
+		*d = displaydepth;
+	}
+
+	*width = (Xsize/4)*(*d/8);
+	*softscreen = 1;
+	displaychan = *chan;
+	displaydepth = *d;
+
+	/* check for X Shared Memory Extension */
+	is_shm = XShmQueryExtension(xdisplay);
+	
+	if(!is_shm || !makesharedfb()){
+		is_shm = 0;
+		depth = xscreendepth;
+		if(depth == 24)
+			depth = 32;
+
+		/* allocate virtual screen */	
+		gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3));
+		xscreendata = malloc(Xsize * Ysize * (depth >> 3));
+		if(gscreendata == nil || xscreendata == nil) {
+			fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d[%d])\n", Xsize, Ysize, displaydepth, depth);
+			return 0;
+		}
+		img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0, 
+				   (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3));
+		if(img == nil) {
+			fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d)\n", Xsize, Ysize, depth);
+			return 0;
+		}
+		
+	}
+
+	if(!triedscreen){
+		triedscreen = 1;
+		kproc("xproc", xproc, xmcon, 0);
+		kproc("xkbdproc", xkbdproc, xkbdcon, KPX11);	/* silly stack size for bloated X11 */
+	}
+
+	return gscreendata;
+}
+
+static void
+copy32to32(Rectangle r)
+{
+	int dx, width;
+	uchar *p, *ep, *cp;
+	u32int v, w, *dp, *wp, *edp, *lp;
+
+	width = Dx(r);
+	dx = Xsize - width;
+	dp = (u32int*)(gscreendata + (r.min.y * Xsize + r.min.x) * 4);
+	wp = (u32int*)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+	edp = (u32int*)(gscreendata + (r.max.y * Xsize + r.max.x) * 4);
+	while(dp < edp) {
+		lp = dp + width;
+		while(dp < lp){
+			v = *dp++;
+			w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0;
+			*wp++ = w;
+		}
+		dp += dx;
+		wp += dx;
+	}
+}
+
+static void
+copy8to32(Rectangle r)
+{
+	int dx, width;
+	uchar *p, *ep, *lp;
+	u32int *wp;
+
+	width = Dx(r);
+	dx = Xsize - width;
+	p = gscreendata + r.min.y * Xsize + r.min.x;
+	wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+	ep = gscreendata + r.max.y * Xsize + r.max.x;
+	while(p < ep) {
+		lp = p + width;
+		while(p < lp) 
+			*wp++ = infernotox11[*p++];
+		p += dx;
+		wp += dx;
+	}
+}
+
+static void
+copy8to24(Rectangle r)
+{
+	int dx, width, v;
+	uchar *p, *cp, *ep, *lp;
+
+	width = Dx(r);
+	dx = Xsize - width;
+	p = gscreendata + r.min.y * Xsize + r.min.x;
+	cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3;
+	ep = gscreendata + r.max.y * Xsize + r.max.x;
+	while(p < ep) {
+		lp = p + width;
+		while(p < lp){
+			v = infernotox11[*p++];
+			cp[0] = (v>>16)&0xff;
+			cp[1] = (v>>8)&0xff;
+			cp[2] = (v>>0)&0xff;
+			cp += 3;
+		}
+		p += dx;
+		cp += 3*dx;
+	}
+}
+
+static void
+copy8to16(Rectangle r)
+{
+	int dx, width;
+	uchar *p, *ep, *lp;
+	u16int *sp;
+
+	width = Dx(r);
+	dx = Xsize - width;
+	p = gscreendata + r.min.y * Xsize + r.min.x;
+	sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2);
+	ep = gscreendata + r.max.y * Xsize + r.max.x;
+	while(p < ep) {
+		lp = p + width;
+		while(p < lp) 
+			*sp++ = infernotox11[*p++];
+		p += dx;
+		sp += dx;
+	}
+}
+
+static void
+copy8to8(Rectangle r)
+{
+	int dx, width;
+	uchar *p, *cp, *ep, *lp;
+
+	width = Dx(r);
+	dx = Xsize - width;
+	p = gscreendata + r.min.y * Xsize + r.min.x;
+	cp = xscreendata + r.min.y * Xsize + r.min.x;
+	ep = gscreendata + r.max.y * Xsize + r.max.x;
+	while(p < ep) {
+		lp = p + width;
+		while(p < lp)
+			*cp++ = infernotox11[*p++];
+		p += dx;
+		cp += dx;
+	}
+}
+
+static void
+copy8topixel(Rectangle r)
+{
+	int x, y;
+	uchar *p;
+
+	/* mainly for 4-bit greyscale */
+	for (y = r.min.y; y < r.max.y; y++) {
+		x = r.min.x;
+		p = gscreendata + y * Xsize + x;
+		while (x < r.max.x)
+			XPutPixel(img, x++, y, infernotox11[*p++]);
+	}
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	char chanbuf[16];
+
+	// Clip to screen
+	if(r.min.x < 0)
+		r.min.x = 0;
+	if(r.min.y < 0)
+		r.min.y = 0;
+	if(r.max.x >= Xsize)
+		r.max.x = Xsize - 1;
+	if(r.max.y >= Ysize)
+                r.max.y = Ysize - 1;
+	if(r.max.x <= r.min.x || r.max.y <= r.min.y)
+		return;
+
+	switch(displaydepth){
+	case 32:
+		copy32to32(r);
+		break;
+	case 8:
+		switch(xscreendepth){
+		case 24:
+			/* copy8to24(r); */	/* doesn't happen? */
+			/* break */
+		case 32:
+			copy8to32(r);
+			break;
+		case 16:
+			copy8to16(r);
+			break;
+		case 8:
+			copy8to8(r);
+			break;
+		default:
+			copy8topixel(r);
+			break;
+		}
+		break;
+	default:
+		fprint(2, "emu: bad display depth %d chan %s xscreendepth %d\n", displaydepth,
+			chantostr(chanbuf, displaychan), xscreendepth);
+		cleanexit(0);
+	}
+
+	XLockDisplay(xdisplay);
+	/* Display image on X11 */
+	if(is_shm)
+		XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r), 0);
+	else
+		XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r));
+	XSync(xdisplay, 0);
+	XUnlockDisplay(xdisplay);
+}
+
+static int
+revbyte(int b)
+{
+	int r;
+
+	r = 0;
+	r |= (b&0x01) << 7;
+	r |= (b&0x02) << 5;
+	r |= (b&0x04) << 3;
+	r |= (b&0x08) << 1;
+	r |= (b&0x10) >> 1;
+	r |= (b&0x20) >> 3;
+	r |= (b&0x40) >> 5;
+	r |= (b&0x80) >> 7;
+	return r;
+}
+
+void
+setpointer(int x, int y)
+{
+	drawqlock();
+	XLockDisplay(xdisplay);
+	XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y);
+	XFlush(xdisplay);
+	XUnlockDisplay(xdisplay);
+	drawqunlock();
+}
+
+static void
+xkbdproc(void *arg)
+{
+	XEvent event;
+	XDisplay *xd;
+
+	xd = arg;
+
+	/* BEWARE: the value of up is not defined for this proc on some systems */
+
+	XLockDisplay(xd);	/* should be ours alone */
+	XSelectInput(xd, xdrawable, KeyPressMask | KeyReleaseMask);		
+	for(;;){
+		XNextEvent(xd, &event);
+		xkeyboard(&event);
+		xsetcursor(&event);
+	}
+}
+
+static void
+xproc(void *arg)
+{
+	ulong mask;
+	XEvent event;
+	XDisplay *xd;
+
+	closepgrp(up->env->pgrp);
+	closefgrp(up->env->fgrp);
+	closeegrp(up->env->egrp);
+	closesigs(up->env->sigs);
+
+	xd = arg;
+	mask = ButtonPressMask|
+		ButtonReleaseMask|
+		PointerMotionMask|
+		Button1MotionMask|
+		Button2MotionMask|
+		Button3MotionMask|
+		Button4MotionMask|
+		Button5MotionMask|
+		ExposureMask|
+		StructureNotifyMask;
+
+	XLockDisplay(xd);	/* should be ours alone */
+	XSelectInput(xd, xdrawable, mask);		
+	for(;;){
+		XNextEvent(xd, &event);
+		xselect(&event, xd);
+		xmouse(&event);
+		xexpose(&event);
+		xdestroy(&event);
+	}
+}
+
+/*
+ * this crud is here because X11 can put huge amount of data
+ * on the stack during keyboard translation and cursor changing(!).
+ * we do both in a dedicated process with lots of stack, perhaps even enough.
+ */
+
+enum {
+	CursorSize=	32	/* biggest cursor size */
+};
+
+typedef struct ICursor ICursor;
+struct ICursor {
+	int	inuse;
+	int	modify;
+	int	hotx;
+	int	hoty;
+	int	w;
+	int	h;
+	uchar	src[(CursorSize/8)*CursorSize];	/* image and mask bitmaps */
+	uchar	mask[(CursorSize/8)*CursorSize];
+};
+static ICursor icursor;
+
+static void
+xcurslock(void)
+{
+	while(_tas(&icursor.inuse) != 0)
+		osyield();
+}
+
+static void
+xcursunlock(void)
+{
+	coherence();
+	icursor.inuse = 0;
+}
+
+static void
+xcursnotify(void)
+{
+	XClientMessageEvent e;
+
+	memset(&e, 0, sizeof e);
+	e.type = ClientMessage;
+	e.window = xdrawable;
+	e.message_type = cursorchange;
+	e.format = 8;
+	XLockDisplay(xdisplay);
+	XSendEvent(xdisplay, xdrawable, True, KeyPressMask, (XEvent*)&e);
+	XFlush(xdisplay);
+	XUnlockDisplay(xdisplay);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+	uchar *bs, *bc, *ps, *pm;
+	int i, j, w, h, bpl;
+
+	if(c->data == nil){
+		drawqlock();
+		if(icursor.h != 0){
+			xcurslock();
+			icursor.h = 0;
+			icursor.modify = 1;
+			xcursunlock();
+		}
+		xcursnotify();
+		drawqunlock();
+		return;
+	}
+
+	drawqlock();
+	xcurslock();
+	icursor.modify = 0;	/* xsetcursor will now ignore it */
+	xcursunlock();
+
+	h = (c->maxy-c->miny)/2;	/* image, then mask */
+	if(h > CursorSize)
+		h = CursorSize;
+	bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+	w = bpl;
+	if(w > CursorSize/8)
+		w = CursorSize/8;
+
+	ps = icursor.src;
+	pm = icursor.mask;
+	bc = c->data;
+	bs = c->data + h*bpl;
+	for(i = 0; i < h; i++){
+		for(j = 0; j < bpl && j < w; j++) {
+			*ps++ = revbyte(bs[j]);
+			*pm++ = revbyte(bs[j] | bc[j]);
+		}
+		bs += bpl;
+		bc += bpl;
+	}
+	icursor.h = h;
+	icursor.w = w*8;
+	icursor.hotx = c->hotx;
+	icursor.hoty = c->hoty;
+	icursor.modify = 1;
+	xcursnotify();
+	drawqunlock();
+}
+
+static void
+xsetcursor(XEvent *e)
+{
+	ICursor ic;
+	XCursor xc;
+	XColor fg, bg;
+	Pixmap xsrc, xmask;
+	static XCursor xcursor;
+
+	if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange)
+		return;
+
+	xcurslock();
+	if(icursor.modify == 0){
+		xcursunlock();
+		return;
+	}
+	icursor.modify = 0;
+	if(icursor.h == 0){
+		xcursunlock();
+		/* set the default system cursor */
+		if(xcursor != 0) {
+			XFreeCursor(xkbdcon, xcursor);
+			xcursor = 0;
+		}
+		XUndefineCursor(xkbdcon, xdrawable);
+		XFlush(xkbdcon);
+		return;
+	}
+	ic = icursor;
+	xcursunlock();
+
+	xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h);
+	xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h);
+
+	fg = map[0];
+	bg = map[255];
+	fg.pixel = infernotox11[0];
+	bg.pixel = infernotox11[255];
+	xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty);
+	if(xc != 0) {
+		XDefineCursor(xkbdcon, xdrawable, xc);
+		if(xcursor != 0)
+			XFreeCursor(xkbdcon, xcursor);
+		xcursor = xc;
+	}
+	XFreePixmap(xkbdcon, xsrc);
+	XFreePixmap(xkbdcon, xmask);
+	XFlush(xkbdcon);
+}
+
+typedef struct Mg Mg;
+struct Mg
+{
+	int	code;
+	int	bit;
+	int	len;
+	ulong	mask;
+};
+
+static int
+maskx(Mg* g, int code, ulong mask)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(mask & (1<<i))
+			break;
+	if(i == 32)
+		return 0;
+	g->code = code;
+	g->bit = i;
+	g->mask = mask;
+	for(g->len = 0; i<32 && (mask & (1<<i))!=0; i++)
+		g->len++;
+	return 1;
+}
+
+/*
+ * for a given depth, we need to check the available formats
+ * to find how many actual bits are used per pixel.
+ */
+static int
+xactualdepth(int screenno, int depth)
+{
+	XPixmapFormatValues *pfmt;
+	int i, n;
+
+	pfmt = XListPixmapFormats(xdisplay, &n);
+	for(i=0; i<n; i++)
+		if(pfmt[i].depth == depth)
+			return pfmt[i].bits_per_pixel;
+	return -1;
+}
+
+static int
+xtruevisual(int screenno, int reqdepth, XVisualInfo *vi, ulong *chan)
+{
+	XVisual *xv;
+	Mg r, g, b;
+	int pad, d;
+	ulong c;
+	char buf[30];
+
+	if(XMatchVisualInfo(xdisplay, screenno, reqdepth, TrueColor, vi) ||
+	   XMatchVisualInfo(xdisplay, screenno, reqdepth, DirectColor, vi)){
+		xv = vi->visual;
+		if(maskx(&r, CRed, xv->red_mask) &&
+		   maskx(&g, CGreen, xv->green_mask) &&
+		   maskx(&b, CBlue, xv->blue_mask)){
+			d = xactualdepth(screenno, reqdepth);
+			if(d < 0)
+				return 0;
+			pad = d - (r.len + g.len + b.len);
+			if(0){
+				fprint(2, "r: %8.8lux %d %d\ng: %8.8lux %d %d\nb: %8.8lux %d %d\n",
+				 xv->red_mask, r.bit, r.len, xv->green_mask, g.bit, g.len, xv->blue_mask, b.bit, b.len);
+			}
+			if(r.bit > b.bit)
+				c = CHAN3(CRed, r.len, CGreen, g.len, CBlue, b.len);
+			else
+				c = CHAN3(CBlue, b.len, CGreen, g.len, CRed, r.len);
+			if(pad > 0)
+				c |= CHAN1(CIgnore, pad) << 24;
+			*chan = c;
+			xscreendepth = reqdepth;
+			if(0)
+				fprint(2, "chan=%s reqdepth=%d bits=%d\n", chantostr(buf, c), reqdepth, d);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+xmapvisual(int screenno, XVisualInfo *vi, ulong *chan)
+{
+	if(XMatchVisualInfo(xdisplay, screenno, 8, PseudoColor, vi) ||
+	   XMatchVisualInfo(xdisplay, screenno, 8, StaticColor, vi)){
+		*chan = CMAP8;
+		xscreendepth = 8;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+xinitscreen(int xsize, int ysize, ulong reqchan, ulong *chan, int *d)
+{
+	char *argv[2];
+	char *dispname;
+	XWindow rootwin;
+	XWMHints hints;
+	XVisualInfo xvi;
+	XScreen *screen;
+	int rootscreennum;
+	XTextProperty name;
+	XClassHint classhints;
+	XSizeHints normalhints;
+	XSetWindowAttributes attrs;
+	char buf[30];
+	int i;
+ 
+	xdrawable = 0;
+
+	dispname = getenv("DISPLAY");
+	if(dispname == nil)
+		dispname = "not set";
+	XInitThreads();
+	xdisplay = XOpenDisplay(NULL);
+	if(xdisplay == 0){
+		fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname);
+		cleanexit(0);
+	}
+
+	rootscreennum = DefaultScreen(xdisplay);
+	rootwin = DefaultRootWindow(xdisplay);
+	xscreendepth = DefaultDepth(xdisplay, rootscreennum);
+	xvis = DefaultVisual(xdisplay, rootscreennum);
+	screen = DefaultScreenOfDisplay(xdisplay);
+	xcmap = DefaultColormapOfScreen(screen);
+
+	if(reqchan == 0){
+		*chan = 0;
+		if(xscreendepth <= 16){	/* try for better colour */
+			xtruevisual(rootscreennum, 16, &xvi, chan) ||
+			xtruevisual(rootscreennum, 15, &xvi, chan) ||
+			xtruevisual(rootscreennum, 24, &xvi, chan) ||
+			xmapvisual(rootscreennum, &xvi, chan);
+		}else{
+			xtruevisual(rootscreennum, xscreendepth, &xvi, chan) ||
+			xtruevisual(rootscreennum, 24, &xvi, chan);
+		}
+		if(*chan == 0){
+			fprint(2, "emu: could not find suitable x11 pixel format for depth %d on this display\n", xscreendepth);
+			cleanexit(0);
+		}
+		reqchan = *chan;
+		*d = chantodepth(reqchan);
+		xvis = xvi.visual;
+	}else{
+		*chan = reqchan;		/* not every channel description will work */
+		*d = chantodepth(reqchan);
+		if(*d != xactualdepth(rootscreennum, *d)){
+			fprint(2, "emu: current x11 display configuration does not support %s (depth %d) directly\n",
+				chantostr(buf, reqchan), *d);
+			cleanexit(0);
+		}
+	}
+
+	if(xvis->class != StaticColor) {
+		if(TYPE(*chan) == CGrey)
+			graphicsgmap(map, NBITS(reqchan));
+		else{
+			graphicscmap(map);
+			graphicsrgbmap(mapr, mapg, mapb);
+		}
+		initxcmap(rootwin);
+	}
+
+	memset(&attrs, 0, sizeof(attrs));
+	attrs.colormap = xcmap;
+	attrs.background_pixel = 0;
+	attrs.border_pixel = 0;
+	/* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
+	xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, 
+				  InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
+
+	/*
+	 * set up property as required by ICCCM
+	 */
+	memset(&name, 0, sizeof(name));
+	name.value = (uchar*)"inferno";
+	name.encoding = XA_STRING;
+	name.format = 8;
+	name.nitems = strlen((char*)name.value);
+
+	memset(&normalhints, 0, sizeof(normalhints));
+	normalhints.flags = USSize|PMaxSize;
+	normalhints.max_width = normalhints.width = xsize;
+	normalhints.max_height = normalhints.height = ysize;
+	hints.flags = InputHint|StateHint;
+	hints.input = 1;
+	hints.initial_state = NormalState;
+
+	memset(&classhints, 0, sizeof(classhints));
+	classhints.res_name = "inferno";
+	classhints.res_class = "Inferno";
+	argv[0] = "inferno";
+	argv[1] = nil;
+	XSetWMProperties(xdisplay, xdrawable,
+		&name,			/* XA_WM_NAME property for ICCCM */
+		&name,			/* XA_WM_ICON_NAME */
+		argv,			/* XA_WM_COMMAND */
+		1,			/* argc */
+		&normalhints,		/* XA_WM_NORMAL_HINTS */
+		&hints,			/* XA_WM_HINTS */
+		&classhints);		/* XA_WM_CLASS */
+
+	XMapWindow(xdisplay, xdrawable);
+	XFlush(xdisplay);
+
+	xgc = creategc(xdrawable);
+
+	xmcon = XOpenDisplay(NULL);
+	xsnarfcon = XOpenDisplay(NULL);
+	xkbdcon = XOpenDisplay(NULL);
+	if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){
+		fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname);
+		cleanexit(0);
+	}
+
+	clipboard = XInternAtom(xmcon, "CLIPBOARD", False);
+	utf8string = XInternAtom(xmcon, "UTF8_STRING", False);
+	targets = XInternAtom(xmcon, "TARGETS", False);
+	text = XInternAtom(xmcon, "TEXT", False);
+	compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False);
+
+	cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False);
+
+}
+
+static void
+graphicsgmap(XColor *map, int d)
+{
+	int i, j, s, m, p;
+
+	s = 8-d;
+	m = 1;
+	while(--d >= 0)
+		m *= 2;
+	m = 255/(m-1);
+	for(i=0; i < 256; i++){
+		j = (i>>s)*m;
+		p = 255-i;
+		map[p].red = map[p].green = map[p].blue = (255-j)*0x0101;
+		map[p].pixel = p;
+		map[p].flags = DoRed|DoGreen|DoBlue;
+	}
+}
+
+static void
+graphicscmap(XColor *map)
+{
+	int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
+
+	for(r=0; r!=4; r++) {
+		for(g = 0; g != 4; g++) {
+			for(b = 0; b!=4; b++) {
+				for(v = 0; v!=4; v++) {
+					den=r;
+					if(g > den)
+						den=g;
+					if(b > den)
+						den=b;
+					/* divide check -- pick grey shades */
+					if(den==0)
+						cr=cg=cb=v*17;
+					else {
+						num=17*(4*den+v);
+						cr=r*num/den;
+						cg=g*num/den;
+						cb=b*num/den;
+					}
+					idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
+					/* was idx = 255 - idx; */
+					map[idx].red = cr*0x0101;
+					map[idx].green = cg*0x0101;
+					map[idx].blue = cb*0x0101;
+					map[idx].pixel = idx;
+					map[idx].flags = DoRed|DoGreen|DoBlue;
+
+					v7 = v >> 1;
+					idx7 = r*32 + v7*16 + g*4 + b;
+					if((v & 1) == v7){
+						map7to8[idx7][0] = idx;
+						if(den == 0) { 		/* divide check -- pick grey shades */
+							cr = ((255.0/7.0)*v7)+0.5;
+							cg = cr;
+							cb = cr;
+						}
+						else {
+							num=17*15*(4*den+v7*2)/14;
+							cr=r*num/den;
+							cg=g*num/den;
+							cb=b*num/den;
+						}
+						map7[idx7].red = cr*0x0101;
+						map7[idx7].green = cg*0x0101;
+						map7[idx7].blue = cb*0x0101;
+						map7[idx7].pixel = idx7;
+						map7[idx7].flags = DoRed|DoGreen|DoBlue;
+					}
+					else
+						map7to8[idx7][1] = idx;
+				}
+			}
+		}
+	}
+}
+
+static void
+graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb)
+{
+	int i;
+
+	memset(mapr, 0, 256*sizeof(XColor));
+	memset(mapg, 0, 256*sizeof(XColor));
+	memset(mapb, 0, 256*sizeof(XColor));
+	for(i=0; i < 256; i++){
+		mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101;
+		mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i;
+		mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue;
+	}
+}
+
+/*
+ * Initialize and install the Inferno colormap as a private colormap for this
+ * application.  Inferno gets the best colors here when it has the cursor focus.
+ */  
+static void 
+initxcmap(XWindow w)
+{
+	XColor c;
+	int i;
+
+	if(xscreendepth <= 1)
+		return;
+
+	switch(xvis->class){
+	case TrueColor:
+	case DirectColor:
+		for(i = 0; i < 256; i++) {
+			c = map[i];
+			/* find index into colormap for our RGB */
+			if(!XAllocColor(xdisplay, xcmap, &c)) {
+				fprint(2, "emu: win-x11 can't alloc color\n");
+				cleanexit(0);
+			}
+			infernotox11[map[i].pixel] = c.pixel;
+			if(xscreendepth >= 24){
+				c = mapr[i];
+				XAllocColor(xdisplay, xcmap, &c);
+				infernortox11[i] = (c.pixel>>16)&0xff;
+				c = mapg[i];
+				XAllocColor(xdisplay, xcmap, &c);
+				infernogtox11[i] = (c.pixel>>8)&0xff;
+				c = mapb[i];
+				XAllocColor(xdisplay, xcmap, &c);
+				infernobtox11[i] = (c.pixel>>0)&0xff;
+			}
+		}
+if(0){int i, j; for(i=0;i<256; i+=16){print("%3d", i); for(j=i; j<i+16; j++)print(" %2.2ux/%2.2ux/%2.2ux", infernortox11[j], infernogtox11[j],infernobtox11[j]); print("\n");}}
+		/* TO DO: if the map(s) used give the identity map, don't use the map during copy */
+		break;
+
+	case PseudoColor:
+		if(xtblbit == 0){
+			xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); 
+			XStoreColors(xdisplay, xcmap, map, 256);
+			for(i = 0; i < 256; i++)
+				infernotox11[i] = i;
+			/* TO DO: the map is the identity, so don't need the map in copy */
+		} else {
+			for(i = 0; i < 128; i++) {
+				c = map7[i];
+				if(!XAllocColor(xdisplay, xcmap, &c)) {
+					fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n");
+					cleanexit(0);
+				}
+				infernotox11[map7to8[i][0]] = c.pixel;
+				infernotox11[map7to8[i][1]] = c.pixel;
+			}
+		}
+		break;
+
+	default:
+		xtblbit = 0;
+		fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class);
+		break;
+	}
+}
+
+static void
+xdestroy(XEvent *e)
+{
+	XDestroyWindowEvent *xe;
+	if(e->type != DestroyNotify)
+		return;
+	xe = (XDestroyWindowEvent*)e;
+	if(xe->window == xdrawable)
+		cleanexit(0);
+}
+
+/*
+ * Disable generation of GraphicsExpose/NoExpose events in the XGC.
+ */
+static XGC
+creategc(XDrawable d)
+{
+	XGCValues gcv;
+
+	gcv.function = GXcopy;
+	gcv.graphics_exposures = False;
+	return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv);
+}
+
+static void
+xexpose(XEvent *e)
+{
+	Rectangle r;
+	XExposeEvent *xe;
+
+	if(e->type != Expose)
+		return;
+	xe = (XExposeEvent*)e;
+	r.min.x = xe->x;
+	r.min.y = xe->y;
+	r.max.x = xe->x + xe->width;
+	r.max.y = xe->y + xe->height;
+	drawqlock();
+	flushmemscreen(r);
+	drawqunlock();
+}
+
+static void
+xkeyboard(XEvent *e)
+{
+	int ind, md;
+	KeySym k;
+
+	if(gkscanq != nil && (e->type == KeyPress || e->type == KeyRelease)){
+		uchar ch = e->xkey.keycode;
+		if(e->xany.type == KeyRelease)
+			ch |= 0x80;
+		qproduce(gkscanq, &ch, 1);
+		return;
+	}
+
+	/*
+	 * I tried using XtGetActionKeysym, but it didn't seem to
+	 * do case conversion properly
+	 * (at least, with Xterminal servers and R4 intrinsics)
+	 */
+	if(e->xany.type != KeyPress)
+		return;
+
+	md = e->xkey.state;
+	ind = 0;
+	if(md & ShiftMask)
+		ind = 1;
+	if(0){
+		k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+
+		/* May have to try unshifted version */
+		if(k == NoSymbol && ind == 1)
+			k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0);
+	}else
+		XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL);
+
+	if(k == XK_Multi_key || k == NoSymbol)
+		return;
+	if(k&0xFF00){
+		switch(k){
+		case XK_BackSpace:
+		case XK_Tab:
+		case XK_Escape:
+		case XK_Delete:
+		case XK_KP_0:
+		case XK_KP_1:
+		case XK_KP_2:
+		case XK_KP_3:
+		case XK_KP_4:
+		case XK_KP_5:
+		case XK_KP_6:
+		case XK_KP_7:
+		case XK_KP_8:
+		case XK_KP_9:
+		case XK_KP_Divide:
+		case XK_KP_Multiply:
+		case XK_KP_Subtract:
+		case XK_KP_Add:
+		case XK_KP_Decimal:
+			k &= 0x7F;
+			break;
+		case XK_Linefeed:
+			k = '\r';
+			break;
+		case XK_KP_Space:
+			k = ' ';
+			break;
+		case XK_Home:
+		case XK_KP_Home:
+			k = Home;
+			break;
+		case XK_Left:
+		case XK_KP_Left:
+			k = Left;
+			break;
+		case XK_Up:
+		case XK_KP_Up:
+			k = Up;
+			break;
+		case XK_Down:
+		case XK_KP_Down:
+			k = Down;
+			break;
+		case XK_Right:
+		case XK_KP_Right:
+			k = Right;
+			break;
+		case XK_Page_Down:
+		case XK_KP_Page_Down:
+			k = Pgdown;
+			break;
+		case XK_End:
+		case XK_KP_End:
+			k = End;
+			break;
+		case XK_Page_Up:	
+		case XK_KP_Page_Up:
+			k = Pgup;
+			break;
+		case XK_Insert:
+		case XK_KP_Insert:
+			k = Ins;
+			break;
+		case XK_KP_Enter:
+		case XK_Return:
+			k = '\n';
+			break;
+		case XK_Alt_L:
+		case XK_Alt_R:
+			k = Latin;
+			break;
+		case XK_Shift_L:
+		case XK_Shift_R:
+		case XK_Control_L:
+		case XK_Control_R:
+		case XK_Caps_Lock:
+		case XK_Shift_Lock:
+
+		case XK_Meta_L:
+		case XK_Meta_R:
+		case XK_Super_L:
+		case XK_Super_R:
+		case XK_Hyper_L:
+		case XK_Hyper_R:
+			return;
+		default:                /* not ISO-1 or tty control */
+ 			if(k>0xff){
+				k = keysym2ucs(k); /* supplied by X */
+				if(k == -1)
+					return;
+			}
+			break;
+		}
+	}
+
+	/* Compensate for servers that call a minus a hyphen */
+	if(k == XK_hyphen)
+		k = XK_minus;
+	/* Do control mapping ourselves if translator doesn't */
+	if(md & ControlMask)
+		k &= 0x9f;
+	if(0){
+		if(k == '\t' && ind)
+			k = BackTab;
+
+		if(md & Mod1Mask)
+			k = APP|(k&0xff);
+	}
+	if(k == NoSymbol)
+		return;
+
+        gkbdputc(gkbdq, k);
+}
+
+static void
+xmouse(XEvent *e)
+{
+	int s, dbl;
+	XButtonEvent *be;
+	XMotionEvent *me;
+	XEvent motion;
+	int x, y, b;
+	static ulong lastb, lastt;
+
+	if(putsnarf != assertsnarf){
+		assertsnarf = putsnarf;
+		XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime);
+		if(clipboard != None)
+			XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime);
+		XFlush(xmcon);
+	}
+
+	dbl = 0;
+	switch(e->type){
+	case ButtonPress:
+		be = (XButtonEvent *)e;
+		/* 
+		 * Fake message, just sent to make us announce snarf.
+		 * Apparently state and button are 16 and 8 bits on
+		 * the wire, since they are truncated by the time they
+		 * get to us.
+		 */
+		if(be->send_event
+		&& (~be->state&0xFFFF)==0
+		&& (~be->button&0xFF)==0)
+			return;
+		x = be->x;
+		y = be->y;
+		s = be->state;
+		if(be->button == lastb && be->time - lastt < DblTime)
+			dbl = 1;
+		lastb = be->button;
+		lastt = be->time;
+		switch(be->button){
+		case 1:
+			s |= Button1Mask;
+			break;
+		case 2:
+			s |= Button2Mask;
+			break;
+		case 3:
+			s |= Button3Mask;
+			break;
+		case 4:
+			s |= Button4Mask;
+			break;
+		case 5:
+			s |= Button5Mask;
+			break;
+		}
+		break;
+	case ButtonRelease:
+		be = (XButtonEvent *)e;
+		x = be->x;
+		y = be->y;
+		s = be->state;
+		switch(be->button){
+		case 1:
+			s &= ~Button1Mask;
+			break;
+		case 2:
+			s &= ~Button2Mask;
+			break;
+		case 3:
+			s &= ~Button3Mask;
+			break;
+		case 4:
+			s &= ~Button4Mask;
+			break;
+		case 5:
+			s &= ~Button5Mask;
+			break;
+		}
+		break;
+	case MotionNotify:
+		me = (XMotionEvent *) e;
+
+		/* remove excess MotionNotify events from queue and keep last one */
+		while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True)
+			me = (XMotionEvent *) &motion;
+
+		s = me->state;
+		x = me->x;
+		y = me->y;
+		break;
+	default:
+		return;
+	}
+
+	b = 0;
+	if(s & Button1Mask)
+		b |= 1;
+	if(s & Button2Mask)
+		b |= 2;
+	if(s & Button3Mask)
+		b |= 4;
+	if(s & Button4Mask)
+		b |= 8;
+	if(s & Button5Mask)
+		b |= 16;
+	if(dbl)
+		b |= 1<<8;
+
+	mousetrack(b, x, y, 0);
+}
+
+#include "x11-keysym2ucs.c"
+
+/*
+ * Cut and paste.  Just couldn't stand to make this simple...
+ */
+
+enum{
+	SnarfSize=	100*1024
+};
+
+typedef struct Clip Clip;
+struct Clip
+{
+	char buf[SnarfSize];
+	QLock lk;
+};
+Clip clip;
+
+#undef long	/* sic */
+#undef ulong
+
+static char*
+_xgetsnarf(XDisplay *xd)
+{
+	uchar *data, *xdata;
+	Atom clipboard, type, prop;
+	unsigned long len, lastlen, dummy;
+	int fmt, i;
+	XWindow w;
+
+	qlock(&clip.lk);
+	/*
+	 * Have we snarfed recently and the X server hasn't caught up?
+	 */
+	if(putsnarf != assertsnarf)
+		goto mine;
+
+	/*
+	 * Is there a primary selection (highlighted text in an xterm)?
+	 */
+	clipboard = XA_PRIMARY;
+	w = XGetSelectionOwner(xd, XA_PRIMARY);
+	if(w == xdrawable){
+	mine:
+		data = (uchar*)strdup(clip.buf);
+		goto out;
+	}
+
+	/*
+	 * If not, is there a clipboard selection?
+	 */
+	if(w == None && clipboard != None){
+		clipboard = clipboard;
+		w = XGetSelectionOwner(xd, clipboard);
+		if(w == xdrawable)
+			goto mine;
+	}
+
+	/*
+	 * If not, give up.
+	 */
+	if(w == None){
+		data = nil;
+		goto out;
+	}
+		
+	/*
+	 * We should be waiting for SelectionNotify here, but it might never
+	 * come, and we have no way to time out.  Instead, we will clear
+	 * local property #1, request our buddy to fill it in for us, and poll
+	 * until he's done or we get tired of waiting.
+	 *
+	 * We should try to go for utf8string instead of XA_STRING,
+	 * but that would add to the polling.
+	 */
+	prop = 1;
+	XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
+	XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime);
+	XFlush(xd);
+	lastlen = 0;
+	for(i=0; i<10 || (lastlen!=0 && i<30); i++){
+		osmillisleep(100);
+		XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType,
+			&type, &fmt, &dummy, &len, &data);
+		if(lastlen == len && len > 0)
+			break;
+		lastlen = len;
+	}
+	if(i == 10){
+		data = nil;
+		goto out;
+	}
+	/* get the property */
+	data = nil;
+	XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, 
+		AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
+	if((type != XA_STRING && type != utf8string) || len == 0){
+		if(xdata)
+			XFree(xdata);
+		data = nil;
+	}else{
+		if(xdata){
+			data = (uchar*)strdup((char*)xdata);
+			XFree(xdata);
+		}else
+			data = nil;
+	}
+out:
+	qunlock(&clip.lk);
+	return (char*)data;
+}
+
+static void
+_xputsnarf(XDisplay *xd, char *data)
+{
+	XButtonEvent e;
+
+	if(strlen(data) >= SnarfSize)
+		return;
+	qlock(&clip.lk);
+	strcpy(clip.buf, data);
+
+	/* leave note for mouse proc to assert selection ownership */
+	putsnarf++;
+
+	/* send mouse a fake event so snarf is announced */
+	memset(&e, 0, sizeof e);
+	e.type = ButtonPress;
+	e.window = xdrawable;
+	e.state = ~0;
+	e.button = ~0;
+	XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e);
+	XFlush(xd);
+	qunlock(&clip.lk);
+}
+
+static void
+xselect(XEvent *e, XDisplay *xd)
+{
+	char *name;
+	XEvent r;
+	XSelectionRequestEvent *xe;
+	Atom a[4];
+
+	if(e->xany.type != SelectionRequest)
+		return;
+
+	memset(&r, 0, sizeof r);
+	xe = (XSelectionRequestEvent*)e;
+if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n",
+	xe->target, xe->requestor, xe->property, xe->selection);
+	r.xselection.property = xe->property;
+	if(xe->target == targets){
+		a[0] = XA_STRING;
+		a[1] = utf8string;
+		a[2] = text;
+		a[3] = compoundtext;
+
+		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+			8, PropModeReplace, (uchar*)a, sizeof a);
+	}else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){
+		/* if the target is STRING we're supposed to reply with Latin1 XXX */
+		qlock(&clip.lk);
+		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+			8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
+		qunlock(&clip.lk);
+	}else{
+		iprint("get %d\n", xe->target);
+		name = XGetAtomName(xd, xe->target);
+		if(name == nil)
+			iprint("XGetAtomName failed\n");
+		else if(strcmp(name, "TIMESTAMP") != 0)
+			iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
+		r.xselection.property = None;
+	}
+
+	r.xselection.display = xe->display;
+	/* r.xselection.property filled above */
+	r.xselection.target = xe->target;
+	r.xselection.type = SelectionNotify;
+	r.xselection.requestor = xe->requestor;
+	r.xselection.time = xe->time;
+	r.xselection.send_event = True;
+	r.xselection.selection = xe->selection;
+	XSendEvent(xd, xe->requestor, False, 0, &r);
+	XFlush(xd);
+}
+
+char*
+clipread(void)
+{
+	char *p;
+
+	if(xsnarfcon == nil)
+		return nil;
+	XLockDisplay(xsnarfcon);
+	p = _xgetsnarf(xsnarfcon);
+	XUnlockDisplay(xsnarfcon);
+	return p;
+}
+
+int
+clipwrite(char *buf)
+{
+	if(xsnarfcon == nil)
+		return 0;
+	XLockDisplay(xsnarfcon);
+	_xputsnarf(xsnarfcon, buf);
+	XUnlockDisplay(xsnarfcon);
+	return 0;
+}
--- /dev/null
+++ b/emu/port/x11-keysym2ucs.c
@@ -1,0 +1,857 @@
+/* $XFree86: xc/programs/xterm/keysym2ucs.c,v 1.5 2001/06/18 19:09:26 dickey Exp $
+ * This module converts keysym values into the corresponding ISO 10646
+ * (UCS, Unicode) values.
+ *
+ * The array keysymtab[] contains pairs of X11 keysym values for graphical
+ * characters and the corresponding Unicode value. The function
+ * keysym2ucs() maps a keysym onto a Unicode value using a binary search,
+ * therefore keysymtab[] must remain SORTED by keysym value.
+ *
+ * The keysym -> UTF-8 conversion will hopefully one day be provided
+ * by Xlib via XmbLookupString() and should ideally not have to be
+ * done in X applications. But we are not there yet.
+ *
+ * We allow to represent any UCS character in the range U-00000000 to
+ * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
+ * This admittedly does not cover the entire 31-bit space of UCS, but
+ * it does cover all of the characters up to U-10FFFF, which can be
+ * represented by UTF-16, and more, and it is very unlikely that higher
+ * UCS codes will ever be assigned by ISO. So to get Unicode character
+ * U+ABCD you can directly use keysym 0x0100abcd.
+ *
+ * NOTE: The comments in the table below contain the actual character
+ * encoded in UTF-8, so for viewing and editing best use an editor in
+ * UTF-8 mode.
+ *
+ * Author: Markus G. Kuhn <mkuhn@acm.org>, University of Cambridge, April 2001
+ *
+ * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
+ * an initial draft of the mapping table.
+ *
+ * This software is in the public domain. Share and enjoy!
+ *
+ * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl)
+ */
+
+#ifndef KEYSYM2UCS_INCLUDED
+  
+#include "keysym2ucs.h"
+#define VISIBLE /* */
+
+#else
+
+#define VISIBLE static
+
+#endif
+
+static struct codepair {
+  unsigned short keysym;
+  unsigned short ucs;
+} keysymtab[] = {
+  { 0x01a1, 0x0104 }, /*                     Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
+  { 0x01a2, 0x02d8 }, /*                       breve ˘ BREVE */
+  { 0x01a3, 0x0141 }, /*                     Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
+  { 0x01a5, 0x013d }, /*                      Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
+  { 0x01a6, 0x015a }, /*                      Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
+  { 0x01a9, 0x0160 }, /*                      Scaron Š LATIN CAPITAL LETTER S WITH CARON */
+  { 0x01aa, 0x015e }, /*                    Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
+  { 0x01ab, 0x0164 }, /*                      Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
+  { 0x01ac, 0x0179 }, /*                      Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
+  { 0x01ae, 0x017d }, /*                      Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
+  { 0x01af, 0x017b }, /*                   Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+  { 0x01b1, 0x0105 }, /*                     aogonek ą LATIN SMALL LETTER A WITH OGONEK */
+  { 0x01b2, 0x02db }, /*                      ogonek ˛ OGONEK */
+  { 0x01b3, 0x0142 }, /*                     lstroke ł LATIN SMALL LETTER L WITH STROKE */
+  { 0x01b5, 0x013e }, /*                      lcaron ľ LATIN SMALL LETTER L WITH CARON */
+  { 0x01b6, 0x015b }, /*                      sacute ś LATIN SMALL LETTER S WITH ACUTE */
+  { 0x01b7, 0x02c7 }, /*                       caron ˇ CARON */
+  { 0x01b9, 0x0161 }, /*                      scaron š LATIN SMALL LETTER S WITH CARON */
+  { 0x01ba, 0x015f }, /*                    scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
+  { 0x01bb, 0x0165 }, /*                      tcaron ť LATIN SMALL LETTER T WITH CARON */
+  { 0x01bc, 0x017a }, /*                      zacute ź LATIN SMALL LETTER Z WITH ACUTE */
+  { 0x01bd, 0x02dd }, /*                 doubleacute ˝ DOUBLE ACUTE ACCENT */
+  { 0x01be, 0x017e }, /*                      zcaron ž LATIN SMALL LETTER Z WITH CARON */
+  { 0x01bf, 0x017c }, /*                   zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
+  { 0x01c0, 0x0154 }, /*                      Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
+  { 0x01c3, 0x0102 }, /*                      Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
+  { 0x01c5, 0x0139 }, /*                      Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
+  { 0x01c6, 0x0106 }, /*                      Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
+  { 0x01c8, 0x010c }, /*                      Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
+  { 0x01ca, 0x0118 }, /*                     Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
+  { 0x01cc, 0x011a }, /*                      Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
+  { 0x01cf, 0x010e }, /*                      Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
+  { 0x01d0, 0x0110 }, /*                     Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
+  { 0x01d1, 0x0143 }, /*                      Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
+  { 0x01d2, 0x0147 }, /*                      Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
+  { 0x01d5, 0x0150 }, /*                Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+  { 0x01d8, 0x0158 }, /*                      Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
+  { 0x01d9, 0x016e }, /*                       Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
+  { 0x01db, 0x0170 }, /*                Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+  { 0x01de, 0x0162 }, /*                    Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
+  { 0x01e0, 0x0155 }, /*                      racute ŕ LATIN SMALL LETTER R WITH ACUTE */
+  { 0x01e3, 0x0103 }, /*                      abreve ă LATIN SMALL LETTER A WITH BREVE */
+  { 0x01e5, 0x013a }, /*                      lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
+  { 0x01e6, 0x0107 }, /*                      cacute ć LATIN SMALL LETTER C WITH ACUTE */
+  { 0x01e8, 0x010d }, /*                      ccaron č LATIN SMALL LETTER C WITH CARON */
+  { 0x01ea, 0x0119 }, /*                     eogonek ę LATIN SMALL LETTER E WITH OGONEK */
+  { 0x01ec, 0x011b }, /*                      ecaron ě LATIN SMALL LETTER E WITH CARON */
+  { 0x01ef, 0x010f }, /*                      dcaron ď LATIN SMALL LETTER D WITH CARON */
+  { 0x01f0, 0x0111 }, /*                     dstroke đ LATIN SMALL LETTER D WITH STROKE */
+  { 0x01f1, 0x0144 }, /*                      nacute ń LATIN SMALL LETTER N WITH ACUTE */
+  { 0x01f2, 0x0148 }, /*                      ncaron ň LATIN SMALL LETTER N WITH CARON */
+  { 0x01f5, 0x0151 }, /*                odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
+  { 0x01f8, 0x0159 }, /*                      rcaron ř LATIN SMALL LETTER R WITH CARON */
+  { 0x01f9, 0x016f }, /*                       uring ů LATIN SMALL LETTER U WITH RING ABOVE */
+  { 0x01fb, 0x0171 }, /*                udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
+  { 0x01fe, 0x0163 }, /*                    tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
+  { 0x01ff, 0x02d9 }, /*                    abovedot ˙ DOT ABOVE */
+  { 0x02a1, 0x0126 }, /*                     Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
+  { 0x02a6, 0x0124 }, /*                 Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+  { 0x02a9, 0x0130 }, /*                   Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
+  { 0x02ab, 0x011e }, /*                      Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
+  { 0x02ac, 0x0134 }, /*                 Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+  { 0x02b1, 0x0127 }, /*                     hstroke ħ LATIN SMALL LETTER H WITH STROKE */
+  { 0x02b6, 0x0125 }, /*                 hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
+  { 0x02b9, 0x0131 }, /*                    idotless ı LATIN SMALL LETTER DOTLESS I */
+  { 0x02bb, 0x011f }, /*                      gbreve ğ LATIN SMALL LETTER G WITH BREVE */
+  { 0x02bc, 0x0135 }, /*                 jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
+  { 0x02c5, 0x010a }, /*                   Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
+  { 0x02c6, 0x0108 }, /*                 Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+  { 0x02d5, 0x0120 }, /*                   Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
+  { 0x02d8, 0x011c }, /*                 Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+  { 0x02dd, 0x016c }, /*                      Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
+  { 0x02de, 0x015c }, /*                 Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+  { 0x02e5, 0x010b }, /*                   cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
+  { 0x02e6, 0x0109 }, /*                 ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
+  { 0x02f5, 0x0121 }, /*                   gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
+  { 0x02f8, 0x011d }, /*                 gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
+  { 0x02fd, 0x016d }, /*                      ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
+  { 0x02fe, 0x015d }, /*                 scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
+  { 0x03a2, 0x0138 }, /*                         kra ĸ LATIN SMALL LETTER KRA */
+  { 0x03a3, 0x0156 }, /*                    Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
+  { 0x03a5, 0x0128 }, /*                      Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
+  { 0x03a6, 0x013b }, /*                    Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
+  { 0x03aa, 0x0112 }, /*                     Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
+  { 0x03ab, 0x0122 }, /*                    Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
+  { 0x03ac, 0x0166 }, /*                      Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
+  { 0x03b3, 0x0157 }, /*                    rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
+  { 0x03b5, 0x0129 }, /*                      itilde ĩ LATIN SMALL LETTER I WITH TILDE */
+  { 0x03b6, 0x013c }, /*                    lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
+  { 0x03ba, 0x0113 }, /*                     emacron ē LATIN SMALL LETTER E WITH MACRON */
+  { 0x03bb, 0x0123 }, /*                    gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
+  { 0x03bc, 0x0167 }, /*                      tslash ŧ LATIN SMALL LETTER T WITH STROKE */
+  { 0x03bd, 0x014a }, /*                         ENG Ŋ LATIN CAPITAL LETTER ENG */
+  { 0x03bf, 0x014b }, /*                         eng ŋ LATIN SMALL LETTER ENG */
+  { 0x03c0, 0x0100 }, /*                     Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
+  { 0x03c7, 0x012e }, /*                     Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
+  { 0x03cc, 0x0116 }, /*                   Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
+  { 0x03cf, 0x012a }, /*                     Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
+  { 0x03d1, 0x0145 }, /*                    Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
+  { 0x03d2, 0x014c }, /*                     Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
+  { 0x03d3, 0x0136 }, /*                    Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
+  { 0x03d9, 0x0172 }, /*                     Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
+  { 0x03dd, 0x0168 }, /*                      Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
+  { 0x03de, 0x016a }, /*                     Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
+  { 0x03e0, 0x0101 }, /*                     amacron ā LATIN SMALL LETTER A WITH MACRON */
+  { 0x03e7, 0x012f }, /*                     iogonek į LATIN SMALL LETTER I WITH OGONEK */
+  { 0x03ec, 0x0117 }, /*                   eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
+  { 0x03ef, 0x012b }, /*                     imacron ī LATIN SMALL LETTER I WITH MACRON */
+  { 0x03f1, 0x0146 }, /*                    ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
+  { 0x03f2, 0x014d }, /*                     omacron ō LATIN SMALL LETTER O WITH MACRON */
+  { 0x03f3, 0x0137 }, /*                    kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
+  { 0x03f9, 0x0173 }, /*                     uogonek ų LATIN SMALL LETTER U WITH OGONEK */
+  { 0x03fd, 0x0169 }, /*                      utilde ũ LATIN SMALL LETTER U WITH TILDE */
+  { 0x03fe, 0x016b }, /*                     umacron ū LATIN SMALL LETTER U WITH MACRON */
+  { 0x047e, 0x203e }, /*                    overline ‾ OVERLINE */
+  { 0x04a1, 0x3002 }, /*               kana_fullstop 。 IDEOGRAPHIC FULL STOP */
+  { 0x04a2, 0x300c }, /*         kana_openingbracket 「 LEFT CORNER BRACKET */
+  { 0x04a3, 0x300d }, /*         kana_closingbracket 」 RIGHT CORNER BRACKET */
+  { 0x04a4, 0x3001 }, /*                  kana_comma 、 IDEOGRAPHIC COMMA */
+  { 0x04a5, 0x30fb }, /*            kana_conjunctive ・ KATAKANA MIDDLE DOT */
+  { 0x04a6, 0x30f2 }, /*                     kana_WO ヲ KATAKANA LETTER WO */
+  { 0x04a7, 0x30a1 }, /*                      kana_a ァ KATAKANA LETTER SMALL A */
+  { 0x04a8, 0x30a3 }, /*                      kana_i ィ KATAKANA LETTER SMALL I */
+  { 0x04a9, 0x30a5 }, /*                      kana_u ゥ KATAKANA LETTER SMALL U */
+  { 0x04aa, 0x30a7 }, /*                      kana_e ェ KATAKANA LETTER SMALL E */
+  { 0x04ab, 0x30a9 }, /*                      kana_o ォ KATAKANA LETTER SMALL O */
+  { 0x04ac, 0x30e3 }, /*                     kana_ya ャ KATAKANA LETTER SMALL YA */
+  { 0x04ad, 0x30e5 }, /*                     kana_yu ュ KATAKANA LETTER SMALL YU */
+  { 0x04ae, 0x30e7 }, /*                     kana_yo ョ KATAKANA LETTER SMALL YO */
+  { 0x04af, 0x30c3 }, /*                    kana_tsu ッ KATAKANA LETTER SMALL TU */
+  { 0x04b0, 0x30fc }, /*              prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
+  { 0x04b1, 0x30a2 }, /*                      kana_A ア KATAKANA LETTER A */
+  { 0x04b2, 0x30a4 }, /*                      kana_I イ KATAKANA LETTER I */
+  { 0x04b3, 0x30a6 }, /*                      kana_U ウ KATAKANA LETTER U */
+  { 0x04b4, 0x30a8 }, /*                      kana_E エ KATAKANA LETTER E */
+  { 0x04b5, 0x30aa }, /*                      kana_O オ KATAKANA LETTER O */
+  { 0x04b6, 0x30ab }, /*                     kana_KA カ KATAKANA LETTER KA */
+  { 0x04b7, 0x30ad }, /*                     kana_KI キ KATAKANA LETTER KI */
+  { 0x04b8, 0x30af }, /*                     kana_KU ク KATAKANA LETTER KU */
+  { 0x04b9, 0x30b1 }, /*                     kana_KE ケ KATAKANA LETTER KE */
+  { 0x04ba, 0x30b3 }, /*                     kana_KO コ KATAKANA LETTER KO */
+  { 0x04bb, 0x30b5 }, /*                     kana_SA サ KATAKANA LETTER SA */
+  { 0x04bc, 0x30b7 }, /*                    kana_SHI シ KATAKANA LETTER SI */
+  { 0x04bd, 0x30b9 }, /*                     kana_SU ス KATAKANA LETTER SU */
+  { 0x04be, 0x30bb }, /*                     kana_SE セ KATAKANA LETTER SE */
+  { 0x04bf, 0x30bd }, /*                     kana_SO ソ KATAKANA LETTER SO */
+  { 0x04c0, 0x30bf }, /*                     kana_TA タ KATAKANA LETTER TA */
+  { 0x04c1, 0x30c1 }, /*                    kana_CHI チ KATAKANA LETTER TI */
+  { 0x04c2, 0x30c4 }, /*                    kana_TSU ツ KATAKANA LETTER TU */
+  { 0x04c3, 0x30c6 }, /*                     kana_TE テ KATAKANA LETTER TE */
+  { 0x04c4, 0x30c8 }, /*                     kana_TO ト KATAKANA LETTER TO */
+  { 0x04c5, 0x30ca }, /*                     kana_NA ナ KATAKANA LETTER NA */
+  { 0x04c6, 0x30cb }, /*                     kana_NI ニ KATAKANA LETTER NI */
+  { 0x04c7, 0x30cc }, /*                     kana_NU ヌ KATAKANA LETTER NU */
+  { 0x04c8, 0x30cd }, /*                     kana_NE ネ KATAKANA LETTER NE */
+  { 0x04c9, 0x30ce }, /*                     kana_NO ノ KATAKANA LETTER NO */
+  { 0x04ca, 0x30cf }, /*                     kana_HA ハ KATAKANA LETTER HA */
+  { 0x04cb, 0x30d2 }, /*                     kana_HI ヒ KATAKANA LETTER HI */
+  { 0x04cc, 0x30d5 }, /*                     kana_FU フ KATAKANA LETTER HU */
+  { 0x04cd, 0x30d8 }, /*                     kana_HE ヘ KATAKANA LETTER HE */
+  { 0x04ce, 0x30db }, /*                     kana_HO ホ KATAKANA LETTER HO */
+  { 0x04cf, 0x30de }, /*                     kana_MA マ KATAKANA LETTER MA */
+  { 0x04d0, 0x30df }, /*                     kana_MI ミ KATAKANA LETTER MI */
+  { 0x04d1, 0x30e0 }, /*                     kana_MU ム KATAKANA LETTER MU */
+  { 0x04d2, 0x30e1 }, /*                     kana_ME メ KATAKANA LETTER ME */
+  { 0x04d3, 0x30e2 }, /*                     kana_MO モ KATAKANA LETTER MO */
+  { 0x04d4, 0x30e4 }, /*                     kana_YA ヤ KATAKANA LETTER YA */
+  { 0x04d5, 0x30e6 }, /*                     kana_YU ユ KATAKANA LETTER YU */
+  { 0x04d6, 0x30e8 }, /*                     kana_YO ヨ KATAKANA LETTER YO */
+  { 0x04d7, 0x30e9 }, /*                     kana_RA ラ KATAKANA LETTER RA */
+  { 0x04d8, 0x30ea }, /*                     kana_RI リ KATAKANA LETTER RI */
+  { 0x04d9, 0x30eb }, /*                     kana_RU ル KATAKANA LETTER RU */
+  { 0x04da, 0x30ec }, /*                     kana_RE レ KATAKANA LETTER RE */
+  { 0x04db, 0x30ed }, /*                     kana_RO ロ KATAKANA LETTER RO */
+  { 0x04dc, 0x30ef }, /*                     kana_WA ワ KATAKANA LETTER WA */
+  { 0x04dd, 0x30f3 }, /*                      kana_N ン KATAKANA LETTER N */
+  { 0x04de, 0x309b }, /*                 voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
+  { 0x04df, 0x309c }, /*             semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+  { 0x05ac, 0x060c }, /*                Arabic_comma ، ARABIC COMMA */
+  { 0x05bb, 0x061b }, /*            Arabic_semicolon ؛ ARABIC SEMICOLON */
+  { 0x05bf, 0x061f }, /*        Arabic_question_mark ؟ ARABIC QUESTION MARK */
+  { 0x05c1, 0x0621 }, /*                Arabic_hamza ء ARABIC LETTER HAMZA */
+  { 0x05c2, 0x0622 }, /*          Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
+  { 0x05c3, 0x0623 }, /*          Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
+  { 0x05c4, 0x0624 }, /*           Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
+  { 0x05c5, 0x0625 }, /*       Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
+  { 0x05c6, 0x0626 }, /*           Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
+  { 0x05c7, 0x0627 }, /*                 Arabic_alef ا ARABIC LETTER ALEF */
+  { 0x05c8, 0x0628 }, /*                  Arabic_beh ب ARABIC LETTER BEH */
+  { 0x05c9, 0x0629 }, /*           Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
+  { 0x05ca, 0x062a }, /*                  Arabic_teh ت ARABIC LETTER TEH */
+  { 0x05cb, 0x062b }, /*                 Arabic_theh ث ARABIC LETTER THEH */
+  { 0x05cc, 0x062c }, /*                 Arabic_jeem ج ARABIC LETTER JEEM */
+  { 0x05cd, 0x062d }, /*                  Arabic_hah ح ARABIC LETTER HAH */
+  { 0x05ce, 0x062e }, /*                 Arabic_khah خ ARABIC LETTER KHAH */
+  { 0x05cf, 0x062f }, /*                  Arabic_dal د ARABIC LETTER DAL */
+  { 0x05d0, 0x0630 }, /*                 Arabic_thal ذ ARABIC LETTER THAL */
+  { 0x05d1, 0x0631 }, /*                   Arabic_ra ر ARABIC LETTER REH */
+  { 0x05d2, 0x0632 }, /*                 Arabic_zain ز ARABIC LETTER ZAIN */
+  { 0x05d3, 0x0633 }, /*                 Arabic_seen س ARABIC LETTER SEEN */
+  { 0x05d4, 0x0634 }, /*                Arabic_sheen ش ARABIC LETTER SHEEN */
+  { 0x05d5, 0x0635 }, /*                  Arabic_sad ص ARABIC LETTER SAD */
+  { 0x05d6, 0x0636 }, /*                  Arabic_dad ض ARABIC LETTER DAD */
+  { 0x05d7, 0x0637 }, /*                  Arabic_tah ط ARABIC LETTER TAH */
+  { 0x05d8, 0x0638 }, /*                  Arabic_zah ظ ARABIC LETTER ZAH */
+  { 0x05d9, 0x0639 }, /*                  Arabic_ain ع ARABIC LETTER AIN */
+  { 0x05da, 0x063a }, /*                Arabic_ghain غ ARABIC LETTER GHAIN */
+  { 0x05e0, 0x0640 }, /*              Arabic_tatweel ـ ARABIC TATWEEL */
+  { 0x05e1, 0x0641 }, /*                  Arabic_feh ف ARABIC LETTER FEH */
+  { 0x05e2, 0x0642 }, /*                  Arabic_qaf ق ARABIC LETTER QAF */
+  { 0x05e3, 0x0643 }, /*                  Arabic_kaf ك ARABIC LETTER KAF */
+  { 0x05e4, 0x0644 }, /*                  Arabic_lam ل ARABIC LETTER LAM */
+  { 0x05e5, 0x0645 }, /*                 Arabic_meem م ARABIC LETTER MEEM */
+  { 0x05e6, 0x0646 }, /*                 Arabic_noon ن ARABIC LETTER NOON */
+  { 0x05e7, 0x0647 }, /*                   Arabic_ha ه ARABIC LETTER HEH */
+  { 0x05e8, 0x0648 }, /*                  Arabic_waw و ARABIC LETTER WAW */
+  { 0x05e9, 0x0649 }, /*          Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
+  { 0x05ea, 0x064a }, /*                  Arabic_yeh ي ARABIC LETTER YEH */
+  { 0x05eb, 0x064b }, /*             Arabic_fathatan ً ARABIC FATHATAN */
+  { 0x05ec, 0x064c }, /*             Arabic_dammatan ٌ ARABIC DAMMATAN */
+  { 0x05ed, 0x064d }, /*             Arabic_kasratan ٍ ARABIC KASRATAN */
+  { 0x05ee, 0x064e }, /*                Arabic_fatha َ ARABIC FATHA */
+  { 0x05ef, 0x064f }, /*                Arabic_damma ُ ARABIC DAMMA */
+  { 0x05f0, 0x0650 }, /*                Arabic_kasra ِ ARABIC KASRA */
+  { 0x05f1, 0x0651 }, /*               Arabic_shadda ّ ARABIC SHADDA */
+  { 0x05f2, 0x0652 }, /*                Arabic_sukun ْ ARABIC SUKUN */
+  { 0x06a1, 0x0452 }, /*                 Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
+  { 0x06a2, 0x0453 }, /*               Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
+  { 0x06a3, 0x0451 }, /*                 Cyrillic_io ё CYRILLIC SMALL LETTER IO */
+  { 0x06a4, 0x0454 }, /*                Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
+  { 0x06a5, 0x0455 }, /*               Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
+  { 0x06a6, 0x0456 }, /*                 Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+  { 0x06a7, 0x0457 }, /*                Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
+  { 0x06a8, 0x0458 }, /*                 Cyrillic_je ј CYRILLIC SMALL LETTER JE */
+  { 0x06a9, 0x0459 }, /*                Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
+  { 0x06aa, 0x045a }, /*                Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
+  { 0x06ab, 0x045b }, /*                Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
+  { 0x06ac, 0x045c }, /*               Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
+  { 0x06ae, 0x045e }, /*         Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
+  { 0x06af, 0x045f }, /*               Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
+  { 0x06b0, 0x2116 }, /*                  numerosign № NUMERO SIGN */
+  { 0x06b1, 0x0402 }, /*                 Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
+  { 0x06b2, 0x0403 }, /*               Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
+  { 0x06b3, 0x0401 }, /*                 Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
+  { 0x06b4, 0x0404 }, /*                Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+  { 0x06b5, 0x0405 }, /*               Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
+  { 0x06b6, 0x0406 }, /*                 Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+  { 0x06b7, 0x0407 }, /*                Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
+  { 0x06b8, 0x0408 }, /*                 Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
+  { 0x06b9, 0x0409 }, /*                Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
+  { 0x06ba, 0x040a }, /*                Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
+  { 0x06bb, 0x040b }, /*                Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
+  { 0x06bc, 0x040c }, /*               Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
+  { 0x06be, 0x040e }, /*         Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
+  { 0x06bf, 0x040f }, /*               Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
+  { 0x06c0, 0x044e }, /*                 Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
+  { 0x06c1, 0x0430 }, /*                  Cyrillic_a а CYRILLIC SMALL LETTER A */
+  { 0x06c2, 0x0431 }, /*                 Cyrillic_be б CYRILLIC SMALL LETTER BE */
+  { 0x06c3, 0x0446 }, /*                Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
+  { 0x06c4, 0x0434 }, /*                 Cyrillic_de д CYRILLIC SMALL LETTER DE */
+  { 0x06c5, 0x0435 }, /*                 Cyrillic_ie е CYRILLIC SMALL LETTER IE */
+  { 0x06c6, 0x0444 }, /*                 Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
+  { 0x06c7, 0x0433 }, /*                Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
+  { 0x06c8, 0x0445 }, /*                 Cyrillic_ha х CYRILLIC SMALL LETTER HA */
+  { 0x06c9, 0x0438 }, /*                  Cyrillic_i и CYRILLIC SMALL LETTER I */
+  { 0x06ca, 0x0439 }, /*             Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
+  { 0x06cb, 0x043a }, /*                 Cyrillic_ka к CYRILLIC SMALL LETTER KA */
+  { 0x06cc, 0x043b }, /*                 Cyrillic_el л CYRILLIC SMALL LETTER EL */
+  { 0x06cd, 0x043c }, /*                 Cyrillic_em м CYRILLIC SMALL LETTER EM */
+  { 0x06ce, 0x043d }, /*                 Cyrillic_en н CYRILLIC SMALL LETTER EN */
+  { 0x06cf, 0x043e }, /*                  Cyrillic_o о CYRILLIC SMALL LETTER O */
+  { 0x06d0, 0x043f }, /*                 Cyrillic_pe п CYRILLIC SMALL LETTER PE */
+  { 0x06d1, 0x044f }, /*                 Cyrillic_ya я CYRILLIC SMALL LETTER YA */
+  { 0x06d2, 0x0440 }, /*                 Cyrillic_er р CYRILLIC SMALL LETTER ER */
+  { 0x06d3, 0x0441 }, /*                 Cyrillic_es с CYRILLIC SMALL LETTER ES */
+  { 0x06d4, 0x0442 }, /*                 Cyrillic_te т CYRILLIC SMALL LETTER TE */
+  { 0x06d5, 0x0443 }, /*                  Cyrillic_u у CYRILLIC SMALL LETTER U */
+  { 0x06d6, 0x0436 }, /*                Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
+  { 0x06d7, 0x0432 }, /*                 Cyrillic_ve в CYRILLIC SMALL LETTER VE */
+  { 0x06d8, 0x044c }, /*           Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
+  { 0x06d9, 0x044b }, /*               Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
+  { 0x06da, 0x0437 }, /*                 Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
+  { 0x06db, 0x0448 }, /*                Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
+  { 0x06dc, 0x044d }, /*                  Cyrillic_e э CYRILLIC SMALL LETTER E */
+  { 0x06dd, 0x0449 }, /*              Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
+  { 0x06de, 0x0447 }, /*                Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
+  { 0x06df, 0x044a }, /*           Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
+  { 0x06e0, 0x042e }, /*                 Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
+  { 0x06e1, 0x0410 }, /*                  Cyrillic_A А CYRILLIC CAPITAL LETTER A */
+  { 0x06e2, 0x0411 }, /*                 Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
+  { 0x06e3, 0x0426 }, /*                Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
+  { 0x06e4, 0x0414 }, /*                 Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
+  { 0x06e5, 0x0415 }, /*                 Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
+  { 0x06e6, 0x0424 }, /*                 Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
+  { 0x06e7, 0x0413 }, /*                Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
+  { 0x06e8, 0x0425 }, /*                 Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
+  { 0x06e9, 0x0418 }, /*                  Cyrillic_I И CYRILLIC CAPITAL LETTER I */
+  { 0x06ea, 0x0419 }, /*             Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
+  { 0x06eb, 0x041a }, /*                 Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
+  { 0x06ec, 0x041b }, /*                 Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
+  { 0x06ed, 0x041c }, /*                 Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
+  { 0x06ee, 0x041d }, /*                 Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
+  { 0x06ef, 0x041e }, /*                  Cyrillic_O О CYRILLIC CAPITAL LETTER O */
+  { 0x06f0, 0x041f }, /*                 Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
+  { 0x06f1, 0x042f }, /*                 Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
+  { 0x06f2, 0x0420 }, /*                 Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
+  { 0x06f3, 0x0421 }, /*                 Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
+  { 0x06f4, 0x0422 }, /*                 Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
+  { 0x06f5, 0x0423 }, /*                  Cyrillic_U У CYRILLIC CAPITAL LETTER U */
+  { 0x06f6, 0x0416 }, /*                Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
+  { 0x06f7, 0x0412 }, /*                 Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
+  { 0x06f8, 0x042c }, /*           Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
+  { 0x06f9, 0x042b }, /*               Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
+  { 0x06fa, 0x0417 }, /*                 Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
+  { 0x06fb, 0x0428 }, /*                Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
+  { 0x06fc, 0x042d }, /*                  Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
+  { 0x06fd, 0x0429 }, /*              Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
+  { 0x06fe, 0x0427 }, /*                Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
+  { 0x06ff, 0x042a }, /*           Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
+  { 0x07a1, 0x0386 }, /*           Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
+  { 0x07a2, 0x0388 }, /*         Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
+  { 0x07a3, 0x0389 }, /*             Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
+  { 0x07a4, 0x038a }, /*            Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
+  { 0x07a5, 0x03aa }, /*         Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+  { 0x07a7, 0x038c }, /*         Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
+  { 0x07a8, 0x038e }, /*         Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
+  { 0x07a9, 0x03ab }, /*       Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+  { 0x07ab, 0x038f }, /*           Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
+  { 0x07ae, 0x0385 }, /*        Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
+  { 0x07af, 0x2015 }, /*              Greek_horizbar ― HORIZONTAL BAR */
+  { 0x07b1, 0x03ac }, /*           Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
+  { 0x07b2, 0x03ad }, /*         Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
+  { 0x07b3, 0x03ae }, /*             Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
+  { 0x07b4, 0x03af }, /*            Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
+  { 0x07b5, 0x03ca }, /*          Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
+  { 0x07b6, 0x0390 }, /*    Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
+  { 0x07b7, 0x03cc }, /*         Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
+  { 0x07b8, 0x03cd }, /*         Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
+  { 0x07b9, 0x03cb }, /*       Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
+  { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
+  { 0x07bb, 0x03ce }, /*           Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
+  { 0x07c1, 0x0391 }, /*                 Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
+  { 0x07c2, 0x0392 }, /*                  Greek_BETA Β GREEK CAPITAL LETTER BETA */
+  { 0x07c3, 0x0393 }, /*                 Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
+  { 0x07c4, 0x0394 }, /*                 Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
+  { 0x07c5, 0x0395 }, /*               Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
+  { 0x07c6, 0x0396 }, /*                  Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
+  { 0x07c7, 0x0397 }, /*                   Greek_ETA Η GREEK CAPITAL LETTER ETA */
+  { 0x07c8, 0x0398 }, /*                 Greek_THETA Θ GREEK CAPITAL LETTER THETA */
+  { 0x07c9, 0x0399 }, /*                  Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
+  { 0x07ca, 0x039a }, /*                 Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
+  { 0x07cb, 0x039b }, /*                Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
+  { 0x07cc, 0x039c }, /*                    Greek_MU Μ GREEK CAPITAL LETTER MU */
+  { 0x07cd, 0x039d }, /*                    Greek_NU Ν GREEK CAPITAL LETTER NU */
+  { 0x07ce, 0x039e }, /*                    Greek_XI Ξ GREEK CAPITAL LETTER XI */
+  { 0x07cf, 0x039f }, /*               Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
+  { 0x07d0, 0x03a0 }, /*                    Greek_PI Π GREEK CAPITAL LETTER PI */
+  { 0x07d1, 0x03a1 }, /*                   Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
+  { 0x07d2, 0x03a3 }, /*                 Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
+  { 0x07d4, 0x03a4 }, /*                   Greek_TAU Τ GREEK CAPITAL LETTER TAU */
+  { 0x07d5, 0x03a5 }, /*               Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
+  { 0x07d6, 0x03a6 }, /*                   Greek_PHI Φ GREEK CAPITAL LETTER PHI */
+  { 0x07d7, 0x03a7 }, /*                   Greek_CHI Χ GREEK CAPITAL LETTER CHI */
+  { 0x07d8, 0x03a8 }, /*                   Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
+  { 0x07d9, 0x03a9 }, /*                 Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
+  { 0x07e1, 0x03b1 }, /*                 Greek_alpha α GREEK SMALL LETTER ALPHA */
+  { 0x07e2, 0x03b2 }, /*                  Greek_beta β GREEK SMALL LETTER BETA */
+  { 0x07e3, 0x03b3 }, /*                 Greek_gamma γ GREEK SMALL LETTER GAMMA */
+  { 0x07e4, 0x03b4 }, /*                 Greek_delta δ GREEK SMALL LETTER DELTA */
+  { 0x07e5, 0x03b5 }, /*               Greek_epsilon ε GREEK SMALL LETTER EPSILON */
+  { 0x07e6, 0x03b6 }, /*                  Greek_zeta ζ GREEK SMALL LETTER ZETA */
+  { 0x07e7, 0x03b7 }, /*                   Greek_eta η GREEK SMALL LETTER ETA */
+  { 0x07e8, 0x03b8 }, /*                 Greek_theta θ GREEK SMALL LETTER THETA */
+  { 0x07e9, 0x03b9 }, /*                  Greek_iota ι GREEK SMALL LETTER IOTA */
+  { 0x07ea, 0x03ba }, /*                 Greek_kappa κ GREEK SMALL LETTER KAPPA */
+  { 0x07eb, 0x03bb }, /*                Greek_lambda λ GREEK SMALL LETTER LAMDA */
+  { 0x07ec, 0x03bc }, /*                    Greek_mu μ GREEK SMALL LETTER MU */
+  { 0x07ed, 0x03bd }, /*                    Greek_nu ν GREEK SMALL LETTER NU */
+  { 0x07ee, 0x03be }, /*                    Greek_xi ξ GREEK SMALL LETTER XI */
+  { 0x07ef, 0x03bf }, /*               Greek_omicron ο GREEK SMALL LETTER OMICRON */
+  { 0x07f0, 0x03c0 }, /*                    Greek_pi π GREEK SMALL LETTER PI */
+  { 0x07f1, 0x03c1 }, /*                   Greek_rho ρ GREEK SMALL LETTER RHO */
+  { 0x07f2, 0x03c3 }, /*                 Greek_sigma σ GREEK SMALL LETTER SIGMA */
+  { 0x07f3, 0x03c2 }, /*       Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
+  { 0x07f4, 0x03c4 }, /*                   Greek_tau τ GREEK SMALL LETTER TAU */
+  { 0x07f5, 0x03c5 }, /*               Greek_upsilon υ GREEK SMALL LETTER UPSILON */
+  { 0x07f6, 0x03c6 }, /*                   Greek_phi φ GREEK SMALL LETTER PHI */
+  { 0x07f7, 0x03c7 }, /*                   Greek_chi χ GREEK SMALL LETTER CHI */
+  { 0x07f8, 0x03c8 }, /*                   Greek_psi ψ GREEK SMALL LETTER PSI */
+  { 0x07f9, 0x03c9 }, /*                 Greek_omega ω GREEK SMALL LETTER OMEGA */
+  { 0x08a1, 0x23b7 }, /*                 leftradical ⎷ ??? */
+  { 0x08a2, 0x250c }, /*              topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+  { 0x08a3, 0x2500 }, /*              horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */
+  { 0x08a4, 0x2320 }, /*                 topintegral ⌠ TOP HALF INTEGRAL */
+  { 0x08a5, 0x2321 }, /*                 botintegral ⌡ BOTTOM HALF INTEGRAL */
+  { 0x08a6, 0x2502 }, /*               vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
+  { 0x08a7, 0x23a1 }, /*            topleftsqbracket ⎡ ??? */
+  { 0x08a8, 0x23a3 }, /*            botleftsqbracket ⎣ ??? */
+  { 0x08a9, 0x23a4 }, /*           toprightsqbracket ⎤ ??? */
+  { 0x08aa, 0x23a6 }, /*           botrightsqbracket ⎦ ??? */
+  { 0x08ab, 0x239b }, /*               topleftparens ⎛ ??? */
+  { 0x08ac, 0x239d }, /*               botleftparens ⎝ ??? */
+  { 0x08ad, 0x239e }, /*              toprightparens ⎞ ??? */
+  { 0x08ae, 0x23a0 }, /*              botrightparens ⎠ ??? */
+  { 0x08af, 0x23a8 }, /*        leftmiddlecurlybrace ⎨ ??? */
+  { 0x08b0, 0x23ac }, /*       rightmiddlecurlybrace ⎬ ??? */
+/*  0x08b1                          topleftsummation ? ??? */
+/*  0x08b2                          botleftsummation ? ??? */
+/*  0x08b3                 topvertsummationconnector ? ??? */
+/*  0x08b4                 botvertsummationconnector ? ??? */
+/*  0x08b5                         toprightsummation ? ??? */
+/*  0x08b6                         botrightsummation ? ??? */
+/*  0x08b7                      rightmiddlesummation ? ??? */
+  { 0x08bc, 0x2264 }, /*               lessthanequal ≤ LESS-THAN OR EQUAL TO */
+  { 0x08bd, 0x2260 }, /*                    notequal ≠ NOT EQUAL TO */
+  { 0x08be, 0x2265 }, /*            greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
+  { 0x08bf, 0x222b }, /*                    integral ∫ INTEGRAL */
+  { 0x08c0, 0x2234 }, /*                   therefore ∴ THEREFORE */
+  { 0x08c1, 0x221d }, /*                   variation ∝ PROPORTIONAL TO */
+  { 0x08c2, 0x221e }, /*                    infinity ∞ INFINITY */
+  { 0x08c5, 0x2207 }, /*                       nabla ∇ NABLA */
+  { 0x08c8, 0x223c }, /*                 approximate ∼ TILDE OPERATOR */
+  { 0x08c9, 0x2243 }, /*                similarequal ≃ ASYMPTOTICALLY EQUAL TO */
+  { 0x08cd, 0x21d4 }, /*                    ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
+  { 0x08ce, 0x21d2 }, /*                     implies ⇒ RIGHTWARDS DOUBLE ARROW */
+  { 0x08cf, 0x2261 }, /*                   identical ≡ IDENTICAL TO */
+  { 0x08d6, 0x221a }, /*                     radical √ SQUARE ROOT */
+  { 0x08da, 0x2282 }, /*                  includedin ⊂ SUBSET OF */
+  { 0x08db, 0x2283 }, /*                    includes ⊃ SUPERSET OF */
+  { 0x08dc, 0x2229 }, /*                intersection ∩ INTERSECTION */
+  { 0x08dd, 0x222a }, /*                       union ∪ UNION */
+  { 0x08de, 0x2227 }, /*                  logicaland ∧ LOGICAL AND */
+  { 0x08df, 0x2228 }, /*                   logicalor ∨ LOGICAL OR */
+  { 0x08ef, 0x2202 }, /*           partialderivative ∂ PARTIAL DIFFERENTIAL */
+  { 0x08f6, 0x0192 }, /*                    function ƒ LATIN SMALL LETTER F WITH HOOK */
+  { 0x08fb, 0x2190 }, /*                   leftarrow ← LEFTWARDS ARROW */
+  { 0x08fc, 0x2191 }, /*                     uparrow ↑ UPWARDS ARROW */
+  { 0x08fd, 0x2192 }, /*                  rightarrow → RIGHTWARDS ARROW */
+  { 0x08fe, 0x2193 }, /*                   downarrow ↓ DOWNWARDS ARROW */
+/*  0x09df                                     blank ? ??? */
+  { 0x09e0, 0x25c6 }, /*                soliddiamond ◆ BLACK DIAMOND */
+  { 0x09e1, 0x2592 }, /*                checkerboard ▒ MEDIUM SHADE */
+  { 0x09e2, 0x2409 }, /*                          ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
+  { 0x09e3, 0x240c }, /*                          ff ␌ SYMBOL FOR FORM FEED */
+  { 0x09e4, 0x240d }, /*                          cr ␍ SYMBOL FOR CARRIAGE RETURN */
+  { 0x09e5, 0x240a }, /*                          lf ␊ SYMBOL FOR LINE FEED */
+  { 0x09e8, 0x2424 }, /*                          nl ␤ SYMBOL FOR NEWLINE */
+  { 0x09e9, 0x240b }, /*                          vt ␋ SYMBOL FOR VERTICAL TABULATION */
+  { 0x09ea, 0x2518 }, /*              lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
+  { 0x09eb, 0x2510 }, /*               uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
+  { 0x09ec, 0x250c }, /*                upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+  { 0x09ed, 0x2514 }, /*               lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
+  { 0x09ee, 0x253c }, /*               crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+  { 0x09ef, 0x23ba }, /*              horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
+  { 0x09f0, 0x23bb }, /*              horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
+  { 0x09f1, 0x2500 }, /*              horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
+  { 0x09f2, 0x23bc }, /*              horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
+  { 0x09f3, 0x23bd }, /*              horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
+  { 0x09f4, 0x251c }, /*                       leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+  { 0x09f5, 0x2524 }, /*                      rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+  { 0x09f6, 0x2534 }, /*                        bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+  { 0x09f7, 0x252c }, /*                        topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+  { 0x09f8, 0x2502 }, /*                     vertbar │ BOX DRAWINGS LIGHT VERTICAL */
+  { 0x0aa1, 0x2003 }, /*                     emspace   EM SPACE */
+  { 0x0aa2, 0x2002 }, /*                     enspace   EN SPACE */
+  { 0x0aa3, 0x2004 }, /*                    em3space   THREE-PER-EM SPACE */
+  { 0x0aa4, 0x2005 }, /*                    em4space   FOUR-PER-EM SPACE */
+  { 0x0aa5, 0x2007 }, /*                  digitspace   FIGURE SPACE */
+  { 0x0aa6, 0x2008 }, /*                  punctspace   PUNCTUATION SPACE */
+  { 0x0aa7, 0x2009 }, /*                   thinspace   THIN SPACE */
+  { 0x0aa8, 0x200a }, /*                   hairspace   HAIR SPACE */
+  { 0x0aa9, 0x2014 }, /*                      emdash — EM DASH */
+  { 0x0aaa, 0x2013 }, /*                      endash – EN DASH */
+/*  0x0aac                               signifblank ? ??? */
+  { 0x0aae, 0x2026 }, /*                    ellipsis … HORIZONTAL ELLIPSIS */
+  { 0x0aaf, 0x2025 }, /*             doubbaselinedot ‥ TWO DOT LEADER */
+  { 0x0ab0, 0x2153 }, /*                    onethird ⅓ VULGAR FRACTION ONE THIRD */
+  { 0x0ab1, 0x2154 }, /*                   twothirds ⅔ VULGAR FRACTION TWO THIRDS */
+  { 0x0ab2, 0x2155 }, /*                    onefifth ⅕ VULGAR FRACTION ONE FIFTH */
+  { 0x0ab3, 0x2156 }, /*                   twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
+  { 0x0ab4, 0x2157 }, /*                 threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
+  { 0x0ab5, 0x2158 }, /*                  fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
+  { 0x0ab6, 0x2159 }, /*                    onesixth ⅙ VULGAR FRACTION ONE SIXTH */
+  { 0x0ab7, 0x215a }, /*                  fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
+  { 0x0ab8, 0x2105 }, /*                      careof ℅ CARE OF */
+  { 0x0abb, 0x2012 }, /*                     figdash ‒ FIGURE DASH */
+  { 0x0abc, 0x2329 }, /*            leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */
+/*  0x0abd                              decimalpoint ? ??? */
+  { 0x0abe, 0x232a }, /*           rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */
+/*  0x0abf                                    marker ? ??? */
+  { 0x0ac3, 0x215b }, /*                   oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
+  { 0x0ac4, 0x215c }, /*                threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
+  { 0x0ac5, 0x215d }, /*                 fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
+  { 0x0ac6, 0x215e }, /*                seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
+  { 0x0ac9, 0x2122 }, /*                   trademark ™ TRADE MARK SIGN */
+  { 0x0aca, 0x2613 }, /*               signaturemark ☓ SALTIRE */
+/*  0x0acb                         trademarkincircle ? ??? */
+  { 0x0acc, 0x25c1 }, /*            leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
+  { 0x0acd, 0x25b7 }, /*           rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
+  { 0x0ace, 0x25cb }, /*                emopencircle ○ WHITE CIRCLE */
+  { 0x0acf, 0x25af }, /*             emopenrectangle ▯ WHITE VERTICAL RECTANGLE */
+  { 0x0ad0, 0x2018 }, /*         leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
+  { 0x0ad1, 0x2019 }, /*        rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
+  { 0x0ad2, 0x201c }, /*         leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
+  { 0x0ad3, 0x201d }, /*        rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
+  { 0x0ad4, 0x211e }, /*                prescription ℞ PRESCRIPTION TAKE */
+  { 0x0ad6, 0x2032 }, /*                     minutes ′ PRIME */
+  { 0x0ad7, 0x2033 }, /*                     seconds ″ DOUBLE PRIME */
+  { 0x0ad9, 0x271d }, /*                  latincross ✝ LATIN CROSS */
+/*  0x0ada                                  hexagram ? ??? */
+  { 0x0adb, 0x25ac }, /*            filledrectbullet ▬ BLACK RECTANGLE */
+  { 0x0adc, 0x25c0 }, /*         filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
+  { 0x0add, 0x25b6 }, /*        filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
+  { 0x0ade, 0x25cf }, /*              emfilledcircle ● BLACK CIRCLE */
+  { 0x0adf, 0x25ae }, /*                emfilledrect ▮ BLACK VERTICAL RECTANGLE */
+  { 0x0ae0, 0x25e6 }, /*            enopencircbullet ◦ WHITE BULLET */
+  { 0x0ae1, 0x25ab }, /*          enopensquarebullet ▫ WHITE SMALL SQUARE */
+  { 0x0ae2, 0x25ad }, /*              openrectbullet ▭ WHITE RECTANGLE */
+  { 0x0ae3, 0x25b3 }, /*             opentribulletup △ WHITE UP-POINTING TRIANGLE */
+  { 0x0ae4, 0x25bd }, /*           opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
+  { 0x0ae5, 0x2606 }, /*                    openstar ☆ WHITE STAR */
+  { 0x0ae6, 0x2022 }, /*          enfilledcircbullet • BULLET */
+  { 0x0ae7, 0x25aa }, /*            enfilledsqbullet ▪ BLACK SMALL SQUARE */
+  { 0x0ae8, 0x25b2 }, /*           filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
+  { 0x0ae9, 0x25bc }, /*         filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
+  { 0x0aea, 0x261c }, /*                 leftpointer ☜ WHITE LEFT POINTING INDEX */
+  { 0x0aeb, 0x261e }, /*                rightpointer ☞ WHITE RIGHT POINTING INDEX */
+  { 0x0aec, 0x2663 }, /*                        club ♣ BLACK CLUB SUIT */
+  { 0x0aed, 0x2666 }, /*                     diamond ♦ BLACK DIAMOND SUIT */
+  { 0x0aee, 0x2665 }, /*                       heart ♥ BLACK HEART SUIT */
+  { 0x0af0, 0x2720 }, /*                maltesecross ✠ MALTESE CROSS */
+  { 0x0af1, 0x2020 }, /*                      dagger † DAGGER */
+  { 0x0af2, 0x2021 }, /*                doubledagger ‡ DOUBLE DAGGER */
+  { 0x0af3, 0x2713 }, /*                   checkmark ✓ CHECK MARK */
+  { 0x0af4, 0x2717 }, /*                 ballotcross ✗ BALLOT X */
+  { 0x0af5, 0x266f }, /*                musicalsharp ♯ MUSIC SHARP SIGN */
+  { 0x0af6, 0x266d }, /*                 musicalflat ♭ MUSIC FLAT SIGN */
+  { 0x0af7, 0x2642 }, /*                  malesymbol ♂ MALE SIGN */
+  { 0x0af8, 0x2640 }, /*                femalesymbol ♀ FEMALE SIGN */
+  { 0x0af9, 0x260e }, /*                   telephone ☎ BLACK TELEPHONE */
+  { 0x0afa, 0x2315 }, /*           telephonerecorder ⌕ TELEPHONE RECORDER */
+  { 0x0afb, 0x2117 }, /*         phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
+  { 0x0afc, 0x2038 }, /*                       caret ‸ CARET */
+  { 0x0afd, 0x201a }, /*          singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
+  { 0x0afe, 0x201e }, /*          doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
+/*  0x0aff                                    cursor ? ??? */
+  { 0x0ba3, 0x003c }, /*                   leftcaret < LESS-THAN SIGN */
+  { 0x0ba6, 0x003e }, /*                  rightcaret > GREATER-THAN SIGN */
+  { 0x0ba8, 0x2228 }, /*                   downcaret ∨ LOGICAL OR */
+  { 0x0ba9, 0x2227 }, /*                     upcaret ∧ LOGICAL AND */
+  { 0x0bc0, 0x00af }, /*                     overbar ¯ MACRON */
+  { 0x0bc2, 0x22a5 }, /*                    downtack ⊥ UP TACK */
+  { 0x0bc3, 0x2229 }, /*                      upshoe ∩ INTERSECTION */
+  { 0x0bc4, 0x230a }, /*                   downstile ⌊ LEFT FLOOR */
+  { 0x0bc6, 0x005f }, /*                    underbar _ LOW LINE */
+  { 0x0bca, 0x2218 }, /*                         jot ∘ RING OPERATOR */
+  { 0x0bcc, 0x2395 }, /*                        quad ⎕ APL FUNCTIONAL SYMBOL QUAD */
+  { 0x0bce, 0x22a4 }, /*                      uptack ⊤ DOWN TACK */
+  { 0x0bcf, 0x25cb }, /*                      circle ○ WHITE CIRCLE */
+  { 0x0bd3, 0x2308 }, /*                     upstile ⌈ LEFT CEILING */
+  { 0x0bd6, 0x222a }, /*                    downshoe ∪ UNION */
+  { 0x0bd8, 0x2283 }, /*                   rightshoe ⊃ SUPERSET OF */
+  { 0x0bda, 0x2282 }, /*                    leftshoe ⊂ SUBSET OF */
+  { 0x0bdc, 0x22a2 }, /*                    lefttack ⊢ RIGHT TACK */
+  { 0x0bfc, 0x22a3 }, /*                   righttack ⊣ LEFT TACK */
+  { 0x0cdf, 0x2017 }, /*        hebrew_doublelowline ‗ DOUBLE LOW LINE */
+  { 0x0ce0, 0x05d0 }, /*                hebrew_aleph א HEBREW LETTER ALEF */
+  { 0x0ce1, 0x05d1 }, /*                  hebrew_bet ב HEBREW LETTER BET */
+  { 0x0ce2, 0x05d2 }, /*                hebrew_gimel ג HEBREW LETTER GIMEL */
+  { 0x0ce3, 0x05d3 }, /*                hebrew_dalet ד HEBREW LETTER DALET */
+  { 0x0ce4, 0x05d4 }, /*                   hebrew_he ה HEBREW LETTER HE */
+  { 0x0ce5, 0x05d5 }, /*                  hebrew_waw ו HEBREW LETTER VAV */
+  { 0x0ce6, 0x05d6 }, /*                 hebrew_zain ז HEBREW LETTER ZAYIN */
+  { 0x0ce7, 0x05d7 }, /*                 hebrew_chet ח HEBREW LETTER HET */
+  { 0x0ce8, 0x05d8 }, /*                  hebrew_tet ט HEBREW LETTER TET */
+  { 0x0ce9, 0x05d9 }, /*                  hebrew_yod י HEBREW LETTER YOD */
+  { 0x0cea, 0x05da }, /*            hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
+  { 0x0ceb, 0x05db }, /*                 hebrew_kaph כ HEBREW LETTER KAF */
+  { 0x0cec, 0x05dc }, /*                hebrew_lamed ל HEBREW LETTER LAMED */
+  { 0x0ced, 0x05dd }, /*             hebrew_finalmem ם HEBREW LETTER FINAL MEM */
+  { 0x0cee, 0x05de }, /*                  hebrew_mem מ HEBREW LETTER MEM */
+  { 0x0cef, 0x05df }, /*             hebrew_finalnun ן HEBREW LETTER FINAL NUN */
+  { 0x0cf0, 0x05e0 }, /*                  hebrew_nun נ HEBREW LETTER NUN */
+  { 0x0cf1, 0x05e1 }, /*               hebrew_samech ס HEBREW LETTER SAMEKH */
+  { 0x0cf2, 0x05e2 }, /*                 hebrew_ayin ע HEBREW LETTER AYIN */
+  { 0x0cf3, 0x05e3 }, /*              hebrew_finalpe ף HEBREW LETTER FINAL PE */
+  { 0x0cf4, 0x05e4 }, /*                   hebrew_pe פ HEBREW LETTER PE */
+  { 0x0cf5, 0x05e5 }, /*            hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
+  { 0x0cf6, 0x05e6 }, /*                 hebrew_zade צ HEBREW LETTER TSADI */
+  { 0x0cf7, 0x05e7 }, /*                 hebrew_qoph ק HEBREW LETTER QOF */
+  { 0x0cf8, 0x05e8 }, /*                 hebrew_resh ר HEBREW LETTER RESH */
+  { 0x0cf9, 0x05e9 }, /*                 hebrew_shin ש HEBREW LETTER SHIN */
+  { 0x0cfa, 0x05ea }, /*                  hebrew_taw ת HEBREW LETTER TAV */
+  { 0x0da1, 0x0e01 }, /*                  Thai_kokai ก THAI CHARACTER KO KAI */
+  { 0x0da2, 0x0e02 }, /*                Thai_khokhai ข THAI CHARACTER KHO KHAI */
+  { 0x0da3, 0x0e03 }, /*               Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
+  { 0x0da4, 0x0e04 }, /*               Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
+  { 0x0da5, 0x0e05 }, /*                Thai_khokhon ฅ THAI CHARACTER KHO KHON */
+  { 0x0da6, 0x0e06 }, /*             Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
+  { 0x0da7, 0x0e07 }, /*                 Thai_ngongu ง THAI CHARACTER NGO NGU */
+  { 0x0da8, 0x0e08 }, /*                Thai_chochan จ THAI CHARACTER CHO CHAN */
+  { 0x0da9, 0x0e09 }, /*               Thai_choching ฉ THAI CHARACTER CHO CHING */
+  { 0x0daa, 0x0e0a }, /*               Thai_chochang ช THAI CHARACTER CHO CHANG */
+  { 0x0dab, 0x0e0b }, /*                   Thai_soso ซ THAI CHARACTER SO SO */
+  { 0x0dac, 0x0e0c }, /*                Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
+  { 0x0dad, 0x0e0d }, /*                 Thai_yoying ญ THAI CHARACTER YO YING */
+  { 0x0dae, 0x0e0e }, /*                Thai_dochada ฎ THAI CHARACTER DO CHADA */
+  { 0x0daf, 0x0e0f }, /*                Thai_topatak ฏ THAI CHARACTER TO PATAK */
+  { 0x0db0, 0x0e10 }, /*                Thai_thothan ฐ THAI CHARACTER THO THAN */
+  { 0x0db1, 0x0e11 }, /*          Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
+  { 0x0db2, 0x0e12 }, /*             Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
+  { 0x0db3, 0x0e13 }, /*                  Thai_nonen ณ THAI CHARACTER NO NEN */
+  { 0x0db4, 0x0e14 }, /*                  Thai_dodek ด THAI CHARACTER DO DEK */
+  { 0x0db5, 0x0e15 }, /*                  Thai_totao ต THAI CHARACTER TO TAO */
+  { 0x0db6, 0x0e16 }, /*               Thai_thothung ถ THAI CHARACTER THO THUNG */
+  { 0x0db7, 0x0e17 }, /*              Thai_thothahan ท THAI CHARACTER THO THAHAN */
+  { 0x0db8, 0x0e18 }, /*               Thai_thothong ธ THAI CHARACTER THO THONG */
+  { 0x0db9, 0x0e19 }, /*                   Thai_nonu น THAI CHARACTER NO NU */
+  { 0x0dba, 0x0e1a }, /*               Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
+  { 0x0dbb, 0x0e1b }, /*                  Thai_popla ป THAI CHARACTER PO PLA */
+  { 0x0dbc, 0x0e1c }, /*               Thai_phophung ผ THAI CHARACTER PHO PHUNG */
+  { 0x0dbd, 0x0e1d }, /*                   Thai_fofa ฝ THAI CHARACTER FO FA */
+  { 0x0dbe, 0x0e1e }, /*                Thai_phophan พ THAI CHARACTER PHO PHAN */
+  { 0x0dbf, 0x0e1f }, /*                  Thai_fofan ฟ THAI CHARACTER FO FAN */
+  { 0x0dc0, 0x0e20 }, /*             Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
+  { 0x0dc1, 0x0e21 }, /*                   Thai_moma ม THAI CHARACTER MO MA */
+  { 0x0dc2, 0x0e22 }, /*                  Thai_yoyak ย THAI CHARACTER YO YAK */
+  { 0x0dc3, 0x0e23 }, /*                  Thai_rorua ร THAI CHARACTER RO RUA */
+  { 0x0dc4, 0x0e24 }, /*                     Thai_ru ฤ THAI CHARACTER RU */
+  { 0x0dc5, 0x0e25 }, /*                 Thai_loling ล THAI CHARACTER LO LING */
+  { 0x0dc6, 0x0e26 }, /*                     Thai_lu ฦ THAI CHARACTER LU */
+  { 0x0dc7, 0x0e27 }, /*                 Thai_wowaen ว THAI CHARACTER WO WAEN */
+  { 0x0dc8, 0x0e28 }, /*                 Thai_sosala ศ THAI CHARACTER SO SALA */
+  { 0x0dc9, 0x0e29 }, /*                 Thai_sorusi ษ THAI CHARACTER SO RUSI */
+  { 0x0dca, 0x0e2a }, /*                  Thai_sosua ส THAI CHARACTER SO SUA */
+  { 0x0dcb, 0x0e2b }, /*                  Thai_hohip ห THAI CHARACTER HO HIP */
+  { 0x0dcc, 0x0e2c }, /*                Thai_lochula ฬ THAI CHARACTER LO CHULA */
+  { 0x0dcd, 0x0e2d }, /*                   Thai_oang อ THAI CHARACTER O ANG */
+  { 0x0dce, 0x0e2e }, /*               Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
+  { 0x0dcf, 0x0e2f }, /*              Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
+  { 0x0dd0, 0x0e30 }, /*                  Thai_saraa ะ THAI CHARACTER SARA A */
+  { 0x0dd1, 0x0e31 }, /*             Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
+  { 0x0dd2, 0x0e32 }, /*                 Thai_saraaa า THAI CHARACTER SARA AA */
+  { 0x0dd3, 0x0e33 }, /*                 Thai_saraam ำ THAI CHARACTER SARA AM */
+  { 0x0dd4, 0x0e34 }, /*                  Thai_sarai ิ THAI CHARACTER SARA I */
+  { 0x0dd5, 0x0e35 }, /*                 Thai_saraii ี THAI CHARACTER SARA II */
+  { 0x0dd6, 0x0e36 }, /*                 Thai_saraue ึ THAI CHARACTER SARA UE */
+  { 0x0dd7, 0x0e37 }, /*                Thai_sarauee ื THAI CHARACTER SARA UEE */
+  { 0x0dd8, 0x0e38 }, /*                  Thai_sarau ุ THAI CHARACTER SARA U */
+  { 0x0dd9, 0x0e39 }, /*                 Thai_sarauu ู THAI CHARACTER SARA UU */
+  { 0x0dda, 0x0e3a }, /*                Thai_phinthu ฺ THAI CHARACTER PHINTHU */
+/*  0x0dde                    Thai_maihanakat_maitho ? ??? */
+  { 0x0ddf, 0x0e3f }, /*                   Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
+  { 0x0de0, 0x0e40 }, /*                  Thai_sarae เ THAI CHARACTER SARA E */
+  { 0x0de1, 0x0e41 }, /*                 Thai_saraae แ THAI CHARACTER SARA AE */
+  { 0x0de2, 0x0e42 }, /*                  Thai_sarao โ THAI CHARACTER SARA O */
+  { 0x0de3, 0x0e43 }, /*          Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
+  { 0x0de4, 0x0e44 }, /*         Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
+  { 0x0de5, 0x0e45 }, /*            Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
+  { 0x0de6, 0x0e46 }, /*               Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
+  { 0x0de7, 0x0e47 }, /*              Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
+  { 0x0de8, 0x0e48 }, /*                  Thai_maiek ่ THAI CHARACTER MAI EK */
+  { 0x0de9, 0x0e49 }, /*                 Thai_maitho ้ THAI CHARACTER MAI THO */
+  { 0x0dea, 0x0e4a }, /*                 Thai_maitri ๊ THAI CHARACTER MAI TRI */
+  { 0x0deb, 0x0e4b }, /*            Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
+  { 0x0dec, 0x0e4c }, /*            Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
+  { 0x0ded, 0x0e4d }, /*               Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
+  { 0x0df0, 0x0e50 }, /*                 Thai_leksun ๐ THAI DIGIT ZERO */
+  { 0x0df1, 0x0e51 }, /*                Thai_leknung ๑ THAI DIGIT ONE */
+  { 0x0df2, 0x0e52 }, /*                Thai_leksong ๒ THAI DIGIT TWO */
+  { 0x0df3, 0x0e53 }, /*                 Thai_leksam ๓ THAI DIGIT THREE */
+  { 0x0df4, 0x0e54 }, /*                  Thai_leksi ๔ THAI DIGIT FOUR */
+  { 0x0df5, 0x0e55 }, /*                  Thai_lekha ๕ THAI DIGIT FIVE */
+  { 0x0df6, 0x0e56 }, /*                 Thai_lekhok ๖ THAI DIGIT SIX */
+  { 0x0df7, 0x0e57 }, /*                Thai_lekchet ๗ THAI DIGIT SEVEN */
+  { 0x0df8, 0x0e58 }, /*                Thai_lekpaet ๘ THAI DIGIT EIGHT */
+  { 0x0df9, 0x0e59 }, /*                 Thai_lekkao ๙ THAI DIGIT NINE */
+  { 0x0ea1, 0x3131 }, /*               Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
+  { 0x0ea2, 0x3132 }, /*          Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
+  { 0x0ea3, 0x3133 }, /*           Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
+  { 0x0ea4, 0x3134 }, /*                Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
+  { 0x0ea5, 0x3135 }, /*           Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
+  { 0x0ea6, 0x3136 }, /*           Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
+  { 0x0ea7, 0x3137 }, /*               Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
+  { 0x0ea8, 0x3138 }, /*          Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
+  { 0x0ea9, 0x3139 }, /*                Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
+  { 0x0eaa, 0x313a }, /*          Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
+  { 0x0eab, 0x313b }, /*           Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
+  { 0x0eac, 0x313c }, /*           Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
+  { 0x0ead, 0x313d }, /*            Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
+  { 0x0eae, 0x313e }, /*           Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
+  { 0x0eaf, 0x313f }, /*          Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
+  { 0x0eb0, 0x3140 }, /*           Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
+  { 0x0eb1, 0x3141 }, /*                Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
+  { 0x0eb2, 0x3142 }, /*                Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
+  { 0x0eb3, 0x3143 }, /*           Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
+  { 0x0eb4, 0x3144 }, /*            Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
+  { 0x0eb5, 0x3145 }, /*                 Hangul_Sios ㅅ HANGUL LETTER SIOS */
+  { 0x0eb6, 0x3146 }, /*            Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
+  { 0x0eb7, 0x3147 }, /*                Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
+  { 0x0eb8, 0x3148 }, /*                Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
+  { 0x0eb9, 0x3149 }, /*           Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
+  { 0x0eba, 0x314a }, /*                Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
+  { 0x0ebb, 0x314b }, /*               Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
+  { 0x0ebc, 0x314c }, /*                Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
+  { 0x0ebd, 0x314d }, /*               Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
+  { 0x0ebe, 0x314e }, /*                Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
+  { 0x0ebf, 0x314f }, /*                    Hangul_A ㅏ HANGUL LETTER A */
+  { 0x0ec0, 0x3150 }, /*                   Hangul_AE ㅐ HANGUL LETTER AE */
+  { 0x0ec1, 0x3151 }, /*                   Hangul_YA ㅑ HANGUL LETTER YA */
+  { 0x0ec2, 0x3152 }, /*                  Hangul_YAE ㅒ HANGUL LETTER YAE */
+  { 0x0ec3, 0x3153 }, /*                   Hangul_EO ㅓ HANGUL LETTER EO */
+  { 0x0ec4, 0x3154 }, /*                    Hangul_E ㅔ HANGUL LETTER E */
+  { 0x0ec5, 0x3155 }, /*                  Hangul_YEO ㅕ HANGUL LETTER YEO */
+  { 0x0ec6, 0x3156 }, /*                   Hangul_YE ㅖ HANGUL LETTER YE */
+  { 0x0ec7, 0x3157 }, /*                    Hangul_O ㅗ HANGUL LETTER O */
+  { 0x0ec8, 0x3158 }, /*                   Hangul_WA ㅘ HANGUL LETTER WA */
+  { 0x0ec9, 0x3159 }, /*                  Hangul_WAE ㅙ HANGUL LETTER WAE */
+  { 0x0eca, 0x315a }, /*                   Hangul_OE ㅚ HANGUL LETTER OE */
+  { 0x0ecb, 0x315b }, /*                   Hangul_YO ㅛ HANGUL LETTER YO */
+  { 0x0ecc, 0x315c }, /*                    Hangul_U ㅜ HANGUL LETTER U */
+  { 0x0ecd, 0x315d }, /*                  Hangul_WEO ㅝ HANGUL LETTER WEO */
+  { 0x0ece, 0x315e }, /*                   Hangul_WE ㅞ HANGUL LETTER WE */
+  { 0x0ecf, 0x315f }, /*                   Hangul_WI ㅟ HANGUL LETTER WI */
+  { 0x0ed0, 0x3160 }, /*                   Hangul_YU ㅠ HANGUL LETTER YU */
+  { 0x0ed1, 0x3161 }, /*                   Hangul_EU ㅡ HANGUL LETTER EU */
+  { 0x0ed2, 0x3162 }, /*                   Hangul_YI ㅢ HANGUL LETTER YI */
+  { 0x0ed3, 0x3163 }, /*                    Hangul_I ㅣ HANGUL LETTER I */
+  { 0x0ed4, 0x11a8 }, /*             Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
+  { 0x0ed5, 0x11a9 }, /*        Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
+  { 0x0ed6, 0x11aa }, /*         Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
+  { 0x0ed7, 0x11ab }, /*              Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
+  { 0x0ed8, 0x11ac }, /*         Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
+  { 0x0ed9, 0x11ad }, /*         Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
+  { 0x0eda, 0x11ae }, /*             Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
+  { 0x0edb, 0x11af }, /*              Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
+  { 0x0edc, 0x11b0 }, /*        Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
+  { 0x0edd, 0x11b1 }, /*         Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
+  { 0x0ede, 0x11b2 }, /*         Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
+  { 0x0edf, 0x11b3 }, /*          Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
+  { 0x0ee0, 0x11b4 }, /*         Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
+  { 0x0ee1, 0x11b5 }, /*        Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
+  { 0x0ee2, 0x11b6 }, /*         Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
+  { 0x0ee3, 0x11b7 }, /*              Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
+  { 0x0ee4, 0x11b8 }, /*              Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
+  { 0x0ee5, 0x11b9 }, /*          Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
+  { 0x0ee6, 0x11ba }, /*               Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
+  { 0x0ee7, 0x11bb }, /*          Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
+  { 0x0ee8, 0x11bc }, /*              Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
+  { 0x0ee9, 0x11bd }, /*              Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
+  { 0x0eea, 0x11be }, /*              Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
+  { 0x0eeb, 0x11bf }, /*             Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
+  { 0x0eec, 0x11c0 }, /*              Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
+  { 0x0eed, 0x11c1 }, /*             Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
+  { 0x0eee, 0x11c2 }, /*              Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
+  { 0x0eef, 0x316d }, /*     Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
+  { 0x0ef0, 0x3171 }, /*    Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
+  { 0x0ef1, 0x3178 }, /*    Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
+  { 0x0ef2, 0x317f }, /*              Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
+  { 0x0ef3, 0x3181 }, /*    Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */
+  { 0x0ef4, 0x3184 }, /*   Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
+  { 0x0ef5, 0x3186 }, /*          Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
+  { 0x0ef6, 0x318d }, /*                Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
+  { 0x0ef7, 0x318e }, /*               Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
+  { 0x0ef8, 0x11eb }, /*            Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
+  { 0x0ef9, 0x11f0 }, /*  Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */
+  { 0x0efa, 0x11f9 }, /*        Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
+  { 0x0eff, 0x20a9 }, /*                  Korean_Won ₩ WON SIGN */
+  { 0x13a4, 0x20ac }, /*                        Euro € EURO SIGN */
+  { 0x13bc, 0x0152 }, /*                          OE Œ LATIN CAPITAL LIGATURE OE */
+  { 0x13bd, 0x0153 }, /*                          oe œ LATIN SMALL LIGATURE OE */
+  { 0x13be, 0x0178 }, /*                  Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
+  { 0x20ac, 0x20ac }, /*                    EuroSign € EURO SIGN */
+};
+
+VISIBLE
+long keysym2ucs(KeySym keysym)
+{
+    int min = 0;
+    int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
+    int mid;
+
+    /* first check for Latin-1 characters (1:1 mapping) */
+    if ((keysym >= 0x0020 && keysym <= 0x007e) ||
+        (keysym >= 0x00a0 && keysym <= 0x00ff))
+        return keysym;
+
+    /* also check for directly encoded 24-bit UCS characters */
+    if ((keysym & 0xff000000) == 0x01000000)
+	return keysym & 0x00ffffff;
+
+    /* binary search in table */
+    while (max >= min) {
+	mid = (min + max) / 2;
+	if (keysymtab[mid].keysym < keysym)
+	    min = mid + 1;
+	else if (keysymtab[mid].keysym > keysym)
+	    max = mid - 1;
+	else {
+	    /* found it */
+	    return keysymtab[mid].ucs;
+	}
+    }
+
+    /* no matching Unicode value found */
+    return -1;
+}
--- /dev/null
+++ b/limbo/NOTICE
@@ -1,0 +1,25 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 1995-1999 Lucent Technologies Inc.
+Portions Copyright © 1997-2000 Vita Nuova Limited
+Portions Copyright © 2000-2009 Vita Nuova Holdings Limited
+
+This program 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 2 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, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--- /dev/null
+++ b/limbo/asm.c
@@ -1,0 +1,289 @@
+#include "limbo.h"
+
+void
+asmentry(Decl *e)
+{
+	if(e == nil)
+		return;
+	Bprint(bout, "\tentry\t%ld, %d\n", e->pc->pc, e->desc->id);
+}
+
+void
+asmmod(Decl *m)
+{
+	Bprint(bout, "\tmodule\t");
+	Bprint(bout, "%s\n", m->sym->name);
+	for(m = m->ty->tof->ids; m != nil; m = m->next){
+		switch(m->store){
+		case Dglobal:
+			Bprint(bout, "\tlink\t-1,-1,0x%lux,\".mp\"\n", sign(m));
+			break;
+		case Dfn:
+			Bprint(bout, "\tlink\t%d,%ld,0x%lux,\"",
+				m->desc->id, m->pc->pc, sign(m));
+			if(m->dot->ty->kind == Tadt)
+				Bprint(bout, "%s.", m->dot->sym->name);
+			Bprint(bout, "%s\"\n", m->sym->name);
+			break;
+		}
+	}
+}
+
+#define	NAMELEN	64
+
+void
+asmpath(void)
+{
+	char name[8*NAMELEN], *sp;
+
+	sp = srcpath(name, 8*NAMELEN);
+	Bprint(bout, "\tsource\t\"%s\"\n", sp);
+}
+
+void
+asmdesc(Desc *d)
+{
+	uchar *m, *e;
+
+	for(; d != nil; d = d->next){
+		Bprint(bout, "\tdesc\t$%d,%lud,\"", d->id, d->size);
+		e = d->map + d->nmap;
+		for(m = d->map; m < e; m++)
+			Bprint(bout, "%.2x", *m);
+		Bprint(bout, "\"\n");
+	}
+}
+
+void
+asmvar(long size, Decl *d)
+{
+	Bprint(bout, "\tvar\t@mp,%ld\n", size);
+
+	for(; d != nil; d = d->next)
+		if(d->store == Dglobal && d->init != nil)
+			asminitializer(d->offset, d->init);
+}
+
+void
+asmldt(long size, Decl *d)
+{
+	Bprint(bout, "\tldts\t@ldt,%ld\n", size);
+
+	for(; d != nil; d = d->next)
+		if(d->store == Dglobal && d->init != nil)
+			asminitializer(d->offset, d->init);
+}
+
+void
+asminitializer(long offset, Node *n)
+{
+	Node *elem, *wild;
+	Case *c;
+	Label *lab;
+	Decl *id;
+	ulong dv[2];
+	long e, last, esz, dotlen, idlen;
+	int i;
+
+	switch(n->ty->kind){
+	case Tbyte:
+		Bprint(bout, "\tbyte\t@mp+%ld,%ld\n", offset, (long)n->val & 0xff);
+		break;
+	case Tint:
+	case Tfix:
+		Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, (long)n->val);
+		break;
+	case Tbig:
+		Bprint(bout, "\tlong\t@mp+%ld,%lld # %.16llux\n", offset, n->val, n->val);
+		break;
+	case Tstring:
+		asmstring(offset, n->decl->sym);
+		break;
+	case Treal:
+		dtocanon(n->rval, dv);
+		Bprint(bout, "\treal\t@mp+%ld,%g # %.8lux%.8lux\n", offset, n->rval, dv[0], dv[1]);
+		break;
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+		id = n->ty->ids;
+		for(n = n->left; n != nil; n = n->right){
+			asminitializer(offset + id->offset, n->left);
+			id = id->next;
+		}
+		break;
+	case Tcase:
+		c = n->ty->cse;
+		Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab);
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			Bprint(bout, ",%ld,%ld,%ld", (long)lab->start->val, (long)lab->stop->val+1, lab->inst->pc);
+		}
+		Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tcasel:
+		c = n->ty->cse;
+		Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab);
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			Bprint(bout, ",%lld,%lld,%ld", lab->start->val, lab->stop->val+1, lab->inst->pc);
+		}
+		Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tcasec:
+		c = n->ty->cse;
+		Bprint(bout, "\tword\t@mp+%ld,%d\n", offset, c->nlab);
+		offset += IBY2WD;
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			asmstring(offset, lab->start->decl->sym);
+			offset += IBY2WD;
+			if(lab->stop != lab->start)
+				asmstring(offset, lab->stop->decl->sym);
+			offset += IBY2WD;
+			Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, lab->inst->pc);
+			offset += IBY2WD;
+		}
+		Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tgoto:
+		c = n->ty->cse;
+		Bprint(bout, "\tword\t@mp+%ld", offset);
+		Bprint(bout, ",%ld", n->ty->size/IBY2WD-1);
+		for(i = 0; i < c->nlab; i++)
+			Bprint(bout, ",%ld", c->labs[i].inst->pc);
+		if(c->iwild != nil)
+			Bprint(bout, ",%ld", c->iwild->pc);
+		Bprint(bout, "\n");
+		break;
+	case Tany:
+		break;
+	case Tarray:
+		Bprint(bout, "\tarray\t@mp+%ld,$%d,%ld\n", offset, n->ty->tof->decl->desc->id, (long)n->left->val);
+		if(n->right == nil)
+			break;
+		Bprint(bout, "\tindir\t@mp+%ld,0\n", offset);
+		c = n->right->ty->cse;
+		wild = nil;
+		if(c->wild != nil)
+			wild = c->wild->right;
+		last = 0;
+		esz = n->ty->tof->size;
+		for(i = 0; i < c->nlab; i++){
+			e = c->labs[i].start->val;
+			if(wild != nil){
+				for(; last < e; last++)
+					asminitializer(esz * last, wild);
+			}
+			last = e;
+			e = c->labs[i].stop->val;
+			elem = c->labs[i].node->right;
+			for(; last <= e; last++)
+				asminitializer(esz * last, elem);
+		}
+		if(wild != nil)
+			for(e = n->left->val; last < e; last++)
+				asminitializer(esz * last, wild);
+		Bprint(bout, "\tapop\n");
+		break;
+	case Tiface:
+		if(LDT)
+			Bprint(bout, "\tword\t@ldt+%ld,%ld\n", offset, (long)n->val);
+		else
+			Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, (long)n->val);
+		offset += IBY2WD;
+		for(id = n->decl->ty->ids; id != nil; id = id->next){
+			offset = align(offset, IBY2WD);
+			if(LDT)
+				Bprint(bout, "\text\t@ldt+%ld,0x%lux,\"", offset, sign(id));
+			else
+				Bprint(bout, "\text\t@mp+%ld,0x%lux,\"", offset, sign(id));
+			dotlen = 0;
+			idlen = id->sym->len + 1;
+			if(id->dot->ty->kind == Tadt){
+				dotlen = id->dot->sym->len + 1;
+				Bprint(bout, "%s.", id->dot->sym->name);
+			}
+			Bprint(bout, "%s\"\n", id->sym->name);
+			offset += idlen + dotlen + IBY2WD;
+		}
+		break;
+	default:
+		nerror(n, "can't asm global %n", n);
+		break;
+	}
+}
+
+void
+asmexc(Except *es)
+{
+	int i, o, n, id;
+	Decl *d;
+	Except *e;
+	Case *c;
+	Label *lab;
+
+	n = 0;
+	for(e = es; e != nil; e = e->next)
+		n++;
+	Bprint(bout, "\texceptions\t%d\n", n);
+	for(e = es; e != nil; e = e->next){
+		if(!e->p1->reach && !e->p2->reach)
+			continue;
+		c = e->c;
+		o = e->d->offset;
+		if(e->desc != nil)
+			id = e->desc->id;
+		else
+			id = -1;
+		Bprint(bout, "\texception\t%ld, %ld, %d, %d, %d, %d\n", getpc(e->p1), getpc(e->p2), o, id, c->nlab, e->ne);
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			d = lab->start->decl;
+			if(lab->start->ty->kind == Texception)
+				d = d->init->decl;
+			Bprint(bout, "\texctab\t\"%s\", %ld\n", d->sym->name, lab->inst->pc);
+		}
+		if(c->iwild == nil)
+			Bprint(bout, "\texctab\t*, %d\n", -1);
+		else
+			Bprint(bout, "\texctab\t*, %ld\n", c->iwild->pc);
+	}
+}
+
+void
+asmstring(long offset, Sym *sym)
+{
+	char *s, *se;
+	int c;
+
+	Bprint(bout, "\tstring\t@mp+%ld,\"", offset);
+	s = sym->name;
+	se = s + sym->len;
+	for(; s < se; s++){
+		c = *s;
+		if(c == '\n')
+			Bwrite(bout, "\\n", 2);
+		else if(c == '\0')
+			Bwrite(bout, "\\z", 2);
+		else if(c == '"')
+			Bwrite(bout, "\\\"", 2);
+		else if(c == '\\')
+			Bwrite(bout, "\\\\", 2);
+		else
+			Bputc(bout, c);
+	}
+	Bprint(bout, "\"\n");
+}
+
+void
+asminst(Inst *in)
+{
+	for(; in != nil; in = in->next){
+		if(in->op == INOOP)
+			continue;
+		if(in->pc % 10 == 0)
+			Bprint(bout, "#%ld\n", in->pc);
+		Bprint(bout, "%I\n", in);
+	}
+}
--- /dev/null
+++ b/limbo/com.c
@@ -1,0 +1,1510 @@
+#include "limbo.h"
+
+static	Inst	**breaks;
+static	Inst	**conts;
+static	Decl	**labels;
+static	Node **bcscps;
+static	int	labdep;
+static	Inst	nocont;
+
+static	int scp;
+static	Node *scps[MaxScope];
+
+static	int trcom(Node*, Node*, int);
+
+static void
+pushscp(Node *n)
+{
+	if (scp >= MaxScope)
+		fatal("scope too deep");
+	scps[scp++] = n;
+}
+
+static void
+popscp(void)
+{
+	scp--;
+}
+
+static Node *
+curscp(void)
+{
+	if (scp == 0)
+		return nil;
+	return scps[scp-1];
+}
+
+static void
+zeroscopes(Node *stop)
+{
+	int i;
+	Node *cs;
+
+	for (i = scp-1; i >= 0; i--) {
+		cs = scps[i];
+		if (cs == stop)
+			break;
+		zcom(cs->left, nil);
+	}
+}
+
+static void
+zeroallscopes(Node *n, Node **nn)
+{
+	if(n == nil)
+		return;
+	for(; n != nil; n = n->right){
+		switch(n->op){
+		case Oscope:
+			zeroallscopes(n->right, nn);
+			zcom(n->left, nn);
+			return;
+		case Olabel:
+		case Odo:
+			zeroallscopes(n->right, nn);
+			return;
+		case Oif:
+		case Ofor:
+			zeroallscopes(n->right->left, nn);
+			zeroallscopes(n->right->right, nn);
+			return;
+		case Oalt:
+		case Ocase:
+		case Opick:
+		case Oexcept:
+			for(n = n->right; n != nil; n = n->right)
+				zeroallscopes(n->left->right, nn);
+			return;
+		case Oseq:
+			zeroallscopes(n->left, nn);
+			break;
+		case Oexstmt:
+			zeroallscopes(n->left, nn);
+			zeroallscopes(n->right, nn);
+			return;
+		default:
+			return;
+		}
+	}
+}
+
+static Except *excs;
+
+static void
+installexc(Node *en, Inst *p1, Inst *p2, Node *zn)
+{
+	int i, ne;
+	Except *e;
+	Case *c;
+	Label *lab;
+
+	e = allocmem(sizeof(Except));
+	e->p1 = p1;
+	e->p2 = p2;
+	e->c = en->ty->cse;
+	e->d = en->left->decl;
+	e->zn = zn;
+	e->desc = nil;
+	e->next = excs;
+	excs = e;
+
+	ne = 0;
+	c = e->c;
+	for(i = 0; i < c->nlab; i++){
+		lab = &c->labs[i];
+		if(lab->start->ty->kind == Texception)
+			ne++;
+	}
+	e->ne = ne;
+}
+
+static int
+inlist(Decl *d, Decl *dd)
+{
+	for( ; dd != nil; dd = dd->next)
+		if(d == dd)
+			return 1;
+	return 0;
+}
+
+static void
+excdesc(void)
+{
+	ulong o, maxo;
+	Except *e;
+	Node *n;
+	Decl *d, *dd, *nd;
+
+	for(e = excs; e != nil; e = e->next){
+		if(e->zn != nil){
+			/* set up a decl list for gendesc */
+			dd = nil;
+			maxo = 0;
+			for(n = e->zn ; n != nil; n = n->right){
+				d = n->decl;
+				d->locals = d->next;
+				if(!inlist(d, dd)){
+					d->next = dd;
+					dd = d;
+					o = d->offset+d->ty->size;
+					if(o > maxo)
+						maxo = o;
+				}
+			}
+			e->desc = gendesc(e->d, align(maxo, MaxAlign), dd);
+			for(d = dd; d != nil; d = nd){
+				nd = d->next;
+				d->next = d->locals;
+				d->locals = nil;
+			}
+			e->zn = nil;
+		}
+	}
+}
+
+static Except*
+reve(Except *e)
+{
+	Except *l, *n;
+
+	l = nil;
+	for( ; e != nil; e = n){
+		n = e->next;
+		e->next = l;
+		l = e;
+	}
+	return l;
+}
+
+static int
+ckinline0(Node *n, Decl *d)
+{
+	Decl *dd;
+
+	if(n == nil)
+		return 1;
+	if(n->op == Oname){
+		dd = n->decl;
+		if(d == dd)
+			return 0;
+		if(dd->caninline == 1)
+			return ckinline0(dd->init->right, d);
+		return 1;
+	}
+	return ckinline0(n->left, d) && ckinline0(n->right, d);
+}
+
+static void
+ckinline(Decl *d)
+{
+	d->caninline = ckinline0(d->init->right, d);
+}
+		
+void
+modcom(Decl *entry)
+{
+	Decl *globals, *m, *nils, *d, *ldts;
+	long ninst, ndata, ndesc, nlink, offset, ldtoff;
+	int ok, i, hints;
+	Dlist *dl;
+
+	if(errors)
+		return;
+
+	if(emitcode || emitstub || emittab != nil){
+		emit(curscope());
+		popscope();
+		return;
+	}
+
+	/*
+	 * scom introduces global variables for case statements
+	 * and unaddressable constants, so it must be done before
+	 * popping the global scope
+	 */
+	nlabel = 0;
+	maxstack = MaxTemp;
+	genstart();
+
+	for(i = 0; i < nfns; i++)
+		if(fns[i]->caninline == 1)
+			ckinline(fns[i]);
+
+	ok = 0;
+	for(i = 0; i < nfns; i++){
+		d = fns[i];
+if(debug['v']) print("fncom: %s %d %p\n", d->sym->name, d->refs, d);
+		if(d->refs > 1 && !(d->caninline == 1 && local(d) && d->iface == nil)){
+			fns[ok++] = d;
+			fncom(d);
+		}
+	}
+	nfns = ok;
+	if(blocks != -1)
+		fatal("blocks not nested correctly");
+	firstinst = firstinst->next;
+	if(errors)
+		return;
+
+	globals = popscope();
+	checkrefs(globals);
+	if(errors)
+		return;
+	globals = vars(globals);
+	moddataref();
+
+	nils = popscope();
+	m = nil;
+	for(d = nils; d != nil; d = d->next){
+		if(debug['n'])
+			print("nil '%s' ref %d\n", d->sym->name, d->refs);
+		if(d->refs && m == nil)
+			m = dupdecl(d);
+		d->offset = 0;
+	}
+	globals = appdecls(m, globals);
+	globals = namesort(globals);
+	globals = modglobals(impdecls->d, globals);
+	vcom(globals);
+	narrowmods();
+	ldts = nil;
+	if(LDT)
+		globals = resolveldts(globals, &ldts);
+	offset = idoffsets(globals, 0, IBY2WD);
+	if(LDT)
+		ldtoff = idindices(ldts);	/* ldtoff = idoffsets(ldts, 0, IBY2WD); */
+	for(d = nils; d != nil; d = d->next){
+		if(debug['n'])
+			print("nil '%s' ref %d\n", d->sym->name, d->refs);
+		if(d->refs)
+			d->offset = m->offset;
+	}
+
+	if(debug['g']){
+		print("globals:\n");
+		printdecls(globals);
+	}
+
+	ndata = 0;
+	for(d = globals; d != nil; d = d->next)
+		ndata++;
+	ndesc = resolvedesc(impdecls->d, offset, globals);
+	ninst = resolvepcs(firstinst);
+	modresolve();
+	if(impdecls->next != nil)
+		for(dl = impdecls; dl != nil; dl = dl->next)
+			resolvemod(dl->d);
+	nlink = resolvemod(impdecl);
+
+	maxstack *= 10;
+	if(fixss != 0)
+		maxstack = fixss;
+
+	if(debug['s'])
+		print("%ld instructions\n%ld data elements\n%ld type descriptors\n%ld functions exported\n%ld stack size\n",
+			ninst, ndata, ndesc, nlink, maxstack);
+
+	excs = reve(excs);
+
+	if(gendis){
+		discon(XMAGIC);
+		hints = 0;
+		if(mustcompile)
+			hints |= MUSTCOMPILE;
+		if(dontcompile)
+			hints |= DONTCOMPILE;
+		if(LDT)
+			hints |= HASLDT;
+		if(excs != nil)
+			hints |= HASEXCEPT;
+		discon(hints);		/* runtime hints */
+		discon(maxstack);	/* minimum stack extent size */
+		discon(ninst);
+		discon(offset);
+		discon(ndesc);
+		discon(nlink);
+		disentry(entry);
+		disinst(firstinst);
+		disdesc(descriptors);
+		disvar(offset, globals);
+		dismod(impdecl);
+		if(LDT)
+			disldt(ldtoff, ldts);
+		if(excs != nil)
+			disexc(excs);
+		dispath();
+	}else{
+		asminst(firstinst);
+		asmentry(entry);
+		asmdesc(descriptors);
+		asmvar(offset, globals);
+		asmmod(impdecl);
+		if(LDT)
+			asmldt(ldtoff, ldts);
+		if(excs != nil)
+			asmexc(excs);
+		asmpath();
+	}
+	if(bsym != nil){
+		sblmod(impdecl);
+
+		sblfiles();
+		sblinst(firstinst, ninst);
+		sblty(adts, nadts);
+		sblfn(fns, nfns);
+		sblvar(globals);
+	}
+
+	firstinst = nil;
+	lastinst = nil;
+
+	excs = nil;
+}
+
+void
+fncom(Decl *decl)
+{
+	Src src;
+	Node *n;
+	Decl *loc, *last;
+	Inst *in;
+	int valued;
+
+	curfn = decl;
+	if(ispoly(decl))
+		addfnptrs(decl, 1);
+
+	/*
+	 * pick up the function body and compile it
+	 * this code tries to clean up the parse nodes as fast as possible
+	 * function is Ofunc(name, body)
+	 */
+	decl->pc = nextinst();
+	tinit();
+	labdep = 0;
+	scp = 0;
+	breaks = allocmem(maxlabdep * sizeof breaks[0]);
+	conts = allocmem(maxlabdep * sizeof conts[0]);
+	labels = allocmem(maxlabdep * sizeof labels[0]);
+	bcscps = allocmem(maxlabdep * sizeof bcscps[0]);
+	n = decl->init;
+	if(decl->caninline == 1)
+		decl->init = dupn(0, nil, n);
+	else
+		decl->init = n->left;
+	src = n->right->src;
+	src.start.line = src.stop.line;
+	src.start.pos = src.stop.pos - 1;
+	for(n = n->right; n != nil; n = n->right){
+		if(n->op != Oseq){
+			if(n->op == Ocall && trcom(n, nil, 1))
+				break;
+			scom(n);
+			break;
+		}
+		if(n->left->op == Ocall && trcom(n->left, n->right, 1)){
+			n = n->right;
+			if(n == nil || n->op != Oseq)
+				break;
+		}
+		else
+			scom(n->left);
+	}
+	pushblock();
+	valued = decl->ty->tof != tnone;
+	in = genrawop(&src, valued? IRAISE: IRET, nil, nil, nil);
+	popblock();
+	reach(decl->pc);
+	if(valued && in->reach)
+		error(src.start, "no return at end of function %D", decl);
+	/* decl->endpc = lastinst; */
+	if(labdep != 0)
+		fatal("unbalanced label stack");
+	free(breaks);
+	free(conts);
+	free(labels);
+	free(bcscps);
+
+	loc = declsort(appdecls(vars(decl->locals), tdecls()));
+	decl->offset = idoffsets(loc, decl->offset, MaxAlign);
+	for(last = decl->ty->ids; last != nil && last->next != nil; last = last->next)
+		;
+	if(last != nil)
+		last->next = loc;
+	else
+		decl->ty->ids = loc;
+
+	if(debug['f']){
+		print("fn: %s\n", decl->sym->name);
+		printdecls(decl->ty->ids);
+	}
+
+	decl->desc = gendesc(decl, decl->offset, decl->ty->ids);
+	decl->locals = loc;
+	excdesc();
+	if(decl->offset > maxstack)
+		maxstack = decl->offset;
+	if(optims)
+		optim(decl->pc, decl);
+	if(last != nil)
+		last->next = nil;
+	else
+		decl->ty->ids = nil;
+}
+
+/*
+ * statement compiler
+ */
+void
+scom(Node *n)
+{
+	Inst *p, *pp, *p1, *p2, *p3;
+	Node tret, *left, *zn;
+
+	for(; n != nil; n = n->right){
+		switch(n->op){
+		case Ocondecl:
+		case Otypedecl:
+		case Ovardecl:
+		case Oimport:
+		case Oexdecl:
+			return;
+		case Ovardecli:
+			break;
+		case Oscope:
+			pushscp(n);
+			scom(n->right);
+			popscp();
+			zcom(n->left, nil);
+			return;
+		case Olabel:
+			scom(n->right);
+			return;
+		case Oif:
+			pushblock();
+			left = simplify(n->left);
+			if(left->op == Oconst && left->ty == tint){
+				if(left->val != 0)
+					scom(n->right->left);
+				else
+					scom(n->right->right);
+				popblock();
+				return;
+			}
+			sumark(left);
+			pushblock();
+			p = bcom(left, 1, nil);
+			tfreenow();
+			popblock();
+			scom(n->right->left);
+			if(n->right->right != nil){
+				pp = p;
+				p = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+				patch(pp, nextinst());
+				scom(n->right->right);
+			}
+			patch(p, nextinst());
+			popblock();
+			return;
+		case Ofor:
+			n->left = left = simplify(n->left);
+			if(left->op == Oconst && left->ty == tint){
+				if(left->val == 0)
+					return;
+				left->op = Onothing;
+				left->ty = tnone;
+				left->decl = nil;
+			}
+			pp = nextinst();
+			pushblock();
+			/* b = pushblock(); */
+			sumark(left);
+			p = bcom(left, 1, nil);
+			tfreenow();
+			popblock();
+
+			if(labdep >= maxlabdep)
+				fatal("label stack overflow");
+			breaks[labdep] = nil;
+			conts[labdep] = nil;
+			labels[labdep] = n->decl;
+			bcscps[labdep] = curscp();
+			labdep++;
+			scom(n->right->left);
+			labdep--;
+
+			patch(conts[labdep], nextinst());
+			if(n->right->right != nil){
+				pushblock();
+				scom(n->right->right);
+				popblock();
+			}
+			repushblock(lastinst->block);	/* was b */
+			patch(genrawop(&lastinst->src, IJMP, nil, nil, nil), pp);	/* for cprof: was &left->src */
+			popblock();
+			patch(p, nextinst());
+			patch(breaks[labdep], nextinst());
+			return;
+		case Odo:
+			pp = nextinst();
+
+			if(labdep >= maxlabdep)
+				fatal("label stack overflow");
+			breaks[labdep] = nil;
+			conts[labdep] = nil;
+			labels[labdep] = n->decl;
+			bcscps[labdep] = curscp();
+			labdep++;
+			scom(n->right);
+			labdep--;
+
+			patch(conts[labdep], nextinst());
+
+			left = simplify(n->left);
+			if(left->op == Onothing
+			|| left->op == Oconst && left->ty == tint){
+				if(left->op == Onothing || left->val != 0){
+					pushblock();
+					p = genrawop(&left->src, IJMP, nil, nil, nil);
+					popblock();
+				}else
+					p = nil;
+			}else{
+				pushblock();
+				p = bcom(sumark(left), 0, nil);
+				tfreenow();
+				popblock();
+			}
+			patch(p, pp);
+			patch(breaks[labdep], nextinst());
+			return;
+		case Oalt:
+		case Ocase:
+		case Opick:
+		case Oexcept:
+/* need push/pop blocks for alt guards */
+			pushblock();
+			if(labdep >= maxlabdep)
+				fatal("label stack overflow");
+			breaks[labdep] = nil;
+			conts[labdep] = &nocont;
+			labels[labdep] = n->decl;
+			bcscps[labdep] = curscp();
+			labdep++;
+			switch(n->op){
+			case Oalt:
+				altcom(n);
+				break;
+			case Ocase:
+			case Opick:
+				casecom(n);
+				break;
+			case Oexcept:
+				excom(n);
+				break;
+			}
+			labdep--;
+			patch(breaks[labdep], nextinst());
+			popblock();
+			return;
+		case Obreak:
+			pushblock();
+			bccom(n, breaks);
+			popblock();
+			break;
+		case Ocont:
+			pushblock();
+			bccom(n, conts);
+			popblock();
+			break;
+		case Oseq:
+			if(n->left->op == Ocall && trcom(n->left, n->right, 0)){
+				n = n->right;
+				if(n == nil || n->op != Oseq)
+					return;
+			}
+			else
+				scom(n->left);
+			break;
+		case Oret:
+			if(n->left != nil && n->left->op == Ocall && trcom(n->left, nil, 1))
+				return;
+			pushblock();
+			if(n->left != nil){
+				n->left = simplify(n->left);
+				sumark(n->left);
+				ecom(&n->left->src, retalloc(&tret, n->left), n->left);
+				tfreenow();
+			}
+			genrawop(&n->src, IRET, nil, nil, nil);
+			popblock();
+			return;
+		case Oexit:
+			pushblock();
+			genrawop(&n->src, IEXIT, nil, nil, nil);
+			popblock();
+			return;
+		case Onothing:
+			return;
+		case Ofunc:
+			fatal("Ofunc");
+			return;
+		case Oexstmt:
+			pushblock();
+			pp = genrawop(&n->right->src, IEXC0, nil, nil, nil);	/* marker */
+			p1 = nextinst();
+			scom(n->left);
+			p2 = nextinst();
+			p3 = genrawop(&n->right->src, IJMP, nil, nil, nil);
+			p = genrawop(&n->right->src, IEXC, nil, nil, nil);	/* marker */
+			p->d.decl = mkdecl(&n->src, 0, n->right->ty);
+			zn = nil;
+			zeroallscopes(n->left, &zn);
+			scom(n->right);
+			patch(p3, nextinst());
+			installexc(n->right, p1, p2, zn);
+			patch(pp, p);
+			popblock();
+			return;
+		default:
+			pushblock();
+			n = simplify(n);
+			sumark(n);
+			ecom(&n->src, nil, n);
+			tfreenow();
+			popblock();
+			return;
+		}
+	}
+}
+
+/*
+ * compile a break, continue
+ */
+void
+bccom(Node *n, Inst **bs)
+{
+	Sym *s;
+	Inst *p;
+	int i, ok;
+
+	s = nil;
+	if(n->decl != nil)
+		s = n->decl->sym;
+	ok = -1;
+	for(i = 0; i < labdep; i++){
+		if(bs[i] == &nocont)
+			continue;
+		if(s == nil || labels[i] != nil && labels[i]->sym == s)
+			ok = i;
+	}
+	if(ok < 0){
+		nerror(n, "no appropriate target for %V", n);
+		return;
+	}
+	zeroscopes(bcscps[ok]);
+	p = genrawop(&n->src, IJMP, nil, nil, nil);
+	p->branch = bs[ok];
+	bs[ok] = p;
+}
+
+static int
+dogoto(Case *c)
+{
+	int i, j, k, n, r, q, v;
+	Label *l, *nl;
+	Src *src;
+
+	l = c->labs;
+	n = c->nlab;
+	if(n == 0)
+		return 0;
+	r = l[n-1].stop->val - l[0].start->val+1;
+	if(r >= 3 && r <= 3*n){
+		if(r != n){
+			/* remove ranges, fill in gaps */
+			c->nlab = r;
+			nl = c->labs = allocmem(r*sizeof(*nl));
+			k = 0;
+			v = l[0].start->val-1;
+			for(i = 0; i < n; i++){
+				/* p = l[i].start->val; */
+				q = l[i].stop->val;
+				src = &l[i].start->src;
+				for(j = v+1; j <= q; j++){
+					nl[k] = l[i];
+					nl[k].start = nl[k].stop = mkconst(src, j);
+					k++;
+				}
+				v = q;
+			}
+			if(k != r)
+				fatal("bad case expansion");
+		}
+		l = c->labs;
+		for(i = 0; i < r; i++)
+			l[i].inst = nil;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+fillrange(Case *c, Node *nn, Inst *in)
+{
+	int i, j, n, p, q;
+	Label *l;
+
+	l = c->labs;
+	n = c->nlab;
+	p = nn->left->val;
+	q = nn->right->val;
+	for(i = 0; i < n; i++)
+		if(l[i].start->val == p)
+			break;
+	if(i == n)
+		fatal("fillrange fails");
+	for(j = p; j <= q; j++)
+		l[i++].inst = in;
+}
+
+static int
+nconstqual(Node *s1)
+{
+	Node *s2;
+	int n;
+
+	n = 0;
+	for(; s1 != nil; s1 = s1->right){
+		for(s2 = s1->left->left; s2 != nil; s2 = s2->right)
+			if(s2->left->op == Oconst)
+				n++;
+	}
+	return n;
+}
+
+void
+casecom(Node *cn)
+{
+	Src *src;
+	Case *c;
+	Decl *d;
+	Type *ctype;
+	Inst *j, *jmps, *wild, *k, *j1, *j2;
+	Node *n, *p, *left, tmp, nto, tmpc;
+	Label *labs;
+	char buf[32];
+	int i, nlab, op, needwild, igoto;
+
+	c = cn->ty->cse;
+
+	needwild = cn->op != Opick || nconstqual(cn->right) != cn->left->right->ty->tof->decl->tag;
+	igoto = cn->left->ty == tint && dogoto(c);
+	j1 = j2 = nil;
+
+	/*
+	 * generate global which has case labels
+	 */
+	if(igoto){
+		seprint(buf, buf+sizeof(buf), ".g%d", nlabel++);
+		cn->ty->kind = Tgoto;
+	}
+	else
+		seprint(buf, buf+sizeof(buf), ".c%d", nlabel++);
+	d = mkids(&cn->src, enter(buf, 0), cn->ty, nil);
+	d->init = mkdeclname(&cn->src, d);
+
+	nto.addable = Rmreg;
+	nto.left = nil;
+	nto.right = nil;
+	nto.op = Oname;
+	nto.ty = d->ty;
+	nto.decl = d;
+
+	tmp.decl = tmpc.decl = nil;
+	left = cn->left;
+	left = simplify(left);
+	cn->left = left;
+	sumark(left);
+	if(debug['c'])
+		print("case %n\n", left);
+	ctype = cn->left->ty;
+	if(left->addable >= Rcant){
+		if(cn->op == Opick){
+			ecom(&left->src, nil, left);
+			tfreenow();
+			left = mkunary(Oind, dupn(1, &left->src, left->left));
+			left->ty = tint;
+			sumark(left);
+			ctype = tint;
+		}else{
+			left = eacom(left, &tmp, nil);
+			tfreenow();
+		}
+	}
+
+	labs = c->labs;
+	nlab = c->nlab;
+
+	if(igoto){
+		if(labs[0].start->val != 0){
+			talloc(&tmpc, left->ty, nil);
+			if(left->addable == Radr || left->addable == Rmadr){
+				genrawop(&left->src, IMOVW, left, nil, &tmpc);
+				left = &tmpc;
+			}
+			genrawop(&left->src, ISUBW, sumark(labs[0].start), left, &tmpc);
+			left = &tmpc;
+		}
+		if(needwild){
+			j1 = genrawop(&left->src, IBLTW, left, sumark(mkconst(&left->src, 0)), nil);
+			j2 = genrawop(&left->src, IBGTW, left, sumark(mkconst(&left->src, labs[nlab-1].start->val-labs[0].start->val)), nil);
+		}
+		j = nextinst();
+		genrawop(&left->src, IGOTO, left, nil, &nto);
+		j->d.reg = IBY2WD;
+	}
+	else{
+		op = ICASE;
+		if(ctype == tbig)
+			op = ICASEL;
+		else if(ctype == tstring)
+			op = ICASEC;
+		genrawop(&left->src, op, left, nil, &nto);
+	}
+	tfree(&tmp);
+	tfree(&tmpc);
+
+	jmps = nil;
+	wild = nil;
+	for(n = cn->right; n != nil; n = n->right){
+		j = nextinst();
+		for(p = n->left->left; p != nil; p = p->right){
+			if(debug['c'])
+				print("case qualifier %n\n", p->left);
+			switch(p->left->op){
+			case Oconst:
+				labs[findlab(ctype, p->left, labs, nlab)].inst = j;
+				break;
+			case Orange:
+				labs[findlab(ctype, p->left->left, labs, nlab)].inst = j;
+				if(igoto)
+					fillrange(c, p->left, j);	
+				break;
+			case Owild:
+				if(needwild)
+					wild = j;
+/*
+				else
+					nwarn(p->left, "default case redundant");
+*/
+				break;
+			}
+		}
+
+		if(debug['c'])
+			print("case body for %V: %n\n", n->left->left, n->left->right);
+
+		k = nextinst();
+		scom(n->left->right);
+
+		src = &lastinst->src;
+		// if(n->left->right == nil || n->left->right->op == Onothing)
+		if(k == nextinst())
+			src = &n->left->left->src;
+		j = genrawop(src, IJMP, nil, nil, nil);
+		j->branch = jmps;
+		jmps = j;
+	}
+	patch(jmps, nextinst());
+	if(wild == nil && needwild)
+		wild = nextinst();
+
+	if(igoto){
+		if(needwild){
+			patch(j1, wild);
+			patch(j2, wild);
+		}
+		for(i = 0; i < nlab; i++)
+			if(labs[i].inst == nil)
+				labs[i].inst = wild;
+	}
+
+	c->iwild = wild;
+
+	d->ty->cse = c;
+	usetype(d->ty);
+	installids(Dglobal, d);
+}
+
+void
+altcom(Node *nalt)
+{
+	Src altsrc;
+	Case *c;
+	Decl *d;
+	Type *talt;
+	Node *n, *p, *left, tab, slot, off, add, which, nto, adr;
+	Node **comm, *op, *tmps;
+	Inst *j, *tj, *jmps, *me, *wild;
+	Label *labs;
+	char buf[32];
+	int i, is, ir, nlab, nsnd, altop, isptr;
+	Inst *pp;
+
+	talt = nalt->ty;
+	c = talt->cse;
+	nlab = c->nlab;
+	nsnd = c->nsnd;
+	comm = allocmem(nlab * sizeof *comm);
+	labs = allocmem(nlab * sizeof *labs);
+	tmps = allocmem(nlab * sizeof *tmps);
+	c->labs = labs;
+
+	/*
+	 * built the type of the alt channel table
+	 * note that we lie to the garbage collector
+	 * if we know that another reference exists for the channel
+	 */
+	is = 0;
+	ir = nsnd;
+	i = 0;
+	for(n = nalt->left; n != nil; n = n->right){
+		for(p = n->left->right->left; p != nil; p = p->right){
+			left = simplify(p->left);
+			p->left = left;
+			if(left->op == Owild)
+				continue;
+			comm[i] = hascomm(left);
+			left = comm[i]->left;
+			sumark(left);
+			isptr = left->addable >= Rcant;
+			if(comm[i]->op == Osnd)
+				labs[is++].isptr = isptr;
+			else
+				labs[ir++].isptr = isptr;
+			i++;
+		}
+	}
+
+	talloc(&which, tint, nil);
+	talloc(&tab, talt, nil);
+
+	/*
+	 * build the node for the address of each channel,
+	 * the values to send, and the storage fro values received
+	 */
+	off = znode;
+	off.op = Oconst;
+	off.ty = tint;
+	off.addable = Rconst;
+	adr = znode;
+	adr.op = Oadr;
+	adr.left = &tab;
+	adr.ty = tint;
+	add = znode;
+	add.op = Oadd;
+	add.left = &adr;
+	add.right = &off;
+	add.ty = tint;
+	slot = znode;
+	slot.op = Oind;
+	slot.left = &add;
+	sumark(&slot);
+
+	/*
+	 * compile the sending and receiving channels and values
+	 */
+	is = 2*IBY2WD;
+	ir = is + nsnd*2*IBY2WD;
+	i = 0;
+	for(n = nalt->left; n != nil; n = n->right){
+		for(p = n->left->right->left; p != nil; p = p->right){
+			if(p->left->op == Owild)
+				continue;
+
+			/*
+			 * gen channel
+			 */
+			op = comm[i];
+			if(op->op == Osnd){
+				off.val = is;
+				is += 2*IBY2WD;
+			}else{
+				off.val = ir;
+				ir += 2*IBY2WD;
+			}
+			left = op->left;
+
+			/*
+			 * this sleaze is lying to the garbage collector
+			 */
+			if(left->addable < Rcant)
+				genmove(&left->src, Mas, tint, left, &slot);
+			else{
+				slot.ty = left->ty;
+				ecom(&left->src, &slot, left);
+				tfreenow();
+				slot.ty = nil;
+			}
+
+			/*
+			 * gen value
+			 */
+			off.val += IBY2WD;
+			tmps[i].decl = nil;
+			p->left = rewritecomm(p->left, comm[i], &tmps[i], &slot);
+
+			i++;
+		}
+	}
+
+	/*
+	 * stuff the number of send & receive channels into the table
+	 */
+	altsrc = nalt->src;
+	altsrc.stop.pos += 3;
+	off.val = 0;
+	genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nsnd)), &slot);
+	off.val += IBY2WD;
+	genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nlab-nsnd)), &slot);
+	off.val += IBY2WD;
+
+	altop = IALT;
+	if(c->wild != nil)
+		altop = INBALT;
+	pp = genrawop(&altsrc, altop, &tab, nil, &which);
+	pp->m.offset = talt->size;	/* for optimizer */
+
+	seprint(buf, buf+sizeof(buf), ".g%d", nlabel++);
+	d = mkids(&nalt->src, enter(buf, 0), mktype(&nalt->src.start, &nalt->src.stop, Tgoto, nil, nil), nil);
+	d->ty->cse = c;
+	d->init = mkdeclname(&nalt->src, d);
+
+	nto.addable = Rmreg;
+	nto.left = nil;
+	nto.right = nil;
+	nto.op = Oname;
+	nto.decl = d;
+	nto.ty = d->ty;
+
+	me = nextinst();
+	genrawop(&altsrc, IGOTO, &which, nil, &nto);
+	me->d.reg = IBY2WD;		/* skip the number of cases field */
+	tfree(&tab);
+	tfree(&which);
+
+	/*
+	 * compile the guard expressions and bodies
+	 */
+	i = 0;
+	is = 0;
+	ir = nsnd;
+	jmps = nil;
+	wild = nil;
+	for(n = nalt->left; n != nil; n = n->right){
+		j = nil;
+		for(p = n->left->right->left; p != nil; p = p->right){
+			tj = nextinst();
+			if(p->left->op == Owild){
+				wild = nextinst();
+			}else{
+				if(comm[i]->op == Osnd)
+					labs[is++].inst = tj;
+				else{
+					labs[ir++].inst = tj;
+					tacquire(&tmps[i]);
+				}
+				sumark(p->left);
+				if(debug['a'])
+					print("alt guard %n\n", p->left);
+				ecom(&p->left->src, nil, p->left);
+				tfree(&tmps[i]);
+				tfreenow();
+				i++;
+			}
+			if(p->right != nil){
+				tj = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+				tj->branch = j;
+				j = tj;
+			}
+		}
+
+		patch(j, nextinst());
+		if(debug['a'])
+			print("alt body %n\n", n->left->right);
+		scom(n->left);
+
+		j = genrawop(&lastinst->src, IJMP, nil, nil, nil);
+		j->branch = jmps;
+		jmps = j;
+	}
+	patch(jmps, nextinst());
+	free(comm);
+
+	c->iwild = wild;
+
+	usetype(d->ty);
+	installids(Dglobal, d);
+}
+
+void
+excom(Node *en)
+{
+	Src *src;
+	Decl *ed;
+	Type *qt;
+	Case *c;
+	Inst *j, *jmps, *wild, *k;
+	Node *n, *p;
+	Label *labs;
+	int nlab;
+
+	ed = en->left->decl;
+	ed->ty = rtexception;
+	c = en->ty->cse;
+	labs = c->labs;
+	nlab = c->nlab;
+	jmps = nil;
+	wild = nil;
+	for(n = en->right; n != nil; n = n->right){
+		qt = nil;
+		j = nextinst();
+		for(p = n->left->left; p != nil; p = p->right){
+			switch(p->left->op){
+			case Oconst:
+				labs[findlab(texception, p->left, labs, nlab)].inst = j;
+				break;
+			case Owild:
+				wild = j;
+				break;
+			}
+			if(qt == nil)
+				qt = p->left->ty;
+			else if(!tequal(qt, p->left->ty))
+				qt = texception;
+		}
+		if(qt != nil)
+			ed->ty = qt;
+		k = nextinst();
+		scom(n->left->right);
+		src = &lastinst->src;
+		if(k == nextinst())
+			src = &n->left->left->src;
+		j = genrawop(src, IJMP, nil, nil, nil);
+		j->branch = jmps;
+		jmps = j;
+	}
+	ed->ty = rtexception;
+	patch(jmps, nextinst());
+	c->iwild = wild;
+}
+
+/*
+ * rewrite the communication operand
+ * allocate any temps needed for holding value to send or receive
+ */
+Node*
+rewritecomm(Node *n, Node *comm, Node *tmp, Node *slot)
+{
+	Node *adr;
+	Inst *p;
+
+	if(n == nil)
+		return nil;
+	adr = nil;
+	if(n == comm){
+		if(comm->op == Osnd && sumark(n->right)->addable < Rcant)
+			adr = n->right;
+		else{
+			adr = talloc(tmp, n->ty, nil);
+			tmp->src = n->src;
+			if(comm->op == Osnd){
+				ecom(&n->right->src, tmp, n->right);
+				tfreenow();
+			}
+			else
+				trelease(tmp);
+		}
+	}
+	if(n->right == comm && n->op == Oas && comm->op == Orcv
+	&& sumark(n->left)->addable < Rcant && (n->left->op != Oname || n->left->decl != nildecl))
+		adr = n->left;
+	if(adr != nil){
+		p = genrawop(&comm->left->src, ILEA, adr, nil, slot);
+		p->m.offset = adr->ty->size;	/* for optimizer */
+		if(comm->op == Osnd)
+			p->m.reg = 1;	/* for optimizer */
+		return adr;
+	}
+	n->left = rewritecomm(n->left, comm, tmp, slot);
+	n->right = rewritecomm(n->right, comm, tmp, slot);
+	return n;
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Decl*
+declmerge(Decl *e, Decl *f)
+{
+	Decl rock, *d;
+	int es, fs, v;
+
+	d = &rock;
+	while(e != nil && f != nil){
+		fs = f->ty->size;
+		es = e->ty->size;
+		/* v = 0; */
+		v = (e->link == nil) - (f->link == nil);
+		if(v == 0 && (es <= IBY2WD || fs <= IBY2WD))
+			v = fs - es;
+		if(v == 0)
+			v = e->refs - f->refs;
+		if(v == 0)
+			v = fs - es;
+		if(v == 0)
+			v = -strcmp(e->sym->name, f->sym->name);
+		if(v >= 0){
+			d->next = e;
+			d = e;
+			e = e->next;
+			while(e != nil && e->nid == 0){
+				d = e;
+				e = e->next;
+			}
+		}else{
+			d->next = f;
+			d = f;
+			f = f->next;
+			while(f != nil && f->nid == 0){
+				d = f;
+				f = f->next;
+			}
+		}
+		/* d = d->next; */
+	}
+	if(e != nil)
+		d->next = e;
+	else
+		d->next = f;
+	return rock.next;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Decl*
+recdeclsort(Decl *d, int n)
+{
+	Decl *r, *dd;
+	int i, m;
+
+	if(n <= 1)
+		return d;
+	m = n / 2 - 1;
+	dd = d;
+	for(i = 0; i < m; i++){
+		dd = dd->next;
+		while(dd->nid == 0)
+			dd = dd->next;
+	}
+	r = dd->next;
+	while(r->nid == 0){
+		dd = r;
+		r = r->next;
+	}
+	dd->next = nil;
+	return declmerge(recdeclsort(d, n / 2),
+			recdeclsort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the ids by size and number of references
+ */
+Decl*
+declsort(Decl *d)
+{
+	Decl *dd;
+	int n;
+
+	n = 0;
+	for(dd = d; dd != nil; dd = dd->next)
+		if(dd->nid > 0)
+			n++;
+	return recdeclsort(d, n);
+}
+
+Src nilsrc;
+
+/* Do we finally
+  * 	(a) pick off pointers as in the code below
+  *	(b) generate a block move from zeroed memory as in tfree() in gen.b in limbo version
+  *	(c) add a new block zero instruction to dis
+  *	(d) reorganize the locals/temps in a frame
+  */
+void
+zcom1(Node *n, Node **nn)
+{
+	Type *ty;
+	Decl *d;
+	Node *e, *dn;
+	Src src;
+
+	ty = n->ty;
+	if (!tmustzero(ty))
+		return;
+	if (n->op == Oname && n->decl->refs == 0)
+		return;
+	if (nn) {
+		if(n->op != Oname)
+			nerror(n, "fatal: bad op in zcom1 map");
+		n->right = *nn;
+		*nn = n;
+		return;
+	}
+	if (debug['Z'])
+		print("zcom1 : %n\n", n);
+	if (ty->kind == Tadtpick)
+		ty = ty->tof;
+	if (ty->kind == Ttuple || ty->kind == Tadt) {
+		for (d = ty->ids; d != nil; d = d->next) {
+			if (tmustzero(d->ty)) {
+				if (d->next != nil)
+					dn = dupn(0, nil, n);
+				else
+					dn = n;
+				e = mkbin(Odot, dn, mkname(&nilsrc, d->sym));
+				e->right->decl = d;
+				e->ty = e->right->ty = d->ty;
+				zcom1(e, nn);
+			}
+		}
+	}
+	else {
+		src = n->src;
+		n->src = nilsrc;
+		e = mkbin(Oas, n, mknil(&nilsrc));
+		e->ty = e->right->ty = ty;
+/*
+		if (debug['Z'])
+			print("ecom %n\n", e);
+*/
+		pushblock();
+		e = simplify(e);
+		sumark(e);
+		ecom(&e->src, nil, e);
+		popblock();
+		n->src = src;
+	}
+}
+
+void
+zcom0(Decl *id, Node **nn)
+{
+	Node *e;
+
+	e = mkname(&nilsrc, id->sym);
+	e->decl = id;
+	e->ty = id->ty;
+	zcom1(e, nn);
+}
+
+/* end of scope */
+void
+zcom(Node *n, Node **nn)
+{
+	Decl *ids, *last;
+	Node *r, *nt;
+
+	for ( ; n != nil; n = r) {
+		r = n->right;
+		n->right = nil;
+		switch (n->op) {
+			case Ovardecl :
+				last = n->left->decl;
+				for (ids = n->decl; ids != last->next; ids = ids->next)
+					zcom0(ids, nn);
+				break;
+			case Oname :
+				if (n->decl != nildecl)
+					zcom1(dupn(0, nil, n), nn);
+				break;
+			case Otuple :
+				for (nt = n->left; nt != nil; nt = nt->right)
+					zcom(nt->left, nn);
+				break;
+			default :
+				fatal("bad node in zcom()");
+				break;
+		}
+		n->right = r;
+	}
+}
+
+static int
+ret(Node *n, int nilret)
+{
+	if(n == nil)
+		return nilret;
+	if(n->op == Oseq)
+		n = n->left;
+	return n->op == Oret && n->left == nil;
+}
+
+/*
+ * tail-recursive call
+ */
+static int
+trcom(Node *e, Node *ne, int nilret)
+{
+	Decl *d, *id;
+	Node *as, *a, *f, *n;
+	Inst *p;
+
+	if(1)
+		return 0;	/* TO DO: should we enable this? */
+	if(e->op != Ocall || e->left->op != Oname)
+		return 0;
+	d = e->left->decl;
+	if(d != curfn || d->handler || ispoly(d))
+		return 0;
+	if(!ret(ne, nilret))
+		return 0;
+	pushblock();
+	id = d->ty->ids;
+	/* evaluate args in same order as normal calls */
+	for(as = e->right; as != nil; as = as->right){
+		a = as->left;
+		if(!(a->op == Oname && id == a->decl)){
+			if(occurs(id, as->right)){
+				f = talloc(mkn(0, nil, nil), id->ty, nil);
+				f->flags |= TEMP;
+			}
+			else
+				f = mkdeclname(&as->src, id);
+			n = mkbin(Oas, f, a);
+			n->ty = id->ty;
+			scom(n);
+			if(f->flags&TEMP)
+				as->left = f;
+		}
+		id = id->next;
+	}
+	id = d->ty->ids;
+	for(as = e->right; as != nil; as = as->right){
+		a = as->left;
+		if(a->flags&TEMP){
+			f = mkdeclname(&as->src, id);
+			n = mkbin(Oas, f, a);
+			n->ty = id->ty;
+			scom(n);
+			tfree(a);
+		}
+		id = id->next;
+	}
+	p = genrawop(&e->src, IJMP, nil, nil, nil);
+	patch(p, d->pc);
+	popblock();
+	return 1;
+}
--- /dev/null
+++ b/limbo/decls.c
@@ -1,0 +1,1366 @@
+#include "limbo.h"
+
+char *storename[Dend]=
+{
+	/* Dtype */	"type",
+	/* Dfn */	"function",
+	/* Dglobal */	"global",
+	/* Darg */	"argument",
+	/* Dlocal */	"local",
+	/* Dconst */	"con",
+	/* Dfield */	"field",
+	/* Dtag */	"pick tag",
+	/* Dimport */	"import",
+	/* Dunbound */	"unbound",
+	/* Dundef */	"undefined",
+	/* Dwundef */	"undefined",
+};
+
+char *storeart[Dend] =
+{
+	/* Dtype */	"a ",
+	/* Dfn */	"a ",
+	/* Dglobal */	"a ",
+	/* Darg */	"an ",
+	/* Dlocal */	"a ",
+	/* Dconst */	"a ",
+	/* Dfield */	"a ",
+	/* Dtag */	"a",
+	/* Dimport */	"an ",
+	/* Dunbound */	"",
+	/* Dundef */	"",
+	/* Dwundef */	"",
+};
+
+int storespace[Dend] =
+{
+	/* Dtype */	0,
+	/* Dfn */	0,
+	/* Dglobal */	1,
+	/* Darg */	1,
+	/* Dlocal */	1,
+	/* Dconst */	0,
+	/* Dfield */	1,
+	/* Dtag */	0,
+	/* Dimport */	0,
+	/* Dunbound */	0,
+	/* Dundef */	0,
+	/* Dwundef */	0,
+};
+
+static	Decl	*scopes[MaxScope];
+static	Decl	*tails[MaxScope];
+static	Node *scopenode[MaxScope];
+static	uchar scopekind[MaxScope];
+
+static	void	freeloc(Decl*);
+
+void
+popscopes(void)
+{
+	Decl *d;
+	Dlist *id;
+
+	/*
+	 * clear out any decls left in syms
+	 */
+	while(scope >= ScopeBuiltin){
+		for(d = scopes[scope--]; d != nil; d = d->next){
+			if(d->sym != nil){
+				d->sym->decl = d->old;
+				d->old = nil;
+			}
+		}
+	}
+
+	for(id = impdecls; id != nil; id = id->next){
+		for(d = id->d->ty->ids; d != nil; d = d->next){
+			d->sym->decl = nil;
+			d->old = nil;
+		}
+	}
+	impdecls = nil;
+
+	scope = ScopeBuiltin;
+	scopes[ScopeBuiltin] = nil;
+	tails[ScopeBuiltin] = nil;
+}
+
+void
+declstart(void)
+{
+	Decl *d;
+
+	iota = mkids(&nosrc, enter("iota", 0), tint, nil);
+	iota->init = mkconst(&nosrc, 0);
+
+	scope = ScopeNils;
+	scopes[ScopeNils] = nil;
+	tails[ScopeNils] = nil;
+
+	nildecl = mkdecl(&nosrc, Dglobal, tany);
+	nildecl->sym = enter("nil", 0);
+	installids(Dglobal, nildecl);
+	d = mkdecl(&nosrc, Dglobal, tstring);
+	d->sym = enter("", 0);
+	installids(Dglobal, d);
+
+	scope = ScopeGlobal;
+	scopes[ScopeGlobal] = nil;
+	tails[ScopeGlobal] = nil;
+}
+
+void
+redecl(Decl *d)
+{
+	Decl *old;
+
+	old = d->sym->decl;
+	if(old->store == Dwundef)
+		return;
+	error(d->src.start, "redeclaration of %K, previously declared as %k on line %L",
+		d, old, old->src.start);
+}
+
+void
+checkrefs(Decl *d)
+{
+	Decl *id, *m;
+	long refs;
+
+	for(; d != nil; d = d->next){
+		if(d->das)
+			d->refs--;
+		switch(d->store){
+		case Dtype:
+			refs = d->refs;
+			if(d->ty->kind == Tadt){
+				for(id = d->ty->ids; id != nil; id = id->next){
+					d->refs += id->refs;
+					if(id->store != Dfn)
+						continue;
+					if(id->init == nil && id->link == nil && d->importid == nil)
+						error(d->src.start, "function %s.%s not defined", d->sym->name, id->sym->name);
+					if(superwarn && !id->refs && d->importid == nil)
+						warn(d->src.start, "function %s.%s not referenced", d->sym->name, id->sym->name);
+				}
+			}
+			if(d->ty->kind == Tmodule){
+				for(id = d->ty->ids; id != nil; id = id->next){
+					refs += id->refs;
+					if(id->iface != nil)
+						id->iface->refs += id->refs;
+					if(id->store == Dtype){
+						for(m = id->ty->ids; m != nil; m = m->next){
+							refs += m->refs;
+							if(m->iface != nil)
+								m->iface->refs += m->refs;
+						}
+					}
+				}
+				d->refs = refs;
+			}
+			if(superwarn && !refs && d->importid == nil)
+				warn(d->src.start, "%K not referenced", d);
+			break;
+		case Dglobal:
+			if(!superwarn)
+				break;
+		case Dlocal:
+		case Darg:
+			if(!d->refs && d->sym != nil
+			&& d->sym->name != nil && d->sym->name[0] != '.')
+				warn(d->src.start, "%K not referenced", d);
+			break;
+		case Dconst:
+			if(superwarn && !d->refs && d->sym != nil)
+				warn(d->src.start, "%K not referenced", d);
+			if(d->ty == tstring && d->init != nil)
+				d->init->decl->refs += d->refs;
+			break;
+		case Dfn:
+			if(d->init == nil && d->importid == nil)
+				error(d->src.start, "%K not defined", d);
+			if(superwarn && !d->refs)
+				warn(d->src.start, "%K not referenced", d);
+			break;
+		case Dimport:
+			if(superwarn && !d->refs)
+				warn(d->src.start, "%K not referenced", d);
+			break;
+		}
+		if(d->das)
+			d->refs++;
+	}
+}
+
+Node*
+vardecl(Decl *ids, Type *t)
+{
+	Node *n;
+
+	n = mkn(Ovardecl, mkn(Oseq, nil, nil), nil);
+	n->decl = ids;
+	n->ty = t;
+	return n;
+}
+
+void
+vardecled(Node *n)
+{
+	Decl *ids, *last;
+	Type *t;
+	int store;
+
+	store = Dlocal;
+	if(scope == ScopeGlobal)
+		store = Dglobal;
+	if(n->ty->kind == Texception && n->ty->cons){
+		store = Dconst;
+		fatal("Texception in vardecled");
+	}
+	ids = n->decl;
+	installids(store, ids);
+	t = n->ty;
+	for(last = ids; ids != nil; ids = ids->next){
+		ids->ty = t;
+		last = ids;
+	}
+	n->left->decl = last;
+}
+
+Node*
+condecl(Decl *ids, Node *init)
+{
+	Node *n;
+
+	n = mkn(Ocondecl, mkn(Oseq, nil, nil), init);
+	n->decl = ids;
+	return n;
+}
+
+void
+condecled(Node *n)
+{
+	Decl *ids, *last;
+
+	ids = n->decl;
+	installids(Dconst, ids);
+	for(last = ids; ids != nil; ids = ids->next){
+		ids->ty = tunknown;
+		last = ids;
+	}
+	n->left->decl = last;
+}
+
+Node*
+exdecl(Decl *ids, Decl *tids)
+{
+	Node *n;
+	Type *t;
+
+	t = mktype(&ids->src.start, &ids->src.stop, Texception, nil, tids);
+	t->cons = 1;
+	n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil);
+	n->decl = ids;
+	n->ty = t;
+	return n;
+}
+
+void
+exdecled(Node *n)
+{
+	Decl *ids, *last;
+	Type *t;
+
+	ids = n->decl;
+	installids(Dconst, ids);
+	t = n->ty;
+	for(last = ids; ids != nil; ids = ids->next){
+		ids->ty = t;
+		last = ids;
+	}
+	n->left->decl = last;
+}
+
+Node*
+importdecl(Node *m, Decl *ids)
+{
+	Node *n;
+
+	n = mkn(Oimport, mkn(Oseq, nil, nil), m);
+	n->decl = ids;
+	return n;
+}
+
+void
+importdecled(Node *n)
+{
+	Decl *ids, *last;
+
+	ids = n->decl;
+	installids(Dimport, ids);
+	for(last = ids; ids != nil; ids = ids->next){
+		ids->ty = tunknown;
+		last = ids;
+	}
+	n->left->decl = last;
+}
+
+Node*
+mkscope(Node *body)
+{
+	Node *n;
+
+	n = mkn(Oscope, nil, body);
+	if(body != nil)
+		n->src = body->src;
+	return n;
+}
+
+Node*
+fndecl(Node *n, Type *t, Node *body)
+{
+	n = mkbin(Ofunc, n, body);
+	n->ty = t;
+	return n;
+}
+
+void
+fndecled(Node *n)
+{
+	Decl *d;
+	Node *left;
+
+	left = n->left;
+	if(left->op == Oname){
+		d = left->decl->sym->decl;
+		if(d == nil || d->store == Dimport){
+			d = mkids(&left->src, left->decl->sym, n->ty, nil);
+			installids(Dfn, d);
+		}
+		left->decl = d;
+		d->refs++;
+	}
+	if(left->op == Odot)
+		pushscope(nil, Sother);
+	if(n->ty->polys != nil){
+		pushscope(nil, Sother);
+		installids(Dtype, n->ty->polys);
+	}
+	pushscope(nil, Sother);
+	installids(Darg, n->ty->ids);
+	n->ty->ids = popscope();
+	if(n->ty->val != nil)
+		mergepolydecs(n->ty);
+	if(n->ty->polys != nil)
+		n->ty->polys = popscope();
+	if(left->op == Odot)
+		popscope();
+}
+
+/*
+ * check the function declaration only
+ * the body will be type checked later by fncheck
+ */
+Decl *
+fnchk(Node *n)
+{
+	int bad;
+	Decl *d, *inadt, *adtp;
+	Type *t;
+
+	bad = 0;
+	d = n->left->decl;
+	if(n->left->op == Odot)
+		d = n->left->right->decl;
+	if(d == nil)
+		fatal("decl() fnchk nil");
+	n->left->decl = d;
+	if(d->store == Dglobal || d->store == Dfield)
+		d->store = Dfn;
+	if(d->store != Dfn || d->init != nil){
+		nerror(n, "redeclaration of function %D, previously declared as %k on line %L",
+			d, d, d->src.start);
+		if(d->store == Dfn && d->init != nil)
+			bad = 1;
+	}
+	d->init = n;
+
+	t = n->ty;
+	inadt = d->dot;
+	if(inadt != nil && (inadt->store != Dtype || inadt->ty->kind != Tadt))
+		inadt = nil;
+	if(n->left->op == Odot){
+		pushscope(nil, Sother);
+		adtp = outerpolys(n->left);
+		if(adtp != nil)
+			installids(Dtype, adtp);
+		if(!polyequal(adtp, n->decl))
+			nerror(n, "adt polymorphic type mismatch");
+		n->decl = nil;
+	}
+	t = validtype(t, inadt);
+	if(n->left->op == Odot)
+		popscope();
+	if(debug['d'])
+		print("declare function %D ty %T newty %T\n", d, d->ty, t);
+	t = usetype(t);
+
+	if(!polyequal(d->ty->polys, t->polys))
+		nerror(n, "function polymorphic type mismatch");
+	if(!tcompat(d->ty, t, 0))
+		nerror(n, "type mismatch: %D defined as %T declared as %T on line %L",
+			d, t, d->ty, d->src.start);
+	else if(!raisescompat(d->ty->u.eraises, t->u.eraises))
+		nerror(n, "raises mismatch: %D", d);
+	if(t->varargs != 0)
+		nerror(n, "cannot define functions with a '*' argument, such as %D", d);
+
+	t->u.eraises = d->ty->u.eraises;
+
+	d->ty = t;
+	d->offset = idoffsets(t->ids, MaxTemp, IBY2WD);
+	d->src = n->src;
+
+	d->locals = nil;
+
+	n->ty = t;
+
+	return bad ? nil: d;
+}
+
+Node*
+globalas(Node *dst, Node *v, int valok)
+{
+	Node *tv;
+
+	if(v == nil)
+		return nil;
+	if(v->op == Oas || v->op == Odas){
+		v = globalas(v->left, v->right, valok);
+		if(v == nil)
+			return nil;
+	}else if(valok && !initable(dst, v, 0))
+		return nil;
+	switch(dst->op){
+	case Oname:
+		if(dst->decl->init != nil)
+			nerror(dst, "duplicate assignment to %V, previously assigned on line %L",
+				dst, dst->decl->init->src.start);
+		if(valok)
+			dst->decl->init = v;
+		return v;
+	case Otuple:
+		if(valok && v->op != Otuple)
+			fatal("can't deal with %n in tuple case of globalas", v);
+		tv = v->left;
+		for(dst = dst->left; dst != nil; dst = dst->right){
+			globalas(dst->left, tv->left, valok);
+			if(valok)
+				tv = tv->right;
+		}
+		return v;
+	}
+	fatal("can't deal with %n in globalas", dst);
+	return nil;
+}
+
+int
+needsstore(Decl *d)
+{
+	if(!d->refs)
+		return 0;
+	if(d->importid != nil)
+		return 0;
+	if(storespace[d->store])
+		return 1;
+	return 0;
+}
+
+/*
+ * return the list of all referenced storage variables
+ */
+Decl*
+vars(Decl *d)
+{
+	Decl *v, *n;
+
+	while(d != nil && !needsstore(d))
+		d = d->next;
+	for(v = d; v != nil; v = v->next){
+		while(v->next != nil){
+			n = v->next;
+			if(needsstore(n))
+				break;
+			v->next = n->next;
+		}
+	}
+	return d;
+}
+
+/*
+ * declare variables from the left side of a := statement
+ */
+static int
+recdasdecl(Node *n, int store, int *nid)
+{
+	Decl *d, *old;
+	int ok;
+
+	switch(n->op){
+	case Otuple:
+		ok = 1;
+		for(n = n->left; n != nil; n = n->right)
+			ok &= recdasdecl(n->left, store, nid);
+		return ok;
+	case Oname:
+		if(n->decl == nildecl){
+			*nid = -1;
+			return 1;
+		}
+		d = mkids(&n->src, n->decl->sym, nil, nil);
+		installids(store, d);
+		old = d->old;
+		if(old != nil
+		&& old->store != Dfn
+		&& old->store != Dwundef
+		&& old->store != Dundef)
+			warn(d->src.start,  "redeclaration of %K, previously declared as %k on line %L",
+				d, old, old->src.start);
+		n->decl = d;
+		d->refs++;
+		d->das = 1;
+		if(*nid >= 0)
+			(*nid)++;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+recmark(Node *n, int nid)
+{
+	switch(n->op){
+	case Otuple:
+		for(n = n->left; n != nil; n = n->right)
+			nid = recmark(n->left, nid);
+		break;
+	case Oname:
+		n->decl->nid = nid;
+		nid = 0;
+		break;
+	}
+	return nid;
+}
+
+int
+dasdecl(Node *n)
+{
+	int store, ok, nid;
+
+	nid = 0;
+	if(scope == ScopeGlobal)
+		store = Dglobal;
+	else
+		store = Dlocal;
+
+	ok = recdasdecl(n, store, &nid);
+	if(!ok)
+		nerror(n, "illegal declaration expression %V", n);
+	if(ok && store == Dlocal && nid > 1)
+		recmark(n, nid);
+	return ok;
+}
+
+/*
+ * declare global variables in nested := expressions
+ */
+void
+gdasdecl(Node *n)
+{
+	if(n == nil)
+		return;
+
+	if(n->op == Odas){
+		gdasdecl(n->right);
+		dasdecl(n->left);
+	}else{
+		gdasdecl(n->left);
+		gdasdecl(n->right);
+	}
+}
+
+Decl*
+undefed(Src *src, Sym *s)
+{
+	Decl *d;
+
+	d = mkids(src, s, tnone, nil);
+	error(src->start, "%s is not declared", s->name);
+	installids(Dwundef, d);
+	return d;
+}
+
+/*
+int
+inloop()
+{
+	int i;
+
+	for (i = scope; i > 0; i--)
+		if (scopekind[i] == Sloop)
+			return 1;
+	return 0;
+}
+*/
+
+int
+nested()
+{
+	int i;
+
+	for (i = scope; i > 0; i--)
+		if (scopekind[i] == Sscope || scopekind[i] == Sloop)
+			return 1;
+	return 0;
+}
+
+void
+decltozero(Node *n)
+{
+	Node *scp;
+
+	if ((scp = scopenode[scope]) != nil) {
+		/* can happen if we do
+		 *	x[i] := ......
+		 * which is an error
+		 */
+		if (n->right != nil && errors == 0)
+			fatal("Ovardecl/Oname/Otuple has right field\n");
+		n->right = scp->left;
+		scp->left = n;
+	}
+}
+
+void
+pushscope(Node *scp, int kind)
+{
+	if(scope >= MaxScope)
+		fatal("scope too deep");
+	scope++;
+	scopes[scope] = nil;
+	tails[scope] = nil;
+	scopenode[scope] = scp;
+	scopekind[scope] = kind;
+}
+
+Decl*
+curscope(void)
+{
+	return scopes[scope];
+}
+
+/*
+ * revert to old declarations for each symbol in the currect scope.
+ * remove the effects of any imported adt types
+ * whenever the adt is imported from a module,
+ * we record in the type's decl the module to use
+ * when calling members.  the process is reversed here.
+ */
+Decl*
+popscope(void)
+{
+	Decl *id;
+	Type *t;
+
+if (debug['X'])
+	print("popscope\n");
+	for(id = scopes[scope]; id != nil; id = id->next){
+		if(id->sym != nil){
+if (debug['X'])
+	print("%s : %s %d\n", id->sym->name, kindname[id->ty->kind], id->init != nil ? id->init->op : 0);
+			id->sym->decl = id->old;
+			id->old = nil;
+		}
+		if(id->importid != nil)
+			id->importid->refs += id->refs;
+		t = id->ty;
+		if(id->store == Dtype
+		&& t->decl != nil
+		&& t->decl->timport == id)
+			t->decl->timport = id->timport;
+		if(id->store == Dlocal)
+			freeloc(id);
+	}
+	return scopes[scope--];
+}
+
+/*
+ * make a new scope,
+ * preinstalled with some previously installed identifiers
+ * don't add the identifiers to the scope chain,
+ * so they remain separate from any newly installed ids
+ *
+ * these routines assume no ids are imports
+ */
+void
+repushids(Decl *ids)
+{
+	Sym *s;
+
+	if(scope >= MaxScope)
+		fatal("scope too deep");
+	scope++;
+	scopes[scope] = nil;
+	tails[scope] = nil;
+	scopenode[scope] = nil;
+	scopekind[scope] = Sother;
+
+	for(; ids != nil; ids = ids->next){
+		if(ids->scope != scope
+		&& (ids->dot == nil || !isimpmod(ids->dot->sym)
+			|| ids->scope != ScopeGlobal || scope != ScopeGlobal + 1))
+			fatal("repushids scope mismatch");
+		s = ids->sym;
+		if(s != nil && ids->store != Dtag){
+			if(s->decl != nil && s->decl->scope >= scope)
+				ids->old = s->decl->old;
+			else
+				ids->old = s->decl;
+			s->decl = ids;
+		}
+	}
+}
+
+/*
+ * pop a scope which was started with repushids
+ * return any newly installed ids
+ */
+Decl*
+popids(Decl *ids)
+{
+	for(; ids != nil; ids = ids->next){
+		if(ids->sym != nil && ids->store != Dtag){
+			ids->sym->decl = ids->old;
+			ids->old = nil;
+		}
+	}
+	return popscope();
+}
+
+void
+installids(int store, Decl *ids)
+{
+	Decl *d, *last;
+	Sym *s;
+
+	last = nil;
+	for(d = ids; d != nil; d = d->next){
+		d->scope = scope;
+		if(d->store == Dundef)
+			d->store = store;
+		s = d->sym;
+		if(s != nil){
+			if(s->decl != nil && s->decl->scope >= scope){
+				redecl(d);
+				d->old = s->decl->old;
+			}else
+				d->old = s->decl;
+			s->decl = d;
+		}
+		last = d;
+	}
+	if(ids != nil){
+		d = tails[scope];
+		if(d == nil)
+			scopes[scope] = ids;
+		else
+			d->next = ids;
+		tails[scope] = last;
+	}
+}
+
+Decl*
+lookup(Sym *sym)
+{
+	int s;
+	Decl *d;
+
+	for(s = scope; s >= ScopeBuiltin; s--){
+		for(d = scopes[s]; d != nil; d = d->next){
+			if(d->sym == sym)
+				return d;
+		}
+	}
+	return nil;
+}
+
+Decl*
+mkids(Src *src, Sym *s, Type *t, Decl *next)
+{
+	Decl *d;
+
+	d = mkdecl(src, Dundef, t);
+	d->next = next;
+	d->sym = s;
+	return d;
+}
+
+Decl*
+mkdecl(Src *src, int store, Type *t)
+{
+	Decl *d;
+	static Decl z;
+
+	d = allocmem(sizeof *d);
+	*d = z;
+	d->src = *src;
+	d->store = store;
+	d->ty = t;
+	d->nid = 1;
+	return d;
+}
+
+Decl*
+dupdecl(Decl *old)
+{
+	Decl *d;
+
+	d = allocmem(sizeof *d);
+	*d = *old;
+	d->next = nil;
+	return d;
+}
+
+Decl*
+dupdecls(Decl *old)
+{
+	Decl *d, *nd, *first, *last;
+
+	first = last = nil;
+	for(d = old; d != nil; d = d->next){
+		nd = dupdecl(d);
+		if(first == nil)
+			first = nd;
+		else
+			last->next = nd;
+		last = nd;
+	}
+	return first;
+}
+		
+Decl*
+appdecls(Decl *d, Decl *dd)
+{
+	Decl *t;
+
+	if(d == nil)
+		return dd;
+	for(t = d; t->next != nil; t = t->next)
+		;
+	t->next = dd;
+	return d;
+}
+
+Decl*
+revids(Decl *id)
+{
+	Decl *d, *next;
+
+	d = nil;
+	for(; id != nil; id = next){
+		next = id->next;
+		id->next = d;
+		d = id;
+	}
+	return d;
+}
+
+long
+idoffsets(Decl *id, long offset, int al)
+{
+	int a, algn;
+	Decl *d;
+
+	algn = 1;
+	for(; id != nil; id = id->next){
+		if(storespace[id->store]){
+usedty(id->ty);
+			if(id->store == Dlocal && id->link != nil){
+				/* id->nid always 1 */
+				id->offset = id->link->offset;
+				continue;
+			}
+			a = id->ty->align;
+			if(id->nid > 1){
+				for(d = id->next; d != nil && d->nid == 0; d = d->next)
+					if(d->ty->align > a)
+						a = d->ty->align;
+				algn = a;
+			}
+			offset = align(offset, a);
+			id->offset = offset;
+			offset += id->ty->size;
+			if(id->nid == 0 && (id->next == nil || id->next->nid != 0))
+				offset = align(offset, algn);
+		}
+	}
+	return align(offset, al);
+}
+
+long
+idindices(Decl *id)
+{
+	int i;
+
+	i = 0;
+	for(; id != nil; id = id->next){
+		if(storespace[id->store]){
+			usedty(id->ty);
+			id->offset = i++;
+		}
+	}
+	return i;
+}
+
+int
+declconv(Fmt *f)
+{
+	Decl *d;
+	char buf[4096], *s;
+
+	d = va_arg(f->args, Decl*);
+	if(d->sym == nil)
+		s = "<nil>";
+	else
+		s = d->sym->name;
+	seprint(buf, buf+sizeof(buf), "%s %s", storename[d->store], s);
+	return fmtstrcpy(f, buf);
+}
+
+int
+storeconv(Fmt *f)
+{
+	Decl *d;
+	char buf[4096];
+
+	d = va_arg(f->args, Decl*);
+	seprint(buf, buf+sizeof(buf), "%s%s", storeart[d->store], storename[d->store]);
+	return fmtstrcpy(f, buf);
+}
+
+int
+dotconv(Fmt *f)
+{
+	Decl *d;
+	char buf[4096], *p, *s;
+
+	d = va_arg(f->args, Decl*);
+	buf[0] = 0;
+	p = buf;
+	if(d->dot != nil && !isimpmod(d->dot->sym)){
+		s = ".";
+		if(d->dot->ty != nil && d->dot->ty->kind == Tmodule)
+			s = "->";
+		p = seprint(buf, buf+sizeof(buf), "%D%s", d->dot, s);
+	}
+	seprint(p, buf+sizeof(buf), "%s", d->sym->name);
+	return fmtstrcpy(f, buf);
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Decl*
+namemerge(Decl *e, Decl *f)
+{
+	Decl rock, *d;
+
+	d = &rock;
+	while(e != nil && f != nil){
+		if(strcmp(e->sym->name, f->sym->name) <= 0){
+			d->next = e;
+			e = e->next;
+		}else{
+			d->next = f;
+			f = f->next;
+		}
+		d = d->next;
+	}
+	if(e != nil)
+		d->next = e;
+	else
+		d->next = f;
+	return rock.next;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Decl*
+recnamesort(Decl *d, int n)
+{
+	Decl *r, *dd;
+	int i, m;
+
+	if(n <= 1)
+		return d;
+	m = n / 2 - 1;
+	dd = d;
+	for(i = 0; i < m; i++)
+		dd = dd->next;
+	r = dd->next;
+	dd->next = nil;
+	return namemerge(recnamesort(d, n / 2),
+			recnamesort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the ids by name
+ */
+Decl*
+namesort(Decl *d)
+{
+	Decl *dd;
+	int n;
+
+	n = 0;
+	for(dd = d; dd != nil; dd = dd->next)
+		n++;
+	return recnamesort(d, n);
+}
+
+void
+printdecls(Decl *d)
+{
+	for(; d != nil; d = d->next)
+		print("%ld: %K %T ref %d\n", d->offset, d, d->ty, d->refs);
+}
+
+void
+mergepolydecs(Type *t)
+{
+	Node *n, *nn;
+	Decl *id, *ids, *ids1;
+
+	for(n = t->val; n != nil; n = n->right){
+		nn = n->left;
+		for(ids = nn->decl; ids != nil; ids = ids->next){
+			id = ids->sym->decl;
+			if(id == nil){
+				undefed(&ids->src, ids->sym);
+				break;
+			}
+			if(id->store != Dtype){
+				error(ids->src.start, "%K is not a type", id);
+				break;
+			}
+			if(id->ty->kind != Tpoly){
+				error(ids->src.start, "%K is not a polymorphic type", id);
+				break;
+			}
+			if(id->ty->ids != nil)
+				error(ids->src.start, "%K redefined", id);
+			pushscope(nil, Sother);
+			fielddecled(nn->left);
+			id->ty->ids = popscope();
+			for(ids1 = id->ty->ids; ids1 != nil; ids1 = ids1->next){
+				ids1->dot = id;
+				bindtypes(ids1->ty);
+				if(ids1->ty->kind != Tfn){
+					error(ids1->src.start, "only function types expected");
+					id->ty->ids = nil;
+				}
+			}
+		}
+	}
+	t->val = nil;
+}
+
+static void
+adjfnptrs(Decl *d, Decl *polys1, Decl *polys2)
+{
+	int n;
+	Decl *id, *idt, *idf, *arg;
+
+	if(debug['U'])
+		print("adjnptrs %s\n", d->sym->name);
+	n = 0;
+	for(id = d->ty->ids; id != nil; id = id->next)
+		n++;
+	for(idt = polys1; idt != nil; idt = idt->next)
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+			n -= 2;
+	for(idt = polys2; idt != nil; idt = idt->next)
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+			n -= 2;
+	for(arg = d->ty->ids; --n >= 0; arg = arg->next)
+		;
+	for(idt = polys1; idt != nil; idt = idt->next){
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+			idf->link = arg;
+			arg = arg->next->next;
+		}
+	}
+	for(idt = polys2; idt != nil; idt = idt->next){
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+			idf->link = arg;
+			arg = arg->next->next;
+		}
+	}
+}
+
+static void
+addptrs(Decl *polys, Decl** fps, Decl **last, int link, Src *src)
+{
+	Decl *idt, *idf, *fp;
+
+	if(debug['U'])
+		print("addptrs\n");
+	for(idt = polys; idt != nil; idt = idt->next){
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+			fp = mkdecl(src, Darg, tany);
+			fp->sym = idf->sym;
+			if(link)
+				idf->link = fp;
+			if(*fps == nil)
+				*fps = fp;
+			else
+				(*last)->next = fp;
+			*last = fp;
+			fp = mkdecl(src, Darg, tint);
+			fp->sym = idf->sym;
+			(*last)->next = fp;
+			*last = fp;
+		}
+	}
+}
+
+void
+addfnptrs(Decl *d, int link)
+{
+	Decl *fps, *last, *polys;
+
+	if(debug['U'])
+		print("addfnptrs %s %d\n", d->sym->name, link);
+	polys = encpolys(d);
+	if(d->ty->flags&FULLARGS){
+		if(link)
+			adjfnptrs(d, d->ty->polys, polys);
+		if(0 && debug['U']){
+			for(d = d->ty->ids; d != nil; d = d->next)
+				print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr);
+			print("\n");
+		}
+		return;
+	}
+	d->ty->flags |= FULLARGS;
+	fps = last = nil;
+	addptrs(d->ty->polys, &fps, &last, link, &d->src);
+	addptrs(polys, &fps, &last, link, &d->src);
+	for(last = d->ty->ids; last != nil && last->next != nil; last = last->next)
+		;
+	if(last != nil)
+		last->next = fps;
+	else
+		d->ty->ids = fps;
+	d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD);
+	if(0 && debug['U']){
+		for(d = d->ty->ids; d != nil; d = d->next)
+			print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr);
+		print("\n");
+	}
+}
+
+void
+rmfnptrs(Decl *d)
+{
+	int n;
+	Decl *id, *idt, *idf;
+
+	if(debug['U'])
+		print("rmfnptrs %s\n", d->sym->name);
+	if(!(d->ty->flags&FULLARGS))
+		return;
+	d->ty->flags &= ~FULLARGS;
+	n = 0;
+	for(id = d->ty->ids; id != nil; id = id->next)
+		n++;
+	for(idt = d->ty->polys; idt != nil; idt = idt->next)
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+			n -= 2;
+	for(idt = encpolys(d); idt != nil; idt = idt->next)
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next)
+			n -= 2;
+	if(n == 0){
+		d->ty->ids = nil;
+		return;
+	}
+	for(id = d->ty->ids; --n > 0; id = id->next)
+		;
+	id->next = nil;
+	d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD);
+}
+
+int
+local(Decl *d)
+{
+	for(d = d->dot; d != nil; d = d->dot)
+		if(d->store == Dtype && d->ty->kind == Tmodule)
+			return 0;
+	return 1;
+}
+
+Decl*
+module(Decl *d)
+{
+	for(d = d->dot; d != nil; d = d->dot)
+		if(d->store == Dtype && d->ty->kind == Tmodule)
+			return d;
+	return nil;
+}
+
+Decl*
+outerpolys(Node *n)
+{
+	Decl *d;
+
+	if(n->op == Odot){
+		d = n->right->decl;
+		if(d == nil)
+			fatal("decl() outeradt nil");
+		d = d->dot;
+		if(d != nil && d->store == Dtype && d->ty->kind == Tadt)
+			return d->ty->polys;
+	}
+	return nil;
+}
+
+Decl*
+encpolys(Decl *d)
+{
+	if((d = d->dot) == nil)
+		return nil;
+	return d->ty->polys;
+}
+
+Decl*
+fnlookup(Sym *s, Type *t, Node **m)
+{
+	Decl *id;
+	Node *mod;
+
+	id = nil;
+	mod = nil;
+	if(t->kind == Tpoly || t->kind == Tmodule)
+		id = namedot(t->ids, s);
+	else if(t->kind == Tref){
+		t = t->tof;
+		if(t->kind == Tadt){
+			id = namedot(t->ids, s);
+			if(t->decl != nil && t->decl->timport != nil)
+				mod = t->decl->timport->eimport;
+		}
+		else if(t->kind == Tadtpick){
+			id = namedot(t->ids, s);
+			if(t->decl != nil && t->decl->timport != nil)
+				mod = t->decl->timport->eimport;
+			t = t->decl->dot->ty;
+			if(id == nil)
+				id = namedot(t->ids, s);
+			if(t->decl != nil && t->decl->timport != nil)
+				mod = t->decl->timport->eimport;	
+		}
+	}
+	if(id == nil){
+		id = lookup(s);
+		if(id != nil)
+			mod = id->eimport;
+	}
+	if(m != nil)
+		*m = mod;
+	return id;
+}
+
+int
+isimpmod(Sym *s)
+{
+	Decl *d;
+
+	for(d = impmods; d != nil; d = d->next)
+		if(d->sym == s)
+			return 1;
+	return 0;
+}
+
+int
+dequal(Decl *d1, Decl *d2, int full)
+{
+	return	d1->sym == d2->sym &&
+			d1->store == d2->store &&
+			d1->implicit == d2->implicit &&
+			d1->cyc == d2->cyc &&
+			(!full || tequal(d1->ty, d2->ty)) &&
+			(!full || d1->store == Dfn || sametree(d1->init, d2->init));
+}
+
+static int
+tzero(Type *t)
+{
+	return t->kind == Texception || tmustzero(t);
+}
+
+static int
+isptr(Type *t)
+{
+	return t->kind == Texception || tattr[t->kind].isptr;
+}
+
+/* can d share the same stack location as another local ? */
+void
+shareloc(Decl *d)
+{
+	int z;
+	Type *t, *tt;
+	Decl *dd, *res;
+
+	if(d->store != Dlocal || d->nid != 1)
+		return;
+	t = d->ty;
+	res = nil;
+	for(dd = fndecls; dd != nil; dd = dd->next){
+		if(d == dd)
+			fatal("d==dd in shareloc");
+		if(dd->store != Dlocal || dd->nid != 1 || dd->link != nil || dd->tref != 0)
+			continue;
+		tt = dd->ty;
+		if(t->size != tt->size || t->align != tt->align)
+			continue;
+		z = tzero(t)+tzero(tt);
+		if(z > 0)
+			continue;	/* for now */
+		if(t == tt || tequal(t, tt))
+			res = dd;
+		else{
+			if(z == 1)
+				continue;
+			if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt))
+				res = dd;
+		}
+		if(res != nil){
+			/* print("%L %K share %L %K\n", d->src.start, d, res->src.start, res); */
+			d->link = res;
+			res->tref = 1;
+			return;
+		}
+	}
+	return;
+}
+
+static void
+freeloc(Decl *d)
+{
+	if(d->link != nil)
+		d->link->tref = 0;
+}
--- /dev/null
+++ b/limbo/dis.c
@@ -1,0 +1,638 @@
+#include "limbo.h"
+
+static	void	disbig(long, Long);
+static	void	disbyte(long, int);
+static	void	disbytes(long, void*, int);
+static	void	disdatum(long, Node*);
+static	void	disflush(int, long, long);
+static	void	disint(long, long);
+static	void	disreal(long, Real);
+static	void	disstring(long, Sym*);
+
+static	uchar	*cache;
+static	int	ncached;
+static	int	ndatum;
+static	int	startoff;
+static	int	lastoff;
+static	int	lastkind;
+static	int	lencache;
+
+void
+discon(long val)
+{
+	if(val >= -64 && val <= 63) {
+		Bputc(bout, val & ~0x80);
+		return;
+	}
+	if(val >= -8192 && val <= 8191) {
+		Bputc(bout, ((val>>8) & ~0xC0) | 0x80);
+		Bputc(bout, val);
+		return;
+	}
+	if(val < 0 && ((val >> 29) & 0x7) != 7
+	|| val > 0 && (val >> 29) != 0)
+		fatal("overflow in constant 0x%lux\n", val);
+	Bputc(bout, (val>>24) | 0xC0);
+	Bputc(bout, val>>16);
+	Bputc(bout, val>>8);
+	Bputc(bout, val);
+}
+
+void
+disword(long w)
+{
+	Bputc(bout, w >> 24);
+	Bputc(bout, w >> 16);
+	Bputc(bout, w >> 8);
+	Bputc(bout, w);
+}
+
+void
+disdata(int kind, long n)
+{
+	if(n < DMAX && n != 0)
+		Bputc(bout, DBYTE(kind, n));
+	else{
+		Bputc(bout, DBYTE(kind, 0));
+		discon(n);
+	}
+}
+
+#define	NAMELEN	64
+
+void
+dismod(Decl *m)
+{
+	char name[8*NAMELEN];
+	vlong fileoff;
+
+	fileoff = Boffset(bout);
+	strncpy(name, m->sym->name, NAMELEN);
+	name[NAMELEN-1] = '\0';
+	Bwrite(bout, name, strlen(name)+1);
+	for(m = m->ty->tof->ids; m != nil; m = m->next){
+		switch(m->store){
+		case Dglobal:
+			discon(-1);
+			discon(-1);
+			disword(sign(m));
+			Bprint(bout, ".mp");
+			Bputc(bout, '\0');
+			break;
+		case Dfn:
+if(debug['v']) print("Dfn: %s %d %p\n", m->sym->name, m->refs, m);
+			discon(m->pc->pc);
+			discon(m->desc->id);
+			disword(sign(m));
+			if(m->dot->ty->kind == Tadt)
+				Bprint(bout, "%s.", m->dot->sym->name);
+			Bprint(bout, "%s", m->sym->name);
+			Bputc(bout, '\0');
+			break;
+		default:
+			fatal("unknown kind %K in dismod", m);
+			break;
+		}
+	}
+	if(debug['s'])
+		print("%lld linkage bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+dispath(void)
+{
+	char name[8*NAMELEN], *sp;
+
+	sp = srcpath(name, 8*NAMELEN);
+	Bwrite(bout, sp, strlen(sp)+1);
+}
+
+void
+disentry(Decl *e)
+{
+	if(e == nil){
+		discon(-1);
+		discon(-1);
+		return;
+	}
+	discon(e->pc->pc);
+	discon(e->desc->id);
+}
+
+void
+disdesc(Desc *d)
+{
+	vlong fileoff;
+
+	fileoff = Boffset(bout);
+	for(; d != nil; d = d->next){
+		discon(d->id);
+		discon(d->size);
+		discon(d->nmap);
+		Bwrite(bout, d->map, d->nmap);
+	}
+	if(debug['s'])
+		print("%lld type descriptor bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disvar(long size, Decl *d)
+{
+	vlong fileoff;
+
+	fileoff = Boffset(bout);
+	USED(size);
+
+	lastkind = -1;
+	ncached = 0;
+	ndatum = 0;
+
+	for(; d != nil; d = d->next)
+		if(d->store == Dglobal && d->init != nil)
+			disdatum(d->offset, d->init);
+
+	disflush(-1, -1, 0);
+
+	Bputc(bout, 0);
+
+	if(debug['s'])
+		print("%lld data bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disldt(long size, Decl *ds)
+{
+	int m;
+	Decl *d, *id;
+	Sym *s;
+	Node *n;
+
+	if(0){
+		discon(size);
+		disvar(size, ds);
+		return;
+	}
+
+	m = 0;
+	for(d = ds; d != nil; d = d->next)
+		if(d->store == Dglobal && d->init != nil)
+			m++;
+	discon(m);
+	for(d = ds; d != nil; d = d->next){
+		if(d->store == Dglobal && d->init != nil){
+			n = d->init;
+			if(n->ty->kind != Tiface)
+				nerror(n, "disldt: not Tiface");
+			discon(n->val);
+			for(id = n->decl->ty->ids; id != nil; id = id->next){
+				disword(sign(id));
+				if(id->dot->ty->kind == Tadt){
+					s = id->dot->sym;
+					Bprint(bout, "%s", s->name);
+					Bputc(bout, '.');
+				}
+				s = id->sym;
+				Bprint(bout, "%s", s->name);
+				Bputc(bout, 0);
+			}
+		}
+	}
+	discon(0);
+}
+
+static void
+disdatum(long offset, Node *n)
+{
+	Node *elem, *wild;
+	Case *c;
+	Label *lab;
+	Decl *id;
+	Sym *s;
+	long e, last, esz;
+	int i;
+
+	switch(n->ty->kind){
+	case Tbyte:
+		disbyte(offset, n->val);
+		break;
+	case Tint:
+	case Tfix:
+		disint(offset, n->val);
+		break;
+	case Tbig:
+		disbig(offset, n->val);
+		break;
+	case Tstring:
+		disstring(offset, n->decl->sym);
+		break;
+	case Treal:
+		disreal(offset, n->rval);
+		break;
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+		id = n->ty->ids;
+		for(n = n->left; n != nil; n = n->right){
+			disdatum(offset + id->offset, n->left);
+			id = id->next;
+		}
+		break;
+	case Tany:
+		break;
+	case Tcase:
+		c = n->ty->cse;
+		disint(offset, c->nlab);
+		offset += IBY2WD;
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			disint(offset, lab->start->val);
+			offset += IBY2WD;
+			disint(offset, lab->stop->val+1);
+			offset += IBY2WD;
+			disint(offset, lab->inst->pc);
+			offset += IBY2WD;
+		}
+		disint(offset, c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tcasel:
+		c = n->ty->cse;
+		disint(offset, c->nlab);
+		offset += 2*IBY2WD;
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			disbig(offset, lab->start->val);
+			offset += IBY2LG;
+			disbig(offset, lab->stop->val+1);
+			offset += IBY2LG;
+			disint(offset, lab->inst->pc);
+			offset += 2*IBY2WD;
+		}
+		disint(offset, c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tcasec:
+		c = n->ty->cse;
+		disint(offset, c->nlab);
+		offset += IBY2WD;
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			disstring(offset, lab->start->decl->sym);
+			offset += IBY2WD;
+			if(lab->stop != lab->start)
+				disstring(offset, lab->stop->decl->sym);
+			offset += IBY2WD;
+			disint(offset, lab->inst->pc);
+			offset += IBY2WD;
+		}
+		disint(offset, c->iwild ? c->iwild->pc : -1);
+		break;
+	case Tgoto:
+		c = n->ty->cse;
+		disint(offset, n->ty->size/IBY2WD-1);
+		offset += IBY2WD;
+		for(i = 0; i < c->nlab; i++){
+			disint(offset, c->labs[i].inst->pc);
+			offset += IBY2WD;
+		}
+		if(c->iwild != nil)
+			disint(offset, c->iwild->pc);
+		break;
+	case Tarray:
+		disflush(-1, -1, 0);
+		disdata(DEFA, 1);		/* 1 is ignored */
+		discon(offset);
+		disword(n->ty->tof->decl->desc->id);
+		disword(n->left->val);
+
+		if(n->right == nil)
+			break;
+
+		disdata(DIND, 1);		/* 1 is ignored */
+		discon(offset);
+		disword(0);
+
+		c = n->right->ty->cse;
+		wild = nil;
+		if(c->wild != nil)
+			wild = c->wild->right;
+		last = 0;
+		esz = n->ty->tof->size;
+		for(i = 0; i < c->nlab; i++){
+			e = c->labs[i].start->val;
+			if(wild != nil){
+				for(; last < e; last++)
+					disdatum(esz * last, wild);
+			}
+			last = e;
+			e = c->labs[i].stop->val;
+			elem = c->labs[i].node->right;
+			for(; last <= e; last++)
+				disdatum(esz * last, elem);
+		}
+		if(wild != nil)
+			for(e = n->left->val; last < e; last++)
+				disdatum(esz * last, wild);
+
+		disflush(-1, -1, 0);
+		disdata(DAPOP, 1);		/* 1 is ignored */
+		discon(0);
+
+		break;
+	case Tiface:
+		disint(offset, n->val);
+		offset += IBY2WD;
+		for(id = n->decl->ty->ids; id != nil; id = id->next){
+			offset = align(offset, IBY2WD);
+			disint(offset, sign(id));
+			offset += IBY2WD;
+
+			if(id->dot->ty->kind == Tadt){
+				s = id->dot->sym;
+				disbytes(offset, s->name, s->len);
+				offset += s->len;
+				disbyte(offset, '.');
+				offset++;
+			}
+			s = id->sym;
+			disbytes(offset, s->name, s->len);
+			offset += s->len;
+			disbyte(offset, 0);
+			offset++;
+		}
+		break;
+	default:
+		nerror(n, "can't dis global %n", n);
+		break;
+	}
+}
+
+void
+disexc(Except *es)
+{
+	int i, n;
+	Decl *d;
+	Except *e;
+	Case *c;
+	Label *lab;
+
+	n = 0;
+	for(e = es; e != nil; e = e->next)
+		if(e->p1->reach || e->p2->reach)
+			n++;
+	discon(n);
+	for(e = es; e != nil; e = e->next){
+		if(!e->p1->reach && !e->p2->reach)
+			continue;
+		c = e->c;
+		discon(e->d->offset);
+		discon(getpc(e->p1));
+		discon(getpc(e->p2));
+		if(e->desc)
+			discon(e->desc->id);
+		else
+			discon(-1);
+		discon(c->nlab|(e->ne<<16));
+		for(i = 0; i < c->nlab; i++){
+			lab = &c->labs[i];
+			d = lab->start->decl;
+			if(lab->start->ty->kind == Texception)
+				d = d->init->decl;
+			Bprint(bout, "%s", d->sym->name);
+			Bputc(bout, '\0');
+			discon(lab->inst->pc);
+		}
+		if(c->iwild == nil)
+			discon(-1);
+		else
+			discon(c->iwild->pc);
+	}
+	discon(0);
+}
+
+static void
+disbyte(long off, int v)
+{
+	disflush(DEFB, off, 1);
+	cache[ncached++] = v;
+	ndatum++;
+}
+
+static void
+disbytes(long off, void *v, int n)
+{
+	disflush(DEFB, off, n);
+	memmove(&cache[ncached], v, n);
+	ncached += n;
+	ndatum += n;
+}
+
+static void
+disint(long off, long v)
+{
+	disflush(DEFW, off, IBY2WD);
+	cache[ncached++] = v >> 24;
+	cache[ncached++] = v >> 16;
+	cache[ncached++] = v >> 8;
+	cache[ncached++] = v;
+	ndatum++;
+}
+
+static void
+disbig(long off, Long v)
+{
+	ulong iv;
+
+	disflush(DEFL, off, IBY2LG);
+	iv = v >> 32;
+	cache[ncached++] = iv >> 24;
+	cache[ncached++] = iv >> 16;
+	cache[ncached++] = iv >> 8;
+	cache[ncached++] = iv;
+	iv = v;
+	cache[ncached++] = iv >> 24;
+	cache[ncached++] = iv >> 16;
+	cache[ncached++] = iv >> 8;
+	cache[ncached++] = iv;
+	ndatum++;
+}
+
+static void
+disreal(long off, Real v)
+{
+	ulong bv[2];
+	ulong iv;
+
+	disflush(DEFF, off, IBY2LG);
+	dtocanon(v, bv);
+	iv = bv[0];
+	cache[ncached++] = iv >> 24;
+	cache[ncached++] = iv >> 16;
+	cache[ncached++] = iv >> 8;
+	cache[ncached++] = iv;
+	iv = bv[1];
+	cache[ncached++] = iv >> 24;
+	cache[ncached++] = iv >> 16;
+	cache[ncached++] = iv >> 8;
+	cache[ncached++] = iv;
+	ndatum++;
+}
+
+static void
+disstring(long offset, Sym *sym)
+{
+	disflush(-1, -1, 0);
+	disdata(DEFS, sym->len);
+	discon(offset);
+	Bwrite(bout, sym->name, sym->len);
+}
+
+static void
+disflush(int kind, long off, long size)
+{
+	if(kind != lastkind || off != lastoff){
+		if(lastkind != -1 && ncached){
+			disdata(lastkind, ndatum);
+			discon(startoff);
+			Bwrite(bout, cache, ncached);
+		}
+		startoff = off;
+		lastkind = kind;
+		ncached = 0;
+		ndatum = 0;
+	}
+	lastoff = off + size;
+	while(kind >= 0 && ncached + size >= lencache){
+		lencache = ncached+1024;
+		cache = reallocmem(cache, lencache);
+	}
+}
+
+static int dismode[Aend] = {
+	/* Aimm */	AIMM,
+	/* Amp */	AMP,
+	/* Ampind */	AMP|AIND,
+	/* Afp */	AFP,
+	/* Afpind */	AFP|AIND,
+	/* Apc */	AIMM,
+	/* Adesc */	AIMM,
+	/* Aoff */	AIMM,
+	/* Anoff */	AIMM,
+	/* Aerr */	AXXX,
+	/* Anone */	AXXX,
+	/* Aldt */	AIMM,
+};
+
+static int disregmode[Aend] = {
+	/* Aimm */	AXIMM,
+	/* Amp */	AXINM,
+	/* Ampind */	AXNON,
+	/* Afp */	AXINF,
+	/* Afpind */	AXNON,
+	/* Apc */	AXIMM,
+	/* Adesc */	AXIMM,
+	/* Aoff */	AXIMM,
+	/* Anoff */	AXIMM,
+	/* Aerr */	AXNON,
+	/* Anone */	AXNON,
+	/* Aldt */	AXIMM,
+};
+
+enum
+{
+	MAXCON	= 4,
+	MAXADDR	= 2*MAXCON,
+	MAXINST	= 3*MAXADDR+2,
+	NIBUF	= 1024
+};
+
+static	uchar	*ibuf;
+static	int	nibuf;
+
+void
+disinst(Inst *in)
+{
+	vlong fileoff;
+
+	fileoff = Boffset(bout);
+	ibuf = allocmem(NIBUF);
+	nibuf = 0;
+	for(; in != nil; in = in->next){
+		if(in->op == INOOP)
+			continue;
+		if(nibuf >= NIBUF-MAXINST){
+			Bwrite(bout, ibuf, nibuf);
+			nibuf = 0;
+		}
+		ibuf[nibuf++] = in->op;
+		ibuf[nibuf++] = SRC(dismode[in->sm]) | DST(dismode[in->dm]) | disregmode[in->mm];
+		if(in->mm != Anone)
+			disaddr(in->mm, &in->m);
+		if(in->sm != Anone)
+			disaddr(in->sm, &in->s);
+		if(in->dm != Anone)
+			disaddr(in->dm, &in->d);
+	}
+	if(nibuf > 0)
+		Bwrite(bout, ibuf, nibuf);
+	free(ibuf);
+	ibuf = nil;
+
+	if(debug['s'])
+		print("%lld instruction bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disaddr(int m, Addr *a)
+{
+	long val;
+
+	val = 0;
+	switch(m){
+	case Anone:
+	case Aerr:
+	default:
+		break;
+	case Aimm:
+	case Apc:
+	case Adesc:
+		val = a->offset;
+		break;
+	case Aoff:
+		val = a->decl->iface->offset;
+		break;
+	case Anoff:
+		val = -(a->decl->iface->offset+1);
+		break;
+	case Afp:
+	case Amp:
+	case Aldt:
+		val = a->reg;
+		break;
+	case Afpind:
+	case Ampind:
+		disbcon(a->reg);
+		val = a->offset;
+		break;
+	}
+	disbcon(val);
+}
+
+void
+disbcon(long val)
+{
+	if(val >= -64 && val <= 63){
+		ibuf[nibuf++] = val & ~0x80;
+		return;
+	}
+	if(val >= -8192 && val <= 8191){
+		ibuf[nibuf++] = val>>8 & ~0xC0 | 0x80;
+		ibuf[nibuf++] = val;
+		return;
+	}
+	if(val < 0 && ((val >> 29) & 7) != 7
+	|| val > 0 && (val >> 29) != 0)
+		fatal("overflow in constant 16r%lux", val);
+	ibuf[nibuf++] = val>>24 | 0xC0;
+	ibuf[nibuf++] = val>>16;
+	ibuf[nibuf++] = val>>8;
+	ibuf[nibuf++] = val;
+}
--- /dev/null
+++ b/limbo/dtocanon.c
@@ -1,0 +1,35 @@
+#include "limbo.h"
+
+void
+dtocanon(double f, ulong v[])
+{
+	union { double d; ulong ul[2]; } a;
+
+	a.d = 1.;
+	if(a.ul[0]){
+		a.d = f;
+		v[0] = a.ul[0];
+		v[1] = a.ul[1];
+	}else{
+		a.d = f;
+		v[0] = a.ul[1];
+		v[1] = a.ul[0];
+	}
+}
+
+double
+canontod(ulong v[2])
+{
+	union { double d; unsigned long ul[2]; } a;
+
+	a.d = 1.;
+	if(a.ul[0]) {
+		a.ul[0] = v[0];
+		a.ul[1] = v[1];
+	}
+	else {
+		a.ul[1] = v[0];
+		a.ul[0] = v[1];
+	}
+	return a.d;
+}
--- /dev/null
+++ b/limbo/ecom.c
@@ -1,0 +1,2558 @@
+#include "limbo.h"
+
+static Node* putinline(Node*);
+static void fpcall(Src*, int, Node*, Node*);
+
+void
+optabinit(void)
+{
+	int i;
+
+	for(i = 0; setisbyteinst[i] >= 0; i++)
+		isbyteinst[setisbyteinst[i]] = 1;
+
+	for(i = 0; setisused[i] >= 0; i++)
+		isused[setisused[i]] = 1;
+
+	for(i = 0; setsideeffect[i] >= 0; i++)
+		sideeffect[setsideeffect[i]] = 1;
+
+	opind[Tbyte] = 1;
+	opind[Tint] = 2;
+	opind[Tbig] = 3;
+	opind[Treal] = 4;
+	opind[Tstring] = 5;
+	opind[Tfix] = 6;
+
+	opcommute[Oeq] = Oeq;
+	opcommute[Oneq] = Oneq;
+	opcommute[Olt] = Ogt;
+	opcommute[Ogt] = Olt;
+	opcommute[Ogeq] = Oleq;
+	opcommute[Oleq] = Ogeq;
+	opcommute[Oadd] = Oadd;
+	opcommute[Omul] = Omul;
+	opcommute[Oxor] = Oxor;
+	opcommute[Oor] = Oor;
+	opcommute[Oand] = Oand;
+
+	oprelinvert[Oeq] = Oneq;
+	oprelinvert[Oneq] = Oeq;
+	oprelinvert[Olt] = Ogeq;
+	oprelinvert[Ogt] = Oleq;
+	oprelinvert[Ogeq] = Olt;
+	oprelinvert[Oleq] = Ogt;
+
+	isrelop[Oeq] = 1;
+	isrelop[Oneq] = 1;
+	isrelop[Olt] = 1;
+	isrelop[Oleq] = 1;
+	isrelop[Ogt] = 1;
+	isrelop[Ogeq] = 1;
+	isrelop[Oandand] = 1;
+	isrelop[Ooror] = 1;
+	isrelop[Onot] = 1;
+
+	precasttab[Tstring][Tbyte] = tint;
+	precasttab[Tbyte][Tstring] = tint;
+	precasttab[Treal][Tbyte] = tint;
+	precasttab[Tbyte][Treal] = tint;
+	precasttab[Tbig][Tbyte] = tint;
+	precasttab[Tbyte][Tbig] = tint;
+	precasttab[Tfix][Tbyte] = tint;
+	precasttab[Tbyte][Tfix] = tint;
+	precasttab[Tbig][Tfix] = treal;
+	precasttab[Tfix][Tbig] = treal;
+	precasttab[Tstring][Tfix] = treal;
+	precasttab[Tfix][Tstring] = treal;
+
+	casttab[Tint][Tint] = IMOVW;
+	casttab[Tbig][Tbig] = IMOVL;
+	casttab[Treal][Treal] = IMOVF;
+	casttab[Tbyte][Tbyte] = IMOVB;
+	casttab[Tstring][Tstring] = IMOVP;
+	casttab[Tfix][Tfix] = ICVTXX;	/* never same type */
+
+	casttab[Tint][Tbyte] = ICVTWB;
+	casttab[Tint][Treal] = ICVTWF;
+	casttab[Tint][Tstring] = ICVTWC;
+	casttab[Tint][Tfix] = ICVTXX;
+	casttab[Tbyte][Tint] = ICVTBW;
+	casttab[Treal][Tint] = ICVTFW;
+	casttab[Tstring][Tint] = ICVTCW;
+	casttab[Tfix][Tint] = ICVTXX;
+
+	casttab[Tint][Tbig] = ICVTWL;
+	casttab[Treal][Tbig] = ICVTFL;
+	casttab[Tstring][Tbig] = ICVTCL;
+	casttab[Tbig][Tint] = ICVTLW;
+	casttab[Tbig][Treal] = ICVTLF;
+	casttab[Tbig][Tstring] = ICVTLC;
+
+	casttab[Treal][Tstring] = ICVTFC;
+	casttab[Tstring][Treal] = ICVTCF;
+
+	casttab[Treal][Tfix] = ICVTFX;
+	casttab[Tfix][Treal] = ICVTXF;
+
+	casttab[Tstring][Tarray] = ICVTCA;
+	casttab[Tarray][Tstring] = ICVTAC;
+
+	/*
+	 * placeholders; fixed in precasttab
+	 */
+	casttab[Tbyte][Tstring] = 0xff;
+	casttab[Tstring][Tbyte] = 0xff;
+	casttab[Tbyte][Treal] = 0xff;
+	casttab[Treal][Tbyte] = 0xff;
+	casttab[Tbyte][Tbig] = 0xff;
+	casttab[Tbig][Tbyte] = 0xff;
+	casttab[Tfix][Tbyte] = 0xff;
+	casttab[Tbyte][Tfix] = 0xff;
+	casttab[Tfix][Tbig] = 0xff;
+	casttab[Tbig][Tfix] = 0xff;
+	casttab[Tfix][Tstring] = 0xff;
+	casttab[Tstring][Tfix] = 0xff;
+}
+
+/*
+ * global variable and constant initialization checking
+ */
+int
+vcom(Decl *ids)
+{
+	Decl *v;
+	int ok;
+
+	ok = 1;
+	for(v = ids; v != nil; v = v->next)
+		ok &= varcom(v);
+	for(v = ids; v != nil; v = v->next)
+		v->init = simplify(v->init);
+	return ok;
+}
+
+Node*
+simplify(Node *n)
+{
+	if(n == nil)
+		return nil;
+	if(debug['F'])
+		print("simplify %n\n", n);
+	n = efold(rewrite(n));
+	if(debug['F'])
+		print("simplified %n\n", n);
+	return n;
+}
+
+static int
+isfix(Node *n)
+{
+	if(n->ty->kind == Tint || n->ty->kind == Tfix){
+		if(n->op == Ocast)
+			return n->left->ty->kind == Tint || n->left->ty->kind == Tfix;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * rewrite an expression to make it easiser to compile,
+ * or give the correct results
+ */
+Node*
+rewrite(Node *n)
+{
+	Long v;
+	Type *t;
+	Decl *d;
+	Node *nn, *left, *right;
+
+	if(n == nil)
+		return nil;
+
+	left = n->left;
+	right = n->right;
+
+	/*
+	 * rewrites
+	 */
+	switch(n->op){
+	case Oname:
+		d = n->decl;
+		if(d->importid != nil){
+			left = mkbin(Omdot, dupn(1, &n->src, d->eimport), mkdeclname(&n->src, d->importid));
+			left->ty = n->ty;
+			return rewrite(left);
+		}
+		if((t = n->ty)->kind == Texception){
+			if(t->cons)
+				fatal("cons in rewrite Oname");
+			n = mkbin(Oadd, n, mkconst(&n->src, 2*IBY2WD));
+			n = mkunary(Oind, n);
+			n->ty = t;
+			n->left->ty = n->left->left->ty = tint;
+			return rewrite(n);
+		}
+		break;
+	case Odas:
+		n->op = Oas;
+		return rewrite(n);
+	case Oneg:
+		n->left = rewrite(left);
+		if(n->ty == treal)
+			break;
+		left = n->left;
+		n->right = left;
+		n->left = mkconst(&n->src, 0);
+		n->left->ty = n->ty;
+		n->op = Osub;
+		break;
+	case Ocomp:
+		v = 0;
+		v = ~v;
+		n->right = mkconst(&n->src, v);
+		n->right->ty = n->ty;
+		n->left = rewrite(left);
+		n->op = Oxor;
+		break;
+	case Oinc:
+	case Odec:
+	case Opreinc:
+	case Opredec:
+		n->left = rewrite(left);
+		switch(n->ty->kind){
+		case Treal:
+			n->right = mkrconst(&n->src, 1.0);
+			break;
+		case Tint:
+		case Tbig:
+		case Tbyte:
+		case Tfix:
+			n->right = mkconst(&n->src, 1);
+			n->right->ty = n->ty;
+			break;
+		default:
+			fatal("can't rewrite inc/dec %n", n);
+			break;
+		}
+		if(n->op == Opreinc)
+			n->op = Oaddas;
+		else if(n->op == Opredec)
+			n->op = Osubas;
+		break;
+	case Oslice:
+		if(right->left->op == Onothing)
+			right->left = mkconst(&right->left->src, 0);
+		n->left = rewrite(left);
+		n->right = rewrite(right);
+		break;
+	case Oindex:
+		n->op = Oindx;
+		n->left = rewrite(left);
+		n->right = rewrite(right);
+		n = mkunary(Oind, n);
+		n->ty = n->left->ty;
+		n->left->ty = tint;
+		break;
+	case Oload:
+		n->right = mkn(Oname, nil, nil);
+		n->right->src = n->left->src;
+		n->right->decl = n->ty->tof->decl;
+		n->right->ty = n->ty;
+		n->left = rewrite(left);
+		break;
+	case Ocast:
+		if(left->ty->kind == Texception){
+			n = rewrite(left);
+			break;
+		}
+		n->op = Ocast;
+		t = precasttab[left->ty->kind][n->ty->kind];
+		if(t != nil){
+			n->left = mkunary(Ocast, left);
+			n->left->ty = t;
+			return rewrite(n);
+		}
+		n->left = rewrite(left);
+		break;
+	case Oraise:
+		if(left->ty == tstring)
+			{}
+		else if(!left->ty->cons)
+			break;
+		else if(left->op != Ocall || left->left->ty->kind == Tfn){
+			left = mkunary(Ocall, left);
+			left->ty = left->left->ty;
+		}
+		n->left = rewrite(left);
+		break;
+	case Ocall:
+		t = left->ty;
+		if(t->kind == Tref)
+			t = t->tof;
+		if(t->kind == Tfn){
+if(debug['U']) print("call %n\n", left);
+			if(left->ty->kind == Tref){	/* call by function reference */
+				n->left = mkunary(Oind, left);
+				n->left->ty = t;
+				return rewrite(n);
+			}
+			d = nil;
+			if(left->op == Oname)
+				d = left->decl;
+			else if(left->op == Omdot && left->right->op == Odot)
+				d = left->right->right->decl;
+			else if(left->op == Omdot || left->op == Odot)
+				d = left->right->decl;
+			else if(left->op != Oind)
+				fatal("cannot deal with call %n in rewrite", n);
+			if(ispoly(d))
+				addfnptrs(d, 0);
+			n->left = rewrite(left);
+			if(right != nil)
+				n->right = rewrite(right);
+			if(d != nil && d->caninline == 1)
+				n = simplify(putinline(n));
+			break;
+		}
+		switch(n->ty->kind){
+		case Tref:
+			n = mkunary(Oref, n);
+			n->ty = n->left->ty;
+			n->left->ty = n->left->ty->tof;
+			n->left->left->ty = n->left->ty;
+			return rewrite(n);
+		case Tadt:
+			n->op = Otuple;
+			n->right = nil;
+			if(n->ty->tags != nil){
+				n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
+				if(right != nil){
+					nn->right = right;
+					nn->src.stop = right->src.stop;
+				}
+				n->ty = left->right->decl->ty->tof;
+			}else
+				n->left = right;
+			return rewrite(n);
+		case Tadtpick:
+			n->op = Otuple;
+			n->right = nil;
+			n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
+			if(right != nil){
+				nn->right = right;
+				nn->src.stop = right->src.stop;
+			}
+			n->ty = left->right->decl->ty->tof;
+			return rewrite(n);
+		case Texception:
+			if(!n->ty->cons)
+				return n->left;
+			if(left->op == Omdot){
+				left->right->ty = left->ty;
+				left = left->right;
+			}
+			n->op = Otuple;
+			n->right = nil;
+			n->left = nn = mkunary(Oseq, left->decl->init);
+			nn->right = mkunary(Oseq, mkconst(&n->src, 0));
+			nn->right->right = right;
+			n->ty = mkexbasetype(n->ty);
+			n = mkunary(Oref, n);
+			n->ty = internaltype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
+			return rewrite(n);
+		default:
+			fatal("can't deal with %n in rewrite/Ocall", n);
+			break;
+		}
+		break;
+	case Omdot:
+		/*
+		 * what about side effects from left?
+		 */
+		d = right->decl;
+		switch(d->store){
+		case Dfn:
+			n->left = rewrite(left);
+			if(right->op == Odot){
+				n->right = dupn(1, &left->src, right->right);
+				n->right->ty = d->ty;
+			}
+			break;
+		case Dconst:
+		case Dtag:
+		case Dtype:
+			/* handled by fold */
+			return n;
+		case Dglobal:
+			right->op = Oconst;
+			right->val = d->offset;
+			right->ty = tint;
+
+			n->left = left = mkunary(Oind, left);
+			left->ty = tint;
+			n->op = Oadd;
+			n = mkunary(Oind, n);
+			n->ty = n->left->ty;
+			n->left->ty = tint;
+			n->left = rewrite(n->left);
+			return n;
+		case Darg:
+			return n;
+		default:
+			fatal("can't deal with %n in rewrite/Omdot", n);
+			break;
+		}
+		break;
+	case Odot:
+		/*
+		 * what about side effects from left?
+		 */
+		d = right->decl;
+		switch(d->store){
+		case Dfn:
+			if(right->left != nil){
+				n = mkbin(Omdot, dupn(1, &left->src, right->left), right);
+				right->left = nil;
+				n->ty = d->ty;
+				return rewrite(n);
+			}
+			if(left->ty->kind == Tpoly){
+				n = mkbin(Omdot, mkdeclname(&left->src, d->link), mkdeclname(&left->src, d->link->next));
+				n->ty = d->ty;
+				return rewrite(n);
+			}
+			n->op = Oname;
+			n->decl = d;
+			n->right = nil;
+			n->left = nil;
+			return n;
+		case Dconst:
+		case Dtag:
+		case Dtype:
+			/* handled by fold */
+			return n;
+		}
+		if(istuple(left))
+			return n;	/* handled by fold */
+		right->op = Oconst;
+		right->val = d->offset;
+		right->ty = tint;
+
+		if(left->ty->kind != Tref){
+			n->left = mkunary(Oadr, left);
+			n->left->ty = tint;
+		}
+		n->op = Oadd;
+		n = mkunary(Oind, n);
+		n->ty = n->left->ty;
+		n->left->ty = tint;
+		n->left = rewrite(n->left);
+		return n;
+	case Oadr:
+		left = rewrite(left);
+		n->left = left;
+		if(left->op == Oind)
+			return left->left;
+		break;
+	case Otagof:
+		if(n->decl == nil){
+			n->op = Oind;
+			return rewrite(n);
+		}
+		return n;
+	case Omul:
+	case Odiv:
+		left = n->left = rewrite(left);
+		right = n->right = rewrite(right);
+		if(n->ty->kind == Tfix && isfix(left) && isfix(right)){
+			if(left->op == Ocast && tequal(left->ty, n->ty))
+				n->left = left->left;
+			if(right->op == Ocast && tequal(right->ty, n->ty))
+				n->right = right->left;
+		}
+		break;
+	case Oself:
+		if(newfnptr)
+			return n;
+		if(selfdecl == nil){
+			d = selfdecl = mkids(&n->src, enter(strdup(".self"), 5), tany, nil);
+			installids(Dglobal, d);
+			d->refs++;
+		}
+		nn = mkn(Oload, nil, nil);
+		nn->src = n->src;
+		nn->left = mksconst(&n->src, enterstring(strdup("$self"), 5));
+		nn->ty = impdecl->ty;
+		usetype(nn->ty);
+		usetype(nn->ty->tof);
+		nn = rewrite(nn);
+		nn->op = Oself;
+		return nn;
+	case Ofnptr:
+		if(n->flags == 0){
+			/* module */
+			if(left == nil)
+				left = mkn(Oself, nil, nil);
+			return rewrite(left);
+		}
+		right->flags = n->flags;
+		n = right;
+		d = n->decl;
+		if(n->flags == FNPTR2){
+			if(left != nil && left->op != Oname)
+				fatal("not Oname for addiface");
+			if(left == nil){
+				addiface(nil, d);
+				if(newfnptr)
+					n->flags |= FNPTRN;
+			}
+			else
+				addiface(left->decl, d);	/* is this necessary ? */
+			n->ty = tint;
+			return n;
+		}
+		if(n->flags == FNPTRA){
+			n = mkdeclname(&n->src, d->link);
+			n->ty = tany;
+			return n;
+		}
+		if(n->flags == (FNPTRA|FNPTR2)){
+			n = mkdeclname(&n->src, d->link->next);
+			n->ty = tint;
+			return n;
+		}
+		break;
+	case Ochan:
+		if(left == nil)
+			left = n->left = mkconst(&n->src, 0);
+		n->left = rewrite(left);
+		break;
+	default:
+		n->left = rewrite(left);
+		n->right = rewrite(right);
+		break;
+	}
+
+	return n;
+}
+
+/*
+ * label a node with sethi-ullman numbers and addressablity
+ * genaddr interprets addable to generate operands,
+ * so a change here mandates a change there.
+ *
+ * addressable:
+ *	const			Rconst	$value		 may also be Roff or Rdesc or Rnoff
+ *	Asmall(local)		Rreg	value(FP)
+ *	Asmall(global)		Rmreg	value(MP)
+ *	ind(Rareg)		Rreg	value(FP)
+ *	ind(Ramreg)		Rmreg	value(MP)
+ *	ind(Rreg)		Radr	*value(FP)
+ *	ind(Rmreg)		Rmadr	*value(MP)
+ *	ind(Raadr)		Radr	value(value(FP))
+ *	ind(Ramadr)		Rmadr	value(value(MP))
+ *
+ * almost addressable:
+ *	adr(Rreg)		Rareg
+ *	adr(Rmreg)		Ramreg
+ *	add(const, Rareg)	Rareg
+ *	add(const, Ramreg)	Ramreg
+ *	add(const, Rreg)	Raadr
+ *	add(const, Rmreg)	Ramadr
+ *	add(const, Raadr)	Raadr
+ *	add(const, Ramadr)	Ramadr
+ *	adr(Radr)		Raadr
+ *	adr(Rmadr)		Ramadr
+ *
+ * strangely addressable:
+ *	fn			Rpc
+ *	mdot(module,exp)	Rmpc
+ */
+Node*
+sumark(Node *n)
+{
+	Node *left, *right;
+	long v;
+
+	if(n == nil)
+		return nil;
+
+	n->temps = 0;
+	n->addable = Rcant;
+
+	left = n->left;
+	right = n->right;
+	if(left != nil){
+		sumark(left);
+		n->temps = left->temps;
+	}
+	if(right != nil){
+		sumark(right);
+		if(right->temps == n->temps)
+			n->temps++;
+		else if(right->temps > n->temps)
+			n->temps = right->temps;
+	}
+
+	switch(n->op){
+	case Oadr:
+		switch(left->addable){
+		case Rreg:
+			n->addable = Rareg;
+			break;
+		case Rmreg:
+			n->addable = Ramreg;
+			break;
+		case Radr:
+			n->addable = Raadr;
+			break;
+		case Rmadr:
+			n->addable = Ramadr;
+			break;
+		}
+		break;
+	case Oind:
+		switch(left->addable){
+		case Rreg:
+			n->addable = Radr;
+			break;
+		case Rmreg:
+			n->addable = Rmadr;
+			break;
+		case Rareg:
+			n->addable = Rreg;
+			break;
+		case Ramreg:
+			n->addable = Rmreg;
+			break;
+		case Raadr:
+			n->addable = Radr;
+			break;
+		case Ramadr:
+			n->addable = Rmadr;
+			break;
+		}
+		break;
+	case Oname:
+		switch(n->decl->store){
+		case Darg:
+		case Dlocal:
+			n->addable = Rreg;
+			break;
+		case Dglobal:
+			n->addable = Rmreg;
+			if(LDT && n->decl->ty->kind == Tiface)
+				n->addable = Rldt;
+			break;
+		case Dtype:
+			/*
+			 * check for inferface to load
+			 */
+			if(n->decl->ty->kind == Tmodule)
+				n->addable = Rmreg;
+			break;
+		case Dfn:
+			if(n->flags & FNPTR){
+				if(n->flags == FNPTR2)
+					n->addable = Roff;
+				else if(n->flags == (FNPTR2|FNPTRN))
+					n->addable = Rnoff;
+			}
+			else
+				n->addable = Rpc;
+			break;
+		default:
+			fatal("cannot deal with %K in Oname in %n", n->decl, n);
+			break;
+		}
+		break;
+	case Omdot:
+		n->addable = Rmpc;
+		break;
+	case Oconst:
+		switch(n->ty->kind){
+		case Tint:
+		case Tfix:
+			v = n->val;
+			if(v < 0 && ((v >> 29) & 0x7) != 7
+			|| v > 0 && (v >> 29) != 0){
+				n->decl = globalconst(n);
+				n->addable = Rmreg;
+			}else
+				n->addable = Rconst;
+			break;
+		case Tbig:
+			n->decl = globalBconst(n);
+			n->addable = Rmreg;
+			break;
+		case Tbyte:
+			n->decl = globalbconst(n);
+			n->addable = Rmreg;
+			break;
+		case Treal:
+			n->decl = globalfconst(n);
+			n->addable = Rmreg;
+			break;
+		case Tstring:
+			n->decl = globalsconst(n);
+			n->addable = Rmreg;
+			break;
+		default:
+			fatal("cannot %T const in sumark", n->ty);
+			break;
+		}
+		break;
+	case Oadd:
+		if(right->addable == Rconst){
+			switch(left->addable){
+			case Rareg:
+				n->addable = Rareg;
+				break;
+			case Ramreg:
+				n->addable = Ramreg;
+				break;
+			case Rreg:
+			case Raadr:
+				n->addable = Raadr;
+				break;
+			case Rmreg:
+			case Ramadr:
+				n->addable = Ramadr;
+				break;
+			}
+		}
+		break;
+	}
+	if(n->addable < Rcant)
+		n->temps = 0;
+	else if(n->temps == 0)
+		n->temps = 1;
+	return n;
+}
+
+Node*
+mktn(Type *t)
+{
+	Node *n;
+
+	n = mkn(Oname, nil, nil);
+	usedesc(mktdesc(t));
+	n->ty = t;
+	n->decl = t->decl;
+	if(n->decl == nil)
+		fatal("mktn t %T nil decl", t);
+	n->addable = Rdesc;
+	return n;
+}
+
+/* does a tuple of the form (a, b, ...) form a contiguous block
+ * of memory on the stack when offsets are assigned later
+ * - only when (a, b, ...) := rhs and none of the names nil
+ * can we guarantee this
+ */
+static int
+tupblk0(Node *n, Decl **dd)
+{
+	Decl *d;
+	int nid;
+
+	switch(n->op){
+	case Otuple:
+		for(n = n->left; n != nil; n = n->right)
+			if(!tupblk0(n->left, dd))
+				return 0;
+		return 1;
+	case Oname:
+		if(n->decl == nildecl)
+			return 0;
+		d = *dd;
+		if(d != nil && d->next != n->decl)
+			return 0;
+		nid = n->decl->nid;
+		if(d == nil && nid == 1)
+			return 0;
+		if(d != nil && nid != 0)
+			return 0;
+		*dd = n->decl;
+		return 1;
+	}
+	return 0;
+}
+
+/* could force locals to be next to each other
+ * - need to shuffle locals list
+ * - later
+ */
+static Node*
+tupblk(Node *n)
+{
+	Decl *d;
+
+	if(n->op != Otuple)
+		return nil;
+	d = nil;
+	if(!tupblk0(n, &d))
+		return nil;
+	while(n->op == Otuple)
+		n = n->left->left;
+	if(n->op != Oname || n->decl->nid == 1)
+		fatal("bad tupblk");
+	return n;
+}
+	
+/* for cprof */
+#define esrc(src, osrc, nto) (src != nil && nto != nil ? src : osrc)
+
+/*
+ * compile an expression with an implicit assignment
+ * note: you are not allowed to use to->src
+ *
+ * need to think carefully about the types used in moves
+ * it particular, it would be nice to gen movp rather than movc sometimes.
+ */
+Node*
+ecom(Src *src, Node *nto, Node *n)
+{
+	Node *left, *right, *tn;
+	Node tl, tr, tto, ttn;
+	Type *t, *tt;
+	Inst *p, *pp;
+	int op;
+
+	if(debug['e']){
+		print("ecom: %n\n", n);
+		if(nto != nil)
+			print("ecom to: %n\n", nto);
+	}
+
+	if(n->addable < Rcant){
+		/*
+		 * think carefully about the type used here
+		 */
+		if(nto != nil)
+			genmove(src, Mas, n->ty, n, nto);
+		return nto;
+	}
+
+	tl.decl = nil;
+	tr.decl = nil;
+	tto.decl = nil;
+	ttn.decl = nil;
+
+	left = n->left;
+	right = n->right;
+	op = n->op;
+	switch(op){
+	default:
+	case Oadr:
+		fatal("can't %n in ecom", n);
+		return nto;
+	case Oif:
+		p = bcom(left, 1, nil);
+		ecom(&right->left->src, nto, right->left);
+		if(right->right != nil){
+			pp = p;
+			p = genrawop(&right->left->src, IJMP, nil, nil, nil);
+			patch(pp, nextinst());
+			ecom(&right->right->src, nto, right->right);
+		}
+		patch(p, nextinst());
+		break;
+	case Ocomma:
+		tn = left->left;
+		ecom(&left->src, nil, left);
+		ecom(&right->src, nto, right);
+		tfree(tn);
+		break;
+	case Oname:
+		if(n->addable == Rpc){
+			if(nto != nil)
+				genmove(src, Mas, n->ty, n, nto);
+			return nto;
+		}
+		fatal("can't %n in ecom", n);
+		break;
+	case Onothing:
+		break;
+	case Oused:
+		if(nto != nil)
+			fatal("superfluous used %n to %n", left, nto);
+		talloc(&tto, left->ty, nil);
+		ecom(&left->src, &tto, left);
+		tfree(&tto);
+		break;
+	case Oas:
+		if(right->ty == tany)
+			right->ty = n->ty;
+		if(left->op == Oname && left->decl->ty == tany){
+			if(nto == nil)
+				nto = talloc(&tto, right->ty, nil);
+			left = nto;
+			nto = nil;
+		}
+		if(left->op == Oinds){
+			indsascom(src, nto, n);
+			tfree(&tto);
+			break;
+		}
+		if(left->op == Oslice){
+			slicelcom(src, nto, n);
+			tfree(&tto);
+			break;
+		}
+
+		if(left->op == Otuple){
+			if(!tupsaliased(right, left)){
+				if((tn = tupblk(left)) != nil){
+					tn->ty = n->ty;
+					ecom(&n->right->src, tn, right);
+					if(nto != nil)
+						genmove(src, Mas, n->ty, tn, nto);
+					tfree(&tto);
+					break;
+				}
+				if((tn = tupblk(right)) != nil){
+					tn->ty = n->ty;
+					tuplcom(tn, left);
+					if(nto != nil)
+						genmove(src, Mas, n->ty, tn, nto);
+					tfree(&tto);
+					break;
+				}
+				if(nto == nil && right->op == Otuple && left->ty->kind != Tadtpick){
+					tuplrcom(right, left);
+					tfree(&tto);
+					break;
+				}
+			}
+			if(right->addable >= Ralways
+			|| right->op != Oname
+			|| tupaliased(right, left)){
+				talloc(&tr, n->ty, nil);
+				ecom(&n->right->src, &tr, right);
+				right = &tr;
+			}
+			tuplcom(right, n->left);
+			if(nto != nil)
+				genmove(src, Mas, n->ty, right, nto);
+			tfree(&tr);
+			tfree(&tto);
+			break;
+		}
+
+		/*
+		 * check for left/right aliasing and build right into temporary
+		 */
+		if(right->op == Otuple){
+			if(!tupsaliased(left, right) && (tn = tupblk(right)) != nil){
+				tn->ty = n->ty;
+				right = tn;
+			}
+			else if(left->op != Oname || tupaliased(left, right))
+				right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+		}
+
+		/*
+		 * think carefully about types here
+		 */
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		ecom(&n->src, left, right);
+		if(nto != nil)
+			genmove(src, Mas, nto->ty, left, nto);
+		tfree(&tl);
+		tfree(&tr);
+		tfree(&tto);
+		break;
+	case Ochan:
+		if(left && left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		genchan(src, left, n->ty->tof, nto);
+		tfree(&tl);
+		break;
+	case Oinds:
+		if(right->addable < Ralways){
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nil);
+		}else if(left->temps <= right->temps){
+			right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nil);
+		}else{
+			left = eacom(left, &tl, nil);
+			right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+		}
+		genop(&n->src, op, left, right, nto);
+		tfree(&tl);
+		tfree(&tr);
+		break;
+	case Osnd:
+		if(right->addable < Rcant){
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nto);
+		}else if(left->temps < right->temps){
+			right = eacom(right, &tr, nto);
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nil);
+		}else{
+			left = eacom(left, &tl, nto);
+			right = eacom(right, &tr, nil);
+		}
+		p = genrawop(&n->src, ISEND, right, nil, left);
+		p->m.offset = n->ty->size;	/* for optimizer */
+		if(nto != nil)
+			genmove(src, Mas, right->ty, right, nto);
+		tfree(&tl);
+		tfree(&tr);
+		break;
+	case Orcv:
+		if(nto == nil){
+			ecom(&n->src, talloc(&tto, n->ty, nil), n);
+			tfree(&tto);
+			return nil;
+		}
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		if(left->ty->kind == Tchan){
+			p = genrawop(src, IRECV, left, nil, nto);
+			p->m.offset = n->ty->size;	/* for optimizer */
+		}else{
+			recvacom(src, nto, n);
+		}
+		tfree(&tl);
+		break;
+	case Ocons:
+		/*
+		 * another temp which can go with analysis
+		 */
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+		if(!sameaddr(right, nto)){
+			ecom(&right->src, talloc(&tto, n->ty, nto), right);
+			genmove(src, Mcons, left->ty, left, &tto);
+			if(!sameaddr(&tto, nto))
+				genmove(src, Mas, nto->ty, &tto, nto);
+		}else
+			genmove(src, Mcons, left->ty, left, nto);
+		tfree(&tl);
+		tfree(&tto);
+		break;
+	case Ohd:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		genmove(src, Mhd, nto->ty, left, nto);
+		tfree(&tl);
+		break;
+	case Otl:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		genmove(src, Mtl, left->ty, left, nto);
+		tfree(&tl);
+		break;
+	case Otuple:
+		if((tn = tupblk(n)) != nil){
+			tn->ty = n->ty;
+			genmove(src, Mas, n->ty, tn, nto);
+			break;
+		}
+		tupcom(nto, n);
+		break;
+	case Oadd:
+	case Osub:
+	case Omul:
+	case Odiv:
+	case Omod:
+	case Oand:
+	case Oor:
+	case Oxor:
+	case Olsh:
+	case Orsh:
+	case Oexp:
+		/*
+		 * check for 2 operand forms
+		 */
+		if(sameaddr(nto, left)){
+			if(right->addable >= Rcant)
+				right = eacom(right, &tr, nto);
+			genop(src, op, right, nil, nto);
+			tfree(&tr);
+			break;
+		}
+
+		if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring){
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nto);
+			genop(src, opcommute[op], left, nil, nto);
+			tfree(&tl);
+			break;
+		}
+
+		if(right->addable < left->addable
+		&& opcommute[op]
+		&& n->ty != tstring){
+			op = opcommute[op];
+			left = right;
+			right = n->left;
+		}
+		if(left->addable < Ralways){
+			if(right->addable >= Rcant)
+				right = eacom(right, &tr, nto);
+		}else if(right->temps <= left->temps){
+			left = ecom(&left->src, talloc(&tl, left->ty, nto), left);
+			if(right->addable >= Rcant)
+				right = eacom(right, &tr, nil);
+		}else{
+			right = eacom(right, &tr, nto);
+			left = ecom(&left->src, talloc(&tl, left->ty, nil), left);
+		}
+
+		/*
+		 * check for 2 operand forms
+		 */
+		if(sameaddr(nto, left))
+			genop(src, op, right, nil, nto);
+		else if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring)
+			genop(src, opcommute[op], left, nil, nto);
+		else
+			genop(src, op, right, left, nto);
+		tfree(&tl);
+		tfree(&tr);
+		break;
+	case Oaddas:
+	case Osubas:
+	case Omulas:
+	case Odivas:
+	case Omodas:
+	case Oexpas:
+	case Oandas:
+	case Ooras:
+	case Oxoras:
+	case Olshas:
+	case Orshas:
+		if(left->op == Oinds){
+			indsascom(src, nto, n);
+			break;
+		}
+		if(right->addable < Rcant){
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nto);
+		}else if(left->temps < right->temps){
+			right = eacom(right, &tr, nto);
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nil);
+		}else{
+			left = eacom(left, &tl, nto);
+			right = eacom(right, &tr, nil);
+		}
+		genop(&n->src, op, right, nil, left);
+		if(nto != nil)
+			genmove(src, Mas, left->ty, left, nto);
+		tfree(&tl);
+		tfree(&tr);
+		break;
+	case Olen:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		op = -1;
+		t = left->ty;
+		if(t == tstring)
+			op = ILENC;
+		else if(t->kind == Tarray)
+			op = ILENA;
+		else if(t->kind == Tlist)
+			op = ILENL;
+		else
+			fatal("can't len %n", n);
+		genrawop(src, op, left, nil, nto);
+		tfree(&tl);
+		break;
+	case Oneg:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		genop(&n->src, op, left, nil, nto);
+		tfree(&tl);
+		break;
+	case Oinc:
+	case Odec:
+		if(left->op == Oinds){
+			indsascom(src, nto, n);
+			break;
+		}
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+		if(nto != nil)
+			genmove(src, Mas, left->ty, left, nto);
+		if(right->addable >= Rcant)
+			fatal("inc/dec amount not addressable: %n", n);
+		genop(&n->src, op, right, nil, left);
+		tfree(&tl);
+		break;
+	case Ospawn:
+		if(left->left->op == Oind)
+			fpcall(&n->src, op, left, nto);
+		else
+			callcom(&n->src, op, left, nto);
+		break;
+	case Oraise:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+		genrawop(&n->src, IRAISE, left, nil, nil);
+		tfree(&tl);
+		break;
+	case Ocall:
+		if(left->op == Oind)
+			fpcall(esrc(src, &n->src, nto), op, n, nto);
+		else
+			callcom(esrc(src, &n->src, nto), op, n, nto);
+		break;
+	case Oref:
+		t = left->ty;
+		if(left->op == Oname && left->decl->store == Dfn || left->op == Omdot && left->right->op == Oname && left->right->decl->store == Dfn){	/* create a function reference */
+			Decl *d;
+			Node *mod, *ind;
+
+			d = left->decl;
+			if(left->op == Omdot){
+				d = left->right->decl;
+				mod = left->left;
+			}
+			else if(d->eimport != nil)
+				mod = d->eimport;
+			else{
+				mod = rewrite(mkn(Oself, nil, nil));
+				addiface(nil, d);
+			}
+			sumark(mod);
+			talloc(&tto, n->ty, nto);
+			genrawop(src, INEW, mktn(usetype(tfnptr)), nil, &tto);
+			tr.src = *src;
+			tr.op = Oind;
+			tr.left = &tto;
+			tr.right = nil;
+			tr.ty = tany;
+			sumark(&tr);
+			ecom(src, &tr, mod);
+			ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, &tto), mkconst(src, IBY2WD)));
+			ind->ty = ind->left->ty = ind->left->right->ty = tint;
+			tr.op = Oas;
+			tr.left = ind;
+			tr.right = mkdeclname(src, d);
+			tr.ty = tr.right->ty = tint;
+			sumark(&tr);
+			tr.right->addable = mod->op == Oself && newfnptr ? Rnoff : Roff;
+			ecom(src, nil, &tr);
+			if(!sameaddr(&tto, nto))
+				genmove(src, Mas, n->ty, &tto, nto);
+			tfree(&tto);
+			break;
+		}
+		if(left->op == Oname && left->decl->store == Dtype){
+			genrawop(src, INEW, mktn(t), nil, nto);
+			break;
+		}
+		if(t->kind == Tadt && t->tags != nil){
+			pickdupcom(src, nto, left);
+			break;
+		}
+
+		tt = t;
+		if(left->op == Oconst && left->decl->store == Dtag)
+			t = left->decl->ty->tof;
+		/*
+		 * could eliminate temp if to does not occur
+		 * in tuple initializer
+		 */
+		talloc(&tto, n->ty, nto);
+		genrawop(src, INEW, mktn(t), nil, &tto);
+		tr.op = Oind;
+		tr.left = &tto;
+		tr.right = nil;
+		tr.ty = tt;
+		sumark(&tr);
+		ecom(src, &tr, left);
+		if(!sameaddr(&tto, nto))
+			genmove(src, Mas, n->ty, &tto, nto);
+		tfree(&tto);
+		break;
+	case Oload:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		talloc(&tr, tint, nil);
+		if(LDT)
+			genrawop(src, ILOAD, left, right, nto);
+		else{
+			genrawop(src, ILEA, right, nil, &tr);
+			genrawop(src, ILOAD, left, &tr, nto);
+		}
+		tfree(&tl);
+		tfree(&tr);
+		break;
+	case Ocast:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		t = left->ty;
+		if(t->kind == Tfix || n->ty->kind == Tfix){
+			op = casttab[t->kind][n->ty->kind];
+			if(op == ICVTXX)
+				genfixcastop(src, op, left, nto);
+			else{
+				tn = sumark(mkrconst(src, scale2(t, n->ty)));
+				genrawop(src, op, left, tn, nto);
+			}
+		}
+		else
+			genrawop(src, casttab[t->kind][n->ty->kind], left, nil, nto);
+		tfree(&tl);
+		break;
+	case Oarray:
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+		genrawop(esrc(src, &left->src, nto), arrayz ? INEWAZ : INEWA, left, mktn(n->ty->tof), nto);
+		if(right != nil)
+			arraycom(nto, right);
+		tfree(&tl);
+		break;
+	case Oslice:
+		tn = right->right;
+		right = right->left;
+
+		/*
+		 * make the left node of the slice directly addressable
+		 * therefore, if it's len is taken (via tn),
+		 * left's tree won't be rewritten
+		 */
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+
+		if(tn->op == Onothing){
+			tn = mkn(Olen, left, nil);
+			tn->src = *src;
+			tn->ty = tint;
+			sumark(tn);
+		}
+		if(tn->addable < Ralways){
+			if(right->addable >= Rcant)
+				right = eacom(right, &tr, nil);
+		}else if(right->temps <= tn->temps){
+			tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
+			if(right->addable >= Rcant)
+				right = eacom(right, &tr, nil);
+		}else{
+			right = eacom(right, &tr, nil);
+			tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
+		}
+		op = ISLICEA;
+		if(nto->ty == tstring)
+			op = ISLICEC;
+
+		/*
+		 * overwrite the destination last,
+		 * since it might be used in computing the slice bounds
+		 */
+		if(!sameaddr(left, nto))
+			ecom(&left->src, nto, left);
+
+		genrawop(src, op, right, tn, nto);
+		tfree(&tl);
+		tfree(&tr);
+		tfree(&ttn);
+		break;
+	case Oindx:
+		if(right->addable < Rcant){
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nto);
+		}else if(left->temps < right->temps){
+			right = eacom(right, &tr, nto);
+			if(left->addable >= Rcant)
+				left = eacom(left, &tl, nil);
+		}else{
+			left = eacom(left, &tl, nto);
+			right = eacom(right, &tr, nil);
+		}
+		if(nto->addable >= Ralways)
+			nto = ecom(src, talloc(&tto, nto->ty, nil), nto);
+		op = IINDX;
+		switch(left->ty->tof->size){
+		case IBY2LG:
+			op = IINDL;
+			if(left->ty->tof == treal)
+				op = IINDF;
+			break;
+		case IBY2WD:
+			op = IINDW;
+			break;
+		case 1:
+			op = IINDB;
+			break;
+		}
+		genrawop(src, op, left, nto, right);
+		// array[] of {....} [index] frees array too early (before index value used)
+		// function(...) [index] frees array too early (before index value used)
+		if(tl.decl != nil)
+			tfreelater(&tl);
+		else
+			tfree(&tl);
+		tfree(&tr);
+		tfree(&tto);
+		break;
+	case Oind:
+		n = eacom(n, &tl, nto);
+		genmove(src, Mas, n->ty, n, nto);
+		tfree(&tl);
+		break;
+	case Onot:
+	case Oandand:
+	case Ooror:
+	case Oeq:
+	case Oneq:
+	case Olt:
+	case Oleq:
+	case Ogt:
+	case Ogeq:
+		p = bcom(n, 1, nil);
+		genmove(src, Mas, tint, sumark(mkconst(src, 1)), nto);
+		pp = genrawop(src, IJMP, nil, nil, nil);
+		patch(p, nextinst());
+		genmove(src, Mas, tint, sumark(mkconst(src, 0)), nto);
+		patch(pp, nextinst());
+		break;
+	case Oself:
+		if(newfnptr){
+			if(nto != nil)
+				genrawop(src, ISELF, nil, nil, nto);
+			break;
+		}
+		tn = sumark(mkdeclname(src, selfdecl));
+		p = genbra(src, Oneq, tn, sumark(mkdeclname(src, nildecl)));
+		n->op = Oload;
+		ecom(src, tn, n);
+		patch(p, nextinst());
+		genmove(src, Mas, n->ty, tn, nto);
+		break;
+	}
+	return nto;
+}
+
+/*
+ * compile exp n to yield an addressable expression
+ * use reg to build a temporary; if t is a temp, it is usable
+ * if dangle leaves the address dangling, generate into a temporary
+ *	this should only happen with arrays
+ *
+ * note that 0adr's are strange as they are only used
+ * for calculating the addresses of fields within adt's.
+ * therefore an Oind is the parent or grandparent of the Oadr,
+ * and we pick off all of the cases where Oadr's argument is not
+ * addressable by looking from the Oind.
+ */
+Node*
+eacom(Node *n, Node *reg, Node *t)
+{
+	Node *left, *tn;
+
+	if(n->op == Ocomma){
+		tn = n->left->left;
+		ecom(&n->left->src, nil, n->left);
+		n = eacom(n->right, reg, t);
+		tfree(tn);
+		return n;
+	}
+
+	if(debug['e'] || debug['E'])
+		print("eacom: %n\n", n);
+
+	left = n->left;
+	if(n->op != Oind){
+		ecom(&n->src, talloc(reg, n->ty, t), n);
+		reg->src = n->src;
+		return reg;
+	}
+		
+	if(left->op == Oadd && left->right->op == Oconst){
+		if(left->left->op == Oadr){
+			left->left->left = eacom(left->left->left, reg, t);
+			sumark(n);
+			if(n->addable >= Rcant)
+				fatal("eacom can't make node addressable: %n", n);
+			return n;
+		}
+		talloc(reg, left->left->ty, t);
+		ecom(&left->left->src, reg, left->left);
+		left->left->decl = reg->decl;
+		left->left->addable = Rreg;
+		left->left = reg;
+		left->addable = Raadr;
+		n->addable = Radr;
+	}else if(left->op == Oadr){
+		talloc(reg, left->left->ty, t);
+		ecom(&left->left->src, reg, left->left);
+
+		/*
+		 * sleaze: treat the temp as the type of the field, not the enclosing structure
+		 */
+		reg->ty = n->ty;
+		reg->src = n->src;
+		return reg;
+	}else{
+		talloc(reg, left->ty, t);
+		ecom(&left->src, reg, left);
+		n->left = reg;
+		n->addable = Radr;
+	}
+	return n;
+}
+
+/*
+ * compile an assignment to an array slice
+ */
+Node*
+slicelcom(Src *src, Node *nto, Node *n)
+{
+	Node *left, *right, *v;
+	Node tl, tr, tv, tu;
+
+	tl.decl = nil;
+	tr.decl = nil;
+	tv.decl = nil;
+	tu.decl = nil;
+
+	left = n->left->left;
+	right = n->left->right->left;
+	v = n->right;
+	if(right->addable < Ralways){
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+	}else if(left->temps <= right->temps){
+		right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+	}else{
+		left = eacom(left, &tl, nil);		/* dangle on right and v */
+		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+	}
+
+	switch(n->op){
+	case Oas:
+		if(v->addable >= Rcant)
+			v = eacom(v, &tv, nil);
+		break;
+	}
+
+	genrawop(&n->src, ISLICELA, v, right, left);
+	if(nto != nil)
+		genmove(src, Mas, n->ty, left, nto);
+	tfree(&tl);
+	tfree(&tv);
+	tfree(&tr);
+	tfree(&tu);
+	return nto;
+}
+
+/*
+ * compile an assignment to a string location
+ */
+Node*
+indsascom(Src *src, Node *nto, Node *n)
+{
+	Node *left, *right, *u, *v;
+	Node tl, tr, tv, tu;
+
+	tl.decl = nil;
+	tr.decl = nil;
+	tv.decl = nil;
+	tu.decl = nil;
+
+	left = n->left->left;
+	right = n->left->right;
+	v = n->right;
+	if(right->addable < Ralways){
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nto);
+	}else if(left->temps <= right->temps){
+		right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+	}else{
+		left = eacom(left, &tl, nil);		/* dangle on right and v */
+		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+	}
+
+	switch(n->op){
+	case Oas:
+		if(v->addable >= Rcant)
+			v = eacom(v, &tv, nil);
+		break;
+	case Oinc:
+	case Odec:
+		if(v->addable >= Rcant)
+			fatal("inc/dec amount not addable");
+		u = talloc(&tu, tint, nil);
+		genop(&n->left->src, Oinds, left, right, u);
+		if(nto != nil)
+			genmove(src, Mas, n->ty, u, nto);
+		nto = nil;
+		genop(&n->src, n->op, v, nil, u);
+		v = u;
+		break;
+	case Oaddas:
+	case Osubas:
+	case Omulas:
+	case Odivas:
+	case Omodas:
+	case Oexpas:
+	case Oandas:
+	case Ooras:
+	case Oxoras:
+	case Olshas:
+	case Orshas:
+		if(v->addable >= Rcant)
+			v = eacom(v, &tv, nil);
+		u = talloc(&tu, tint, nil);
+		genop(&n->left->src, Oinds, left, right, u);
+		genop(&n->src, n->op, v, nil, u);
+		v = u;
+		break;
+	}
+
+	genrawop(&n->src, IINSC, v, right, left);
+	tfree(&tl);
+	tfree(&tv);
+	tfree(&tr);
+	tfree(&tu);
+	if(nto != nil)
+		genmove(src, Mas, n->ty, v, nto);
+	return nto;
+}
+
+void
+callcom(Src *src, int op, Node *n, Node *ret)
+{
+	Node frame, tadd, toff, pass, *a, *mod, *ind, *nfn, *args, tmod, tind, *tn;
+	Inst *in,*p;
+	Decl *d, *callee;
+	long off;
+	int iop;
+
+	args = n->right;
+	nfn = n->left;
+	switch(nfn->op){
+		case Odot:
+			callee = nfn->right->decl;
+			nfn->addable = Rpc;
+			break;
+		case Omdot:
+			callee = nfn->right->decl;
+			break;
+		case Oname:
+			callee = nfn->decl;
+			break;
+		default:
+			callee = nil;
+			fatal("bad call op in callcom");
+	}
+	if(nfn->addable != Rpc && nfn->addable != Rmpc)
+		fatal("can't gen call addresses");
+	if(nfn->ty->tof != tnone && ret == nil){
+		ecom(src, talloc(&tmod, nfn->ty->tof, nil), n);
+		tfree(&tmod);
+		return;
+	}
+	if(ispoly(callee))
+		addfnptrs(callee, 0);
+	if(nfn->ty->varargs){
+		nfn->decl = dupdecl(nfn->right->decl);
+		nfn->decl->desc = gendesc(nfn->right->decl, idoffsets(nfn->ty->ids, MaxTemp, MaxAlign), nfn->ty->ids);
+	}
+
+	talloc(&frame, tint, nil);
+
+	mod = nfn->left;
+	ind = nfn->right;
+	tmod.decl = tind.decl = nil;
+	if(nfn->addable == Rmpc){
+		if(mod->addable >= Rcant)
+			mod = eacom(mod, &tmod, nil);		/* dangle always */
+		if(ind->op != Oname && ind->addable >= Ralways){
+			talloc(&tind, ind->ty, nil);
+			ecom(&ind->src, &tind, ind);
+			ind = &tind;
+		}
+		else if(ind->decl != nil && ind->decl->store != Darg)
+			ind->addable = Roff;
+	}
+
+	/*
+	 * stop nested uncalled frames
+	 * otherwise exception handling very complicated
+	 */
+	for(a = args; a != nil; a = a->right){
+		if(hascall(a->left)){
+			tn = mkn(0, nil, nil);
+			talloc(tn, a->left->ty, nil);
+			ecom(&a->left->src, tn, a->left);
+			a->left = tn;
+			tn->flags |= TEMP;
+		}
+	}
+
+	/*
+	 * allocate the frame
+	 */
+	if(nfn->addable == Rmpc && !nfn->ty->varargs){
+		genrawop(src, IMFRAME, mod, ind, &frame);
+	}else if(nfn->op == Odot){
+		genrawop(src, IFRAME, nfn->left, nil, &frame);
+	}else{
+		in = genrawop(src, IFRAME, nil, nil, &frame);
+		in->sm = Adesc;
+		in->s.decl = nfn->decl;
+	}
+
+	/*
+	 * build a fake node for the argument area
+	 */
+	toff = znode;
+	tadd = znode;
+	pass = znode;
+	toff.op = Oconst;
+	toff.addable = Rconst;
+	toff.ty = tint;
+	tadd.op = Oadd;
+	tadd.addable = Raadr;
+	tadd.left = &frame;
+	tadd.right = &toff;
+	tadd.ty = tint;
+	pass.op = Oind;
+	pass.addable = Radr;
+	pass.left = &tadd;
+
+	/*
+	 * compile all the args
+	 */
+	d = nfn->ty->ids;
+	off = 0;
+	for(a = args; a != nil; a = a->right){
+		off = d->offset;
+		toff.val = off;
+		if(d->ty->kind == Tpoly)
+			pass.ty = a->left->ty;
+		else
+			pass.ty = d->ty;
+		ecom(&a->left->src, &pass, a->left);
+		d = d->next;
+		if(a->left->flags & TEMP)
+			tfree(a->left);
+	}
+	if(off > maxstack)
+		maxstack = off;
+
+	/*
+	 * pass return value
+	 */
+	if(ret != nil){
+		toff.val = REGRET*IBY2WD;
+		pass.ty = nfn->ty->tof;
+		p = genrawop(src, ILEA, ret, nil, &pass);
+		p->m.offset = ret->ty->size;	/* for optimizer */
+	}
+
+	/*
+	 * call it
+	 */
+	if(nfn->addable == Rmpc){
+		iop = IMCALL;
+		if(op == Ospawn)
+			iop = IMSPAWN;
+		genrawop(src, iop, &frame, ind, mod);
+		tfree(&tmod);
+		tfree(&tind);
+	}else if(nfn->op == Odot){
+		iop = ICALL;
+		if(op == Ospawn)
+			iop = ISPAWN;
+		genrawop(src, iop, &frame, nil, nfn->right);
+	}else{
+		iop = ICALL;
+		if(op == Ospawn)
+			iop = ISPAWN;
+		in = genrawop(src, iop, &frame, nil, nil);
+		in->d.decl = nfn->decl;
+		in->dm = Apc;
+	}
+	tfree(&frame);
+}
+
+/*
+ * initialization code for arrays
+ * a must be addressable (< Rcant)
+ */
+void
+arraycom(Node *a, Node *elems)
+{
+	Node tindex, fake, tmp, ri, *e, *n, *q, *body, *wild;
+	Inst *top, *out;
+	/* Case *c; */
+
+	if(debug['A'])
+		print("arraycom: %n %n\n", a, elems);
+
+	/* c = elems->ty->cse; */
+	/* don't use c->wild in case we've been inlined */
+	wild = nil;
+	for(e = elems; e != nil; e = e->right)
+		for(q = e->left->left; q != nil; q = q->right)
+			if(q->left->op == Owild)
+				wild = e->left;
+	if(wild != nil)
+		arraydefault(a, wild->right);
+
+	tindex = znode;
+	fake = znode;
+	talloc(&tmp, tint, nil);
+	tindex.op = Oindx;
+	tindex.addable = Rcant;
+	tindex.left = a;
+	tindex.right = nil;
+	tindex.ty = tint;
+	fake.op = Oind;
+	fake.addable = Radr;
+	fake.left = &tmp;
+	fake.ty = a->ty->tof;
+
+	for(e = elems; e != nil; e = e->right){
+		/*
+		 * just duplicate the initializer for Oor
+		 */
+		for(q = e->left->left; q != nil; q = q->right){
+			if(q->left->op == Owild)
+				continue;
+	
+			body = e->left->right;
+			if(q->right != nil)
+				body = dupn(0, &nosrc, body);
+			top = nil;
+			out = nil;
+			ri.decl = nil;
+			if(q->left->op == Orange){
+				/*
+				 * for(i := q.left.left; i <= q.left.right; i++)
+				 */
+				talloc(&ri, tint, nil);
+				ri.src = q->left->src;
+				ecom(&q->left->src, &ri, q->left->left);
+	
+				/* i <= q.left.right; */
+				n = mkn(Oleq, &ri, q->left->right);
+				n->src = q->left->src;
+				n->ty = tint;
+				top = nextinst();
+				out = bcom(n, 1, nil);
+	
+				tindex.right = &ri;
+			}else{
+				tindex.right = q->left;
+			}
+	
+			tindex.addable = Rcant;
+			tindex.src = q->left->src;
+			ecom(&tindex.src, &tmp, &tindex);
+	
+			ecom(&body->src, &fake, body);
+	
+			if(q->left->op == Orange){
+				/* i++ */
+				n = mkbin(Oinc, &ri, sumark(mkconst(&ri.src, 1)));
+				n->ty = tint;
+				n->addable = Rcant;
+				ecom(&n->src, nil, n);
+	
+				/* jump to test */
+				patch(genrawop(&q->left->src, IJMP, nil, nil, nil), top);
+				patch(out, nextinst());
+				tfree(&ri);
+			}
+		}
+	}
+	tfree(&tmp);
+}
+
+/*
+ * default initialization code for arrays.
+ * compiles to
+ *	n = len a;
+ *	while(n){
+ *		n--;
+ *		a[n] = elem;
+ *	}
+ */
+void
+arraydefault(Node *a, Node *elem)
+{
+	Inst *out, *top;
+	Node n, e, *t;
+
+	if(debug['A'])
+		print("arraydefault: %n %n\n", a, elem);
+
+	t = mkn(Olen, a, nil);
+	t->src = elem->src;
+	t->ty = tint;
+	t->addable = Rcant;
+	talloc(&n, tint, nil);
+	n.src = elem->src;
+	ecom(&t->src, &n, t);
+
+	top = nextinst();
+	out = bcom(&n, 1, nil);
+
+	t = mkbin(Odec, &n, sumark(mkconst(&elem->src, 1)));
+	t->ty = tint;
+	t->addable = Rcant;
+	ecom(&t->src, nil, t);
+
+	e.decl = nil;
+	if(elem->addable >= Rcant)
+		elem = eacom(elem, &e, nil);
+
+	t = mkn(Oindx, a, &n);
+	t->src = elem->src;
+	t = mkbin(Oas, mkunary(Oind, t), elem);
+	t->ty = elem->ty;
+	t->left->ty = elem->ty;
+	t->left->left->ty = tint;
+	sumark(t);
+	ecom(&t->src, nil, t);
+
+	patch(genrawop(&t->src, IJMP, nil, nil, nil), top);
+
+	tfree(&n);
+	tfree(&e);
+	patch(out, nextinst());
+}
+
+void
+tupcom(Node *nto, Node *n)
+{
+	Node tadr, tadd, toff, fake, *e;
+	Decl *d;
+
+	if(debug['Y'])
+		print("tupcom %n\nto %n\n", n, nto);
+
+	/*
+	 * build a fake node for the tuple
+	 */
+	toff = znode;
+	tadd = znode;
+	fake = znode;
+	tadr = znode;
+	toff.op = Oconst;
+	toff.ty = tint;
+	tadr.op = Oadr;
+	tadr.left = nto;
+	tadr.ty = tint;
+	tadd.op = Oadd;
+	tadd.left = &tadr;
+	tadd.right = &toff;
+	tadd.ty = tint;
+	fake.op = Oind;
+	fake.left = &tadd;
+	sumark(&fake);
+	if(fake.addable >= Rcant)
+		fatal("tupcom: bad value exp %n", &fake);
+
+	/*
+	 * compile all the exps
+	 */
+	d = n->ty->ids;
+	for(e = n->left; e != nil; e = e->right){
+		toff.val = d->offset;
+		fake.ty = d->ty;
+		ecom(&e->left->src, &fake, e->left);
+		d = d->next;
+	}
+}
+
+void
+tuplcom(Node *n, Node *nto)
+{
+	Node tadr, tadd, toff, fake, tas, *e, *as;
+	Decl *d;
+
+	if(debug['Y'])
+		print("tuplcom %n\nto %n\n", n, nto);
+
+	/*
+	 * build a fake node for the tuple
+	 */
+	toff = znode;
+	tadd = znode;
+	fake = znode;
+	tadr = znode;
+	toff.op = Oconst;
+	toff.ty = tint;
+	tadr.op = Oadr;
+	tadr.left = n;
+	tadr.ty = tint;
+	tadd.op = Oadd;
+	tadd.left = &tadr;
+	tadd.right = &toff;
+	tadd.ty = tint;
+	fake.op = Oind;
+	fake.left = &tadd;
+	sumark(&fake);
+	if(fake.addable >= Rcant)
+		fatal("tuplcom: bad value exp for %n", &fake);
+
+	/*
+	 * compile all the exps
+	 */
+	d = nto->ty->ids;
+	if(nto->ty->kind == Tadtpick)
+		d = nto->ty->tof->ids->next;
+	for(e = nto->left; e != nil; e = e->right){
+		as = e->left;
+		if(as->op != Oname || as->decl != nildecl){
+			toff.val = d->offset;
+			fake.ty = d->ty;
+			fake.src = as->src;
+			if(as->addable < Rcant)
+				genmove(&as->src, Mas, d->ty, &fake, as);
+			else{
+				tas.op = Oas;
+				tas.ty = d->ty;
+				tas.src = as->src;
+				tas.left = as;
+				tas.right = &fake;
+				tas.addable = Rcant;
+				ecom(&tas.src, nil, &tas);
+			}
+		}
+		d = d->next;
+	}
+}
+
+void
+tuplrcom(Node *n, Node *nto)
+{
+	Node *s, *d, tas;
+	Decl *de;
+
+	de = nto->ty->ids;
+	for(s = n->left, d = nto->left; s != nil && d != nil; s = s->right, d = d->right){
+		if(d->left->op != Oname || d->left->decl != nildecl){
+			tas.op = Oas;
+			tas.ty = de->ty;
+			tas.src = s->left->src;
+			tas.left = d->left;
+			tas.right = s->left;
+			sumark(&tas);
+			ecom(&tas.src, nil, &tas);
+		}
+		de = de->next;
+	}
+	if(s != nil || d != nil)
+		fatal("tuplrcom");
+}
+
+/*
+ * boolean compiler
+ * fall through when condition == true
+ */
+Inst*
+bcom(Node *n, int iftrue, Inst *b)
+{
+	Inst *bb;
+	Node tl, tr, *t, *left, *right, *tn;
+	int op;
+
+	if(n->op == Ocomma){
+		tn = n->left->left;
+		ecom(&n->left->src, nil, n->left);
+		bb = bcom(n->right, iftrue, b);
+		tfree(tn);
+		return bb;
+	}
+
+	if(debug['b'])
+		print("bcom %n %d\n", n, iftrue);
+
+	left = n->left;
+	right = n->right;
+	op = n->op;
+	
+	switch(op){
+	case Onothing:
+		return b;
+	case Onot:
+		return bcom(n->left, !iftrue, b);
+	case Oandand:
+		if(!iftrue)
+			return oror(n, iftrue, b);
+		return andand(n, iftrue, b);
+	case Ooror:
+		if(!iftrue)
+			return andand(n, iftrue, b);
+		return oror(n, iftrue, b);
+	case Ogt:
+	case Ogeq:
+	case Oneq:
+	case Oeq:
+	case Olt:
+	case Oleq:
+		break;
+	default:
+		if(n->ty->kind == Tint){
+			right = mkconst(&n->src, 0);
+			right->addable = Rconst;
+			left = n;
+			op = Oneq;
+			break;
+		}
+		fatal("can't bcom %n", n);
+		return b;
+	}
+
+	if(iftrue)
+		op = oprelinvert[op];
+
+	if(left->addable < right->addable){
+		t = left;
+		left = right;
+		right = t;
+		op = opcommute[op];
+	}
+
+	tl.decl = nil;
+	tr.decl = nil;
+	if(right->addable < Ralways){
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+	}else if(left->temps <= right->temps){
+		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+		if(left->addable >= Rcant)
+			left = eacom(left, &tl, nil);
+	}else{
+		left = eacom(left, &tl, nil);
+		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
+	}
+	bb = genbra(&n->src, op, left, right);
+	bb->branch = b;
+	tfree(&tl);
+	tfree(&tr);
+	return bb;
+}
+
+Inst*
+andand(Node *n, int iftrue, Inst *b)
+{
+	if(debug['b'])
+		print("andand %n\n", n);
+	b = bcom(n->left, iftrue, b);
+	b = bcom(n->right, iftrue, b);
+	return b;
+}
+
+Inst*
+oror(Node *n, int iftrue, Inst *b)
+{
+	Inst *bb;
+
+	if(debug['b'])
+		print("oror %n\n", n);
+	bb = bcom(n->left, !iftrue, nil);
+	b = bcom(n->right, iftrue, b);
+	patch(bb, nextinst());
+	return b;
+}
+
+/*
+ * generate code for a recva expression
+ * this is just a hacked up small alt
+ */
+void
+recvacom(Src *src, Node *nto, Node *n)
+{
+	Label *labs;
+	Case *c;
+	Node which, tab, off, add, adr, slot, *left;
+	Type *talt;
+	Inst *p;
+
+	left = n->left;
+
+	labs = allocmem(1 * sizeof *labs);
+	labs[0].isptr = left->addable >= Rcant;
+	c = allocmem(sizeof *c);
+	c->nlab = 1;
+	c->labs = labs;
+	talt = mktalt(c);
+
+	talloc(&which, tint, nil);
+	talloc(&tab, talt, nil);
+
+	/*
+	 * build the node for the address of each channel,
+	 * the values to send, and the storage fro values received
+	 */
+	off = znode;
+	off.op = Oconst;
+	off.ty = tint;
+	off.addable = Rconst;
+	adr = znode;
+	adr.op = Oadr;
+	adr.left = &tab;
+	adr.ty = tint;
+	add = znode;
+	add.op = Oadd;
+	add.left = &adr;
+	add.right = &off;
+	add.ty = tint;
+	slot = znode;
+	slot.op = Oind;
+	slot.left = &add;
+	sumark(&slot);
+
+	/*
+	 * gen the channel
+	 * this sleaze is lying to the garbage collector
+	 */
+	off.val = 2*IBY2WD;
+	if(left->addable < Rcant)
+		genmove(src, Mas, tint, left, &slot);
+	else{
+		slot.ty = left->ty;
+		ecom(src, &slot, left);
+		slot.ty = nil;
+	}
+
+	/*
+	 * gen the value
+	 */
+	off.val += IBY2WD;
+	p = genrawop(&left->src, ILEA, nto, nil, &slot);
+	p->m.offset = nto->ty->size;	/* for optimizer */
+
+	/*
+	 * number of senders and receivers
+	 */
+	off.val = 0;
+	genmove(src, Mas, tint, sumark(mkconst(src, 0)), &slot);
+	off.val += IBY2WD;
+	genmove(src, Mas, tint, sumark(mkconst(src, 1)), &slot);
+	off.val += IBY2WD;
+
+	p = genrawop(src, IALT, &tab, nil, &which);
+	p->m.offset = talt->size;	/* for optimizer */
+	tfree(&which);
+	tfree(&tab);
+}
+
+/*
+ * generate code to duplicate an adt with pick fields
+ * this is just a hacked up small pick
+ * n is Oind(exp)
+ */
+void
+pickdupcom(Src *src, Node *nto, Node *n)
+{
+	Node *start, *stop, *node, *orig, *dest, tmp, clab;
+	Case *c;
+	Inst *j, *jmps, *wild;
+	Label *labs;
+	Decl *d, *tg, *stg;
+	Type *t;
+	int i, nlab;
+	char buf[32];
+
+	if(n->op != Oind)
+		fatal("pickdupcom not Oind: %n" ,n);
+
+	t = n->ty;
+	nlab = t->decl->tag;
+
+	/*
+	 * generate global which has case labels
+	 */
+	seprint(buf, buf+sizeof(buf), ".c%d", nlabel++);
+	d = mkids(src, enter(buf, 0), mktype(&src->start, &src->stop, Tcase, nil, nil), nil);
+	d->init = mkdeclname(src, d);
+
+	clab.addable = Rmreg;
+	clab.left = nil;
+	clab.right = nil;
+	clab.op = Oname;
+	clab.ty = d->ty;
+	clab.decl = d;
+
+	/*
+	 * generate a temp to hold the real value
+	 * then generate a case on the tag
+	 */
+	orig = n->left;
+	talloc(&tmp, orig->ty, nil);
+	ecom(src, &tmp, orig);
+	orig = mkunary(Oind, &tmp);
+	orig->ty = tint;
+	sumark(orig);
+
+	dest = mkunary(Oind, nto);
+	dest->ty = nto->ty->tof;
+	sumark(dest);
+
+	genrawop(src, ICASE, orig, nil, &clab);
+
+	labs = allocmem(nlab * sizeof *labs);
+
+	i = 0;
+	jmps = nil;
+	for(tg = t->tags; tg != nil; tg = tg->next){
+		stg = tg;
+		for(; tg->next != nil; tg = tg->next)
+			if(stg->ty != tg->next->ty)
+				break;
+		start = sumark(simplify(mkdeclname(src, stg)));
+		stop = start;
+		node = start;
+		if(stg != tg){
+			stop = sumark(simplify(mkdeclname(src, tg)));
+			node = mkbin(Orange, start, stop);
+		}
+
+		labs[i].start = start;
+		labs[i].stop = stop;
+		labs[i].node = node;
+		labs[i++].inst = nextinst();
+
+		genrawop(src, INEW, mktn(tg->ty->tof), nil, nto);
+		genmove(src, Mas, tg->ty->tof, orig, dest);
+
+		j = genrawop(src, IJMP, nil, nil, nil);
+		j->branch = jmps;
+		jmps = j;
+	}
+
+	/*
+	 * this should really be a runtime error
+	 */
+	wild = genrawop(src, IJMP, nil, nil, nil);
+	patch(wild, wild);
+
+	patch(jmps, nextinst());
+	tfree(&tmp);
+
+	if(i > nlab)
+		fatal("overflowed label tab for pickdupcom");
+
+	c = allocmem(sizeof *c);
+	c->nlab = i;
+	c->nsnd = 0;
+	c->labs = labs;
+	c->iwild = wild;
+
+	d->ty->cse = c;
+	usetype(d->ty);
+	installids(Dglobal, d);
+}
+
+/*
+ * see if name n occurs anywhere in e
+ */
+int
+tupaliased(Node *n, Node *e)
+{
+	for(;;){
+		if(e == nil)
+			return 0;
+		if(e->op == Oname && e->decl == n->decl)
+			return 1;
+		if(tupaliased(n, e->left))
+			return 1;
+		e = e->right;
+	}
+}
+
+/*
+ * see if any name in n occurs anywere in e
+ */
+int
+tupsaliased(Node *n, Node *e)
+{
+	for(;;){
+		if(n == nil)
+			return 0;
+		if(n->op == Oname && tupaliased(n, e))
+			return 1;
+		if(tupsaliased(n->left, e))
+			return 1;
+		n = n->right;
+	}
+}
+
+/*
+ * put unaddressable constants in the global data area
+ */
+Decl*
+globalconst(Node *n)
+{
+	Decl *d;
+	Sym *s;
+	char buf[32];
+
+	seprint(buf, buf+sizeof(buf), ".i.%.8lux", (long)n->val);
+	s = enter(buf, 0);
+	d = s->decl;
+	if(d == nil){
+		d = mkids(&n->src, s, tint, nil);
+		installids(Dglobal, d);
+		d->init = n;
+		d->refs++;
+	}
+	return d;
+}
+
+Decl*
+globalBconst(Node *n)
+{
+	Decl *d;
+	Sym *s;
+	char buf[32];
+
+	seprint(buf, buf+sizeof(buf), ".B.%.8lux.%8lux", (long)(n->val>>32), (long)n->val);
+
+	s = enter(buf, 0);
+	d = s->decl;
+	if(d == nil){
+		d = mkids(&n->src, s, tbig, nil);
+		installids(Dglobal, d);
+		d->init = n;
+		d->refs++;
+	}
+	return d;
+}
+
+Decl*
+globalbconst(Node *n)
+{
+	Decl *d;
+	Sym *s;
+	char buf[32];
+
+	seprint(buf, buf+sizeof(buf), ".b.%.2lux", (long)n->val & 0xff);
+	s = enter(buf, 0);
+	d = s->decl;
+	if(d == nil){
+		d = mkids(&n->src, s, tbyte, nil);
+		installids(Dglobal, d);
+		d->init = n;
+		d->refs++;
+	}
+	return d;
+}
+
+Decl*
+globalfconst(Node *n)
+{
+	Decl *d;
+	Sym *s;
+	char buf[32];
+	ulong dv[2];
+
+	dtocanon(n->rval, dv);
+	seprint(buf, buf+sizeof(buf), ".f.%.8lux.%8lux", dv[0], dv[1]);
+	s = enter(buf, 0);
+	d = s->decl;
+	if(d == nil){
+		d = mkids(&n->src, s, treal, nil);
+		installids(Dglobal, d);
+		d->init = n;
+		d->refs++;
+	}
+	return d;
+}
+
+Decl*
+globalsconst(Node *n)
+{
+	Decl *d;
+	Sym *s;
+
+	s = n->decl->sym;
+	d = s->decl;
+	if(d == nil){
+		d = mkids(&n->src, s, tstring, nil);
+		installids(Dglobal, d);
+		d->init = n;
+	}
+	d->refs++;
+	return d;
+}
+
+static Node*
+subst(Decl *d, Node *e, Node *n)
+{
+	if(n == nil)
+		return nil;
+	if(n->op == Oname){
+		if(d == n->decl){
+			n = dupn(0, nil, e);
+			n->ty = d->ty;
+		}
+		return n;
+	}
+	n->left = subst(d, e, n->left);
+	n->right = subst(d, e, n->right);
+	return n;
+}
+
+static Node*
+putinline(Node *n)
+{
+	Node *e, *tn;
+	Type *t;
+	Decl *d;
+
+if(debug['z']) print("inline1: %n\n", n);
+	if(n->left->op == Oname)
+		d = n->left->decl;
+	else
+		d = n->left->right->decl;
+	e = d->init;
+	t = e->ty;
+	e = dupn(1, &n->src, e->right->left->left);
+	for(d = t->ids, n = n->right; d != nil && n != nil; d = d->next, n = n->right){
+		if(hasside(n->left, 0) && occurs(d, e) != 1){
+			tn = talloc(mkn(0, nil, nil), d->ty, nil);
+			e = mkbin(Ocomma, mkbin(Oas, tn, n->left), subst(d, tn, e));
+			e->ty = e->right->ty;
+			e->left->ty = d->ty;
+		}
+		else
+			e = subst(d, n->left, e);
+	}
+	if(d != nil || n != nil)
+		fatal("bad arg match in putinline()");
+if(debug['z']) print("inline2: %n\n", e);
+	return e;
+}
+
+static void
+fpcall(Src *src, int op, Node *n, Node *ret)
+{
+	Node tp, *e, *mod, *ind;
+
+	tp.decl = nil;
+	e = n->left->left;
+	if(e->addable >= Rcant)
+		e = eacom(e, &tp, nil);
+	mod = mkunary(Oind, e);
+	ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, IBY2WD)));
+	n->left = mkbin(Omdot, mod, ind);
+	n->left->ty = e->ty->tof;
+	mod->ty = ind->ty = ind->left->ty = ind->left->right->ty = tint;
+	sumark(n);
+	callcom(src, op, n, ret);
+	tfree(&tp);
+}
--- /dev/null
+++ b/limbo/fns.h
@@ -1,0 +1,391 @@
+int	addfile(File*);
+void	addfnptrs(Decl*, int);
+void	addiface(Decl*, Decl*);
+void	addinclude(char*);
+char	*addrprint(char*, char*, int, Addr*);
+Typelist	*addtype(Type*, Typelist*);
+Node	*adtdecl(Decl *ids, Node *fields);
+void	adtdecled(Node *n);
+void	adtdefd(Type*);
+Decl	*adtmeths(Type*);
+void	adtstub(Decl*);
+long	align(long, int);
+void	*allocmem(ulong);
+void	altcheck(Node *an, Type *ret);
+void	altcom(Node*);
+Inst	*andand(Node*, int, Inst*);
+Decl	*appdecls(Decl*, Decl*);
+int	argcompat(Node*, Decl*, Node*);
+void	arraycom(Node*, Node*);
+void	arraydefault(Node*, Node*);
+Type	*arrowtype(Type*, Decl*);
+void	asmdesc(Desc*);
+void	asmentry(Decl*);
+void	asmexc(Except*);
+void	asminitializer(long, Node*);
+void	asminst(Inst*);
+void	asmldt(long, Decl*);
+void	asmmod(Decl*);
+void	asmpath(void);
+void	asmstring(long, Sym*);
+void	asmvar(long, Decl*);
+int	assignindices(Node*);
+void	bccom(Node*, Inst**);
+Inst	*bcom(Node*, int, Inst*);
+void	bindnames(Node*);
+void	bindtypes(Type *t);
+Ok	callcast(Node*, int, int);
+void	callcom(Src*, int, Node*, Node*);
+Type*	calltype(Type*, Node*, Type*);
+double	canontod(ulong v[2]);
+void	casecheck(Node *cn, Type *ret);
+int	casecmp(Type*, Node*, Node*);
+void	casecom(Node*);
+Node	*caselist(Node*, Node*);
+void	casesort(Type*, Label*, Label*, int, int);
+Case	*checklabels(Node *inits, Type *ctype, int nlab, char *title);
+void	checkrefs(Decl*);
+Node	*checkused(Node*);
+int	circlval(Node*, Node*);
+void	concheck(Node *n, int isglobal);
+Node	*condecl(Decl*, Node*);
+void	condecled(Node *n);
+void	constub(Decl*);
+Type	*copytypeids(Type*);
+char	*ctprint(char*, char*, Type*);
+int	ctypeconv(Fmt*);
+Line	curline(void);
+Decl	*curscope(void);
+int	cycarc(Type*, Type*);
+void	cycfield(Type*, Decl*);
+void	cycsizetype(Type*);
+void	cyctype(Type*);
+int	dasdecl(Node *n);
+void	declaserr(Node*);
+int	declasinfer(Node*, Type*);
+int	declconv(Fmt*);
+Decl	*declsort(Decl*);
+void	declstart(void);
+void	decltozero(Node *n);
+void	deldecl(Decl*);
+int	dequal(Decl*, Decl*, int);
+long	descmap(Decl*, uchar*, long);
+void	disaddr(int, Addr*);
+void	disbcon(long);
+void	discon(long);
+void	disdata(int, long);
+void	disdesc(Desc*);
+void	disentry(Decl*);
+void	disexc(Except*);
+void	disinst(Inst*);
+void	disldt(long, Decl*);
+void	dismod(Decl*);
+void	dispath(void);
+void	disvar(long, Decl*);
+void	disword(long);
+int	dotconv(Fmt*);
+char	*dotprint(char*, char*, Decl*, int);
+Type	*dottype(Type*, Decl*);
+void	dtocanon(double, ulong[2]);
+Decl	*dupdecl(Decl*);
+Decl *dupdecls(Decl*);
+Node	*dupn(int, Src*, Node*);
+Node	*eacom(Node*, Node*, Node*);
+Ok	echeck(Node *n, int typeok, int isglobal, Node* par);
+Node	*ecom(Src*, Node*, Node*);
+Node	*efold(Node *n);
+Node	*elemsort(Node*);
+void	emit(Decl*);
+Decl	*encpolys(Decl*);
+Sym	*enter(char*, int);
+Desc	*enterdesc(uchar*, long, long);
+Sym	*enterstring(char*, int);
+char	*eprint(char*, char*, Node*);
+char	*eprintlist(char*, char*, Node*, char*);
+void	error(Line, char*, ...);
+#pragma	varargck	argpos	error	2
+int	etconv(Fmt*);
+Node	*etolist(Node*);
+void	excheck(Node *n, int isglobal);
+void	exccheck(Node *cn, Type *ret);
+void	excom(Node*);
+Node	*exdecl(Decl*, Decl*);
+void	exdecled(Node *n);
+Type	*expandtype(Type*, Type*, Decl*, Tpair**);
+Type	*expandtypes(Type*, Decl*);
+int	expconv(Fmt*);
+Type	*exptotype(Node*);
+void	fatal(char*, ...);
+#pragma	varargck	argpos	fatal	1
+void	fielddecled(Node *n);
+Node	*fielddecl(int store, Decl *ids);
+int	findlab(Type *ty, Node *v, Label *labs, int nlab);
+int	fixop(int, Type*, Type*, Type*, int*, int*);
+Fline	fline(int);
+void	fmtcheck(Node*, Node*, Node*);
+void	fncheck(Decl *d);
+Decl	*fnchk(Node *n);
+void	fncom(Decl*);
+Node	*fndecl(Node *n, Type *t, Node *body);
+void	fndecled(Node *n);
+Decl*	fnlookup(Sym*, Type*, Node**);
+Node	*fold(Node*);
+void	foldbranch(Inst*);
+Node	*foldc(Node*);
+Node	*foldcast(Node*, Node*);
+Node	*foldcasti(Node*, Node*);
+Node	*foldr(Node*);
+Node	*foldvc(Node*);
+void	gbind(Node *n);
+int	gcheck(Node*, Decl**, int);
+void	gdasdecl(Node *n);
+void	gdecl(Node *n);
+Addr	genaddr(Node*);
+Inst	*genbra(Src*, int, Node*, Node*);
+Inst	*genchan(Src*, Node*, Type*, Node*);
+Desc	*gendesc(Decl*, long, Decl*);
+Inst	*genfixcastop(Src*, int, Node*, Node*);
+Inst	*genmove(Src*, int, Type*, Node*, Node*);
+Inst	*genop(Src*, int, Node*, Node*, Node*);
+Inst	*genrawop(Src*, int, Node*, Node*, Node*);
+void	genstart(void);
+long	getpc(Inst*);
+int	gfltconv(Fmt*);
+Decl	*globalBconst(Node*);
+Node	*globalas(Node*, Node*, int);
+Decl	*globalbconst(Node*);
+Decl	*globalconst(Node*);
+Decl	*globalfconst(Node*);
+Decl	*globalsconst(Node*);
+void	gsort(Node*);
+int	hasasgns(Node*);
+int	hascall(Node*);
+Node	*hascomm(Node*);
+int	hasside(Node*, int);
+long	idindices(Decl*);
+long	idoffsets(Decl*, long, int);
+Type	*idtype(Type*);
+void	importcheck(Node *n, int isglobal);
+void	importchk(Node*);
+Node	*importdecl(Node *m, Decl *ids);
+void	importdecled(Node *n);
+void	includef(Sym*);
+Node	*indsascom(Src*, Node*, Node*);
+int	initable(Node*, Node*, int);
+int	inloop(void);
+void	installids(int store, Decl *ids);
+int	instconv(Fmt*);
+Type	*insttype(Type*, Decl*, Tpair**);
+Type	*internaltype(Type*);
+int	isimpmod(Sym*);
+int	islval(Node*);
+int	ispoly(Decl*);
+int	ispolyadt(Type*);
+int	istuple(Node*);
+void	joiniface(Type*, Type*);
+void	lexinit(void);
+void	lexstart(char*);
+int	lineconv(Fmt*);
+int	local(Decl*);
+Decl	*lookdot(Decl*, Sym*);
+Decl *lookup(Sym*);
+int	mapconv(Fmt*);
+int	marklval(Node*);
+int	mathchk(Node*, int);
+void	mergepolydecs(Type*);
+Type	*mkadtcon(Type*);
+Type	*mkadtpickcon(Type*, Type*);
+Type	*mkarrowtype(Line*, Line*, Type*, Sym*);
+Node	*mkbin(int, Node*, Node*);
+Node	*mkconst(Src*, Long);
+Decl	*mkdecl(Src*, int, Type*);
+Node	*mkdeclname(Src*, Decl*);
+Desc	*mkdesc(long, Decl*);
+Type	*mkdottype(Line*, Line*, Type*, Sym*);
+Type	*mkexbasetype(Type*);
+Type	*mkextuptype(Type*);
+Type	*mkextype(Type*);
+File	*mkfile(char*, int, int, int, char*, int, int);
+Decl	*mkids(Src*, Sym*, Type*, Decl*);
+Type	*mkidtype(Src*, Sym*);
+Type	*mkiface(Decl*);
+Inst	*mkinst(void);
+Type *mkinsttype(Src*, Type*, Typelist*);
+Node	*mkname(Src*, Sym*);
+Node	*mknil(Src*);
+Node	*mkn(int, Node*, Node*);
+Node	*mkrconst(Src*, Real);
+Node	*mksconst(Src*, Sym*);
+Node	*mkscope(Node *body);
+Type	*mktalt(Case*);
+Desc	*mktdesc(Type*);
+Node	*mktn(Type*);
+Type	*mktype(Line*, Line*, int, Type*, Decl*);
+Node	*mkunary(int, Node*);
+Type	*mkvarargs(Node*, Node*);
+Teq	*modclass(void);
+void	modcode(Decl*);
+void	modcom(Decl*);
+void	moddataref(void);
+Node	*moddecl(Decl *ids, Node *fields);
+void	moddecled(Node *n);
+Decl	*modglobals(Decl*, Decl*);
+Decl	*modimp(Dlist*, Decl*);
+void	modrefable(Type*);
+void	modresolve(void);
+void	modstub(Decl*);
+void	modtab(Decl*);
+Decl	*module(Decl*);
+int	mustzero(Decl *);
+int	mpatof(char*, double*);
+Decl	*namedot(Decl*, Sym*);
+Decl	*namesort(Decl*);
+void	narrowmods(void);
+void	nerror(Node*, char*, ...);
+#pragma	varargck	argpos	nerror	2
+int	nested(void);
+Inst	*nextinst(void);
+int	nodeconv(Fmt*);
+int	nodes(Node*);
+char	*nprint(char*, char*, Node*, int);
+void	nwarn(Node*, char*, ...);
+#pragma	varargck	argpos	nwarn	2
+int	occurs(Decl*, Node*);
+int	opconv(Fmt*);
+void	optabinit(void);
+void	optim(Inst*, Decl*);
+Inst	*oror(Node*, int, Inst*);
+Decl	*outerpolys(Node*);
+Node	*passfns(Src*, Decl*, Node*, Node*, Type*, Tpair*);
+Node	*passimplicit(Node*, Node*);
+void	patch(Inst*, Inst*);
+void	pickcheck(Node*, Type*);
+int	pickdecled(Node *n);
+Decl	*pickdefd(Type*, Decl*);
+void	pickdupcom(Src*, Node*, Node*);
+Decl*	polydecl(Decl*);
+int	polyequal(Decl*, Decl*);
+void	popblock(void);
+Decl	*popids(Decl*);
+void	popscopes(void);
+Decl	*popscope(void);
+void	printdecls(Decl*);
+int	pushblock(void);
+void	pushlabel(Node*);
+void	pushscope(Node *, int);
+void	raisescheck(Type*);
+int	raisescompat(Node*, Node*);
+void	reach(Inst*);
+void	*reallocmem(void*, ulong);
+void	recvacom(Src*, Node*, Node*);
+void	redecl(Decl *d);
+void	reftype(Type*);
+void	repushblock(int);
+void	repushids(Decl*);
+void	resizetype(Type*);
+long	resolvedesc(Decl*, long, Decl*);
+Decl* resolveldts(Decl*, Decl**);
+int	resolvemod(Decl*);
+long	resolvepcs(Inst*);
+Node	*retalloc(Node*, Node*);
+Decl	*revids(Decl*);
+Node	*rewrite(Node *n);
+Node	*rewritecomm(Node*, Node*, Node*, Node*);
+Inst	*rewritedestreg(Inst*, int, int);
+Inst	*rewritesrcreg(Inst*, int, int, int);
+void	rmfnptrs(Decl*);
+Node	*rotater(Node*);
+double	rpow(double, int);
+int	sameaddr(Node*, Node*);
+int	sametree(Node*, Node*);
+void	sblfiles(void);
+void	sblfn(Decl**, int);
+void	sblinst(Inst*, long);
+void	sblmod(Decl*);
+void	sblty(Decl**, int);
+void	sblvar(Decl*);
+double	scale(Type*);
+double	scale2(Type*, Type*);
+Node*	scheck(Node*, Type*, int);
+void	scom(Node*);
+char	*secpy(char*, char*, char*);
+char	*seprint(char*, char*, char*, ...);
+#pragma	varargck	argpos	seprint	3
+void	shareloc(Decl*);
+int	shiftchk(Node*);
+ulong	sign(Decl*);
+Node	*simplify(Node*);
+Szal	sizeids(Decl*, long);
+void	sizetype(Type*);
+Node	*slicelcom(Src*, Node*, Node*);
+int	specific(Type*);
+int	srcconv(Fmt*);
+char*	srcpath(char*, int);
+int	storeconv(Fmt*);
+char	*stprint(char*, char*, Type*);
+Sym	*stringcat(Sym*, Sym*);
+char	*stringpr(char*, char*, Sym*);
+Long	strtoi(char*, int);
+int	stypeconv(Fmt*);
+Node	*sumark(Node*);
+int	symcmp(Sym*, Sym*);
+Node	*tacquire(Node*);
+Ok	tagcast(Node*, Node*, Node*, Decl*, int, int);
+Node	*talloc(Node*, Type*, Node*);
+int	tcompat(Type*, Type*, int);
+void tcycle(Type*);
+Decl	*tdecls(void);
+long	tdescmap(Type*, uchar*, long);
+void	teqclass(Type*);
+int	tequal(Type*, Type*);
+void	tfree(Node*);
+void	tfreelater(Node*);
+void	tfreenow(void);
+void	tinit(void);
+int	tmustzero(Type *);
+Type	*toptype(Src*, Type*);
+Type	*topvartype(Type *t, Decl *id, int tyok, int polyok);
+Type*	tparent(Type*, Type*);
+char	*tprint(char*, char*, Type*);
+void	translate(char*, char*, char*);
+void	trelease(Node*);
+int	tunify(Type*, Type*, Tpair**);
+int	tupaliased(Node*, Node*);
+int	tupsaliased(Node*, Node*);
+void	tupcom(Node*, Node*);
+void	tuplcom(Node*, Node*);
+void	tuplrcom(Node*, Node*);
+Decl	*tuplefields(Node*);
+void	typebuiltin(Decl*, Type*);
+Decl	*typecheck(int);
+int	typeconv(Fmt*);
+Node	*typedecl(Decl *ids, Type *t);
+void	typedecled(Node *n);
+Decl	*typeids(Decl*, Type*);
+void	typeinit(void);
+void	typestart(void);
+Decl	*undefed(Src *src, Sym *s);
+Desc	*usedesc(Desc*);
+void	usedty(Type*);
+Type	*usetype(Type*);
+Type	*validtype(Type*, Decl*);
+int	valistype(Node*);
+Type	*valtmap(Type*, Tpair*);
+void	varcheck(Node *n, int isglobal);
+int	varcom(Decl*);
+Node	*vardecl(Decl*, Type*);
+void	vardecled(Node *n);
+Node	*varinit(Decl*, Node*);
+Decl	*varlistdecl(Decl*, Node*);
+Decl	*vars(Decl*);
+int	vcom(Decl*);
+Type	*verifytypes(Type*, Decl*, Decl*);
+void	warn(Line, char*, ...);
+#pragma	varargck	argpos	warn	2
+void	yyerror(char*, ...);
+#pragma	varargck	argpos	yyerror	1
+int	yylex(void);
+int	yyparse(void);
+void	zcom(Node *, Node**);
+void zcom0(Decl *, Node**);
+void zcom1(Node *, Node**);
--- /dev/null
+++ b/limbo/gen.c
@@ -1,0 +1,1096 @@
+#include "limbo.h"
+
+static	int	addrmode[Rend] =
+{
+	/* Rreg */	Afp,
+	/* Rmreg */	Amp,
+	/* Roff */	Aoff,
+	/* Rnoff */	Anoff,
+	/* Rdesc */	Adesc,
+	/* Rdescp */	Adesc,
+	/* Rconst */	Aimm,
+	/* Ralways */	Aerr,
+	/* Radr */	Afpind,
+	/* Rmadr */	Ampind,
+	/* Rcant */	Aerr,
+	/* Rpc */	Apc,
+	/* Rmpc */	Aerr,
+	/* Rareg */	Aerr,
+	/* Ramreg */	Aerr,
+	/* Raadr */	Aerr,
+	/* Ramadr */	Aerr,
+	/* Rldt */	Aldt,
+};
+
+static	Decl	*wtemp;
+static	Decl	*bigtemp;
+static	int	ntemp;
+static	Node	retnode;
+static	Inst	zinst;
+
+	int	*blockstack;
+	int	blockdep;
+	int	nblocks;
+static	int	lenblockstack;
+static	Node	*ntoz;
+
+static Inst* genfixop(Src *src, int op, Node *s, Node *m, Node *d);
+
+void
+genstart(void)
+{
+	Decl *d;
+
+	d = mkdecl(&nosrc, Dlocal, tint);
+	d->sym = enter(".ret", 0);
+	d->offset = IBY2WD * REGRET;
+
+	retnode = znode;
+	retnode.op = Oname;
+	retnode.addable = Rreg;
+	retnode.decl = d;
+	retnode.ty = tint;
+
+	zinst.op = INOP;
+	zinst.sm = Anone;
+	zinst.dm = Anone;
+	zinst.mm = Anone;
+
+	firstinst = allocmem(sizeof *firstinst);
+	*firstinst = zinst;
+	lastinst = firstinst;
+
+	blocks = -1;
+	blockdep = 0;
+	nblocks = 0;
+}
+
+/*
+ * manage nested control flow blocks
+ */
+int
+pushblock(void)
+{
+	if(blockdep >= lenblockstack){
+		lenblockstack = blockdep + 32;
+		blockstack = reallocmem(blockstack, lenblockstack * sizeof *blockstack);
+	}
+	blockstack[blockdep++] = blocks;
+	return blocks = nblocks++;
+}
+
+void
+repushblock(int b)
+{
+	blockstack[blockdep++] = blocks;
+	blocks = b;
+}
+
+void
+popblock(void)
+{
+	blocks = blockstack[blockdep -= 1];
+}
+
+void
+tinit(void)
+{
+	wtemp = nil;
+	bigtemp = nil;
+}
+
+Decl*
+tdecls(void)
+{
+	Decl *d;
+
+	for(d = wtemp; d != nil; d = d->next){
+		if(d->tref != 1)
+			fatal("temporary %s has %d references", d->sym->name, d->tref-1);
+	}
+
+	for(d = bigtemp; d != nil; d = d->next){
+		if(d->tref != 1)
+			fatal("temporary %s has %d references", d->sym->name, d->tref-1);
+	}
+
+	return appdecls(wtemp, bigtemp);
+}
+
+Node*
+talloc(Node *n, Type *t, Node *nok)
+{
+	Decl *d, *ok;
+	Desc *desc;
+	char buf[StrSize];
+
+	ok = nil;
+	if(nok != nil)
+		ok = nok->decl;
+	if(ok == nil || ok->tref == 0 || tattr[ok->ty->kind].big != tattr[t->kind].big || ok->ty->align != t->align)
+		ok = nil;
+	*n = znode;
+	n->op = Oname;
+	n->addable = Rreg;
+	n->ty = t;
+	if(tattr[t->kind].big){
+		desc = mktdesc(t);
+		if(ok != nil && ok->desc == desc){
+			ok->tref++;
+			ok->refs++;
+			n->decl = ok;
+			return n;
+		}
+		for(d = bigtemp; d != nil; d = d->next){
+			if(d->tref == 1 && d->desc == desc && d->ty->align == t->align){
+				d->tref++;
+				d->refs++;
+				n->decl = d;
+				return n;
+			}
+		}
+		d = mkdecl(&nosrc, Dlocal, t);
+		d->desc = desc;
+		d->tref = 2;
+		d->refs = 1;
+		n->decl = d;
+		seprint(buf, buf+sizeof(buf), ".b%d", ntemp++);
+		d->sym = enter(buf, 0);
+		d->next = bigtemp;
+		bigtemp = d;
+		return n;
+	}
+	if(ok != nil
+	&& tattr[ok->ty->kind].isptr == tattr[t->kind].isptr
+	&& ok->ty->size == t->size){
+		ok->tref++;
+		n->decl = ok;
+		return n;
+	}
+	for(d = wtemp; d != nil; d = d->next){
+		if(d->tref == 1
+		&& tattr[d->ty->kind].isptr == tattr[t->kind].isptr
+		&& d->ty->size == t->size
+		&& d->ty->align == t->align){
+			d->tref++;
+			n->decl = d;
+			return n;
+		}
+	}
+	d = mkdecl(&nosrc, Dlocal, t);
+	d->tref = 2;
+	d->refs = 1;
+	n->decl = d;
+	seprint(buf, buf+sizeof(buf), ".t%d", ntemp++);
+	d->sym = enter(buf, 0);
+	d->next = wtemp;
+	wtemp = d;
+	return n;
+}
+
+void
+tfree(Node *n)
+{
+	if(n == nil || n->decl == nil || n->decl->tref == 0)
+		return;
+	if(n->decl->tref == 1)
+		fatal("double free of temporary %s", n->decl->sym->name);
+	if (--n->decl->tref == 1)
+		zcom1(n, nil);
+}
+
+void
+tfreelater(Node *n)
+{
+	if(n == nil || n->decl == nil || n->decl->tref == 0)
+		return;
+	if(n->decl->tref == 1)
+		fatal("double free of temporary %s", n->decl->sym->name);
+	if(--n->decl->tref == 1){
+		Node *nn = mkn(Oname, nil, nil);
+
+		*nn = *n;
+		nn->left = ntoz;
+		ntoz = nn;
+		n->decl->tref++;
+	}
+}
+
+void
+tfreenow()
+{
+	Node *n, *nn;
+
+	for(n = ntoz; n != nil; n = nn){
+		nn = n->left;
+		n->left = nil;
+		if(n->decl->tref != 2)
+			fatal("bad free of temporary %s", n->decl->sym->name);
+		--n->decl->tref;
+		zcom1(n, nil);
+	}
+	ntoz = nil;
+}
+
+/*
+ * realloc a temporary after it's been freed
+ */
+Node*
+tacquire(Node *n)
+{
+	if(n == nil || n->decl == nil || n->decl->tref == 0)
+		return n;
+/*
+	if(n->decl->tref != 1)
+		fatal("tacquire ref != 1: %d", n->decl->tref);
+*/
+	n->decl->tref++;
+	return n;
+}
+
+void
+trelease(Node *n)
+{
+	if(n == nil || n->decl == nil || n->decl->tref == 0)
+		return;
+	if(n->decl->tref == 1)
+		fatal("double release of temporary %s", n->decl->sym->name);
+	n->decl->tref--;
+}
+
+Inst*
+mkinst(void)
+{
+	Inst *in;
+
+	in = lastinst->next;
+	if(in == nil){
+		in = allocmem(sizeof *in);
+		*in = zinst;
+		lastinst->next = in;
+	}
+	lastinst = in;
+	in->block = blocks;
+	if(blocks < 0)
+		fatal("mkinst no block");
+	return in;
+}
+
+Inst*
+nextinst(void)
+{
+	Inst *in;
+
+	in = lastinst->next;
+	if(in != nil)
+		return in;
+	in = allocmem(sizeof(*in));
+	*in = zinst;
+	lastinst->next = in;
+	return in;
+}
+
+/*
+ * allocate a node for returning
+ */
+Node*
+retalloc(Node *n, Node *nn)
+{
+	if(nn->ty == tnone)
+		return nil;
+	*n = znode;
+	n->op = Oind;
+	n->addable = Radr;
+	n->left = dupn(1, &n->src, &retnode);
+	n->ty = nn->ty;
+	return n;
+}
+
+Inst*
+genrawop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+	Inst *in;
+
+	in = mkinst();
+	in->op = op;
+	in->src = *src;
+if(in->sm != Anone || in->mm != Anone || in->dm != Anone)
+fatal("bogus mkinst in genrawop: %I\n", in);
+	if(s != nil){
+		in->s = genaddr(s);
+		in->sm = addrmode[s->addable];
+	}
+	if(m != nil){
+		in->m = genaddr(m);
+		in->mm = addrmode[m->addable];
+		if(in->mm == Ampind || in->mm == Afpind)
+			fatal("illegal addressing mode in register %n", m);
+	}
+	if(d != nil){
+		in->d = genaddr(d);
+		in->dm = addrmode[d->addable];
+	}
+	return in;
+}
+
+Inst*
+genop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+	Inst *in;
+	int iop;
+
+	iop = disoptab[op][opind[d->ty->kind]];
+	if(iop == 0)
+		fatal("can't deal with op %s on %n %n %n in genop", opname[op], s, m, d);
+	if(iop == IMULX || iop == IDIVX)
+		return genfixop(src, iop, s, m, d);
+	in = mkinst();
+	in->op = iop;
+	in->src = *src;
+	if(s != nil){
+		in->s = genaddr(s);
+		in->sm = addrmode[s->addable];
+	}
+	if(m != nil){
+		in->m = genaddr(m);
+		in->mm = addrmode[m->addable];
+		if(in->mm == Ampind || in->mm == Afpind)
+			fatal("illegal addressing mode in register %n", m);
+	}
+	if(d != nil){
+		in->d = genaddr(d);
+		in->dm = addrmode[d->addable];
+	}
+	return in;
+}
+
+Inst*
+genbra(Src *src, int op, Node *s, Node *m)
+{
+	Type *t;
+	Inst *in;
+	int iop;
+
+	t = s->ty;
+	if(t == tany)
+		t = m->ty;
+	iop = disoptab[op][opind[t->kind]];
+	if(iop == 0)
+		fatal("can't deal with op %s on %n %n in genbra", opname[op], s, m);
+	in = mkinst();
+	in->op = iop;
+	in->src = *src;
+	if(s != nil){
+		in->s = genaddr(s);
+		in->sm = addrmode[s->addable];
+	}
+	if(m != nil){
+		in->m = genaddr(m);
+		in->mm = addrmode[m->addable];
+		if(in->mm == Ampind || in->mm == Afpind)
+			fatal("illegal addressing mode in register %n", m);
+	}
+	return in;
+}
+
+Inst*
+genchan(Src *src, Node *sz, Type *mt, Node *d)
+{
+	Inst *in;
+	Desc *td;
+	Addr reg;
+	int op, regm;
+
+	regm = Anone;
+	reg.decl = nil;
+	reg.reg = 0;
+	reg.offset = 0;
+	op = chantab[mt->kind];
+	if(op == 0)
+		fatal("can't deal with op %d in genchan", mt->kind);
+
+	switch(mt->kind){
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+		td = mktdesc(mt);
+		if(td->nmap != 0){
+			op++;		/* sleazy */
+			usedesc(td);
+			regm = Adesc;
+			reg.decl = mt->decl;
+		}else{
+			regm = Aimm;
+			reg.offset = mt->size;
+		}
+		break;
+	}
+	in = mkinst();
+	in->op = op;
+	in->src = *src;
+	in->s = reg;
+	in->sm = regm;
+	if(sz != nil){
+		in->m = genaddr(sz);
+		in->mm = addrmode[sz->addable];
+	}
+	if(d != nil){
+		in->d = genaddr(d);
+		in->dm = addrmode[d->addable];
+	}
+	return in;
+}
+
+Inst*
+genmove(Src *src, int how, Type *mt, Node *s, Node *d)
+{
+	Inst *in;
+	Desc *td;
+	Addr reg;
+	int op, regm;
+
+	regm = Anone;
+	reg.decl = nil;
+	reg.reg = 0;
+	reg.offset = 0;
+	op = movetab[how][mt->kind];
+	if(op == 0)
+		fatal("can't deal with op %d on %n %n in genmove", how, s, d);
+
+	switch(mt->kind){
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+	case Texception:
+		if(mt->size == 0 && how == Mas)
+			return nil;
+		td = mktdesc(mt);
+		if(td->nmap != 0){
+			op++;		/* sleazy */
+			usedesc(td);
+			regm = Adesc;
+			reg.decl = mt->decl;
+		}else{
+			regm = Aimm;
+			reg.offset = mt->size;
+		}
+		break;
+	}
+	in = mkinst();
+	in->op = op;
+	in->src = *src;
+	if(s != nil){
+		in->s = genaddr(s);
+		in->sm = addrmode[s->addable];
+	}
+	in->m = reg;
+	in->mm = regm;
+	if(d != nil){
+		in->d = genaddr(d);
+		in->dm = addrmode[d->addable];
+	}
+	if(s->addable == Rpc)
+		in->op = IMOVPC;
+	return in;
+}
+
+void
+patch(Inst *b, Inst *dst)
+{
+	Inst *n;
+
+	for(; b != nil; b = n){
+		n = b->branch;
+		b->branch = dst;
+	}
+}
+
+long
+getpc(Inst *i)
+{
+	if(i->pc == 0 && i != firstinst && (firstinst->op != INOOP || i != firstinst->next)){
+		do
+			i = i->next;
+		while(i != nil && i->pc == 0);
+		if(i == nil || i->pc == 0)
+			fatal("bad instruction in getpc");
+	}
+	return i->pc;
+}
+
+/*
+ * follow all possible paths from n,
+ * marking reached code, compressing branches, and reclaiming unreached insts
+ */
+void
+reach(Inst *in)
+{
+	Inst *last;
+
+	foldbranch(in);
+	last = in;
+	for(in = in->next; in != nil; in = in->next){
+		if(!in->reach)
+			last->next = in->next;
+		else
+			last = in;
+	}
+	lastinst = last;
+}
+
+/*
+ * follow all possible paths from n,
+ * marking reached code, compressing branches, and eliminating tail recursion
+ */
+void
+foldbranch(Inst *in)
+{
+	Inst *b, *next;
+	Label *lab;
+	int i, n;
+
+	while(in != nil && !in->reach){
+		in->reach = 1;
+		if(in->branch != nil)
+			while(in->branch->op == IJMP){
+				if(in == in->branch || in->branch == in->branch->branch)
+					break;
+				in->branch = in->branch->branch;
+			}
+		switch(in->op){
+		case IGOTO:
+		case ICASE:
+		case ICASEL:
+		case ICASEC:
+		case IEXC:
+			foldbranch(in->d.decl->ty->cse->iwild);
+			lab = in->d.decl->ty->cse->labs;
+			n = in->d.decl->ty->cse->nlab;
+			for(i = 0; i < n; i++)
+				foldbranch(lab[i].inst);
+			if(in->op == IEXC)
+				in->op = INOOP;
+			return;
+		case IEXC0:
+			foldbranch(in->branch);
+			in->op = INOOP;
+			break;
+		case IRET:
+		case IEXIT:
+		case IRAISE:
+			return;
+		case IJMP:
+			b = in->branch;
+			switch(b->op){
+			case ICASE:
+			case ICASEL:
+			case ICASEC:
+			case IRET:
+			case IEXIT:
+				next = in->next;
+				*in = *b;
+				in->next = next;
+				continue;
+			}
+			foldbranch(b);
+			return;
+		default:
+			if(in->branch != nil)
+				foldbranch(in->branch);
+			break;
+		}
+
+		in = in->next;
+	}
+}
+
+/*
+ * convert the addressable node into an operand
+ * see the comment for sumark
+ */
+Addr
+genaddr(Node *n)
+{
+	Addr a;
+
+	a.reg = 0;
+	a.offset = 0;
+	a.decl = nil;
+	if(n == nil)
+		return a;
+	switch(n->addable){
+	case Rreg:
+		if(n->decl != nil)
+			a.decl = n->decl;
+		else
+			a = genaddr(n->left);
+		break;
+	case Rmreg:
+		if(n->decl != nil)
+			a.decl = n->decl;
+		else
+			a = genaddr(n->left);
+		break;
+	case Rdesc:
+		a.decl = n->ty->decl;
+		break;
+	case Roff:
+	case Rnoff:
+		a.decl = n->decl;
+		break;
+	case Rconst:
+		a.offset = n->val;
+		break;
+	case Radr:
+		a = genaddr(n->left);
+		break;
+	case Rmadr:
+		a = genaddr(n->left);
+		break;
+	case Rareg:
+	case Ramreg:
+		a = genaddr(n->left);
+		if(n->op == Oadd)
+			a.reg += n->right->val;
+		break;
+	case Raadr:
+	case Ramadr:
+		a = genaddr(n->left);
+		if(n->op == Oadd)
+			a.offset += n->right->val;
+		break;
+	case Rldt:
+		a.decl = n->decl;
+		break;
+	case Rdescp:
+	case Rpc:
+		a.decl = n->decl;
+		break;
+	default:
+		fatal("can't deal with %n in genaddr", n);
+		break;
+	}
+	return a;
+}
+
+int
+sameaddr(Node *n, Node *m)
+{
+	Addr a, b;
+
+	if(n->addable != m->addable)
+		return 0;
+	a = genaddr(n);
+	b = genaddr(m);
+	return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl;
+}
+
+long
+resolvedesc(Decl *mod, long length, Decl *decls)
+{
+	Desc *g, *d, *last;
+	int descid;
+
+	g = gendesc(mod, length, decls);
+	g->used = 0;
+	last = nil;
+	for(d = descriptors; d != nil; d = d->next){
+		if(!d->used){
+			if(last != nil)
+				last->next = d->next;
+			else
+				descriptors = d->next;
+			continue;
+		}
+		last = d;
+	}
+
+	g->next = descriptors;
+	descriptors = g;
+
+	descid = 0;
+	for(d = descriptors; d != nil; d = d->next)
+		d->id = descid++;
+	if(g->id != 0)
+		fatal("bad global descriptor id");
+
+	return descid;
+}
+
+int
+resolvemod(Decl *m)
+{
+	Decl *id, *d;
+
+	for(id = m->ty->ids; id != nil; id = id->next){
+		switch(id->store){
+		case Dfn:
+			id->iface->pc = id->pc;
+			id->iface->desc = id->desc;
+if(debug['v']) print("R1: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
+			break;
+		case Dtype:
+			if(id->ty->kind != Tadt)
+				break;
+			for(d = id->ty->ids; d != nil; d = d->next){
+				if(d->store == Dfn){
+					d->iface->pc = d->pc;
+					d->iface->desc = d->desc;
+if(debug['v']) print("R2: %s %p %p %p\n", d->sym->name, d, d->iface, d->pc);
+				}
+			}
+			break;
+		}
+	}
+	/* for addiface */
+	for(id = m->ty->tof->ids; id != nil; id = id->next){
+		if(id->store == Dfn){
+			if(id->pc == nil)
+				id->pc = id->iface->pc;
+			if(id->desc == nil)
+				id->desc = id->iface->desc;
+if(debug['v']) print("R3: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
+		}
+	}
+	return m->ty->tof->decl->init->val;
+}
+
+/*
+ * place the Tiface decs in another list
+ */
+Decl*
+resolveldts(Decl *d, Decl **dd)
+{
+	Decl *d1, *ld1, *d2, *ld2, *n;
+
+	d1 = d2 = nil;
+	ld1 = ld2 = nil;
+	for( ; d != nil; d = n){
+		n = d->next;
+		d->next = nil;
+		if(d->ty->kind == Tiface){
+			if(d2 == nil)
+				d2 = d;
+			else
+				ld2->next = d;
+			ld2 = d;
+		}
+		else{
+			if(d1 == nil)
+				d1 = d;
+			else
+				ld1->next = d;
+			ld1 = d;
+		}
+	}
+	*dd = d2;
+	return d1;
+}
+
+/*
+ * fix up all pc's
+ * finalize all data offsets
+ * fix up instructions with offsets too large
+ */
+long
+resolvepcs(Inst *inst)
+{
+	Decl *d;
+	Inst *in;
+	int op;
+	ulong r, off;
+	long v, pc;
+
+	pc = 0;
+	for(in = inst; in != nil; in = in->next){
+		if(!in->reach || in->op == INOP)
+			fatal("unreachable pc: %I %ld", in, pc);
+		if(in->op == INOOP){
+			in->pc = pc;
+			continue;
+		}
+		d = in->s.decl;
+		if(d != nil){
+			if(in->sm == Adesc){
+				if(d->desc != nil)
+					in->s.offset = d->desc->id;
+			}else
+				in->s.reg += d->offset;
+		}
+		r = in->s.reg;
+		off = in->s.offset;
+		if((in->sm == Afpind || in->sm == Ampind)
+		&& (r >= MaxReg || off >= MaxReg))
+			fatal("big offset in %I\n", in);
+
+		d = in->m.decl;
+		if(d != nil){
+			if(in->mm == Adesc){
+				if(d->desc != nil)
+					in->m.offset = d->desc->id;
+			}else
+				in->m.reg += d->offset;
+		}
+		v = 0;
+		switch(in->mm){
+		case Anone:
+			break;
+		case Aimm:
+		case Apc:
+		case Adesc:
+			v = in->m.offset;
+			break;
+		case Aoff:
+		case Anoff:
+			v = in->m.decl->iface->offset;
+			break;
+		case Afp:
+		case Amp:
+		case Aldt:
+			v = in->m.reg;
+			if(v < 0)
+				v = 0x8000;
+			break;
+
+		default:
+			fatal("can't deal with %I's m mode\n", in);
+			break;
+		}
+		if(v > 0x7fff || v < -0x8000){
+			switch(in->op){
+			case IALT:
+			case IINDX:
+warn(in->src.start, "possible bug: temp m too big in %I: %ld %ld %d\n", in, in->m.reg, in->m.reg, MaxReg);
+				rewritedestreg(in, IMOVW, RTemp);
+				break;
+			default:
+				op = IMOVW;
+				if(isbyteinst[in->op])
+					op = IMOVB;
+				in = rewritesrcreg(in, op, RTemp, pc++);
+				break;
+			}
+		}
+
+		d = in->d.decl;
+		if(d != nil){
+			if(in->dm == Apc)
+				in->d.offset = d->pc->pc;
+			else
+				in->d.reg += d->offset;
+		}
+		r = in->d.reg;
+		off = in->d.offset;
+		if((in->dm == Afpind || in->dm == Ampind)
+		&& (r >= MaxReg || off >= MaxReg))
+			fatal("big offset in %I\n", in);
+
+		in->pc = pc;
+		pc++;
+	}
+	for(in = inst; in != nil; in = in->next){
+		d = in->s.decl;
+		if(d != nil && in->sm == Apc)
+			in->s.offset = d->pc->pc;
+		d = in->d.decl;
+		if(d != nil && in->dm == Apc)
+			in->d.offset = d->pc->pc;
+		if(in->branch != nil){
+			in->dm = Apc;
+			in->d.offset = in->branch->pc;
+		}
+	}
+	return pc;
+}
+
+/*
+ * fixp up a big register constant uses as a source
+ * ugly: smashes the instruction
+ */
+Inst*
+rewritesrcreg(Inst *in, int op, int treg, int pc)
+{
+	Inst *new;
+	Addr a;
+	int am;
+
+	a = in->m;
+	am = in->mm;
+	in->mm = Afp;
+	in->m.reg = treg;
+	in->m.decl = nil;
+
+	new = allocmem(sizeof(*in));
+	*new = *in;
+
+	*in = zinst;
+	in->src = new->src;
+	in->next = new;
+	in->op = op;
+	in->s = a;
+	in->sm = am;
+	in->dm = Afp;
+	in->d.reg = treg;
+	in->pc = pc;
+	in->reach = 1;
+	in->block = new->block;
+	return new;
+}
+
+/*
+ * fix up a big register constant by moving to the destination
+ * after the instruction completes
+ */
+Inst*
+rewritedestreg(Inst *in, int op, int treg)
+{
+	Inst *n;
+
+	n = allocmem(sizeof(*n));
+	*n = zinst;
+	n->next = in->next;
+	in->next = n;
+	n->src = in->src;
+	n->op = op;
+	n->sm = Afp;
+	n->s.reg = treg;
+	n->d = in->m;
+	n->dm = in->mm;
+	n->reach = 1;
+	n->block = in->block;
+
+	in->mm = Afp;
+	in->m.reg = treg;
+	in->m.decl = nil;
+
+	return n;
+}
+
+int
+instconv(Fmt *f)
+{
+	Inst *in;
+	char buf[512], *p;
+	char *op, *comma;
+
+	in = va_arg(f->args, Inst*);
+	op = nil;
+	if(in->op < MAXDIS)
+		op = instname[in->op];
+	if(op == nil)
+		op = "??";
+	buf[0] = '\0';
+	if(in->op == INOP)
+		return fmtstrcpy(f, "\tnop");
+	p = seprint(buf, buf + sizeof(buf), "\t%s\t", op);
+	comma = "";
+	if(in->sm != Anone){
+		p = addrprint(p, buf + sizeof(buf), in->sm, &in->s);
+		comma = ",";
+	}
+	if(in->mm != Anone){
+		p = seprint(p, buf + sizeof(buf), "%s", comma);
+		p = addrprint(p, buf + sizeof(buf), in->mm, &in->m);
+		comma = ",";
+	}
+	if(in->dm != Anone){
+		p = seprint(p, buf + sizeof(buf), "%s", comma);
+		p = addrprint(p, buf + sizeof(buf), in->dm, &in->d);
+	}
+	
+	if(asmsym && in->s.decl != nil && in->sm == Adesc)
+		p = seprint(p, buf+sizeof(buf), "	#%D", in->s.decl);
+	if(0 && asmsym && in->m.decl != nil)
+		p = seprint(p, buf+sizeof(buf), "	#%D", in->m.decl);
+	if(asmsym && in->d.decl != nil && in->dm == Apc)
+		p = seprint(p, buf+sizeof(buf), "	#%D", in->d.decl);
+	if(asmsym)
+		p = seprint(p, buf+sizeof(buf), "	#%U", in->src);
+	USED(p);
+	return fmtstrcpy(f, buf);
+}
+
+char*
+addrprint(char *buf, char *end, int am, Addr *a)
+{
+	switch(am){
+	case Anone:
+		return buf;
+	case Aimm:
+	case Apc:
+	case Adesc:
+		return seprint(buf, end, "$%ld", a->offset);
+	case Aoff:
+		return seprint(buf, end, "$%ld", a->decl->iface->offset);
+	case Anoff:
+		return seprint(buf, end, "-$%ld", a->decl->iface->offset);
+	case Afp:
+		return seprint(buf, end, "%ld(fp)", a->reg);
+	case Afpind:
+		return seprint(buf, end, "%ld(%ld(fp))", a->offset, a->reg);
+	case Amp:
+		return seprint(buf, end, "%ld(mp)", a->reg);
+	case Ampind:
+		return seprint(buf, end, "%ld(%ld(mp))", a->offset, a->reg);
+	case Aldt:
+		return seprint(buf, end, "$%ld", a->reg);
+	case Aerr:
+	default:
+		return seprint(buf, end, "%ld(%ld(?%d?))", a->offset, a->reg, am);
+	}
+}
+
+static void
+genstore(Src *src, Node *n, int offset)
+{
+	Decl *de;
+	Node d;
+
+	de = mkdecl(&nosrc, Dlocal, tint);
+	de->sym = nil;
+	de->offset = offset;
+
+	d = znode;
+	d.op = Oname;
+	d.addable = Rreg;
+	d.decl = de;
+	d.ty = tint;
+	genrawop(src, IMOVW, n, nil, &d);
+}
+
+static Inst*
+genfixop(Src *src, int op, Node *s, Node *m, Node *d)
+{
+	int p, a;
+	Node *mm;
+	Inst *i;
+
+	mm = m ? m: d;
+	op = fixop(op, mm->ty, s->ty, d->ty, &p, &a);
+	if(op == IMOVW){	/* just zero d */
+		s = sumark(mkconst(src, 0));
+		return genrawop(src, op, s, nil, d);
+	}
+	if(op != IMULX && op != IDIVX)
+		genstore(src, sumark(mkconst(src, a)), STemp);
+	genstore(src, sumark(mkconst(src, p)), DTemp);
+	i =  genrawop(src, op, s, m, d);
+	return i;
+}
+
+Inst*
+genfixcastop(Src *src, int op, Node *s, Node *d)
+{
+	int p, a;
+	Node *m;
+
+	op = fixop(op, s->ty, tint, d->ty, &p, &a);
+	if(op == IMOVW){	/* just zero d */
+		s = sumark(mkconst(src, 0));
+		return genrawop(src, op, s, nil, d);
+	}
+	m = sumark(mkconst(src, p));
+	if(op != ICVTXX)
+		genstore(src, sumark(mkconst(src, a)), STemp);
+	return genrawop(src, op, s, m, d);
+}
--- /dev/null
+++ b/limbo/lex.c
@@ -1,0 +1,1453 @@
+#define Extern
+#include "limbo.h"
+#include "y.tab.h"
+
+enum
+{
+	Leof		= -1,
+	Linestart	= 0,
+
+	Mlower		= 1,
+	Mupper		= 2,
+	Munder		= 4,
+	Malpha		= Mupper|Mlower|Munder,
+	Mdigit		= 8,
+	Msign		= 16,
+	Mexp		= 32,
+	Mhex		= 64,
+	Mradix		= 128,
+
+	HashSize	= 1024,
+	MaxPath		= 4096
+};
+
+typedef	struct Keywd	Keywd;
+struct	Keywd
+{
+	char	*name;
+	int	token;
+};
+
+	File	**files;			/* files making up the module, sorted by absolute line */
+	int	nfiles;
+static	int	lenfiles;
+static	int	lastfile;			/* index of last file looked up */
+
+static	char	*incpath[MaxIncPath];
+static	Sym	*symbols[HashSize];
+static	Sym	*strings[HashSize];
+static	char	map[256];
+static	Biobuf	*bin;
+static	Line	linestack[MaxInclude];
+static	int	lineno;
+static	int	linepos;
+static	int	bstack;
+static	int	ineof;
+static	int	lasttok;
+static	YYSTYPE	lastyylval;
+static	char	srcdir[MaxPath];
+
+static	Keywd	keywords[] =
+{
+	"adt",		Ladt,
+	"alt",		Lalt,
+	"array",	Larray,
+	"big",		Ltid,
+	"break",	Lbreak,
+	"byte",		Ltid,
+	"case",		Lcase,
+	"chan",		Lchan,
+	"con",		Lcon,
+	"continue",	Lcont,
+	"cyclic",	Lcyclic,
+	"do",		Ldo,
+	"dynamic",	Ldynamic,
+	"else",		Lelse,
+	"exception",	Lexcept,
+	"exit",		Lexit,
+	"fixed",	Lfix,
+	"fn",		Lfn,
+	"for",		Lfor,
+	"hd",		Lhd,
+	"if",		Lif,
+	"implement",	Limplement,
+	"import",	Limport,
+	"include",	Linclude,
+	"int",		Ltid,
+	"len",		Llen,
+	"list",		Llist,
+	"load",		Lload,
+	"module",	Lmodule,
+	"nil",		Lnil,
+	"of",		Lof,
+	"or",		Lor,
+	"pick",		Lpick,
+	"raise",	Lraise,
+	"raises",	Lraises,
+	"real",		Ltid,
+	"ref",		Lref,
+	"return",	Lreturn,
+	"self",		Lself,
+	"spawn",	Lspawn,
+	"string",	Ltid,
+	"tagof",	Ltagof,
+	"tl",		Ltl,
+	"to",		Lto,
+	"type",		Ltype,
+	"while",	Lwhile,
+	0,
+};
+
+static	Keywd	tokwords[] =
+{
+	"&=",	Landeq,
+	"|=",	Loreq,
+	"^=",	Lxoreq,
+	"<<=",	Llsheq,
+	">>=",	Lrsheq,
+	"+=",	Laddeq,
+	"-=",	Lsubeq,
+	"*=",	Lmuleq,
+	"/=",	Ldiveq,
+	"%=",	Lmodeq,
+	"**=", Lexpeq,
+	":=",	Ldeclas,
+	"||",	Loror,
+	"&&",	Landand,
+	"::",	Lcons,
+	"==",	Leq,
+	"!=",	Lneq,
+	"<=",	Lleq,
+	">=",	Lgeq,
+	"<<",	Llsh,
+	">>",	Lrsh,
+	"<-",	Lcomm,
+	"++", Linc,
+	"--",	Ldec,
+	"->", Lmdot,
+	"=>", Llabs,
+	"**", Lexp,
+	"EOF",	Leof,
+	"eof",	Beof,
+	0,
+};
+
+void
+lexinit(void)
+{
+	Keywd *k;
+	int i;
+
+	for(i = 0; i < 256; i++){
+		if(i == '_' || i > 0xa0)
+			map[i] |= Munder;
+		if(i >= 'A' && i <= 'Z')
+			map[i] |= Mupper;
+		if(i >= 'a' && i <= 'z')
+			map[i] |= Mlower;
+		if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f')
+			map[i] |= Mhex;
+		if(i == 'e' || i == 'E')
+			map[i] |= Mexp;
+		if(i == 'r' || i == 'R')
+			map[i] |= Mradix;
+		if(i == '-' || i == '+')
+			map[i] |= Msign;
+		if(i >= '0' && i <= '9')
+			map[i] |= Mdigit;
+	}
+
+	memset(escmap, -1, sizeof(escmap));
+	escmap['\''] = '\'';
+	unescmap['\''] = '\'';
+	escmap['"'] = '"';
+	unescmap['"'] = '"';
+	escmap['\\'] = '\\';
+	unescmap['\\'] = '\\';
+	escmap['a'] = '\a';
+	unescmap['\a'] = 'a';
+	escmap['b'] = '\b';
+	unescmap['\b'] = 'b';
+	escmap['f'] = '\f';
+	unescmap['\f'] = 'f';
+	escmap['n'] = '\n';
+	unescmap['\n'] = 'n';
+	escmap['r'] = '\r';
+	unescmap['\r'] = 'r';
+	escmap['t'] = '\t';
+	unescmap['\t'] = 't';
+	escmap['v'] = '\v';
+	unescmap['\v'] = 'v';
+	escmap['0'] = '\0';
+	unescmap['\0'] = '0';
+
+	for(k = keywords; k->name != nil; k++)
+		enter(k->name, k->token);
+}
+
+int
+cmap(int c)
+{
+	if(c<0)
+		return 0;
+	if(c<256)
+		return map[c];
+	return Mlower;
+}
+
+void
+lexstart(char *in)
+{
+	char *p;
+
+	ineof = 0;
+	bstack = 0;
+	nfiles = 0;
+	lastfile = 0;
+	addfile(mkfile(strdup(in), 1, 0, -1, nil, 0, -1));
+	bin = bins[bstack];
+	lineno = 1;
+	linepos = Linestart;
+
+	secpy(srcdir, srcdir+MaxPath, in);
+	p = strrchr(srcdir, '/');
+	if(p == nil)
+		srcdir[0] = '\0';
+	else
+		p[1] = '\0';
+}
+
+static int
+Getc(void)
+{
+	int c;
+
+	if(ineof)
+		return Beof;
+	c = BGETC(bin);
+	if(c == Beof)
+		ineof = 1;
+	linepos++;
+	return c;
+}
+
+static void
+unGetc(void)
+{
+	if(ineof)
+		return;
+	Bungetc(bin);
+	linepos--;
+}
+
+static int
+getrune(void)
+{
+	int c;
+
+	if(ineof)
+		return Beof;
+	c = Bgetrune(bin);
+	if(c == Beof)
+		ineof = 1;
+	linepos++;
+	return c;
+}
+
+static void
+ungetrune(void)
+{
+	if(ineof)
+		return;
+	Bungetrune(bin);
+	linepos--;
+}
+
+void
+addinclude(char *s)
+{
+	int i;
+
+	for(i = 0; i < MaxIncPath; i++){
+		if(incpath[i] == 0){
+			incpath[i] = s;
+			return;
+		}
+	}
+	fatal("out of include path space");
+}
+
+File*
+mkfile(char *name, int abs, int off, int in, char *act, int actoff, int sbl)
+{
+	File *f;
+
+	f = allocmem(sizeof *f);
+	f->name = name;
+	f->abs = abs;
+	f->off = off;
+	f->in = in;
+	f->act = act;
+	f->actoff = actoff;
+	f->sbl = sbl;
+	return f;
+}
+
+int
+addfile(File *f)
+{
+	if(nfiles >= lenfiles){
+		lenfiles = nfiles+32;
+		files = reallocmem(files, lenfiles*sizeof(File*));
+	}
+	files[nfiles] = f;
+	return nfiles++;
+}
+
+void
+includef(Sym *file)
+{
+	Biobuf *b;
+	char *p, buf[MaxPath];
+	int i;
+
+	linestack[bstack].line = lineno;
+	linestack[bstack].pos = linepos;
+	bstack++;
+	if(bstack >= MaxInclude)
+		fatal("%L: include file depth too great", curline());
+	p = "";
+	if(file->name[0] != '/')
+		p = srcdir;
+	seprint(buf, buf+sizeof(buf), "%s%s", p, file->name);
+	b = Bopen(buf, OREAD);
+	for(i = 0; b == nil && i < MaxIncPath && incpath[i] != nil && file->name[0] != '/'; i++){
+		seprint(buf, buf+sizeof(buf), "%s/%s", incpath[i], file->name);
+		b = Bopen(buf, OREAD);
+	}
+	bins[bstack] = b;
+	if(bins[bstack] == nil){
+		yyerror("can't include %s: %r", file->name);
+		bstack--;
+	}else{
+		addfile(mkfile(strdup(buf), lineno+1, -lineno, lineno, nil, 0, -1));
+		lineno++;
+		linepos = Linestart;
+	}
+	bin = bins[bstack];
+}
+
+/*
+ * we hit eof in the current file
+ * revert to the file which included it.
+ */
+static void
+popinclude(void)
+{
+	Fline fl;
+	File *f;
+	int oline, opos, ln;
+
+	ineof = 0;
+	bstack--;
+	bin = bins[bstack];
+	oline = linestack[bstack].line;
+	opos = linestack[bstack].pos;
+	fl = fline(oline);
+	f =  fl.file;
+	ln = fl.line;
+	lineno++;
+	linepos = opos;
+	addfile(mkfile(f->name, lineno, ln-lineno, f->in, f->act, f->actoff, -1));
+}
+
+/*
+ * convert an absolute Line into a file and line within the file
+ */
+Fline
+fline(int absline)
+{
+	Fline fl;
+	int l, r, m, s;
+
+	if(absline < files[lastfile]->abs
+	|| lastfile+1 < nfiles && absline >= files[lastfile+1]->abs){
+		lastfile = 0;
+		l = 0;
+		r = nfiles - 1;
+		while(l <= r){
+			m = (r + l) / 2;
+			s = files[m]->abs;
+			if(s <= absline){
+				l = m + 1;
+				lastfile = m;
+			}else
+				r = m - 1;
+		}
+	}
+
+	fl.file = files[lastfile];
+	fl.line = absline + files[lastfile]->off;
+	return fl;
+}
+
+/*
+ * read a comment
+ */
+static int
+lexcom(void)
+{
+	File *f;
+	char buf[StrSize], *s, *t, *act;
+	int i, n, c, actline;
+
+	i = 0;
+	while((c = Getc()) != '\n'){
+		if(c == Beof)
+			return -1;
+		if(i < sizeof(buf)-1)
+			buf[i++] = c;
+	}
+	buf[i] = 0;
+
+	lineno++;
+	linepos = Linestart;
+
+	if(strncmp(buf, "line ", 5) != 0 && strncmp(buf, "line\t", 5) != 0)
+		return 0;
+	for(s = buf+5; *s == ' ' || *s == '\t'; s++)
+		;
+	if(!(cmap(*s) & Mdigit))
+		return 0;
+	n = 0;
+	for(; cmap(c = *s) & Mdigit; s++)
+		n = n * 10 + c - '0';
+	for(; *s == ' ' || *s == '\t'; s++)
+		;
+	if(*s != '"')
+		return 0;
+	s++;
+	t = strchr(s, '"');
+	if(t == nil || t[1] != '\0')
+		return 0;
+	*t = '\0';
+
+	f = files[nfiles - 1];
+	if(n == f->off+lineno && strcmp(s, f->name) == 0)
+		return 1;
+	act = f->name;
+	actline = lineno + f->off;
+	if(f->act != nil){
+		actline += f->actoff;
+		act = f->act;
+	}
+	addfile(mkfile(strdup(s), lineno, n-lineno, f->in, act, actline - n, -1));
+
+	return 1;
+}
+
+Line
+curline(void)
+{
+	Line line;
+
+	line.line = lineno;
+	line.pos = linepos;
+	return line;
+}
+
+int
+lineconv(Fmt *f)
+{
+	Fline fl;
+	File *file;
+	Line inl, line;
+	char buf[StrSize], *s;
+
+	line = va_arg(f->args, Line);
+
+	if(line.line < 0)
+		return fmtstrcpy(f, "<noline>");
+	fl = fline(line.line);
+	file = fl.file;
+
+	s = seprint(buf, buf+sizeof(buf), "%s:%d", file->name, fl.line);
+	if(file->act != nil)
+		s = seprint(s, buf+sizeof(buf), " [ %s:%d ]", file->act, file->actoff+fl.line);
+	if(file->in >= 0){
+		inl.line = file->in;
+		inl.pos = 0;
+		seprint(s, buf+sizeof(buf), ": %L", inl);
+	}
+	return fmtstrcpy(f, buf);
+}
+
+static char*
+posconv(char *s, char *e, Line line)
+{
+	Fline fl;
+
+	if(line.line < 0)
+		return secpy(s, e, "nopos");
+
+	fl = fline(line.line);
+	return seprint(s, e, "%s:%d.%d", fl.file->name, fl.line, line.pos);
+}
+
+int
+srcconv(Fmt *f)
+{
+	Src src;
+	char buf[StrSize], *s;
+
+	src = va_arg(f->args, Src);
+	s = posconv(buf, buf+sizeof(buf), src.start);
+	s = secpy(s, buf+sizeof(buf), ",");
+	posconv(s, buf+sizeof(buf), src.stop);
+
+	return fmtstrcpy(f, buf);
+}
+
+int
+lexid(int c)
+{
+	Sym *sym;
+	char id[StrSize*UTFmax+1], *p;
+	Rune r;
+	int i, t;
+
+	p = id;
+	i = 0;
+	for(;;){
+		if(i < StrSize){
+			if(c < Runeself)
+				*p++ = c;
+			else{
+				r = c;
+				p += runetochar(p, &r);
+			}
+			i++;
+		}
+		c = getrune();
+		if(c == Beof
+		|| !(cmap(c) & (Malpha|Mdigit))){
+			ungetrune();
+			break;
+		}
+	}
+	*p = '\0';
+	sym = enter(id, Lid);
+	t = sym->token;
+	if(t == Lid || t == Ltid)
+		yylval.tok.v.idval = sym;
+	return t;
+}
+
+Long
+strtoi(char *t, int base)
+{
+	char *s;
+	Long v;
+	int c, neg, ck;
+
+	neg = 0;
+	if(t[0] == '-'){
+		neg = 1;
+		t++;
+	}else if(t[0] == '+')
+		t++;
+	v = 0;
+	for(s = t; c = *s; s++){
+		ck = cmap(c);
+		if(ck & Mdigit)
+			c -= '0';
+		else if(ck & Mlower)
+			c = c - 'a' + 10;
+		else if(ck & Mupper)
+			c = c - 'A' + 10;
+		if(c >= base){
+			yyerror("digit '%c' not radix %d", *s, base);
+			return -1;
+		}
+		v = v * base + c;
+	}
+	if(neg)
+		return -v;
+	return v;
+}
+
+static int
+digit(int c, int base)
+{
+	int cc, ck;
+
+	cc = c;
+	ck = cmap(c);
+	if(ck & Mdigit)
+		c -= '0';
+	else if(ck & Mlower)
+		c = c - 'a' + 10;
+	else if(ck & Mupper)
+		c = c - 'A' + 10;
+	else if(ck & Munder)
+		{}
+	else
+		return -1;
+	if(c >= base)
+		yyerror("digit '%c' not radix %d", cc, base);
+	return c;
+}
+
+double
+strtodb(char *t, int base)
+{
+	double num, dem;
+	int neg, eneg, dig, exp, c, d;
+
+	num = 0;
+	neg = 0;
+	dig = 0;
+	exp = 0;
+	eneg = 0;
+
+	c = *t++;
+	if(c == '-' || c == '+'){
+		if(c == '-')
+			neg = 1;
+		c = *t++;
+	}
+	while((d = digit(c, base)) >= 0){
+		num = num*base + d;
+		c = *t++;
+	}
+	if(c == '.')
+		c = *t++;
+	while((d = digit(c, base)) >= 0){
+		num = num*base + d;
+		dig++;
+		c = *t++;
+	}
+	if(c == 'e' || c == 'E'){
+		c = *t++;
+		if(c == '-' || c == '+'){
+			if(c == '-'){
+				dig = -dig;
+				eneg = 1;
+			}
+			c = *t++;
+		}
+		while((d = digit(c, base)) >= 0){
+			exp = exp*base + d;
+			c = *t++;
+		}
+	}
+	exp -= dig;
+	if(exp < 0){
+		exp = -exp;
+		eneg = !eneg;
+	}
+	dem = rpow(base, exp);
+	if(eneg)
+		num /= dem;
+	else
+		num *= dem;
+	if(neg)
+		return -num;
+	return num;
+}
+
+/*
+ * parse a numeric identifier
+ * format [0-9]+(r[0-9A-Za-z]+)?
+ * or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?
+ */
+int
+lexnum(int c)
+{
+	char buf[StrSize], *base;
+	enum { Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp, FracB } state;
+	double d;
+	Long v;
+	int i, ck;
+
+	i = 0;
+	buf[i++]  = c;
+	state = Int;
+	if(c == '.')
+		state = Frac;
+	base = nil;
+	for(;;){
+		c = Getc();
+		if(c == Beof){
+			yyerror("end of file in numeric constant");
+			return Leof;
+		}
+
+		ck = cmap(c);
+		switch(state){
+		case Int:
+			if(ck & Mdigit)
+				break;
+			if(ck & Mexp){
+				state = ExpSeen;
+				break;
+			}
+			if(ck & Mradix){
+				base = &buf[i];
+				state = RadixSeen;
+				break;
+			}
+			if(c == '.'){
+				state = Frac;
+				break;
+			}
+			goto done;
+		case RadixSeen:
+		case Radix:
+			if(ck & (Mdigit|Malpha)){
+				state = Radix;
+				break;
+			}
+			if(c == '.'){
+				state = FracB;
+				break;
+			}
+			goto done;
+		case Frac:
+			if(ck & Mdigit)
+				break;
+			if(ck & Mexp)
+				state = ExpSeen;
+			else
+				goto done;
+			break;
+		case FracB:
+			if(ck & (Mdigit|Malpha))
+				break;
+			goto done;
+		case ExpSeen:
+			if(ck & Msign){
+				state = ExpSignSeen;
+				break;
+			}
+			/* fall through */
+		case ExpSignSeen:
+		case Exp:
+			if(ck & Mdigit){
+				state = Exp;
+				break;
+			}
+			goto done;
+		}
+		if(i < StrSize-1)
+			buf[i++] = c;
+	}
+done:
+	buf[i] = 0;
+	unGetc();
+	switch(state){
+	default:
+		yyerror("malformed numerical constant '%s'", buf);
+		yylval.tok.v.ival = 0;
+		return Lconst;
+	case Radix:
+		*base++ = '\0';
+		v = strtoi(buf, 10);
+		if(v < 0)
+			break;
+		if(v < 2 || v > 36){
+			yyerror("radix '%s' must be between 2 and 36", buf);
+			break;
+		}
+		v = strtoi(base, v);
+		break;
+	case Int:
+		v = strtoi(buf, 10);
+		break;
+	case Frac:
+	case Exp:
+		d = strtod(buf, nil);
+		yylval.tok.v.rval = d;
+		return Lrconst;
+	case FracB:
+		*base++ = '\0';
+		v = strtoi(buf, 10);
+		if(v < 0)
+			break;
+		if(v < 2 || v > 36){
+			yyerror("radix '%s' must be between 2 and 36", buf);
+			break;
+		}
+		d = strtodb(base, v);
+		yylval.tok.v.rval = d;
+		return Lrconst;
+	}
+	yylval.tok.v.ival = v;
+	return Lconst;
+}
+
+int
+escchar(void)
+{
+	char buf[4+1];
+	int c, i;
+
+	c = getrune();
+	if(c == Beof)
+		return Beof;
+	if(c == 'u'){
+		for(i = 0; i < 4; i++){
+			c = getrune();
+			if(c == Beof || !(cmap(c) & (Mdigit|Mhex))){
+				yyerror("malformed \\u escape sequence");
+				ungetrune();
+				break;
+			}
+			buf[i] = c;
+		}
+		buf[i] = 0;
+		return strtoul(buf, 0, 16);
+	}
+	if(c < 256 && (i = escmap[c]) >= 0)
+		return i;
+	yyerror("unrecognized escape \\%C", c);
+	return c;
+}
+
+void
+lexstring(int israw)
+{
+	char *str;
+	int c, t, startlno;
+	Rune r;
+	int len, alloc;
+
+	alloc = 32;
+	len = 0;
+	str = allocmem(alloc * sizeof(str));
+	startlno = lineno;
+	for(;;){
+		c = getrune();
+		if(israw){
+			switch(c){
+			case '`':
+				yylval.tok.v.idval = enterstring(str, len);
+				return;
+			case '\n':
+				lineno++;
+				linepos = Linestart;
+				break;
+			case Beof:
+				t = lineno;
+				lineno = startlno;
+				yyerror("end of file in raw string constant");
+				lineno = t;
+				yylval.tok.v.idval = enterstring(str, len);
+				return;
+			}
+		}else{
+			switch(c){
+			case '\\':
+				c = escchar();
+				if(c != Beof)
+					break;
+				/* fall through */
+			case Beof:
+				yyerror("end of file in string constant");
+				yylval.tok.v.idval = enterstring(str, len);
+				return;
+			case '\n':
+				yyerror("newline in string constant");
+				lineno++;
+				linepos = Linestart;
+				yylval.tok.v.idval = enterstring(str, len);
+				return;
+			case '"':
+				yylval.tok.v.idval = enterstring(str, len);
+				return;
+			}
+		}
+		while(len+UTFmax+1 >= alloc){
+			alloc += 32;
+			str = reallocmem(str, alloc * sizeof(str));
+		}
+		r = c;
+		len += runetochar(&str[len], &r);
+		str[len] = '\0';
+	}
+}
+
+static int
+lex(void)
+{
+	int c;
+
+loop:
+	yylval.tok.src.start.line = lineno;
+	yylval.tok.src.start.pos = linepos;
+	c = getrune(); /* ehg: outside switch() to avoid bug in VisualC++5.0 */
+	switch(c){
+	case Beof:
+		Bterm(bin);
+		if(bstack == 0)
+			return Leof;
+		popinclude();
+		break;
+	case '#':
+		if(lexcom() < 0){
+			Bterm(bin);
+			if(bstack == 0)
+				return Leof;
+			popinclude();
+		}
+		break;
+
+	case '\n':
+		lineno++;
+		linepos = Linestart;
+		goto loop;
+	case ' ':
+	case '\t':
+	case '\r':
+	case '\v':
+	case '\f':
+		goto loop;
+	case '"':
+		lexstring(0);
+		return Lsconst;
+	case '`':
+		lexstring(1);
+		return Lsconst;
+	case '\'':
+		c = getrune();
+		if(c == '\\')
+			c = escchar();
+		if(c == Beof){
+			yyerror("end of file in character constant");
+			return Beof;
+		}else
+			yylval.tok.v.ival = c;
+		c = Getc();
+		if(c != '\'') {
+			yyerror("missing closing '");
+			unGetc();
+		}
+		return Lconst;
+	case '(':
+	case ')':
+	case '[':
+	case ']':
+	case '{':
+	case '}':
+	case ',':
+	case ';':
+	case '~':
+		return c;
+
+	case ':':
+		c = Getc();
+		if(c == ':')
+			return Lcons;
+		if(c == '=')
+			return Ldeclas;
+		unGetc();
+		return ':';
+
+	case '.':
+		c = Getc();
+		unGetc();
+		if(c != Beof && (cmap(c) & Mdigit))
+			return lexnum('.');
+		return '.';
+
+	case '|':
+		c = Getc();
+		if(c == '=')
+			return Loreq;
+		if(c == '|')
+			return Loror;
+		unGetc();
+		return '|';
+
+	case '&':
+		c = Getc();
+		if(c == '=')
+			return Landeq;
+		if(c == '&')
+			return Landand;
+		unGetc();
+		return '&';
+
+	case '^':
+		c = Getc();
+		if(c == '=')
+			return Lxoreq;
+		unGetc();
+		return '^';
+
+	case '*':
+		c = Getc();
+		if(c == '=')
+			return Lmuleq;
+		if(c == '*'){
+			c = Getc();
+			if(c == '=')
+				return Lexpeq;
+			unGetc();
+			return Lexp;
+		}
+		unGetc();
+		return '*';
+	case '/':
+		c = Getc();
+		if(c == '=')
+			return Ldiveq;
+		unGetc();
+		return '/';
+	case '%':
+		c = Getc();
+		if(c == '=')
+			return Lmodeq;
+		unGetc();
+		return '%';
+	case '=':
+		c = Getc();
+		if(c == '=')
+			return Leq;
+		if(c == '>')
+			return Llabs;
+		unGetc();
+		return '=';
+	case '!':
+		c = Getc();
+		if(c == '=')
+			return Lneq;
+		unGetc();
+		return '!';
+	case '>':
+		c = Getc();
+		if(c == '=')
+			return Lgeq;
+		if(c == '>'){
+			c = Getc();
+			if(c == '=')
+				return Lrsheq;
+			unGetc();
+			return Lrsh;
+		}
+		unGetc();
+		return '>';
+
+	case '<':
+		c = Getc();
+		if(c == '=')
+			return Lleq;
+		if(c == '-')
+			return Lcomm;
+		if(c == '<'){
+			c = Getc();
+			if(c == '=')
+				return Llsheq;
+			unGetc();
+			return Llsh;
+		}
+		unGetc();
+		return '<';
+
+	case '+':
+		c = Getc();
+		if(c == '=')
+			return Laddeq;
+		if(c == '+')
+			return Linc;
+		unGetc();
+		return '+';
+
+	case '-':
+		c = Getc();
+		if(c == '=')
+			return Lsubeq;
+		if(c == '-')
+			return Ldec;
+		if(c == '>')
+			return Lmdot;
+		unGetc();
+		return '-';
+
+	case '1': case '2': case '3': case '4': case '5':
+	case '0': case '6': case '7': case '8': case '9':
+		return lexnum(c);
+
+	default:
+		if(cmap(c) & Malpha)
+			return lexid(c);
+		yyerror("unknown character %c", c);
+		break;
+	}
+	goto loop;
+}
+
+int
+yylex(void)
+{
+	int t;
+
+	t = lex();
+	yylval.tok.src.stop.line = lineno;
+	yylval.tok.src.stop.pos = linepos;
+	lasttok = t;
+	lastyylval = yylval;
+	return t;
+}
+
+static char*
+toksp(int t)
+{
+	Keywd *k;
+	static char buf[256];
+
+	switch(t){
+		case Lconst:
+			snprint(buf, sizeof(buf), "%lld", lastyylval.tok.v.ival);
+			return buf;
+		case Lrconst:
+			snprint(buf, sizeof(buf), "%f", lastyylval.tok.v.rval);
+			return buf;
+		case Lsconst:
+			snprint(buf, sizeof(buf), "\"%s\"", lastyylval.tok.v.idval->name);
+			return buf;
+		case Ltid:
+		case Lid:
+			return lastyylval.tok.v.idval->name;
+	}
+	for(k = keywords; k->name != nil; k++)
+		if(t == k->token)
+			return k->name;
+	for(k = tokwords; k->name != nil; k++)
+		if(t == k->token)
+			return k->name;
+	if(t < 0 || t > 255)
+		fatal("bad token %d in toksp()", t);
+	buf[0] = t;
+	buf[1] = '\0';
+	return buf;
+}
+
+Sym*
+enterstring(char *str, int n)
+{
+	Sym *s;
+	char *p, *e;
+	ulong h;
+	int c, c0;
+
+	e = str + n;
+	h = 0;
+	for(p = str; p < e; p++){
+		c = *p;
+		c ^= c << 6;
+		h += (c << 11) ^ (c >> 1);
+		c = *p;
+		h ^= (c << 14) + (c << 7) + (c << 4) + c;
+	}
+
+	c0 = str[0];
+	h %= HashSize;
+	for(s = strings[h]; s != nil; s = s->next){
+		if(s->name[0] == c0 && s->len == n && memcmp(s->name, str, n) == 0){
+			free(str);
+			return s;
+		}
+	}
+
+	if(n == 0)
+		return enter("", 0);
+
+	s = allocmem(sizeof(Sym));
+	memset(s, 0, sizeof(Sym));
+	s->name = str;
+	s->len = n;
+	s->next = strings[h];
+	strings[h] = s;
+	return s;
+}
+
+int
+symcmp(Sym *s, Sym *t)
+{
+	int n, c;
+
+	n = s->len;
+	if(n > t->len)
+		n = t->len;
+	c = memcmp(s->name, t->name, n);
+	if(c == 0)
+		return s->len - t->len;
+	return c;
+}
+
+Sym*
+stringcat(Sym *s, Sym *t)
+{
+	char *str;
+	int n;
+
+	n = s->len + t->len;
+	str = allocmem(n+1);
+	memmove(str, s->name, s->len);
+	memmove(str+s->len, t->name, t->len);
+	str[n] = '\0';
+	return enterstring(str, n);
+}
+
+Sym*
+enter(char *name, int token)
+{
+	Sym *s;
+	char *p;
+	ulong h;
+	int c0, c, n;
+
+	c0 = name[0];
+	h = 0;
+	for(p = name; c = *p; p++){
+		c ^= c << 6;
+		h += (c << 11) ^ (c >> 1);
+		c = *p;
+		h ^= (c << 14) + (c << 7) + (c << 4) + c;
+	}
+	n = p - name;
+
+	h %= HashSize;
+	for(s = symbols[h]; s != nil; s = s->next)
+		if(s->name[0] == c0 && strcmp(s->name, name) == 0)
+			return s;
+
+	s = allocmem(sizeof(Sym));
+	memset(s, 0, sizeof(Sym));
+	s->hash = h;
+	s->name = allocmem(n+1);
+	memmove(s->name, name, n+1);
+	if(token == 0)
+		token = Lid;
+	s->token = token;
+	s->next = symbols[h];
+	s->len = n;
+	symbols[h] = s;
+	return s;
+}
+
+char*
+stringpr(char *buf, char *end, Sym *sym)
+{
+	char sb[30], *s, *p;
+	int i, c, n;
+
+	s = sym->name;
+	n = sym->len;
+	if(n > 10)
+		n = 10;
+	p = sb;
+	*p++ = '"';
+	for(i = 0; i < n; i++){
+		c = s[i];
+		switch(c){
+		case '\\':
+		case '"':
+		case '\n':
+		case '\r':
+		case '\t':
+		case '\b':
+		case '\a':
+		case '\v':
+		case '\0':
+			*p++ = '\\';
+			*p++ = unescmap[c];
+			break;
+		default:
+			*p++ = c;
+			break;
+		}
+	}
+	if(n != sym->len){
+		*p++ = '.';
+		*p++ = '.';
+		*p++ = '.';
+	}
+	*p++ = '"';
+	*p = 0;
+	return secpy(buf, end, sb);
+}
+
+void
+warn(Line line, char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	if(errors || !dowarn)
+		return;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L: warning: %s\n", line, buf);
+}
+
+void
+nwarn(Node *n, char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	if(errors || !dowarn)
+		return;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L: warning: %s\n", n->src.start, buf);
+}
+
+void
+error(Line line, char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	errors++;
+	if(errors >= maxerr){
+		if(errors == maxerr)
+			fprint(2, "too many errors, stopping\n");
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L: %s\n", line, buf);
+}
+
+void
+nerror(Node *n, char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	errors++;
+	if(errors >= maxerr){
+		if(errors == maxerr)
+			fprint(2, "too many errors, stopping\n");
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L: %s\n", n->src.start, buf);
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	errors++;
+	if(errors >= maxerr){
+		if(errors == maxerr)
+			fprint(2, "too many errors, stopping\n");
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	if(lasttok != 0)
+		fprint(2, "%L: near ` %s ` : %s\n", curline(), toksp(lasttok), buf);
+	else
+		fprint(2, "%L: %s\n", curline(), buf);
+}
+
+void
+fatal(char *fmt, ...)
+{
+	char buf[4096];
+	va_list arg;
+
+	if(errors == 0 || isfatal){
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		fprint(2, "fatal limbo compiler error: %s\n", buf);
+	}
+	if(bout != nil)
+		remove(outfile);
+	if(bsym != nil)
+		remove(symfile);
+	if(isfatal)
+		abort();
+	exits(buf);
+}
+
+int
+gfltconv(Fmt *f)
+{
+	double d;
+	char buf[32];
+
+	d = va_arg(f->args, double);
+	g_fmt(buf, d, 'e');
+	return fmtstrcpy(f, buf);
+}
+
+char*
+secpy(char *p, char *e, char *s)
+{
+	int c;
+
+	if(p == e){
+		p[-1] = '\0';
+		return p;
+	}
+	for(; c = *s; s++){
+		*p++ = c;
+		if(p == e){
+			p[-1] = '\0';
+			return p;
+		}
+	}
+	*p = '\0';
+	return p;
+}
+
+char*
+seprint(char *buf, char *end, char *fmt, ...)
+{
+	va_list arg;
+
+	if(buf == end)
+		return buf;
+	va_start(arg, fmt);
+	buf = vseprint(buf, end, fmt, arg);
+	va_end(arg);
+	return buf;
+}
+
+void*
+allocmem(ulong n)
+{
+	void *p;
+
+	p = malloc(n);
+	if(p == nil)
+		fatal("out of memory");
+	return p;
+}
+
+void*
+reallocmem(void *p, ulong n)
+{
+	if(p == nil)
+		p = malloc(n);
+	else
+		p = realloc(p, n);
+	if(p == nil)
+		fatal("out of memory");
+	return p;
+}
--- /dev/null
+++ b/limbo/limbo.h
@@ -1,0 +1,702 @@
+#include "lib9.h"
+#include "bio.h"
+#include "isa.h"
+#include "mathi.h"
+
+/* internal dis ops */
+#define IEXC	MAXDIS
+#define IEXC0	(MAXDIS+1)
+#define INOOP	(MAXDIS+2)
+
+/* temporary */
+#define	LDT	1
+
+#ifndef Extern
+#define Extern extern
+#endif
+
+#define YYMAXDEPTH	200
+
+typedef	struct Addr	Addr;
+typedef	struct Case	Case;
+typedef	struct Decl	Decl;
+typedef	struct Desc	Desc;
+typedef	struct Dlist	Dlist;
+typedef	struct Except	Except;
+typedef struct File	File;
+typedef struct Fline	Fline;
+typedef	struct Inst	Inst;
+typedef	struct Label	Label;
+typedef	struct Line	Line;
+typedef	struct Node	Node;
+typedef struct Ok	Ok;
+typedef	struct Src	Src;
+typedef	struct Sym	Sym;
+typedef struct Szal	Szal;
+typedef	struct Tattr	Tattr;
+typedef	struct Teq	Teq;
+typedef	struct Tpair	Tpair;
+typedef	struct Type	Type;
+typedef	struct Typelist	Typelist;
+
+typedef	double		Real;
+typedef	vlong		Long;
+
+enum
+{
+	STemp		= NREG * IBY2WD,
+	RTemp		= STemp+IBY2WD,
+	DTemp		= RTemp+IBY2WD,
+	MaxTemp		= DTemp+IBY2WD,
+	MaxReg		= 1<<16,
+	MaxAlign	= IBY2LG,
+	StrSize		= 256,
+	NumSize		= 32,		/* max length of printed  */
+	MaxIncPath	= 32,		/* max directories in include path */
+	MaxScope	= 64,		/* max nested {} */
+	MaxInclude	= 32,		/* max nested include "" */
+	ScopeBuiltin	= 0,
+	ScopeNils	= 1,
+	ScopeGlobal	= 2
+};
+
+/*
+ * return tuple from expression type checking
+ */
+struct Ok
+{
+	int	ok;
+	int	allok;
+};
+
+/*
+ * return tuple from type sizing
+ */
+struct Szal
+{
+	int	size;
+	int	align;
+};
+
+/*
+ * return tuple for file/line numbering
+ */
+struct Fline
+{
+	File	*file;
+	int	line;
+};
+
+struct File
+{
+	char	*name;
+	int	abs;			/* absolute line of start of the part of file */
+	int	off;			/* offset to line in the file */
+	int	in;			/* absolute line where included */
+	char	*act;			/* name of real file with #line fake file */
+	int	actoff;			/* offset from fake line to real line */
+	int	sbl;			/* symbol file number */
+};
+
+struct Line
+{
+	int	line;
+	int	pos;			/* character within the line */
+};
+
+struct Src
+{
+	Line	start;
+	Line	stop;
+};
+
+enum
+{
+	Aimm,				/* immediate */
+	Amp,				/* global */
+	Ampind,				/* global indirect */
+	Afp,				/* activation frame */
+	Afpind,				/* frame indirect */
+	Apc,				/* branch */
+	Adesc,				/* type descriptor immediate */
+	Aoff,				/* offset in module description table */
+	Anoff,			/* above encoded as -ve */
+	Aerr,				/* error */
+	Anone,				/* no operand */
+	Aldt,				/* linkage descriptor table immediate */
+	Aend
+};
+
+struct Addr
+{
+	long	reg;
+	long	offset;
+	Decl	*decl;
+};
+
+struct Inst
+{
+	Src	src;
+	ushort	op;
+	long	pc;
+	uchar	reach;			/* could a control path reach this instruction? */
+	uchar	sm;			/* operand addressing modes */
+	uchar	mm;
+	uchar	dm;
+	Addr	s;			/* operands */
+	Addr	m;
+	Addr	d;
+	Inst	*branch;		/* branch destination */
+	Inst	*next;
+	int	block;			/* blocks nested inside */
+};
+
+struct Case
+{
+	int	nlab;
+	int	nsnd;
+	long	offset;			/* offset in mp */
+	Label	*labs;
+	Node	*wild;			/* if nothing matches */
+	Inst	*iwild;
+};
+
+struct Label
+{
+	Node	*node;
+	char	isptr;			/* true if the labelled alt channel is a pointer */
+	Node	*start;			/* value in range [start, stop) => code */
+	Node	*stop;
+	Inst	*inst;
+};
+
+enum
+{
+	Dtype,
+	Dfn,
+	Dglobal,
+	Darg,
+	Dlocal,
+	Dconst,
+	Dfield,
+	Dtag,				/* pick tags */
+	Dimport,			/* imported identifier */
+	Dunbound,			/* unbound identified */
+	Dundef,
+	Dwundef,			/* undefined, but don't whine */
+
+	Dend
+};
+
+struct Decl
+{
+	Src	src;			/* where declaration */
+	Sym	*sym;
+	uchar	store;			/* storage class */
+	uchar	nid;		/* block grouping for locals */
+	schar	caninline;	/* inline function */
+	uchar	das;		/* declared with := */
+	Decl	*dot;			/* parent adt or module */
+	Type	*ty;
+	int	refs;			/* number of references */
+	long	offset;
+	int	tag;			/* union tag */
+
+	uchar	scope;			/* in which it was declared */
+	uchar	handler;		/* fn has exception handler in body */
+	Decl	*next;			/* list in same scope, field or argument list, etc. */
+	Decl	*old;			/* declaration of the symbol in enclosing scope */
+
+	Node	*eimport;		/* expr from which imported */
+	Decl	*importid;		/* identifier imported */
+	Decl	*timport;		/* stack of identifiers importing a type */
+
+	Node	*init;			/* data initialization */
+	int	tref;			/* 1 => is a tmp; >=2 => tmp in use */
+	char	cycle;			/* can create a cycle */
+	char	cyc;			/* so labelled in source */
+	char	cycerr;			/* delivered an error message for cycle? */
+	char	implicit;		/* implicit first argument in an adt? */
+
+	Decl	*iface;			/* used external declarations in a module */
+
+	Decl	*locals;		/* locals for a function */
+	Decl *link;			/* pointer to parent function or function argument or local share or parent type dec */
+	Inst	*pc;			/* start of function */
+	/* Inst	*endpc; */			/* limit of function - unused */
+
+	Desc	*desc;			/* heap descriptor */
+};
+
+struct Desc
+{
+	int	id;			/* dis type identifier */
+	uchar	used;			/* actually used in output? */
+	uchar	*map;			/* byte map of pointers */
+	long	size;			/* length of the object */
+	long	nmap;			/* length of good bytes in map */
+	Desc	*next;
+};
+
+struct Dlist
+{
+	Decl *d;
+	Dlist *next;
+};
+
+struct Except
+{
+	Inst *p1;		/* first pc covered */
+	Inst *p2;		/* last pc not covered */
+	Case *c;		/* exception case instructions */
+	Decl *d;		/* exception definition if any */
+	Node *zn;		/* list of nodes to zero in handler */
+	Desc *desc;	/* descriptor map for above */
+	int ne;		/* number of exceptions (ie not strings) in case */
+	Except *next;
+};
+
+struct Sym
+{
+	ushort	token;
+	char	*name;
+	int	len;
+	int	hash;
+	Sym	*next;
+	Decl	*decl;
+	Decl	*unbound;		/* place holder for unbound symbols */
+};
+
+/*
+ * ops for nodes
+ */
+enum
+{
+	Oadd = 1,
+	Oaddas,
+	Oadr,
+	Oadtdecl,
+	Oalt,
+	Oand,
+	Oandand,
+	Oandas,
+	Oarray,
+	Oas,
+	Obreak,
+	Ocall,
+	Ocase,
+	Ocast,
+	Ochan,
+	Ocomma,
+	Ocomp,
+	Ocondecl,
+	Ocons,
+	Oconst,
+	Ocont,
+	Odas,
+	Odec,
+	Odiv,
+	Odivas,
+	Odo,
+	Odot,
+	Oelem,
+	Oeq,
+	Oexcept,
+	Oexdecl,
+	Oexit,
+	Oexp,
+	Oexpas,
+	Oexstmt,
+	Ofielddecl,
+	Ofnptr,
+	Ofor,
+	Ofunc,
+	Ogeq,
+	Ogt,
+	Ohd,
+	Oif,
+	Oimport,
+	Oinc,
+	Oind,
+	Oindex,
+	Oinds,
+	Oindx,
+	Oinv,
+	Ojmp,
+	Olabel,
+	Olen,
+	Oleq,
+	Oload,
+	Olsh,
+	Olshas,
+	Olt,
+	Omdot,
+	Omod,
+	Omodas,
+	Omoddecl,
+	Omul,
+	Omulas,
+	Oname,
+	Oneg,
+	Oneq,
+	Onot,
+	Onothing,
+	Oor,
+	Ooras,
+	Ooror,
+	Opick,
+	Opickdecl,
+	Opredec,
+	Opreinc,
+	Oraise,
+	Orange,
+	Orcv,
+	Oref,
+	Oret,
+	Orsh,
+	Orshas,
+	Oscope,
+	Oself,
+	Oseq,
+	Oslice,
+	Osnd,
+	Ospawn,
+	Osub,
+	Osubas,
+	Otagof,
+	Otl,
+	Otuple,
+	Otype,
+	Otypedecl,
+	Oused,
+	Ovardecl,
+	Ovardecli,
+	Owild,
+	Oxor,
+	Oxoras,
+
+	Oend
+};
+
+/*
+ * moves
+ */
+enum
+{
+	Mas,
+	Mcons,
+	Mhd,
+	Mtl,
+
+	Mend
+};
+
+/*
+ * addressability
+ */
+enum
+{
+	Rreg,				/* v(fp) */
+	Rmreg,				/* v(mp) */
+	Roff,				/* $v */
+	Rnoff,			/* $v encoded as -ve */
+	Rdesc,				/* $v */
+	Rdescp,				/* $v */
+	Rconst,				/* $v */
+	Ralways,			/* preceeding are always addressable */
+	Radr,				/* v(v(fp)) */
+	Rmadr,				/* v(v(mp)) */
+	Rcant,				/* following are not quite addressable */
+	Rpc,				/* branch address */
+	Rmpc,				/* cross module branch address */
+	Rareg,				/* $v(fp) */
+	Ramreg,				/* $v(mp) */
+	Raadr,				/* $v(v(fp)) */
+	Ramadr,				/* $v(v(mp)) */
+	Rldt,				/* $v */
+
+	Rend
+};
+
+#define PARENS	1
+#define TEMP		2
+#define FNPTRA	4	/* argument */
+#define FNPTR2	8	/* 2nd parameter */
+#define FNPTRN	16	/* use -ve offset */
+#define FNPTR		(FNPTRA|FNPTR2|FNPTRN)
+
+struct Node
+{
+	Src	src;
+	uchar	op;
+	uchar	addable;
+	uchar	flags;
+	uchar	temps;
+	Node	*left;
+	Node	*right;
+	Type	*ty;
+	Decl	*decl;
+	Long	val;			/* for Oconst */
+	Real	rval;			/* for Oconst */
+};
+
+enum
+{
+	/*
+	 * types visible to limbo
+	 */
+	Tnone	= 0,
+	Tadt,
+	Tadtpick,			/* pick case of an adt */
+	Tarray,
+	Tbig,				/* 64 bit int */
+	Tbyte,				/* 8 bit unsigned int */
+	Tchan,
+	Treal,
+	Tfn,
+	Tint,				/* 32 bit int */
+	Tlist,
+	Tmodule,
+	Tref,
+	Tstring,
+	Ttuple,
+	Texception,
+	Tfix,
+	Tpoly,
+
+	/*
+	 * internal use types
+	 */
+	Tainit,				/* array initializers */
+	Talt,				/* alt channels */
+	Tany,				/* type of nil */
+	Tarrow,				/* unresolved ty->id types */
+	Tcase,				/* case labels */
+	Tcasel,				/* case big labels */
+	Tcasec,				/* case string labels */
+	Tdot,				/* unresolved ty.id types */
+	Terror,
+	Tgoto,				/* goto labels */
+	Tid,				/* id with unknown type */
+	Tiface,				/* module interface */
+	Texcept,			/* exception handler tables */
+	Tinst,			/* instantiated adt */
+
+	Tend
+};
+
+enum
+{
+	OKbind		= 1 << 0,	/* type decls are bound */
+	OKverify	= 1 << 1,	/* type looks ok */
+	OKsized		= 1 << 2,	/* started figuring size */
+	OKref		= 1 << 3,	/* recorded use of type */
+	OKclass		= 1 << 4,	/* equivalence class found */
+	OKcyc		= 1 << 5,	/* checked for cycles */
+	OKcycsize	= 1 << 6,	/* checked for cycles and size */
+	OKmodref	= 1 << 7,	/* started checking for a module handle */
+
+	OKmask		= 0xff,
+
+	/*
+	 * recursive marks
+	 */
+	TReq		= 1 << 0,
+	TRcom		= 1 << 1,
+	TRcyc		= 1 << 2,
+	TRvis		= 1 << 3,
+};
+
+/* type flags */
+#define	FULLARGS	1	/* all hidden args added */
+#define	INST	2		/* instantiated adt */
+#define	CYCLIC	4	/* cyclic type */
+#define	POLY	8	/* polymorphic types inside */
+#define	NOPOLY	16	/* no polymorphic types inside */
+
+struct Type
+{
+	Src	src;
+	uchar	kind;
+	uchar	varargs;		/* if a function, ends with vargs? */
+	uchar	ok;			/* set when type is verified */
+	uchar	linkall;		/* put all iface fns in external linkage? */
+	uchar	rec;			/* in the middle of recursive type */
+	uchar	cons;		/* exception constant */
+	uchar	align;		/* alignment in bytes */
+	uchar	flags;
+	int	sbl;			/* slot in .sbl adt table */
+	long	sig;			/* signature for dynamic type check */
+	long	size;			/* storage required, in bytes */
+	Decl	*decl;
+	Type	*tof;
+	Decl	*ids;
+	Decl	*tags;			/* tagged fields in an adt */
+	Decl *polys;			/* polymorphic fields in fn or adt */
+	Case	*cse;			/* case or goto labels */
+	Type	*teq;			/* temporary equiv class for equiv checking */
+	Type	*tcom;			/* temporary equiv class for compat checking */
+	Teq	*eq;			/* real equiv class */
+	Node *val;		/* for Tfix, Tfn, Tadt only */
+	union {
+		Node *eraises;		/* for Tfn only */
+		Typelist *tlist;		/* for Tinst only */
+		Tpair *tmap;		/* for Tadt only */
+	} u;
+};
+
+/*
+ * type equivalence classes
+ */
+struct Teq
+{
+	int	id;		/* for signing */
+	Type	*ty;		/* an instance of the class */
+	Teq	*eq;		/* used to link eq sets */
+};
+
+struct Tattr
+{
+	char	isptr;
+	char	refable;
+	char	conable;
+	char	big;
+	char	vis;			/* type visible to users */
+};
+
+enum {
+	Sother,
+	Sloop,
+	Sscope
+};
+
+struct Tpair
+{
+	Type *t1;
+	Type *t2;
+	Tpair *nxt;
+};
+
+struct Typelist
+{
+	Type *t;
+	Typelist *nxt;
+};
+	
+Extern	Decl	**adts;
+Extern	Sym	*anontupsym;		/* name assigned to all anonymouse tuples */
+Extern	int	arrayz;
+Extern	int	asmsym;			/* generate symbols in assembly language? */
+Extern	Biobuf	*bins[MaxInclude];
+Extern	int	blocks;
+Extern	Biobuf	*bout;			/* output file */
+Extern	Biobuf	*bsym;			/* symbol output file; nil => no sym out */
+Extern	double	canonnan;		/* standard nan */
+Extern	uchar	casttab[Tend][Tend];	/* instruction to cast from [1] to [2] */
+Extern	long	constval;
+Extern	Decl	*curfn;
+Extern	char	debug[256];
+Extern	Desc	*descriptors;		/* list of all possible descriptors */
+Extern	int	dontcompile;		/* dis header flag */
+Extern	int	dowarn;
+Extern	char	*emitcode;		/* emit stub routines for system module functions */
+Extern	int	emitdyn;		/* emit stub routines as above but for dynamic modules */
+Extern	int	emitstub;		/* emit type and call frames for system modules */
+Extern	char	*emittab;		/* emit table of runtime functions for this module */
+Extern	int	errors;
+Extern	char	escmap[256];
+Extern	Inst	*firstinst;
+Extern	long	fixss;			/* set extent from command line */
+Extern	Decl	*fndecls;
+Extern	Decl	**fns;
+Extern	int	gendis;			/* generate dis or asm? */
+Extern	Decl	*impdecl;			/* id of implementation module or union if many */
+Extern	Dlist	*impdecls;		/* id(s) of implementation module(s) */
+/* Extern	Sym	*impmod;	*/	/* name of implementation module */
+Extern	Decl	*impmods;		/* name of implementation module(s) */
+Extern	Decl	*iota;
+Extern	uchar	isbyteinst[256];
+Extern	int	isfatal;
+Extern	int	isrelop[Oend];
+Extern	uchar	isused[Oend];
+Extern	Inst	*lastinst;
+Extern	int	lenadts;
+Extern	int	maxerr;
+Extern	int	maxlabdep;		/* maximum nesting of breakable/continuable statements */
+Extern	long	maxstack;		/* max size of a stack frame called */
+Extern	int	mustcompile;		/* dis header flag */
+Extern	int	oldcycles;
+Extern	int	nadts;
+Extern	int	newfnptr;		/* ISELF and -ve indices */
+Extern	int	nfns;
+Extern	Decl	*nildecl;		/* declaration for limbo's nil */
+Extern	int	nlabel;
+Extern	int	dontinline;
+Extern	Line	noline;
+Extern	Src	nosrc;
+Extern	uchar	opcommute[Oend];
+Extern	int	opind[Tend];
+Extern	uchar	oprelinvert[Oend];
+Extern	int	optims;
+Extern	char	*outfile;
+Extern	Type	*precasttab[Tend][Tend];
+Extern	int	scope;
+Extern	Decl	*selfdecl;		/* declaration for limbo's self */
+Extern	uchar	sideeffect[Oend];
+Extern	char	*signdump;		/* dump sig for this fn */
+Extern	int	superwarn;
+Extern	char	*symfile;
+Extern	Type	*tany;
+Extern	Type	*tbig;
+Extern	Type	*tbyte;
+Extern	Type	*terror;
+Extern	Type	*tint;
+Extern	Type	*tnone;
+Extern	Type	*treal;
+Extern	Node	*tree;
+Extern	Type	*tstring;
+Extern	Type *texception;
+Extern	Type	*tunknown;
+Extern	Type *tfnptr;
+Extern	Type	*rtexception;
+Extern	char	unescmap[256];
+Extern	Src	unifysrc;
+Extern	Node	znode;
+
+extern	int	*blockstack;
+extern	int	blockdep;
+extern	int	nblocks;
+extern	File	**files;
+extern	int	nfiles;
+extern	uchar	chantab[Tend];
+extern	uchar	disoptab[Oend+1][7];
+extern	char	*instname[];
+extern	char	*kindname[Tend];
+extern	uchar	movetab[Mend][Tend];
+extern	char	*opname[];
+extern	int	setisbyteinst[];
+extern	int	setisused[];
+extern	int	setsideeffect[];
+extern	char	*storename[Dend];
+extern	int	storespace[Dend];
+extern	Tattr	tattr[Tend];
+
+#include "fns.h"
+
+#pragma varargck	type	"D"	Decl*
+#pragma varargck	type	"I"	Inst*
+#pragma varargck	type	"K"	Decl*
+#pragma varargck	type	"k"	Decl*
+#pragma varargck	type	"L"	Line
+#pragma varargck	type	"M"	Desc*
+#pragma varargck	type	"n"	Node*
+#pragma varargck	type	"O"	int
+#pragma varargck	type	"O"	uint
+#pragma varargck	type	"g"	double
+#pragma varargck	type	"Q"	Node*
+#pragma varargck	type	"R"	Type*
+#pragma varargck	type	"T"	Type*
+#pragma varargck	type	"t"	Type*
+#pragma varargck	type	"U"	Src
+#pragma varargck	type	"v"	Node*
+#pragma	varargck	type	"V"	Node*
--- /dev/null
+++ b/limbo/limbo.y
@@ -1,0 +1,1995 @@
+%{
+#include "limbo.h"
+%}
+
+%union
+{
+	struct{
+		Src	src;
+		union{
+			Sym	*idval;
+			Long	ival;
+			Real	rval;
+		}v;
+	}tok;
+	Decl	*ids;
+	Node	*node;
+	Type	*type;
+	Typelist *types;
+}
+
+%type	<type>	type fnarg fnargret fnargretp adtk fixtype iditype dotiditype
+%type	<ids>	ids rids nids nrids tuplist forms ftypes ftype
+		bclab bctarg ptags rptags polydec
+%type	<node>	zexp exp monexp term elist zelist celist
+		idatom idterms idterm idlist
+		initlist elemlist elem qual
+		decl topdecls topdecl fndef fbody stmt stmts qstmts qbodies cqstmts cqbodies
+		mdecl adtdecl mfield mfields field fields fnname
+		pstmts pbodies pqual pfields pfbody pdecl dfield dfields
+		eqstmts eqbodies idexc edecl raises tpoly tpolys texp export exportlist forpoly
+%type	<types>	types
+
+%right	<tok.src>	'=' Landeq Loreq Lxoreq Llsheq Lrsheq
+			Laddeq Lsubeq Lmuleq Ldiveq Lmodeq Lexpeq Ldeclas
+%left	<tok.src>	Lload
+%left	<tok.src>	Loror
+%left	<tok.src>	Landand
+%right	<tok.src>	Lcons
+%left	<tok.src>	'|'
+%left	<tok.src>	'^'
+%left	<tok.src>	'&'
+%left	<tok.src>	Leq Lneq
+%left	<tok.src>	'<' '>' Lleq Lgeq
+%left	<tok.src>	Llsh Lrsh
+%left	<tok.src>	'+' '-'
+%left	<tok.src>	'*' '/' '%'
+%right <tok.src> Lexp
+%right	<tok.src>	Lcomm
+
+%left	<tok.src>	'(' ')' '[' ']' Linc Ldec Lof Lref
+%right	<tok.src>	Lif Lelse Lfn ':' Lexcept Lraises
+%left	<tok.src>	Lmdot
+%left	<tok.src>	'.'
+
+%left	<tok.src>	Lto
+%left	<tok.src>	Lor
+
+
+%nonassoc	<tok.v.rval>	Lrconst
+%nonassoc	<tok.v.ival>	Lconst
+%nonassoc	<tok.v.idval>	Lid Ltid Lsconst
+%nonassoc	<tok.src>	Llabs Lnil
+			'!' '~' Llen Lhd Ltl Ltagof
+			'{' '}' ';'
+			Limplement Limport Linclude
+			Lcon Ltype Lmodule Lcyclic
+			Ladt Larray Llist Lchan Lself
+			Ldo Lwhile Lfor Lbreak
+			Lalt Lcase Lpick Lcont
+			Lreturn Lexit Lspawn Lraise Lfix
+			Ldynamic
+%%
+prog	: Limplement ids ';'
+	{
+		impmods = $2;
+	} topdecls
+	{
+		tree = rotater($5);
+	}
+	| topdecls
+	{
+		impmods = nil;
+		tree = rotater($1);
+	}
+	;
+
+topdecls: topdecl
+	| topdecls topdecl
+	{
+		if($1 == nil)
+			$$ = $2;
+		else if($2 == nil)
+			$$ = $1;
+		else
+			$$ = mkbin(Oseq, $1, $2);
+	}
+	;
+
+topdecl	: error ';'
+	{
+		$$ = nil;
+	}
+	| decl
+	| fndef
+	| adtdecl ';'
+	| mdecl ';'
+	| idatom '=' exp ';'
+	{
+		$$ = mkbin(Oas, $1, $3);
+	}
+	| idterm '=' exp ';'
+	{
+		$$ = mkbin(Oas, $1, $3);
+	}
+	| idatom Ldeclas exp ';'
+	{
+		$$ = mkbin(Odas, $1, $3);
+	}
+	| idterm Ldeclas exp ';'
+	{
+		$$ = mkbin(Odas, $1, $3);
+	}
+	| idterms ':' type ';'
+	{
+		yyerror("illegal declaration");
+		$$ = nil;
+	}
+	| idterms ':' type '=' exp ';'
+	{
+		yyerror("illegal declaration");
+		$$ = nil;
+	}
+	;
+
+idterms : idterm
+	| idterms ',' idterm
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	;
+
+decl	: Linclude Lsconst ';'
+	{
+		includef($2);
+		$$ = nil;
+	}
+	| ids ':' Ltype type ';'
+	{
+		$$ = typedecl($1, $4);
+	}
+	| ids ':' Limport exp ';'
+	{
+		$$ = importdecl($4, $1);
+		$$->src.start = $1->src.start;
+		$$->src.stop = $5.stop;
+	}
+	| ids ':' type ';'
+	{
+		$$ = vardecl($1, $3);
+	}
+	| ids ':' type '=' exp ';'
+	{
+		$$ = mkbin(Ovardecli, vardecl($1, $3), varinit($1, $5));
+	}
+	| ids ':' Lcon exp ';'
+	{
+		$$ = condecl($1, $4);
+	}
+	| edecl
+	;
+
+edecl	: ids ':' Lexcept ';'
+	{
+		$$ = exdecl($1, nil);
+	}
+	| ids ':' Lexcept '(' tuplist ')' ';'
+	{
+		$$ = exdecl($1, revids($5));
+	}
+	;
+
+mdecl	: ids ':' Lmodule '{' mfields '}'
+	{
+		$1->src.stop = $6.stop;
+		$$ = moddecl($1, rotater($5));
+	}
+	;
+
+mfields	:
+	{
+		$$ = nil;
+	}
+	| mfields mfield
+	{
+		if($1 == nil)
+			$$ = $2;
+		else if($2 == nil)
+			$$ = $1;
+		else
+			$$ = mkn(Oseq, $1, $2);
+	}
+	| error
+	{
+		$$ = nil;
+	}
+	;
+
+mfield	: ids ':' type ';'
+	{
+		$$ = fielddecl(Dglobal, typeids($1, $3));
+	}
+	| adtdecl ';'
+	| ids ':' Ltype type ';'
+	{
+		$$ = typedecl($1, $4);
+	}
+	| ids ':' Lcon exp ';'
+	{
+		$$ = condecl($1, $4);
+	}
+	| edecl
+	;
+
+adtdecl	: ids ':' Ladt polydec '{' fields '}' forpoly
+	{
+		$1->src.stop = $7.stop;
+		$$ = adtdecl($1, rotater($6));
+		$$->ty->polys = $4;
+		$$->ty->val = rotater($8);
+	}
+	| ids ':' Ladt polydec Lfor '{' tpolys '}' '{' fields '}'
+	{
+		$1->src.stop = $11.stop;
+		$$ = adtdecl($1, rotater($10));
+		$$->ty->polys = $4;
+		$$->ty->val = rotater($7);
+	}
+	;
+
+forpoly	:
+	{
+		$$ = nil;
+	}
+	| Lfor '{' tpolys '}'
+	{
+		$$ = $3;
+	}
+	;
+
+fields	:
+	{
+		$$ = nil;
+	}
+	| fields field
+	{
+		if($1 == nil)
+			$$ = $2;
+		else if($2 == nil)
+			$$ = $1;
+		else
+			$$ = mkn(Oseq, $1, $2);
+	}
+	| error
+	{
+		$$ = nil;
+	}
+	;
+
+field	: dfield
+	| pdecl
+	| ids ':' Lcon exp ';'
+	{
+		$$ = condecl($1, $4);
+	}
+	;
+
+dfields	:
+	{
+		$$ = nil;
+	}
+	| dfields dfield
+	{
+		if($1 == nil)
+			$$ = $2;
+		else if($2 == nil)
+			$$ = $1;
+		else
+			$$ = mkn(Oseq, $1, $2);
+	}
+	;
+
+dfield	: ids ':' Lcyclic type ';'
+	{
+		Decl *d;
+
+		for(d = $1; d != nil; d = d->next)
+			d->cyc = 1;
+		$$ = fielddecl(Dfield, typeids($1, $4));
+	}
+	| ids ':' type ';'
+	{
+		$$ = fielddecl(Dfield, typeids($1, $3));
+	}
+	;
+
+pdecl	: Lpick '{' pfields '}'
+	{
+		$$ = $3;
+	}
+	;
+
+pfields	: pfbody dfields
+	{
+		$1->right->right = $2;
+		$$ = $1;
+	}
+	| pfbody error
+	{
+		$$ = nil;
+	}
+	| error
+	{
+		$$ = nil;
+	}
+	;
+
+pfbody	: ptags Llabs
+	{
+		$$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $1), nil));
+		typeids($1, mktype(&$1->src.start, &$1->src.stop, Tadtpick, nil, nil));
+	}
+	| pfbody dfields ptags Llabs
+	{
+		$1->right->right = $2;
+		$$ = mkn(Opickdecl, $1, mkn(Oseq, fielddecl(Dtag, $3), nil));
+		typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil));
+	}
+	| pfbody error ptags Llabs
+	{
+		$$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $3), nil));
+		typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil));
+	}
+	;
+
+ptags	: rptags
+	{
+		$$ = revids($1);
+	}
+	;
+
+rptags	: Lid
+	{
+		$$ = mkids(&$<tok.src>1, $1, nil, nil);
+	}
+	| rptags Lor Lid
+	{
+		$$ = mkids(&$<tok.src>3, $3, nil, $1);
+	}
+	;
+
+ids	: rids
+	{
+		$$ = revids($1);
+	}
+	;
+
+rids	: Lid
+	{
+		$$ = mkids(&$<tok.src>1, $1, nil, nil);
+	}
+	| rids ',' Lid
+	{
+		$$ = mkids(&$<tok.src>3, $3, nil, $1);
+	}
+	;
+
+fixtype	: Lfix '(' exp ',' exp ')'
+	{
+		$$ = mktype(&$1.start, &$6.stop, Tfix, nil, nil);
+		$$->val = mkbin(Oseq, $3, $5);
+	}
+	|	Lfix '(' exp ')'
+	{
+		$$ = mktype(&$1.start, &$4.stop, Tfix, nil, nil);
+		$$->val = $3;
+	}
+	;
+
+types	: type
+	{
+		$$ = addtype($1, nil);
+	}
+	|	Lcyclic type
+	{
+		$$ = addtype($2, nil);
+		$2->flags |= CYCLIC;
+	}
+	| types ',' type
+	{
+		$$ = addtype($3, $1);
+	}
+	| types ',' Lcyclic type
+	{
+		$$ = addtype($4, $1);
+		$4->flags |= CYCLIC;
+	}
+	;
+
+type	: Ltid
+	{
+		$$ = mkidtype(&$<tok.src>1, $1);
+	}
+	| iditype
+	{
+		$$ = $1;
+	}
+	| dotiditype
+	{
+		$$ = $1;
+	}
+	| type Lmdot Lid
+	{
+		$$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+	}
+	| type Lmdot Lid '[' types ']'
+	{
+		$$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+		$$ = mkinsttype(&$1->src, $$, $5);
+	}
+	| Lref type
+	{
+		$$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil);
+	}
+	| Lchan Lof type
+	{
+		$$ = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil);
+	}
+	| '(' tuplist ')'
+	{
+		if($2->next == nil)
+			$$ = $2->ty;
+		else
+			$$ = mktype(&$1.start, &$3.stop, Ttuple, nil, revids($2));
+	}
+	| Larray Lof type
+	{
+		$$ = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil);
+	}
+	| Llist Lof type
+	{
+		$$ = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil);
+	}
+	| Lfn polydec fnargretp raises
+	{
+		$3->src.start = $1.start;
+		$3->polys = $2;
+		$3->u.eraises = $4;
+		$$ = $3;
+	}
+	| fixtype
+/*
+	| Lexcept
+	{
+		$$ = mktype(&$1.start, &$1.stop, Texception, nil, nil);
+		$$->cons = 1;
+	}
+	| Lexcept '(' tuplist ')'
+	{
+		$$ = mktype(&$1.start, &$4.stop, Texception, nil, revids($3));
+		$$->cons = 1;
+	}
+*/
+	;
+
+iditype	: Lid
+	{
+		$$ = mkidtype(&$<tok.src>1, $1);
+	}
+	| Lid '[' types ']'
+	{
+		$$ = mkinsttype(&$<tok.src>1, mkidtype(&$<tok.src>1, $1), $3);
+	}
+	;
+
+dotiditype	: type '.' Lid
+	{
+		$$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+	}
+	| type '.' Lid '[' types ']'
+	{
+		$$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+		$$ = mkinsttype(&$1->src, $$, $5);
+	}
+	;
+
+tuplist	: type
+	{
+		$$ = mkids(&$1->src, nil, $1, nil);
+	}
+	| tuplist ',' type
+	{
+		$$ = mkids(&$1->src, nil, $3, $1);
+	}
+	;
+
+polydec	:
+	{
+		$$ = nil;
+	}
+	|	'[' ids ']'
+	{
+		$$ = polydecl($2);
+	}
+	;
+
+fnarg	: '(' forms ')'
+	{
+		$$ = mktype(&$1.start, &$3.stop, Tfn, tnone, $2);
+	}
+	| '(' '*' ')'
+	{
+		$$ = mktype(&$1.start, &$3.stop, Tfn, tnone, nil);
+		$$->varargs = 1;
+	}
+	| '(' ftypes ',' '*' ')'
+	{
+		$$ = mktype(&$1.start, &$5.stop, Tfn, tnone, $2);
+		$$->varargs = 1;
+	}
+	;
+
+fnargret: fnarg %prec ':'
+	{
+		$$ = $1;
+	}
+	| fnarg ':' type
+	{
+		$1->tof = $3;
+		$1->src.stop = $3->src.stop;
+		$$ = $1;
+	}
+	;
+
+fnargretp:	fnargret %prec '='
+	{
+		$$ = $1;
+	}
+	| fnargret Lfor '{' tpolys '}'
+	{
+		$$ = $1;
+		$$->val = rotater($4);
+	}
+	;
+
+forms	:
+	{
+		$$ = nil;
+	}
+	| ftypes
+	;
+
+ftypes	: ftype
+	| ftypes ',' ftype
+	{
+		$$ = appdecls($1, $3);
+	}
+	;
+
+ftype	: nids ':' type
+	{
+		$$ = typeids($1, $3);
+	}
+	| nids ':' adtk
+	{
+		Decl *d;
+
+		$$ = typeids($1, $3);
+		for(d = $$; d != nil; d = d->next)
+			d->implicit = 1;
+	}
+	| idterms ':' type
+	{
+		$$ = mkids(&$1->src, enter("junk", 0), $3, nil);
+		$$->store = Darg;
+		yyerror("illegal argument declaraion");
+	}
+	| idterms ':' adtk
+	{
+		$$ = mkids(&$1->src, enter("junk", 0), $3, nil);
+		$$->store = Darg;
+		yyerror("illegal argument declaraion");
+	}
+	;
+
+nids	: nrids
+	{
+		$$ = revids($1);
+	}
+	;
+
+nrids	: Lid
+	{
+		$$ = mkids(&$<tok.src>1, $1, nil, nil);
+		$$->store = Darg;
+	}
+	| Lnil
+	{
+		$$ = mkids(&$1, nil, nil, nil);
+		$$->store = Darg;
+	}
+	| nrids ',' Lid
+	{
+		$$ = mkids(&$<tok.src>3, $3, nil, $1);
+		$$->store = Darg;
+	}
+	| nrids ',' Lnil
+	{
+		$$ = mkids(&$3, nil, nil, $1);
+		$$->store = Darg;
+	}
+	;
+
+/*
+adttype	: Lid
+	{
+		$$ = mkidtype(&$<tok.src>1, $1);
+	}
+	| adttype '.' Lid
+	{
+		$$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+	}
+	| adttype Lmdot Lid
+	{
+		$$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3);
+	}
+	| Lref adttype
+	{
+		$$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil);
+	}
+	;
+
+adtk	:	Lself adttype
+	{
+		$$ = $2;
+	}
+	;
+*/
+
+adtk	: Lself iditype
+	{
+		$$ = $2;
+	}
+	| Lself Lref iditype
+	{
+		$$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil);
+	}
+	| Lself dotiditype
+	{
+		$$ = $2;
+	}
+	| Lself Lref dotiditype
+	{
+		$$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil);
+	}
+	;
+
+fndef	: fnname fnargretp raises fbody
+	{
+		$$ = fndecl($1, $2, $4);
+		nfns++;
+		/* patch up polydecs */
+		if($1->op == Odot){
+			if($1->right->left != nil){
+				$2->polys = $1->right->left->decl;
+				$1->right->left = nil;
+			}
+			if($1->left->op == Oname && $1->left->left != nil){
+				$$->decl = $1->left->left->decl;
+				$1->left->left = nil;
+			}
+		}
+		else{
+			if($1->left != nil){
+				$2->polys = $1->left->decl;
+				$1->left = nil;
+			}
+		}
+		$2->u.eraises = $3;
+		$$->src = $1->src;
+	}
+	;
+
+raises	: Lraises '(' idlist ')'
+	{
+		$$ = mkn(Otuple, rotater($3), nil);
+		$$->src.start = $1.start;
+		$$->src.stop = $4.stop;
+	}
+	|	Lraises idatom
+	{
+		$$ = mkn(Otuple, mkunary(Oseq, $2), nil);
+		$$->src.start = $1.start;
+		$$->src.stop = $2->src.stop;
+	}
+	|	/* empty */	%prec Lraises
+	{
+		$$ = nil;
+	}
+	;
+
+fbody	: '{' stmts '}'
+	{
+		if($2 == nil){
+			$2 = mkn(Onothing, nil, nil);
+			$2->src.start = curline();
+			$2->src.stop = $2->src.start;
+		}
+		$$ = rotater($2);
+		$$->src.start = $1.start;
+		$$->src.stop = $3.stop;
+	}
+	| error '}'
+	{
+		$$ = mkn(Onothing, nil, nil);
+	}
+	| error '{' stmts '}'
+	{
+		$$ = mkn(Onothing, nil, nil);
+	}
+	;
+
+fnname	: Lid polydec
+	{
+		$$ = mkname(&$<tok.src>1, $1);
+		if($2 != nil){
+			$$->left = mkn(Onothing, nil ,nil);
+			$$->left->decl = $2;
+		}
+	}
+	| fnname '.' Lid polydec
+	{
+		$$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3));
+		if($4 != nil){
+			$$->right->left = mkn(Onothing, nil ,nil);
+			$$->right->left->decl = $4;
+		}
+	}
+	;
+
+stmts	:
+	{
+		$$ = nil;
+	}
+	| stmts decl
+	{
+		if($1 == nil)
+			$$ = $2;
+		else if($2 == nil)
+			$$ = $1;
+		else
+			$$ = mkbin(Oseq, $1, $2);
+	}
+	| stmts stmt
+	{
+		if($1 == nil)
+			$$ = $2;
+		else
+			$$ = mkbin(Oseq, $1, $2);
+	}
+	;
+
+elists	: '(' elist ')'
+	| elists ',' '(' elist ')'
+	;
+
+stmt	: error ';'
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| error '}'
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| error '{' stmts '}'
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| '{' stmts '}'
+	{
+		if($2 == nil){
+			$2 = mkn(Onothing, nil, nil);
+			$2->src.start = curline();
+			$2->src.stop = $2->src.start;
+		}
+		$$ = mkscope(rotater($2));
+	}
+	| elists ':' type ';'
+	{
+		yyerror("illegal declaration");
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| elists ':' type '=' exp';'
+	{
+		yyerror("illegal declaration");
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| zexp ';'
+	{
+		$$ = $1;
+	}
+	| Lif '(' exp ')' stmt
+	{
+		$$ = mkn(Oif, $3, mkunary(Oseq, $5));
+		$$->src.start = $1.start;
+		$$->src.stop = $5->src.stop;
+	}
+	| Lif '(' exp ')' stmt Lelse stmt
+	{
+		$$ = mkn(Oif, $3, mkbin(Oseq, $5, $7));
+		$$->src.start = $1.start;
+		$$->src.stop = $7->src.stop;
+	}
+	| bclab Lfor '(' zexp ';' zexp ';' zexp ')' stmt
+	{
+		$$ = mkunary(Oseq, $10);
+		if($8->op != Onothing)
+			$$->right = $8;
+		$$ = mkbin(Ofor, $6, $$);
+		$$->decl = $1;
+		if($4->op != Onothing)
+			$$ = mkbin(Oseq, $4, $$);
+	}
+	| bclab Lwhile '(' zexp ')' stmt
+	{
+		$$ = mkn(Ofor, $4, mkunary(Oseq, $6));
+		$$->src.start = $2.start;
+		$$->src.stop = $6->src.stop;
+		$$->decl = $1;
+	}
+	| bclab Ldo stmt Lwhile '(' zexp ')' ';'
+	{
+		$$ = mkn(Odo, $6, $3);
+		$$->src.start = $2.start;
+		$$->src.stop = $7.stop;
+		$$->decl = $1;
+	}
+	| Lbreak bctarg ';'
+	{
+		$$ = mkn(Obreak, nil, nil);
+		$$->decl = $2;
+		$$->src = $1;
+	}
+	| Lcont bctarg ';'
+	{
+		$$ = mkn(Ocont, nil, nil);
+		$$->decl = $2;
+		$$->src = $1;
+	}
+	| Lreturn zexp ';'
+	{
+		$$ = mkn(Oret, $2, nil);
+		$$->src = $1;
+		if($2->op == Onothing)
+			$$->left = nil;
+		else
+			$$->src.stop = $2->src.stop;
+	}
+	| Lspawn exp ';'
+	{
+		$$ = mkn(Ospawn, $2, nil);
+		$$->src.start = $1.start;
+		$$->src.stop = $2->src.stop;
+	}
+	| Lraise zexp ';'
+	{
+		$$ = mkn(Oraise, $2, nil);
+		$$->src.start = $1.start;
+		$$->src.stop = $2->src.stop;
+	}
+	| bclab Lcase exp '{' cqstmts '}'
+	{
+		$$ = mkn(Ocase, $3, caselist($5, nil));
+		$$->src = $3->src;
+		$$->decl = $1;
+	}
+	| bclab Lalt '{' qstmts '}'
+	{
+		$$ = mkn(Oalt, caselist($4, nil), nil);
+		$$->src = $2;
+		$$->decl = $1;
+	}
+	| bclab Lpick Lid Ldeclas exp '{' pstmts '}'
+	{
+		$$ = mkn(Opick, mkbin(Odas, mkname(&$<tok.src>3, $3), $5), caselist($7, nil));
+		$$->src.start = $<tok.src>3.start;
+		$$->src.stop = $5->src.stop;
+		$$->decl = $1;
+	}
+	| Lexit ';'
+	{
+		$$ = mkn(Oexit, nil, nil);
+		$$->src = $1;
+	}
+	| '{' stmts '}' Lexcept idexc '{' eqstmts '}'
+	{
+		if($2 == nil){
+			$2 = mkn(Onothing, nil, nil);
+			$2->src.start = curline();
+			$2->src.stop = curline();
+		}
+		$2 = mkscope(rotater($2));
+		$$ = mkbin(Oexstmt, $2, mkn(Oexcept, $5, caselist($7, nil)));
+	}
+/*
+	| stmt Lexcept idexc '{' eqstmts '}'
+	{
+		$$ = mkbin(Oexstmt, $1, mkn(Oexcept, $3, caselist($5, nil)));
+	}
+*/
+	;
+
+bclab	:
+	{
+		$$ = nil;
+	}
+	| ids ':'
+	{
+		if($1->next != nil)
+			yyerror("only one identifier allowed in a label");
+		$$ = $1;
+	}
+	;
+
+bctarg	:
+	{
+		$$ = nil;
+	}
+	| Lid
+	{
+		$$ = mkids(&$<tok.src>1, $1, nil, nil);
+	}
+	;
+
+qstmts	: qbodies stmts
+	{
+		$1->left->right->right = $2;
+		$$ = $1;
+	}
+	;
+
+qbodies	: qual Llabs
+	{
+		$$ = mkunary(Oseq, mkscope(mkunary(Olabel, rotater($1))));
+	}
+	| qbodies stmts qual Llabs
+	{
+		$1->left->right->right = $2;
+		$$ = mkbin(Oseq, mkscope(mkunary(Olabel, rotater($3))), $1);
+	}
+	;
+
+cqstmts	: cqbodies stmts
+	{
+		$1->left->right = mkscope($2);
+		$$ = $1;
+	}
+	;
+
+cqbodies	: qual Llabs
+	{
+		$$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+	}
+	| cqbodies stmts qual Llabs
+	{
+		$1->left->right = mkscope($2);
+		$$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+	}
+	;
+
+eqstmts	: eqbodies stmts
+	{
+		$1->left->right = mkscope($2);
+		$$ = $1;
+	}
+	;
+
+eqbodies	: qual Llabs
+	{
+		$$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+	}
+	| eqbodies stmts qual Llabs
+	{
+		$1->left->right = mkscope($2);
+		$$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+	}
+	;
+
+qual	: exp
+	| exp Lto exp
+	{
+		$$ = mkbin(Orange, $1, $3);
+	}
+	| '*'
+	{
+		$$ = mkn(Owild, nil, nil);
+		$$->src = $1;
+	}
+	| qual Lor qual
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	| error
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	;
+
+pstmts	: pbodies stmts
+	{
+		$1->left->right = mkscope($2);
+		$$ = $1;
+	}
+	;
+
+pbodies	: pqual Llabs
+	{
+		$$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+	}
+	| pbodies stmts pqual Llabs
+	{
+		$1->left->right = mkscope($2);
+		$$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+	}
+	;
+
+pqual	: Lid
+	{
+		$$ = mkname(&$<tok>1.src, $1);
+	}
+	| '*'
+	{
+		$$ = mkn(Owild, nil, nil);
+		$$->src = $1;
+	}
+	| pqual Lor pqual
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	| error
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	;
+
+zexp	:
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = curline();
+		$$->src.stop = $$->src.start;
+	}
+	| exp
+	;
+
+exp	: monexp
+	| exp '=' exp
+	{
+		$$ = mkbin(Oas, $1, $3);
+	}
+	| exp Landeq exp
+	{
+		$$ = mkbin(Oandas, $1, $3);
+	}
+	| exp Loreq exp
+	{
+		$$ = mkbin(Ooras, $1, $3);
+	}
+	| exp Lxoreq exp
+	{
+		$$ = mkbin(Oxoras, $1, $3);
+	}
+	| exp Llsheq exp
+	{
+		$$ = mkbin(Olshas, $1, $3);
+	}
+	| exp Lrsheq exp
+	{
+		$$ = mkbin(Orshas, $1, $3);
+	}
+	| exp Laddeq exp
+	{
+		$$ = mkbin(Oaddas, $1, $3);
+	}
+	| exp Lsubeq exp
+	{
+		$$ = mkbin(Osubas, $1, $3);
+	}
+	| exp Lmuleq exp
+	{
+		$$ = mkbin(Omulas, $1, $3);
+	}
+	| exp Ldiveq exp
+	{
+		$$ = mkbin(Odivas, $1, $3);
+	}
+	| exp Lmodeq exp
+	{
+		$$ = mkbin(Omodas, $1, $3);
+	}
+	| exp Lexpeq exp
+	{
+		$$ = mkbin(Oexpas, $1, $3);
+	}
+	| exp Lcomm '=' exp
+	{
+		$$ = mkbin(Osnd, $1, $4);
+	}
+	| exp Ldeclas exp
+	{
+		$$ = mkbin(Odas, $1, $3);
+	}
+	| Lload Lid exp %prec Lload
+	{
+		$$ = mkn(Oload, $3, nil);
+		$$->src.start = $<tok.src.start>1;
+		$$->src.stop = $3->src.stop;
+		$$->ty = mkidtype(&$<tok.src>2, $2);
+	}
+	| exp Lexp exp
+	{
+		$$ = mkbin(Oexp, $1, $3);
+	}
+	| exp '*' exp
+	{
+		$$ = mkbin(Omul, $1, $3);
+	}
+	| exp '/' exp
+	{
+		$$ = mkbin(Odiv, $1, $3);
+	}
+	| exp '%' exp
+	{
+		$$ = mkbin(Omod, $1, $3);
+	}
+	| exp '+' exp
+	{
+		$$ = mkbin(Oadd, $1, $3);
+	}
+	| exp '-' exp
+	{
+		$$ = mkbin(Osub, $1, $3);
+	}
+	| exp Lrsh exp
+	{
+		$$ = mkbin(Orsh, $1, $3);
+	}
+	| exp Llsh exp
+	{
+		$$ = mkbin(Olsh, $1, $3);
+	}
+	| exp '<' exp
+	{
+		$$ = mkbin(Olt, $1, $3);
+	}
+	| exp '>' exp
+	{
+		$$ = mkbin(Ogt, $1, $3);
+	}
+	| exp Lleq exp
+	{
+		$$ = mkbin(Oleq, $1, $3);
+	}
+	| exp Lgeq exp
+	{
+		$$ = mkbin(Ogeq, $1, $3);
+	}
+	| exp Leq exp
+	{
+		$$ = mkbin(Oeq, $1, $3);
+	}
+	| exp Lneq exp
+	{
+		$$ = mkbin(Oneq, $1, $3);
+	}
+	| exp '&' exp
+	{
+		$$ = mkbin(Oand, $1, $3);
+	}
+	| exp '^' exp
+	{
+		$$ = mkbin(Oxor, $1, $3);
+	}
+	| exp '|' exp
+	{
+		$$ = mkbin(Oor, $1, $3);
+	}
+	| exp Lcons exp
+	{
+		$$ = mkbin(Ocons, $1, $3);
+	}
+	| exp Landand exp
+	{
+		$$ = mkbin(Oandand, $1, $3);
+	}
+	| exp Loror exp
+	{
+		$$ = mkbin(Ooror, $1, $3);
+	}
+	;
+
+monexp	: term
+	| '+' monexp
+	{
+		$2->src.start = $1.start;
+		$$ = $2;
+	}
+	| '-' monexp
+	{
+		$$ = mkunary(Oneg, $2);
+		$$->src.start = $1.start;
+	}
+	| '!' monexp
+	{
+		$$ = mkunary(Onot, $2);
+		$$->src.start = $1.start;
+	}
+	| '~' monexp
+	{
+		$$ = mkunary(Ocomp, $2);
+		$$->src.start = $1.start;
+	}
+	| '*' monexp
+	{
+		$$ = mkunary(Oind, $2);
+		$$->src.start = $1.start;
+	}
+	| Linc monexp
+	{
+		$$ = mkunary(Opreinc, $2);
+		$$->src.start = $1.start;
+	}
+	| Ldec monexp
+	{
+		$$ = mkunary(Opredec, $2);
+		$$->src.start = $1.start;
+	}
+	| Lcomm monexp
+	{
+		$$ = mkunary(Orcv, $2);
+		$$->src.start = $1.start;
+	}
+	| Lhd monexp
+	{
+		$$ = mkunary(Ohd, $2);
+		$$->src.start = $1.start;
+	}
+	| Ltl monexp
+	{
+		$$ = mkunary(Otl, $2);
+		$$->src.start = $1.start;
+	}
+	| Llen monexp
+	{
+		$$ = mkunary(Olen, $2);
+		$$->src.start = $1.start;
+	}
+	| Lref monexp
+	{
+		$$ = mkunary(Oref, $2);
+		$$->src.start = $1.start;
+	}
+	| Ltagof monexp
+	{
+		$$ = mkunary(Otagof, $2);
+		$$->src.start = $1.start;
+	}
+	| Larray '[' exp ']' Lof type
+	{
+		$$ = mkn(Oarray, $3, nil);
+		$$->ty = mktype(&$1.start, &$6->src.stop, Tarray, $6, nil);
+		$$->src = $$->ty->src;
+	}
+	| Larray '[' exp ']' Lof '{' initlist '}'
+	{
+		$$ = mkn(Oarray, $3, $7);
+		$$->src.start = $1.start;
+		$$->src.stop = $8.stop;
+	}
+	| Larray '[' ']' Lof '{' initlist '}'
+	{
+		$$ = mkn(Onothing, nil, nil);
+		$$->src.start = $2.start;
+		$$->src.stop = $3.stop;
+		$$ = mkn(Oarray, $$, $6);
+		$$->src.start = $1.start;
+		$$->src.stop = $7.stop;
+	}
+	| Llist Lof '{' celist '}'
+	{
+		$$ = etolist($4);
+		$$->src.start = $1.start;
+		$$->src.stop = $5.stop;
+	}
+	| Lchan Lof type
+	{
+		$$ = mkn(Ochan, nil, nil);
+		$$->ty = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil);
+		$$->src = $$->ty->src;
+	}
+	| Lchan '[' exp ']' Lof type
+	{
+		$$ = mkn(Ochan, $3, nil);
+		$$->ty = mktype(&$1.start, &$6->src.stop, Tchan, $6, nil);
+		$$->src = $$->ty->src;
+	}
+	| Larray Lof Ltid monexp
+	{
+		$$ = mkunary(Ocast, $4);
+		$$->ty = mktype(&$1.start, &$4->src.stop, Tarray, mkidtype(&$<tok.src>3, $3), nil);
+		$$->src = $$->ty->src;
+	}
+	| Ltid monexp
+	{
+		$$ = mkunary(Ocast, $2);
+		$$->src.start = $<tok.src>1.start;
+		$$->ty = mkidtype(&$$->src, $1);
+	}
+	| Lid monexp
+	{
+		$$ = mkunary(Ocast, $2);
+		$$->src.start = $<tok.src>1.start;
+		$$->ty = mkidtype(&$$->src, $1);
+	}
+	| fixtype monexp
+	{
+		$$ = mkunary(Ocast, $2);
+		$$->src.start = $<tok.src>1.start;
+		$$->ty = $1;
+	}
+	;
+
+term	: idatom
+	| term '(' zelist ')'
+	{
+		$$ = mkn(Ocall, $1, $3);
+		$$->src.start = $1->src.start;
+		$$->src.stop = $4.stop;
+	}
+	| '(' elist ')'
+	{
+		$$ = $2;
+		if($2->op == Oseq)
+			$$ = mkn(Otuple, rotater($2), nil);
+		else
+			$$->flags |= PARENS;
+		$$->src.start = $1.start;
+		$$->src.stop = $3.stop;
+	}
+	| term '.' Lid
+	{
+		$$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3));
+	}
+	| term Lmdot term
+	{
+		$$ = mkbin(Omdot, $1, $3);
+	}
+	| term '[' export ']'
+	{
+		$$ = mkbin(Oindex, $1, $3);
+		$$->src.stop = $4.stop;
+	}
+	| term '[' zexp ':' zexp ']'
+	{
+		if($3->op == Onothing)
+			$3->src = $4;
+		if($5->op == Onothing)
+			$5->src = $4;
+		$$ = mkbin(Oslice, $1, mkbin(Oseq, $3, $5));
+		$$->src.stop = $6.stop;
+	}
+	| term Linc
+	{
+		$$ = mkunary(Oinc, $1);
+		$$->src.stop = $2.stop;
+	}
+	| term Ldec
+	{
+		$$ = mkunary(Odec, $1);
+		$$->src.stop = $2.stop;
+	}
+	| Lsconst
+	{
+		$$ = mksconst(&$<tok.src>1, $1);
+	}
+	| Lconst
+	{
+		$$ = mkconst(&$<tok.src>1, $1);
+		if($1 > 0x7fffffff || $1 < -0x7fffffff)
+			$$->ty = tbig;
+		$$ = $$;
+	}
+	| Lrconst
+	{
+		$$ = mkrconst(&$<tok.src>1, $1);
+	}
+	| term '[' exportlist ',' export ']'
+	{
+		$$ = mkbin(Oindex, $1, rotater(mkbin(Oseq, $3, $5)));
+		$$->src.stop = $6.stop;
+	}
+	;
+
+idatom	: Lid
+	{
+		$$ = mkname(&$<tok.src>1, $1);
+	}
+	| Lnil
+	{
+		$$ = mknil(&$<tok.src>1);
+	}
+	;
+
+idterm	: '(' idlist ')'
+	{
+		$$ = mkn(Otuple, rotater($2), nil);
+		$$->src.start = $1.start;
+		$$->src.stop = $3.stop;
+	}
+	;
+
+exportlist	: export
+	| exportlist ',' export
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	;
+
+export	: exp
+	|	texp
+	;
+
+texp	:	Ltid
+	{
+		$$ = mkn(Otype, nil, nil);
+		$$->ty = mkidtype(&$<tok.src>1, $1);
+		$$->src = $$->ty->src;
+	}
+	| Larray Lof type
+	{
+		$$ = mkn(Otype, nil, nil);
+		$$->ty = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil);
+		$$->src = $$->ty->src;
+	}
+	| Llist Lof type
+	{
+		$$ = mkn(Otype, nil, nil);
+		$$->ty = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil);
+		$$->src = $$->ty->src;
+	}
+	| Lcyclic type
+	{
+		$$ = mkn(Otype, nil ,nil);
+		$$->ty = $2;
+		$$->ty->flags |= CYCLIC;
+		$$->src = $$->ty->src;
+	}
+	;
+
+idexc	: Lid
+	{
+		$$ = mkname(&$<tok.src>1, $1);
+	}
+	|	/* empty */
+	{
+		$$ = nil;
+	}
+	;
+
+idlist	: idterm
+	| idatom
+	| idlist ',' idterm
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	| idlist ',' idatom
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	;
+
+zelist	:
+	{
+		$$ = nil;
+	}
+	| elist
+	{
+		$$ = rotater($1);
+	}
+	;
+
+celist	: elist
+	| elist ','
+	;
+
+elist	: exp
+	| elist ',' exp
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	;
+
+initlist	: elemlist
+	{
+		$$ = rotater($1);
+	}
+	| elemlist ','
+	{
+		$$ = rotater($1);
+	}
+	;
+
+elemlist	: elem
+	| elemlist ',' elem
+	{
+		$$ = mkbin(Oseq, $1, $3);
+	}
+	;
+
+elem	: exp
+	{
+		$$ = mkn(Oelem, nil, $1);
+		$$->src = $1->src;
+	}
+	| qual Llabs exp
+	{
+		$$ = mkbin(Oelem, rotater($1), $3);
+	}
+	;
+
+/*
+tpoly	: ids Llabs '{' dfields '}'
+	{
+		$$ = typedecl($1, mktype(&$1->src.start, &$5.stop, Tpoly, nil, nil));
+		$$->left = rotater($4);
+	}
+	;
+
+tpolys	: tpoly
+	{
+		$$ = $1;
+	}
+	| tpolys tpoly
+	{
+		$$ = mkbin(Oseq, $1, $2);
+	}
+	;
+*/
+
+tpolys	: tpoly dfields
+	{
+		if($1->op == Oseq)
+			$1->right->left = rotater($2);
+		else
+			$1->left = rotater($2);
+		$$ = $1;
+	}
+	;
+
+tpoly	: ids Llabs
+	{
+		$$ = typedecl($1, mktype(&$1->src.start, &$2.stop, Tpoly, nil, nil));
+	}
+	| tpoly dfields ids Llabs
+	{
+		if($1->op == Oseq)
+			$1->right->left = rotater($2);
+		else
+			$1->left = rotater($2);
+		$$ = mkbin(Oseq, $1, typedecl($3, mktype(&$3->src.start, &$4.stop, Tpoly, nil, nil)));
+	}
+	;
+
+%%
+
+static	char	*mkfileext(char*, char*, char*);
+static	void	usage(void);
+
+static	int	dosym;
+static	int	toterrors;
+static	ulong	canonnanbits[] = { 0x7fffffff, 0xffffffff};
+static	char*	infile;
+
+#define SLASHMOD	"/module"
+
+static char*
+getroot(void)
+{
+	int n;
+	char *e, *l, *s;
+
+	if((e = getenv("EMU")) != nil){
+		for(s = e; *e != '\0'; e++){
+			if(*e == '-' && *(e+1) == 'r' && (e == s || *(e-1) == ' ' || *(e-1) == '\t')){
+				e += 2;
+				l = strchr(e, ' ');
+				if(l != nil)
+					*l = '\0';
+				if((n = strlen(e)) > 0){
+					s = malloc(n+1);
+					strcpy(s, e);
+					return s;
+				}
+			}
+		}
+	}
+	if((e = getenv("ROOT")) != nil)
+		return strdup(e);
+	return nil;
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *s, *ofile, *ext, *root;
+	int i;
+
+	FPinit();
+	FPcontrol(0, INVAL|ZDIV|OVFL|UNFL|INEX);
+	canonnan = canontod(canonnanbits);
+
+	fmtinstall('D', dotconv);
+	fmtinstall('I', instconv);
+	fmtinstall('K', declconv);
+	fmtinstall('k', storeconv);
+	fmtinstall('L', lineconv);
+	fmtinstall('M', mapconv);
+	fmtinstall('n', nodeconv);		/* exp structure */
+	fmtinstall('O', opconv);
+	fmtinstall('g', gfltconv);
+	fmtinstall('Q', etconv);		/* src expression with type */
+	fmtinstall('R', ctypeconv);		/* c equivalent type */
+	fmtinstall('P', ctypeconv);		/* c equivalent type - pointer type */
+	fmtinstall('T', typeconv);		/* source style types */
+	fmtinstall('t', stypeconv);		/* structurally descriptive type */
+	fmtinstall('U', srcconv);
+	fmtinstall('v', expconv);		/* src expression */
+	fmtinstall('V', expconv);		/* src expression in '' */
+	lexinit();
+	typeinit();
+	optabinit();
+
+	gendis = 1;
+	asmsym = 0;
+	maxerr = 20;
+	ofile = nil;
+	ext = nil;
+	ARGBEGIN{
+	case 'D':
+		/*
+		 * debug flags:
+		 *
+		 * a	alt compilation
+		 * A	array constructor compilation
+		 * b	boolean and branch compilation
+		 * c	case compilation
+		 * d	function declaration
+		 * D	descriptor generation
+		 * e	expression compilation
+		 * E	addressable expression compilation
+		 * f	print arguments for compiled functions
+		 * F	constant folding
+		 * g	print out globals
+		 * m	module declaration and type checking
+		 * n	nil references
+		 * s	print sizes of output file sections
+		 * S	type signing
+		 * t	type checking function bodies
+		 * T	timing
+		 * v	global var and constant compilation
+		 * x	adt verification
+		 * Y	tuple compilation
+		 * z Z	bug fixes
+		 */
+		s = ARGF();
+		while(s && *s)
+			debug[*s++] = 1;
+		break;
+	case 'I':
+		s = ARGF();
+		if(s == nil)
+			usage();
+		addinclude(s);
+		break;
+	case 'G':
+		asmsym = 1;
+		break;
+	case 'S':
+		gendis = 0;
+		break;
+	case 'a':
+		emitstub = 1;
+		break;
+	case 'A':
+		emitstub = emitdyn = 1;
+		break;
+	case 'c':
+		mustcompile = 1;
+		break;
+	case 'C':
+		dontcompile = 1;
+		break;
+	case 'e':
+		maxerr = 1000;
+		break;
+	case 'f':
+		isfatal = 1;
+		break;
+	case 'F':
+		newfnptr = 1;
+		break;
+	case 'g':
+		dosym = 1;
+		break;
+	case 'i':
+		dontinline = 1;
+		break;
+	case 'o':
+		ofile = ARGF();
+		break;
+	case 'O':
+		optims = 1;
+		break;
+	case 's':
+		s = ARGF();
+		if(s != nil)
+			fixss = atoi(s);
+		break;
+	case 't':
+		emittab = ARGF();
+		if(emittab == nil)
+			usage();
+		break;
+	case 'T':
+		emitcode = ARGF();
+		if(emitcode == nil)
+			usage();
+		break;
+	case 'd':
+		emitcode = ARGF();
+		if(emitcode == nil)
+			usage();
+		emitdyn = 1;
+		break;
+	case 'w':
+		superwarn = dowarn;
+		dowarn = 1;
+		break;
+	case 'x':
+		ext = ARGF();
+		break;
+	case 'X':
+		signdump = ARGF();
+		break;
+	case 'y':
+		oldcycles = 1;
+		break;
+	case 'z':
+		arrayz = 1;
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND
+
+	if((root = getroot()) != nil){
+		char *r;
+
+		r = malloc(strlen(root)+strlen(SLASHMOD)+1);
+		strcpy(r, root);
+		strcat(r, SLASHMOD);
+		addinclude(r);
+		free(root);
+	}
+	else
+		addinclude(INCPATH);
+
+	if(argc == 0){
+		usage();
+	}else if(ofile != nil){
+		if(argc != 1)
+			usage();
+		translate(argv[0], ofile, mkfileext(ofile, ".dis", ".sbl"));
+	}else{
+		if(ext == nil){
+			ext = ".s";
+			if(gendis)
+				ext = ".dis";
+		}
+		for(i = 0; i < argc; i++){
+			s = strrchr(argv[i], '/');
+			if(s == nil)
+				s = argv[i];
+			else
+				s++;
+			if(argc > 1)
+				print("%s:\n", argv[i]);
+			ofile = mkfileext(s, ".b", ext);
+			translate(argv[i], ofile, mkfileext(ofile, ext, ".sbl"));
+		}
+	}
+	if(toterrors)
+		exits("errors");
+	exits(0);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: limbo [-CGSacgwe] [-I incdir] [-o outfile] [-{T|t|d} module] [-D debug] file ...\n");
+	exits("usage");
+}
+
+static char*
+mkfileext(char *file, char *oldext, char *ext)
+{
+	char *ofile;
+	int n, n2;
+
+	n = strlen(file);
+	n2 = strlen(oldext);
+	if(n >= n2 && strcmp(&file[n-n2], oldext) == 0)
+		n -= n2;
+	ofile = malloc(n + strlen(ext) + 1);
+	memmove(ofile, file, n);
+	strcpy(ofile+n, ext);
+	return ofile;
+}
+
+void
+translate(char *in, char *out, char *dbg)
+{
+	Decl *entry;
+	int doemit;
+
+	infile = in;
+	outfile = out;
+	symfile = dbg;
+	errors = 0;
+	bins[0] = Bopen(in, OREAD);
+	if(bins[0] == nil){
+		fprint(2, "can't open %s: %r\n", in);
+		toterrors++;
+		return;
+	}
+	doemit = emitstub || emittab || emitcode;
+	if(!doemit){
+		bout = Bopen(out, OWRITE);
+		if(bout == nil){
+			fprint(2, "can't open %s: %r\n", out);
+			toterrors++;
+			Bterm(bins[0]);
+			return;
+		}
+		if(dosym){
+			bsym = Bopen(dbg, OWRITE);
+			if(bsym == nil)
+				fprint(2, "can't open %s: %r\n", dbg);
+		}
+	}
+
+	lexstart(in);
+
+	popscopes();
+	typestart();
+	declstart();
+
+	yyparse();
+
+	entry = typecheck(!doemit);
+
+	modcom(entry);
+
+	fns = nil;
+	nfns = 0;
+	descriptors = nil;
+
+	if(bout != nil)
+		Bterm(bout);
+	if(bsym != nil)
+		Bterm(bsym);
+	toterrors += errors;
+	if(errors && bout != nil)
+		remove(out);
+	if(errors && bsym != nil)
+		remove(dbg);
+}
+
+void
+trapFPE(unsigned exception[5], int value[2])
+{
+	/* can't happen; it's just here to keep FPinit happy. */
+	USED(exception);
+	USED(value);
+}
+
+static char *
+win2inf(char *s)
+{
+	int nt = 0;
+	char *t;
+
+	if(strlen(s) > 1 && s[1] == ':'){
+		s[1] = '/';
+		s++;
+		nt = 1;
+	}
+	for(t = s; *t != '\0'; t++){
+		if(*t == '\\')
+			*t = '/';
+		if(nt)
+			*t = tolower(*t);
+	}
+	return s;
+}
+
+static char *
+cleann(char *s)
+{
+	char *p, *r, *t;
+	char buf[256];
+
+	r = t = malloc(strlen(s)+1);
+	strcpy(t, s);
+	t = win2inf(t);
+	if(*t != '/'){
+		p = win2inf(getwd(buf, sizeof(buf)));
+		s = malloc(strlen(p)+strlen(t)+2);
+		strcpy(s, p);
+		strcat(s, "/");
+		strcat(s, t);
+	}
+	else{
+		s = malloc(strlen(t)+1);
+		strcpy(s, t);
+	}
+	free(r);
+	/* print("cleann: %s\n", p); */
+	return cleanname(s);
+}
+
+char *
+srcpath(char *name, int nlen)
+{
+	int l1, l2;
+	char *r, *srcp, *t;
+
+	srcp = cleann(infile);
+	r = getroot();
+	if(r == nil){
+		l1 = strlen(INCPATH);
+		r = malloc(l1+1);
+		strcpy(r, INCPATH);
+		if(l1 >= strlen(SLASHMOD) && strcmp(r+l1-strlen(SLASHMOD), SLASHMOD) == 0)
+			r[l1-strlen(SLASHMOD)] = '\0';
+	}
+	t = cleann(r);
+	free(r);
+	r = t;
+	/* srcp relative to r */
+	l1 = strlen(srcp);
+	l2 = strlen(r);
+	if(l1 >= l2 && strncmp(srcp, r, l2) == 0){
+		/* nothing to do */
+	}else
+		l2 = 0;
+	strncpy(name, srcp+l2, nlen);
+	name[nlen-1] = '\0';
+	free(r);
+	free(srcp);
+	/* print("srcpath: %s\n", name); */
+	return name;
+}
--- /dev/null
+++ b/limbo/mkfile
@@ -1,0 +1,41 @@
+<../mkconfig
+
+TARG=limbo
+
+OFILES=	asm.$O\
+	com.$O\
+	decls.$O\
+	dis.$O\
+	dtocanon.$O\
+	ecom.$O\
+	gen.$O\
+	lex.$O\
+	nodes.$O\
+	optab.$O\
+	optim.$O\
+	sbl.$O\
+	stubs.$O\
+	typecheck.$O\
+	types.$O\
+	y.tab.$O\
+
+HFILES=	limbo.h\
+	fns.h\
+	y.tab.h\
+	$ROOT/include/interp.h\
+	$ROOT/include/isa.h\
+
+LIBS=	bio\
+	math\
+	sec\
+	mp\
+	9\
+
+YFILES=	limbo.y
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS='-DINCPATH="'$ROOT/module'"' $CFLAGS
+YFLAGS=-d
--- /dev/null
+++ b/limbo/nodes.c
@@ -1,0 +1,1538 @@
+#include "limbo.h"
+
+static vlong
+ipow(vlong x, int n)
+{
+	int inv;
+	vlong r;
+
+	inv = 0;
+	if(n < 0){
+		n = -n;
+		inv = 1;
+	}
+	r = 1;
+	for(;;){
+		if(n&1)
+			r *= x;
+		if((n >>= 1) == 0)
+			break;
+		x *= x;
+	}
+	if(inv)
+		r = 1/r;
+	return r;
+}
+
+double
+rpow(double x, int n)
+{
+	int inv;
+	double r;
+
+	inv = 0;
+	if(n < 0){
+		n = -n;
+		inv = 1;
+	}
+	r = 1;
+	for(;;){
+		if(n&1)
+			r *= x;
+		if((n >>= 1) == 0)
+			break;
+		x *= x;
+	}
+	if(inv)
+		r = 1/r;
+	return r;
+}
+
+Long
+real2fix(double v, Type *t)
+{
+	v /= scale(t);
+	v = v < 0 ? v-0.5: v+0.5;
+	return v;
+}
+
+Long
+fix2fix(Long v, Type *f, Type *t)
+{
+	double r;
+
+	r = (double)v * (scale(f)/scale(t));
+	r = r < 0 ? r-0.5: r+0.5;
+	return r;
+}
+
+double
+fix2real(Long v, Type *f)
+{
+	return (double)v * scale(f);
+}
+
+int
+istuple(Node *n)
+{
+	Decl *d;
+
+	switch(n->op){
+	case Otuple:
+		return 1;
+	case Oname:
+		d = n->decl;
+		if(d->importid != nil)
+			d = d->importid;
+		return d->store == Dconst && (n->ty->kind == Ttuple || n->ty->kind == Tadt);
+	case Odot:
+		return 0;	/* istuple(n->left); */
+	}
+	return 0;
+}
+
+static Node*
+tuplemem(Node *n, Decl *d)
+{
+	Type *ty;
+	Decl *ids;
+
+	ty = n->ty;
+	n = n->left;
+	for(ids = ty->ids; ids != nil; ids = ids->next){
+		if(ids->sym == d->sym)
+			break;
+		else
+			n = n->right;
+	}
+	if(n == nil)
+		fatal("tuplemem cannot cope !\n");
+	return n->left;
+}
+
+int
+varcom(Decl *v)
+{
+	Node *n, tn;
+
+	n = v->init;
+	n = fold(n);
+	v->init = n;
+	if(debug['v'])
+		print("variable '%D' val %V\n", v, n);
+	if(n == nil)
+		return 1;
+
+	tn = znode;
+	tn.op = Oname;
+	tn.decl = v;
+	tn.src = v->src;
+	tn.ty = v->ty;
+	return initable(&tn, n, 0);
+}
+
+int
+initable(Node *v, Node *n, int allocdep)
+{
+	Node *e;
+
+	switch(n->ty->kind){
+	case Tiface:
+	case Tgoto:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Talt:
+	case Texcept:
+		return 1;
+	case Tint:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+	case Tstring:
+	case Tfix:
+		if(n->op != Oconst)
+			break;
+		return 1;
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+		if(n->op == Otuple)
+			n = n->left;
+		else if(n->op == Ocall)
+			n = n->right;
+		else
+			break;
+		for(; n != nil; n = n->right)
+			if(!initable(v, n->left, allocdep))
+				return 0;
+		return 1;
+	case Tarray:
+		if(n->op != Oarray)
+			break;
+		if(allocdep >= DADEPTH){
+			nerror(v, "%Vs initializer has arrays nested more than %d deep", v, allocdep);
+			return 0;
+		}
+		allocdep++;
+		usedesc(mktdesc(n->ty->tof));
+		if(n->left->op != Oconst){
+			nerror(v, "%Vs size is not a constant", v);
+			return 0;
+		}
+		for(e = n->right; e != nil; e = e->right)
+			if(!initable(v, e->left->right, allocdep))
+				return 0;
+		return 1;
+	case Tany:
+		return 1;
+	case Tref:
+	case Tlist:
+	case Tpoly:
+	default:
+		nerror(v, "can't initialize %Q", v);
+		return 0;
+	}
+	nerror(v, "%Vs initializer, %V, is not a constant expression", v, n);
+	return 0;
+}
+
+/*
+ * merge together two sorted lists, yielding a sorted list
+ */
+static Node*
+elemmerge(Node *e, Node *f)
+{
+	Node rock, *r;
+
+	r = &rock;
+	while(e != nil && f != nil){
+		if(e->left->left->val <= f->left->left->val){
+			r->right = e;
+			e = e->right;
+		}else{
+			r->right = f;
+			f = f->right;
+		}
+		r = r->right;
+	}
+	if(e != nil)
+		r->right = e;
+	else
+		r->right = f;
+	return rock.right;
+}
+
+/*
+ * recursively split lists and remerge them after they are sorted
+ */
+static Node*
+recelemsort(Node *e, int n)
+{
+	Node *r, *ee;
+	int i, m;
+
+	if(n <= 1)
+		return e;
+	m = n / 2 - 1;
+	ee = e;
+	for(i = 0; i < m; i++)
+		ee = ee->right;
+	r = ee->right;
+	ee->right = nil;
+	return elemmerge(recelemsort(e, n / 2),
+			recelemsort(r, (n + 1) / 2));
+}
+
+/*
+ * sort the elems by index; wild card is first
+ */
+Node*
+elemsort(Node *e)
+{
+	Node *ee;
+	int n;
+
+	n = 0;
+	for(ee = e; ee != nil; ee = ee->right){
+		if(ee->left->left->op == Owild)
+			ee->left->left->val = -1;
+		n++;
+	}
+	return recelemsort(e, n);
+}
+
+int
+sametree(Node *n1, Node *n2)
+{
+	if(n1 == n2)
+		return 1;
+	if(n1 == nil || n2 == nil)
+		return 0;
+	if(n1->op != n2->op || n1->ty != n2->ty)
+		return 0;
+	if(n1->op == Oconst){
+		switch(n1->ty->kind){
+		case Tbig:
+		case Tbyte:
+		case Tint:
+			return n1->val == n2->val;
+		case Treal:
+			return n1->rval == n2->rval;
+		case Tfix:
+			return n1->val == n2->val && tequal(n1->ty, n2->ty);
+		case Tstring:
+			return n1->decl->sym == n2->decl->sym;
+		}
+		return 0;
+	}
+	return n1->decl == n2->decl && sametree(n1->left, n2->left) && sametree(n1->right, n2->right);
+}
+
+int
+occurs(Decl *d, Node *n)
+{
+	if(n == nil)
+		return 0;
+	if(n->op == Oname){
+		if(d == n->decl)
+			return 1;
+		return 0;
+	}
+	return occurs(d, n->left) + occurs(d, n->right);
+}
+
+/*
+ * left and right subtrees the same
+ */
+Node*
+folds(Node *n)
+{
+	if(hasside(n, 1))
+		return n;
+	switch(n->op){
+	case Oeq:
+	case Oleq:
+	case Ogeq:
+		n->val = 1;
+		break;
+	case Osub:
+		n->val = 0;
+		n->rval = 0.0;
+		break;
+	case Oxor:
+	case Oneq:
+	case Olt:
+	case Ogt:
+		n->val = 0;
+		break;
+	case Oand:
+	case Oor:
+	case Oandand:
+	case Ooror:
+		return n->left;
+	default:
+		return n;
+	}
+	n->op = Oconst;
+	n->left = n->right = nil;
+	n->decl = nil;
+	return n;
+}
+
+/*
+ * constant folding for typechecked expressions,
+ */
+Node*
+fold(Node *n)
+{
+	if(n == nil)
+		return nil;
+	if(debug['F'])
+		print("fold %n\n", n);
+	n = efold(n);
+	if(debug['F'])
+		print("folded %n\n", n);
+	return n;
+}
+
+Node*
+efold(Node *n)
+{
+	Decl *d;
+	Node *left, *right;
+
+	if(n == nil)
+		return nil;
+
+	left = n->left;
+	right = n->right;
+	switch(n->op){
+	case Oname:
+		d = n->decl;
+		if(d->importid != nil)
+			d = d->importid;
+		if(d->store != Dconst){
+			if(d->store == Dtag){
+				n->op = Oconst;
+				n->ty = tint;
+				n->val = d->tag;
+			}
+			break;
+		}
+		switch(n->ty->kind){
+		case Tbig:
+			n->op = Oconst;
+			n->val = d->init->val;
+			break;
+		case Tbyte:
+			n->op = Oconst;
+			n->val = d->init->val & 0xff;
+			break;
+		case Tint:
+		case Tfix:
+			n->op = Oconst;
+			n->val = d->init->val;
+			break;
+		case Treal:
+			n->op = Oconst;
+			n->rval = d->init->rval;
+			break;
+		case Tstring:
+			n->op = Oconst;
+			n->decl = d->init->decl;
+			break;
+		case Ttuple:
+			*n = *d->init;
+			break;
+		case Tadt:
+			*n = *d->init;
+			n = rewrite(n);	/* was call */
+			break;
+		case Texception:
+			if(!n->ty->cons)
+				fatal("non-const exception type in efold");
+			n->op = Oconst;
+			break;
+		default:
+			fatal("unknown const type %T in efold", n->ty);
+			break;
+		}
+		break;
+	case Oadd:
+		left = efold(left);
+		right = efold(right);
+		n->left = left;
+		n->right = right;
+		if(n->ty == tstring && right->op == Oconst){
+			if(left->op == Oconst)
+				n = mksconst(&n->src, stringcat(left->decl->sym, right->decl->sym));
+			else if(left->op == Oadd && left->ty == tstring && left->right->op == Oconst){
+				left->right = mksconst(&n->src, stringcat(left->right->decl->sym, right->decl->sym));
+				n = left;
+			}
+		}
+		break;
+	case Olen:
+		left = efold(left);
+		n->left = left;
+		if(left->ty == tstring && left->op == Oconst)
+			n = mkconst(&n->src, utflen(left->decl->sym->name));
+		break;
+	case Oslice:
+		if(right->left->op == Onothing)
+			right->left = mkconst(&right->left->src, 0);
+		n->left = efold(left);
+		n->right = efold(right);
+		break;
+	case Oinds:
+		n->left = left = efold(left);
+		n->right = right = efold(right);
+		if(right->op == Oconst && left->op == Oconst){
+			;
+		}
+		break;
+	case Ocast:
+		n->op = Ocast;
+		left = efold(left);
+		n->left = left;
+		if(n->ty == left->ty || n->ty->kind == Tfix && tequal(n->ty, left->ty))
+			return left;
+		if(left->op == Oconst)
+			return foldcast(n, left);
+		break;
+	case Odot:
+	case Omdot:
+		/*
+		 * what about side effects from left?
+		 */
+		d = right->decl;
+		switch(d->store){
+		case Dconst:
+		case Dtag:
+		case Dtype:
+			/*
+			 * set it up as a name and let that case do the hard work
+			 */
+			n->op = Oname;
+			n->decl = d;
+			n->left = nil;
+			n->right = nil;
+			return efold(n);
+		}
+		n->left = efold(left);
+		if(n->left->op == Otuple)
+			n = tuplemem(n->left, d);
+		else
+			n->right = efold(right);
+		break;
+	case Otagof:
+		if(n->decl != nil){
+			n->op = Oconst;
+			n->left = nil;
+			n->right = nil;
+			n->val = n->decl->tag;			
+			return efold(n);
+		}
+		n->left = efold(left);
+		break;
+	case Oif:
+		n->left = left = efold(left);
+		n->right = right = efold(right);
+		if(left->op == Oconst){
+			if(left->val)
+				return right->left;
+			else
+				return right->right;
+		}
+		break;
+	default:
+		n->left = efold(left);
+		n->right = efold(right);
+		break;
+	}
+
+	left = n->left;
+	right = n->right;
+	if(left == nil)
+		return n;
+
+	if(right == nil){
+		if(left->op == Oconst){
+			if(left->ty == tint || left->ty == tbyte || left->ty == tbig)
+				return foldc(n);
+			if(left->ty == treal)
+				return foldr(n);
+		}
+		return n;
+	}
+
+	if(left->op == Oconst){
+		switch(n->op){
+		case Olsh:
+		case Orsh:
+			if(left->val == 0 && !hasside(right, 1))
+				return left;
+			break;
+		case Ooror:
+			if(left->ty == tint || left->ty == tbyte || left->ty == tbig){
+				if(left->val == 0){
+					n = mkbin(Oneq, right, mkconst(&right->src, 0));
+					n->ty = right->ty;
+					n->left->ty = right->ty;
+					return efold(n);
+				}
+				left->val = 1;
+				return left;
+			}
+			break;
+		case Oandand:
+			if(left->ty == tint || left->ty == tbyte || left->ty == tbig){
+				if(left->val == 0)
+					return left;
+				n = mkbin(Oneq, right, mkconst(&right->src, 0));
+				n->ty = right->ty;
+				n->left->ty = right->ty;
+				return efold(n);
+			}
+			break;
+		}
+	}
+	if(left->op == Oconst && right->op != Oconst
+	&& opcommute[n->op]
+	&& n->ty != tstring){
+		n->op = opcommute[n->op];
+		n->left = right;
+		n->right = left;
+		left = right;
+		right = n->right;
+	}
+	if(right->op == Oconst && left->op == n->op && left->right->op == Oconst
+	&& (n->op == Oadd || n->op == Omul || n->op == Oor || n->op == Oxor || n->op == Oand)
+	&& n->ty != tstring){
+		n->left = left->left;
+		left->left = right;
+		right = efold(left);
+		n->right = right;
+		left = n->left;
+	}
+	if(right->op == Oconst){
+		if(n->op == Oexp && left->ty == treal){
+			if(left->op == Oconst)
+				return foldr(n);
+			return n;
+		}
+		if(right->ty == tint || right->ty == tbyte || left->ty == tbig){
+			if(left->op == Oconst)
+				return foldc(n);
+			return foldvc(n);
+		}
+		if(right->ty == treal && left->op == Oconst)
+			return foldr(n);
+	}
+	if(sametree(left, right))
+		return folds(n);
+	return n;
+}
+
+/*
+ * does evaluating the node have any side effects?
+ */
+int
+hasside(Node *n, int strict)
+{
+	for(; n != nil; n = n->right){
+		if(sideeffect[n->op] && (strict || n->op != Oadr && n->op != Oind))
+			return 1;
+		if(hasside(n->left, strict))
+			return 1;
+	}
+	return 0;
+}
+
+int
+hascall(Node *n)
+{
+	for(; n != nil; n = n->right){
+		if(n->op == Ocall || n->op == Ospawn)
+			return 1;
+		if(hascall(n->left))
+			return 1;
+	}
+	return 0;
+}
+
+int
+hasasgns(Node *n)
+{
+	if(n == nil)
+		return 0;
+	if(n->op != Ocall && isused[n->op] && n->op != Onothing)
+		return 1;
+	return hasasgns(n->left) || hasasgns(n->right);
+}
+
+int
+nodes(Node *n)
+{
+	if(n == nil)
+		return 0;
+	return 1+nodes(n->left)+nodes(n->right);
+}
+
+Node*
+foldcast(Node *n, Node *left)
+{
+	Real r;
+	char *buf, *e;
+
+	switch(left->ty->kind){
+	case Tint:
+		left->val &= 0xffffffff;
+		if(left->val & 0x80000000)
+			left->val |= (Long)0xffffffff << 32;
+		return foldcasti(n, left);
+	case Tbyte:
+		left->val &= 0xff;
+		return foldcasti(n, left);
+	case Tbig:
+		return foldcasti(n, left);
+	case Treal:
+		switch(n->ty->kind){
+		case Tint:
+		case Tbyte:
+		case Tbig:
+			r = left->rval;
+			left->val = r < 0 ? r - .5 : r + .5;
+			break;
+		case Tfix:
+			left->val = real2fix(left->rval, n->ty);
+			break;
+		case Tstring:
+			buf = allocmem(NumSize);
+			e = seprint(buf, buf+NumSize, "%g", left->rval);
+			return mksconst(&n->src, enterstring(buf, e-buf));
+		default:
+			return n;
+		}
+		break;
+	case Tfix:
+		switch(n->ty->kind){
+		case Tint:
+		case Tbyte:
+		case Tbig:
+			left->val = fix2real(left->val, left->ty);
+			break;
+		case Treal:
+			left->rval = fix2real(left->val, left->ty);
+			break;
+		case Tfix:
+			if(tequal(left->ty, n->ty))
+				return left;
+			left->val = fix2fix(left->val, left->ty, n->ty);
+			break;
+		case Tstring:
+			buf = allocmem(NumSize);
+			e = seprint(buf, buf+NumSize, "%g", fix2real(left->val, left->ty));
+			return mksconst(&n->src, enterstring(buf, e-buf));
+		default:
+			return n;
+		}
+		break;
+	case Tstring:
+		switch(n->ty->kind){
+		case Tint:
+		case Tbyte:
+		case Tbig:
+			left->val = strtoi(left->decl->sym->name, 10);
+			break;
+		case Treal:
+			left->rval = strtod(left->decl->sym->name, nil);
+			break;
+		case Tfix:
+			left->val = real2fix(strtod(left->decl->sym->name, nil), n->ty);
+			break;
+		default:
+			return n;
+		}
+		break;
+	default:
+		return n;
+	}
+	left->ty = n->ty;
+	left->src = n->src;
+	return left;
+}
+
+/*
+ * left is some kind of int type
+ */
+Node*
+foldcasti(Node *n, Node *left)
+{
+	char *buf, *e;
+
+	switch(n->ty->kind){
+	case Tint:
+		left->val &= 0xffffffff;
+		if(left->val & 0x80000000)
+			left->val |= (Long)0xffffffff << 32;
+		break;
+	case Tbyte:
+		left->val &= 0xff;
+		break;
+	case Tbig:
+		break;
+	case Treal:
+		left->rval = left->val;
+		break;
+	case Tfix:
+		left->val = real2fix(left->val, n->ty);
+		break;
+	case Tstring:
+		buf = allocmem(NumSize);
+		e = seprint(buf, buf+NumSize, "%lld", left->val);
+		return mksconst(&n->src, enterstring(buf, e-buf));
+	default:
+		return n;
+	}
+	left->ty = n->ty;
+	left->src = n->src;
+	return left;
+}
+
+/*
+ * right is a const int
+ */
+Node*
+foldvc(Node *n)
+{
+	Node *left, *right;
+
+	left = n->left;
+	right = n->right;
+	switch(n->op){
+	case Oadd:
+	case Osub:
+	case Oor:
+	case Oxor:
+	case Olsh:
+	case Orsh:
+	case Ooror:
+		if(right->val == 0)
+			return left;
+		if(n->op == Ooror && !hasside(left, 1))
+			return right;
+		break;
+	case Oand:
+		if(right->val == 0 && !hasside(left, 1))
+			return right;
+		break;
+	case Omul:
+		if(right->val == 1)
+			return left;
+		if(right->val == 0 && !hasside(left, 1))
+			return right;
+		break;
+	case Odiv:
+		if(right->val == 1)
+			return left;
+		break;
+	case Omod:
+		if(right->val == 1 && !hasside(left, 1)){
+			right->val = 0;
+			return right;
+		}
+		break;
+	case Oexp:
+		if(right->val == 0){
+			right->val = 1;
+			return right;
+		}
+		if(right->val == 1)
+			return left;
+		break;
+	case Oandand:
+		if(right->val != 0)
+			return left;
+		if(!hasside(left, 1))
+			return right;
+		break;
+	case Oneq:
+		if(!isrelop[left->op])
+			return n;
+		if(right->val == 0)
+			return left;
+		n->op = Onot;
+		n->right = nil;
+		break;
+	case Oeq:
+		if(!isrelop[left->op])
+			return n;
+		if(right->val != 0)
+			return left;
+		n->op = Onot;
+		n->right = nil;
+		break;
+	}
+	return n;
+}
+
+/*
+ * left and right are const ints
+ */
+Node*
+foldc(Node *n)
+{
+	Node *left, *right;
+	Long lv, v;
+	int rv, nb;
+
+	left = n->left;
+	right = n->right;
+	switch(n->op){
+	case Oadd:
+		v = left->val + right->val;
+		break;
+	case Osub:
+		v = left->val - right->val;
+		break;
+	case Omul:
+		v = left->val * right->val;
+		break;
+	case Odiv:
+		if(right->val == 0){
+			nerror(n, "divide by 0 in constant expression");
+			return n;
+		}
+		v = left->val / right->val;
+		break;
+	case Omod:
+		if(right->val == 0){
+			nerror(n, "mod by 0 in constant expression");
+			return n;
+		}
+		v = left->val % right->val;
+		break;
+	case Oexp:
+		if(left->val == 0 && right->val < 0){
+			nerror(n, "0 to negative power in constant expression");
+			return n;
+		}
+		v = ipow(left->val, right->val);
+		break;
+	case Oand:
+		v = left->val & right->val;
+		break;
+	case Oor:
+		v = left->val | right->val;
+		break;
+	case Oxor:
+		v = left->val ^ right->val;
+		break;
+	case Olsh:
+		lv = left->val;
+		rv = right->val;
+		if(rv < 0 || rv >= n->ty->size * 8){
+			nwarn(n, "shift amount %d out of range", rv);
+			rv = 0;
+		}
+		if(rv == 0){
+			v = lv;
+			break;
+		}
+		v = lv << rv;
+		break;
+	case Orsh:
+		lv = left->val;
+		rv = right->val;
+		nb = n->ty->size * 8;
+		if(rv < 0 || rv >= nb){
+			nwarn(n, "shift amount %d out of range", rv);
+			rv = 0;
+		}
+		if(rv == 0){
+			v = lv;
+			break;
+		}
+		v = lv >> rv;
+
+		/*
+		 * properly sign extend c right shifts
+		 */
+		if((n->ty == tint || n->ty == tbig)
+		&& rv != 0
+		&& (lv & (1<<(nb-1)))){
+			lv = 0;
+			lv = ~lv;
+			v |= lv << (nb - rv);
+		}
+		break;
+	case Oneg:
+		v = -left->val;
+		break;
+	case Ocomp:
+		v = ~left->val;
+		break;
+	case Oeq:
+		v = left->val == right->val;
+		break;
+	case Oneq:
+		v = left->val != right->val;
+		break;
+	case Ogt:
+		v = left->val > right->val;
+		break;
+	case Ogeq:
+		v = left->val >= right->val;
+		break;
+	case Olt:
+		v = left->val < right->val;
+		break;
+	case Oleq:
+		v = left->val <= right->val;
+		break;
+	case Oandand:
+		v = left->val && right->val;
+		break;
+	case Ooror:
+		v = left->val || right->val;
+		break;
+	case Onot:
+		v = !left->val;
+		break;
+	default:
+		return n;
+	}
+	if(n->ty == tint){
+		v &= 0xffffffff;
+		if(v & 0x80000000)
+			v |= (Long)0xffffffff << 32;
+	}else if(n->ty == tbyte)
+		v &= 0xff;
+	n->left = nil;
+	n->right = nil;
+	n->decl = nil;
+	n->op = Oconst;
+	n->val = v;
+	return n;
+}
+
+/*
+ * left and right are const reals
+ */
+Node*
+foldr(Node *n)
+{
+	Node *left, *right;
+	double rv;
+	Long v;
+
+	rv = 0.;
+	v = 0;
+
+	left = n->left;
+	right = n->right;
+	switch(n->op){
+	case Ocast:
+		return n;
+	case Oadd:
+		rv = left->rval + right->rval;
+		break;
+	case Osub:
+		rv = left->rval - right->rval;
+		break;
+	case Omul:
+		rv = left->rval * right->rval;
+		break;
+	case Odiv:
+		rv = left->rval / right->rval;
+		break;
+	case Oexp:
+		rv = rpow(left->rval, right->val);
+		break;
+	case Oneg:
+		rv = -left->rval;
+		break;
+	case Oinv:
+		if(left->rval == 0.0){
+			error(n->src.start, "divide by 0 in fixed point type");
+			return n;
+		}
+		rv = 1/left->rval;
+		break;
+	case Oeq:
+		v = left->rval == right->rval;
+		break;
+	case Oneq:
+		v = left->rval != right->rval;
+		break;
+	case Ogt:
+		v = left->rval > right->rval;
+		break;
+	case Ogeq:
+		v = left->rval >= right->rval;
+		break;
+	case Olt:
+		v = left->rval < right->rval;
+		break;
+	case Oleq:
+		v = left->rval <= right->rval;
+		break;
+	default:
+		return n;
+	}
+	n->left = nil;
+	n->right = nil;
+
+	if(isNaN(rv))
+		rv = canonnan;
+
+	n->rval = rv;
+	n->val = v;
+
+	n->op = Oconst;
+	return n;
+}
+
+Node*
+varinit(Decl *d, Node *e)
+{
+	Node *n;
+
+	n = mkdeclname(&e->src, d);
+	if(d->next == nil)
+		return mkbin(Oas, n, e);
+	return mkbin(Oas, n, varinit(d->next, e));
+}
+
+/*
+ * given: an Oseq list with left == next or the last child
+ * make a list with the right == next
+ * ie: Oseq(Oseq(a, b),c) ==> Oseq(a, Oseq(b, Oseq(c, nil))))
+ */
+Node*
+rotater(Node *e)
+{
+	Node *left;
+
+	if(e == nil)
+		return e;
+	if(e->op != Oseq)
+		return mkunary(Oseq, e);
+	e->right = mkunary(Oseq, e->right);
+	while(e->left->op == Oseq){
+		left = e->left;
+		e->left = left->right;
+		left->right = e;
+		e = left;
+	}
+	return e;
+}
+
+/*
+ * reverse the case labels list
+ */
+Node*
+caselist(Node *s, Node *nr)
+{
+	Node *r;
+
+	r = s->right;
+	s->right = nr;
+	if(r == nil)
+		return s;
+	return caselist(r, s);
+}
+
+/*
+ * e is a seq of expressions; make into cons's to build a list
+ */
+Node*
+etolist(Node *e)
+{
+	Node *left, *n;
+
+	if(e == nil)
+		return nil;
+	n = mknil(&e->src);
+	n->src.start = n->src.stop;
+	if(e->op != Oseq)
+		return mkbin(Ocons, e, n);
+	e->right = mkbin(Ocons, e->right, n);
+	while(e->left->op == Oseq){
+		e->op = Ocons;
+		left = e->left;
+		e->left = left->right;
+		left->right = e;
+		e = left;
+	}
+	e->op = Ocons;
+	return e;
+}
+
+Node*
+dupn(int resrc, Src *src, Node *n)
+{
+	Node *nn;
+
+	nn = allocmem(sizeof *nn);
+	*nn = *n;
+	if(resrc)
+		nn->src = *src;
+	if(nn->left != nil)
+		nn->left = dupn(resrc, src, nn->left);
+	if(nn->right != nil)
+		nn->right = dupn(resrc, src, nn->right);
+	return nn;
+}
+
+Node*
+mkn(int op, Node *left, Node *right)
+{
+	Node *n;
+
+	n = allocmem(sizeof *n);
+	*n = znode;
+	n->op = op;
+	n->left = left;
+	n->right = right;
+	return n;
+}
+
+Node*
+mkunary(int op, Node *left)
+{
+	Node *n;
+
+	n = mkn(op, left, nil);
+	n->src = left->src;
+	return n;
+}
+
+Node*
+mkbin(int op, Node *left, Node *right)
+{
+	Node *n;
+
+	n = mkn(op, left, right);
+	n->src.start = left->src.start;
+	n->src.stop = right->src.stop;
+	return n;
+}
+
+Node*
+mkdeclname(Src *src, Decl *d)
+{
+	Node *n;
+
+	n = mkn(Oname, nil, nil);
+	n->src = *src;
+	n->decl = d;
+	n->ty = d->ty;
+	d->refs++;
+	return n;
+}
+
+Node*
+mknil(Src *src)
+{
+	return mkdeclname(src, nildecl);
+}
+
+Node*
+mkname(Src *src, Sym *s)
+{
+	Node *n;
+
+	n = mkn(Oname, nil, nil);
+	n->src = *src;
+	if(s->unbound == nil){
+		s->unbound = mkdecl(src, Dunbound, nil);
+		s->unbound->sym = s;
+	}
+	n->decl = s->unbound;
+	return n;
+}
+
+Node*
+mkconst(Src *src, Long v)
+{
+	Node *n;
+
+	n = mkn(Oconst, nil, nil);
+	n->ty = tint;
+	n->val = v;
+	n->src = *src;
+	return n;
+}
+
+Node*
+mkrconst(Src *src, Real v)
+{
+	Node *n;
+
+	n = mkn(Oconst, nil, nil);
+	n->ty = treal;
+	n->rval = v;
+	n->src = *src;
+	return n;
+}
+
+Node*
+mksconst(Src *src, Sym *s)
+{
+	Node *n;
+
+	n = mkn(Oconst, nil, nil);
+	n->ty = tstring;
+	n->decl = mkdecl(src, Dconst, tstring);
+	n->decl->sym = s;
+	n->src = *src;
+	return n;
+}
+
+int
+opconv(Fmt *f)
+{
+	int op;
+	char buf[32];
+
+	op = va_arg(f->args, int);
+	if(op < 0 || op > Oend) {
+		seprint(buf, buf+sizeof(buf), "op %d", op);
+		return fmtstrcpy(f, buf);
+	}
+	return fmtstrcpy(f, opname[op]);
+}
+
+int
+etconv(Fmt *f)
+{
+	Node *n;
+	char buf[1024];
+
+	n = va_arg(f->args, Node*);
+	if(n->ty == tany || n->ty == tnone || n->ty == terror)
+		seprint(buf, buf+sizeof(buf), "%V", n);
+	else
+		seprint(buf, buf+sizeof(buf), "%V of type %T", n, n->ty);
+	return fmtstrcpy(f, buf);
+}
+
+int
+expconv(Fmt *f)
+{
+	Node *n;
+	char buf[4096], *p;
+
+	n = va_arg(f->args, Node*);
+	p = buf;
+	*p = 0;
+	if(f->r == 'V')
+		*p++ = '\'';
+	p = eprint(p, buf+sizeof(buf)-1, n);
+	if(f->r == 'V')
+		*p++ = '\'';
+	*p = 0;
+	return fmtstrcpy(f, buf);
+}
+
+char*
+eprint(char *buf, char *end, Node *n)
+{
+	if(n == nil)
+		return buf;
+	if(n->flags & PARENS)
+		buf = secpy(buf, end, "(");
+	switch(n->op){
+	case Obreak:
+	case Ocont:
+		buf = secpy(buf, end, opname[n->op]);
+		if(n->decl != nil){
+			buf = seprint(buf, end, " %s", n->decl->sym->name);
+		}
+		break;
+	case Oexit:
+	case Owild:
+		buf = secpy(buf, end, opname[n->op]);
+		break;
+	case Onothing:
+		break;
+	case Oadr:
+	case Oused:
+		buf = eprint(buf, end, n->left);
+		break;
+	case Oseq:
+		buf = eprintlist(buf, end, n, ", ");
+		break;
+	case Oname:
+		if(n->decl == nil)
+			buf = secpy(buf, end, "<nil>");
+		else
+			buf = seprint(buf, end, "%s", n->decl->sym->name);
+		break;
+	case Oconst:
+		if(n->ty->kind == Tstring){
+			buf = stringpr(buf, end, n->decl->sym);
+			break;
+		}
+		if(n->decl != nil && n->decl->sym != nil){
+			buf = seprint(buf, end, "%s", n->decl->sym->name);
+			break;
+		}
+		switch(n->ty->kind){
+		case Tint:
+		case Tbyte:
+			buf = seprint(buf, end, "%ld", (long)n->val);
+			break;
+		case Tbig:
+			buf = seprint(buf, end, "%lld", n->val);
+			break;
+		case Treal:
+			buf = seprint(buf, end, "%g", n->rval);
+			break;
+		case Tfix:
+			buf = seprint(buf, end, "%ld(%g)", (long)n->val, n->ty->val->rval);
+			break;
+		default:
+			buf = secpy(buf, end, opname[n->op]);
+			break;
+		}
+		break;
+	case Ocast:
+		buf = seprint(buf, end, "%T ", n->ty);
+		buf = eprint(buf, end, n->left);
+		break;
+	case Otuple:
+		if(n->ty != nil && n->ty->kind == Tadt)
+			buf = seprint(buf, end, "%s", n->ty->decl->sym->name);
+		buf = seprint(buf, end, "(");
+		buf = eprintlist(buf, end, n->left, ", ");
+		buf = secpy(buf, end, ")");
+		break;
+	case Ochan:
+		if(n->left){
+			buf = secpy(buf, end, "chan [");
+			buf = eprint(buf, end, n->left);
+			buf = secpy(buf, end, "] of ");
+			buf = seprint(buf, end, "%T", n->ty->tof);
+		}else
+			buf = seprint(buf, end, "chan of %T", n->ty->tof);
+		break;
+	case Oarray:
+		buf = secpy(buf, end, "array [");
+		if(n->left != nil)
+			buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, "] of ");
+		if(n->right != nil){
+			buf = secpy(buf, end, "{");
+			buf = eprintlist(buf, end, n->right, ", ");
+			buf = secpy(buf, end, "}");
+		}else{
+			buf = seprint(buf, end, "%T", n->ty->tof);
+		}
+		break;
+	case Oelem:
+	case Olabel:
+		if(n->left != nil){
+			buf = eprintlist(buf, end, n->left, " or ");
+			buf = secpy(buf, end, " =>");
+		}
+		buf = eprint(buf, end, n->right);
+		break;
+	case Orange:
+		buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, " to ");
+		buf = eprint(buf, end, n->right);
+		break;
+	case Ospawn:
+		buf = secpy(buf, end, "spawn ");
+		buf = eprint(buf, end, n->left);
+		break;
+	case Oraise:
+		buf = secpy(buf, end, "raise ");
+		buf = eprint(buf, end, n->left);
+		break;
+	case Ocall:
+		buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, "(");
+		buf = eprintlist(buf, end, n->right, ", ");
+		buf = secpy(buf, end, ")");
+		break;
+	case Oinc:
+	case Odec:
+		buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, opname[n->op]);
+		break;
+	case Oindex:
+	case Oindx:
+	case Oinds:
+		buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, "[");
+		buf = eprint(buf, end, n->right);
+		buf = secpy(buf, end, "]");
+		break;
+	case Oslice:
+		buf = eprint(buf, end, n->left);
+		buf = secpy(buf, end, "[");
+		buf = eprint(buf, end, n->right->left);
+		buf = secpy(buf, end, ":");
+		buf = eprint(buf, end, n->right->right);
+		buf = secpy(buf, end, "]");
+		break;
+	case Oload:
+		buf = seprint(buf, end, "load %T ", n->ty);
+		buf = eprint(buf, end, n->left);
+		break;
+	case Oref:
+	case Olen:
+	case Ohd:
+	case Otl:
+	case Otagof:
+		buf = secpy(buf, end, opname[n->op]);
+		buf = secpy(buf, end, " ");
+		buf = eprint(buf, end, n->left);
+		break;
+	default:
+		if(n->right == nil){
+			buf = secpy(buf, end, opname[n->op]);
+			buf = eprint(buf, end, n->left);
+		}else{
+			buf = eprint(buf, end, n->left);
+			buf = secpy(buf, end, opname[n->op]);
+			buf = eprint(buf, end, n->right);
+		}
+		break;
+	}
+	if(n->flags & PARENS)
+		buf = secpy(buf, end, ")");
+	return buf;
+}
+
+char*
+eprintlist(char *buf, char *end, Node *elist, char *sep)
+{
+	if(elist == nil)
+		return buf;
+	for(; elist->right != nil; elist = elist->right){
+		if(elist->op == Onothing)
+			continue;
+		if(elist->left->op == Ofnptr)
+			return buf;
+		buf = eprint(buf, end, elist->left);
+		if(elist->right->left->op != Ofnptr)
+			buf = secpy(buf, end, sep);
+	}
+	buf = eprint(buf, end, elist->left);
+	return buf;
+}
+
+int
+nodeconv(Fmt *f)
+{
+	Node *n;
+	char buf[4096];
+
+	n = va_arg(f->args, Node*);
+	buf[0] = 0;
+	nprint(buf, buf+sizeof(buf), n, 0);
+	return fmtstrcpy(f, buf);
+}
+
+char*
+nprint(char *buf, char *end, Node *n, int indent)
+{
+	int i;
+
+	if(n == nil)
+		return buf;
+	buf = seprint(buf, end, "\n");
+	for(i = 0; i < indent; i++)
+		if(buf < end-1)
+			*buf++ = ' ';
+	switch(n->op){
+	case Oname:
+		if(n->decl == nil)
+			buf = secpy(buf, end, "name <nil>");
+		else
+			buf = seprint(buf, end, "name %s", n->decl->sym->name);
+		break;
+	case Oconst:
+		if(n->decl != nil && n->decl->sym != nil)
+			buf = seprint(buf, end, "const %s", n->decl->sym->name);
+		else
+			buf = seprint(buf, end, "%O", n->op);
+		if(n->ty == tint || n->ty == tbyte || n->ty == tbig)
+			buf = seprint(buf, end, " (%ld)", (long)n->val);
+		break;
+	default:
+		buf = seprint(buf, end, "%O", n->op);
+		break;
+	}
+	buf = seprint(buf, end, " %T %d %d", n->ty, n->addable, n->temps);
+	indent += 2;
+	buf = nprint(buf, end, n->left, indent);
+	buf = nprint(buf, end, n->right, indent);
+	return buf;
+}
--- /dev/null
+++ b/limbo/optab.c
@@ -1,0 +1,658 @@
+#include "limbo.h"
+
+uchar movetab[Mend][Tend] =
+{
+	/* Mas */
+	{
+		/* Tnone */	0,
+		/* Tadt */	IMOVM,
+		/* Tadtpick */	IMOVM,
+		/* Tarray */	IMOVP,
+		/* Tbig */	IMOVL,
+		/* Tbyte */	IMOVB,
+		/* Tchan */	IMOVP,
+		/* Treal */	IMOVF,
+		/* Tfn */	0,
+		/* Tint */	IMOVW,
+		/* Tlist */	IMOVP,
+		/* Tmodule */	IMOVP,
+		/* Tref */	IMOVP,
+		/* Tstring */	IMOVP,
+		/* Ttuple */	IMOVM,
+		/* Texception */	IMOVM,
+		/* Tfix */	IMOVW,
+		/* Tpoly */	IMOVP,
+
+		/* Tainit */	0,
+		/* Talt */	0,
+		/* Tany */	IMOVP,
+		/* Tarrow */	0,
+		/* Tcase */	0,
+		/* Tcasel */	0,
+		/* Tcasec */	0,
+		/* Tdot */	0,
+		/* Terror */	0,
+		/* Tgoto */	0,
+		/* Tid */	0,
+	},
+	/* Mcons */
+	{
+		/* Tnone */	0,
+		/* Tadt */	ICONSM,
+		/* Tadtpick */	0,
+		/* Tarray */	ICONSP,
+		/* Tbig */	ICONSL,
+		/* Tbyte */	ICONSB,
+		/* Tchan */	ICONSP,
+		/* Treal */	ICONSF,
+		/* Tfn */	0,
+		/* Tint */	ICONSW,
+		/* Tlist */	ICONSP,
+		/* Tmodule */	ICONSP,
+		/* Tref */	ICONSP,
+		/* Tstring */	ICONSP,
+		/* Ttuple */	ICONSM,
+		/* Texception */	ICONSM,
+		/* Tfix */	ICONSW,
+		/* Tpoly */	ICONSP,
+
+		/* Tainit */	0,
+		/* Talt */	0,
+		/* Tany */	ICONSP,
+		/* Tarrow */	0,
+		/* Tcase */	0,
+		/* Tcasel */	0,
+		/* Tcasec */	0,
+		/* Tdot */	0,
+		/* Terror */	0,
+		/* Tgoto */	0,
+		/* Tid */	0,
+	},
+	/* Mhd */
+	{
+		/* Tnone */	0,
+		/* Tadt */	IHEADM,
+		/* Tadtpick */	0,
+		/* Tarray */	IHEADP,
+		/* Tbig */	IHEADL,
+		/* Tbyte */	IHEADB,
+		/* Tchan */	IHEADP,
+		/* Treal */	IHEADF,
+		/* Tfn */	0,
+		/* Tint */	IHEADW,
+		/* Tlist */	IHEADP,
+		/* Tmodule */	IHEADP,
+		/* Tref */	IHEADP,
+		/* Tstring */	IHEADP,
+		/* Ttuple */	IHEADM,
+		/* Texception */	IHEADM,
+		/* Tfix */	IHEADW,
+		/* Tpoly */	IHEADP,
+
+		/* Tainit */	0,
+		/* Talt */	0,
+		/* Tany */	IHEADP,
+		/* Tarrow */	0,
+		/* Tcase */	0,
+		/* Tcasel */	0,
+		/* Tcasec */	0,
+		/* Tdot*/	0,
+		/* Terror */	0,
+		/* Tgoto */	0,
+		/* Tid */	0,
+	},
+	/* Mtl */
+	{
+		/* Tnone */	0,
+		/* Tadt */	0,
+		/* Tadtpick */	0,
+		/* Tarray */	0,
+		/* Tbig */	0,
+		/* Tbyte */	0,
+		/* Tchan */	0,
+		/* Treal */	0,
+		/* Tfn */	0,
+		/* Tint */	0,
+		/* Tlist */	ITAIL,
+		/* Tmodule */	0,
+		/* Tref */	0,
+		/* Tstring */	0,
+		/* Ttuple */	0,
+		/* Texception */	0,
+		/* Tfix */	0,
+		/* Tpoly */	0,
+
+		/* Tainit */	0,
+		/* Talt */	0,
+		/* Tany */	0,
+		/* Tarrow */	0,
+		/* Tcase */	0,
+		/* Tcasel */	0,
+		/* Tcasec */	0,
+		/* Tdot */	0,
+		/* Terror */	0,
+		/* Tgoto */	0,
+		/* Tid */	0,
+	},
+};
+
+uchar chantab[Tend] =
+{
+	/* Tnone */	0,
+	/* Tadt */	INEWCM,
+	/* Tadtpick */	0,
+	/* Tarray */	INEWCP,
+	/* Tbig */	INEWCL,
+	/* Tbyte */	INEWCB,
+	/* Tchan */	INEWCP,
+	/* Treal */	INEWCF,
+	/* Tfn */	0,
+	/* Tint */	INEWCW,
+	/* Tlist */	INEWCP,
+	/* Tmodule */	INEWCP,
+	/* Tref */	INEWCP,
+	/* Tstring */	INEWCP,
+	/* Ttuple */	INEWCM,
+	/* Texception */	INEWCM,
+	/* Tfix */	INEWCW,
+	/* Tpoly */	INEWCP,
+
+	/* Tainit */	0,
+	/* Talt */	0,
+	/* Tany */	INEWCP,
+	/* Tarrow */	0,
+	/* Tcase */	0,
+	/* Tcasel */	0,
+	/* Tcasec */	0,
+	/* Tdot */	0,
+	/* Terror */	0,
+	/* Tgoto */	0,
+	/* Tid */	0,
+};
+
+uchar disoptab[Oend+1][7] = {
+	/* opcode	default	byte	word	big	real	string	fixed	*/
+			{0},
+	/* Oadd */	{0,	IADDB,	IADDW,	IADDL,	IADDF,	IADDC,	IADDW,},
+	/* Oaddas */	{0,	IADDB,	IADDW,	IADDL,	IADDF,	IADDC,	IADDW,},
+	/* Oadr */	{0},
+	/* Oadtdecl */	{0},
+	/* Oalt */	{0},
+	/* Oand */	{0,	IANDB,	IANDW,	IANDL,	0,	0,	0,},
+	/* Oandand */	{0},
+	/* Oandas */	{0,	IANDB,	IANDW,	IANDL,	0, 	0,	0,},
+	/* Oarray */	{0},
+	/* Oas */	{0},
+	/* Obreak */	{0},
+	/* Ocall */	{0},
+	/* Ocase */	{0},
+	/* Ocast */	{0},
+	/* Ochan */	{0},
+	/* Ocomma */	{0},
+	/* Ocomp */	{0},
+	/* Ocondecl */	{0},
+	/* Ocons */	{0},
+	/* Oconst */	{0},
+	/* Ocont */	{0},
+	/* Odas */	{0},
+	/* Odec */	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	/* Odiv */	{0,	IDIVB,	IDIVW,	IDIVL,	IDIVF,	0,	IDIVX,},
+	/* Odivas */	{0,	IDIVB,	IDIVW,	IDIVL,	IDIVF,	0,	IDIVX,},
+	/* Odo */	{0},
+	/* Odot */	{0},
+	/* Oelem */	{0},
+	/* Oeq */	{IBEQW,	IBEQB,	IBEQW,	IBEQL,	IBEQF,	IBEQC,	IBEQW,},
+	/* Oexcept */	{0},
+	/* Oexdecl */	{0},
+	/* Oexit */	{0},
+	/* Oexp */	{0,	0,	IEXPW,	IEXPL,	IEXPF,	0,	0,},
+	/* Oexpas */	{0,	0,	IEXPW,	IEXPL,	IEXPF,	0,	0,},
+	/* Oexstmt */	{0},
+	/* Ofielddecl */{0},
+	/* Ofnptr */	{0},
+	/* Ofor */	{0},
+	/* Ofunc */	{0},
+	/* Ogeq */	{0,	IBGEB,	IBGEW,	IBGEL,	IBGEF,	IBGEC,	IBGEW,},
+	/* Ogt */	{0,	IBGTB,	IBGTW,	IBGTL,	IBGTF,	IBGTC,	IBGTW,},
+	/* Ohd */	{0},
+	/* Oif */	{0},
+	/* Oimport */	{0},
+	/* Oinc */	{0,	IADDB,	IADDW,	IADDL,	IADDF,	0,	IADDW,},
+	/* Oind */	{0},
+	/* Oindex */	{0,},
+	/* Oinds */	{0,	0,	IINDC,	0,	0,	0,	0,},
+	/* Oindx */	{0,	0,	IINDX,	0,	0,	0,	0,},
+	/* Oinv */	{0},
+	/* Ojmp */	{0},
+	/* Olabel */	{0},
+	/* Olen */	{ILENA,	0,	0,	0,	0,	ILENC,	0,},
+	/* Oleq */	{0,	IBLEB,	IBLEW,	IBLEL,	IBLEF,	IBLEC,	IBLEW,},
+	/* Oload */	{0},
+	/* Olsh */	{0,	ISHLB,	ISHLW,	ISHLL,	0,	0,	0,},
+	/* Olshas */	{0,	ISHLB,	ISHLW,	ISHLL,	0,	0,	0,},
+	/* Olt */	{0,	IBLTB,	IBLTW,	IBLTL,	IBLTF,	IBLTC,	IBLTW,},
+	/* Omdot */	{0},
+	/* Omod */	{0,	IMODB,	IMODW,	IMODL,	0,	0,	0,},
+	/* Omodas */	{0,	IMODB,	IMODW,	IMODL,	0,	0,	0,},
+	/* Omoddecl */	{0},
+	/* Omul */	{0,	IMULB,	IMULW,	IMULL,	IMULF,	0,	IMULX,},
+	/* Omulas */	{0,	IMULB,	IMULW,	IMULL,	IMULF,	0,	IMULX,},
+	/* Oname */	{0},
+	/* Oneg */	{0,	0,	0,	0,	INEGF,	0, 	0,},
+	/* Oneq */	{IBNEW,	IBNEB,	IBNEW,	IBNEL,	IBNEF,	IBNEC,	IBNEW,},
+	/* Onot */	{0},
+	/* Onothing */	{0},
+	/* Oor */	{0,	IORB,	IORW,	IORL,	0,	0,	0,},
+	/* Ooras */	{0,	IORB,	IORW,	IORL,	0,	0,	0,},
+	/* Ooror */	{0},
+	/* Opick */	{0},
+	/* Opickdecl */	{0},
+	/* Opredec */	{0},
+	/* Opreinc */	{0},
+	/* Oraise */	{0},
+	/* Orange */	{0},
+	/* Orcv */	{0},
+	/* Oref */	{0},
+	/* Oret */	{0},
+	/* Orsh */	{0,	ISHRB,	ISHRW,	ISHRL,	0,	0,	0,},
+	/* Orshas */	{0,	ISHRB,	ISHRW,	ISHRL,	0,	0,	0,},
+	/* Oscope */	{0},
+	/* Oself */	{0},
+	/* Oseq */	{0},
+	/* Oslice */	{ISLICEA,0,	0,	0,	0,	ISLICEC,	0,},
+	/* Osnd */	{0},
+	/* Ospawn */	{0},
+	/* Osub */	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	/* Osubas */	{0,	ISUBB,	ISUBW,	ISUBL,	ISUBF,	0,	ISUBW,},
+	/* Otagof */	{0},
+	/* Otl */	{0},
+	/* Otuple */	{0},
+	/* Otype */	{0},
+	/* Otypedecl */	{0},
+	/* Oused */	{0},
+	/* Ovardecl */	{0},
+	/* Ovardecli */	{0},
+	/* Owild */	{0},
+	/* Oxor */	{0,	IXORB,	IXORW,	IXORL,	0,	0,	0,},
+	/* Oxoras */	{0,	IXORB,	IXORW,	IXORL,	0,	0,	0,},
+
+	/* Oend	*/	{0}
+};
+
+int setisused[] = {
+	Oas,
+	Odas,
+	Oaddas,
+	Osubas,
+	Omulas,
+	Odivas,
+	Omodas,
+	Oexpas,
+	Oandas,
+	Ooras,
+	Oxoras,
+	Olshas,
+	Onothing,
+	Orshas,
+	Oinc,
+	Odec,
+	Opreinc,
+	Opredec,
+	Ocall,
+	Oraise,
+	Ospawn,
+	Osnd,
+	Orcv,
+
+	-1
+};
+
+int setsideeffect[] = {
+	Oas,
+	Odas,
+	Oaddas,
+	Osubas,
+	Omulas,
+	Odivas,
+	Omodas,
+	Oexpas,
+	Oandas,
+	Ooras,
+	Oxoras,
+	Olshas,
+	Orshas,
+	Oinc,
+	Odec,
+	Opreinc,
+	Opredec,
+	Ocall,
+	Oraise,
+	Ospawn,
+	Osnd,
+	Orcv,
+
+	Oadr,
+	Oarray,
+	Ocast,
+	Ochan,
+	Ocons,
+	Odiv,
+	Odot,
+	Oind,
+	Oindex,
+	Oinds,
+	Oindx,
+	Olen,
+	Oload,
+	Omod,
+	Oref,
+
+	-1
+};
+
+char *opname[Oend+1] = {
+			"unknown",
+	/* Oadd */	"+",
+	/* Oaddas */	"+=",
+	/* Oadr */	"adr",
+	/* Oadtdecl */	"adtdecl",
+	/* Oalt */	"alt",
+	/* Oand */	"&",
+	/* Oandand */	"&&",
+	/* Oandas */	"&=",
+	/* Oarray */	"array",
+	/* Oas */	"=",
+	/* Obreak */	"break",
+	/* Ocall */	"call",
+	/* Ocase */	"case",
+	/* Ocast */	"cast",
+	/* Ochan */	"chan",
+	/* Ocomma */	",",
+	/* Ocomp */	"~",
+	/* Ocondecl */	"condecl",
+	/* Ocons */	"::",
+	/* Oconst */	"const",
+	/* Ocont */	"continue",
+	/* Odas */	":=",
+	/* Odec */	"--",
+	/* Odiv */	"/",
+	/* Odivas */	"/=",
+	/* Odo */	"do",
+	/* Odot */	".",
+	/* Oelem */	"elem",
+	/* Oeq */	"==",
+	/* Oexcept */	"except",
+	/* Oexdecl */	"exdecl",
+	/* Oexit */	"exit",
+	/* Oexp */	"**",
+	/* Oexpas */	"**=",
+	/* Oexstmt */	"exstat",
+	/* Ofielddecl */"fielddecl",
+	/* Ofnptr */	"fnptr",
+	/* Ofor */	"for",
+	/* Ofunc */	"fn(){}",
+	/* Ogeq */	">=",
+	/* Ogt */	">",
+	/* Ohd */	"hd",
+	/* Oif */	"if",
+	/* Oimport */	"import",
+	/* Oinc */	"++",
+	/* Oind */	"*",
+	/* Oindex */	"index",
+	/* Oinds */	"inds",
+	/* Oindx */	"indx",
+	/* Oinv */		"inv",
+	/* Ojmp */	"jmp",
+	/* Olabel */	"label",
+	/* Olen */	"len",
+	/* Oleq */	"<=",
+	/* Oload */	"load",
+	/* Olsh */	"<<",
+	/* Olshas */	"<<=",
+	/* Olt */	"<",
+	/* Omdot */	"->",
+	/* Omod */	"%",
+	/* Omodas */	"%=",
+	/* Omoddecl */	"moddecl",
+	/* Omul */	"*",
+	/* Omulas */	"*=",
+	/* Oname */	"name",
+	/* Oneg */	"-",
+	/* Oneq */	"!=",
+	/* Onot */	"!",
+	/* Onothing */	"nothing",
+	/* Oor */	"|",
+	/* Ooras */	"|=",
+	/* Ooror */	"||",
+	/* Opick */	"pick",
+	/* Opickdecl */	"pickdecl",
+	/* Opredec */	"--",
+	/* Opreinc */	"++",
+	/* Oraise */	"raise",
+	/* Orange */	"range",
+	/* Orcv */	"<-",
+	/* Oref */	"ref",
+	/* Oret */	"return",
+	/* Orsh */	">>",
+	/* Orshas */	">>=",
+	/* Oscope */	"scope",
+	/* Oself */	"self",
+	/* Oseq */	"seq",
+	/* Oslice */	"slice",
+	/* Osnd */	"<-=",
+	/* Ospawn */	"spawn",
+	/* Osub */	"-",
+	/* Osubas */	"-=",
+	/* Otl */	"tagof",
+	/* Otl */	"tl",
+	/* Otuple */	"tuple",
+	/* Otype */	"type",
+	/* Otypedecl */	"typedecl",
+	/* Oused */	"used",
+	/* Ovardecl */	"vardecl",
+	/* Ovardecli */	"vardecli",
+	/* Owild */	"*",
+	/* Oxor */	"^",
+	/* Oxoras */	"^=",
+
+	/* Oend */	"unknown"
+};
+
+int setisbyteinst[] = {
+	IMULB,
+	ISUBB,
+	IADDB,
+	IDIVB,
+	IORB,
+	IXORB,
+	ISHLB,
+	ISHRB,
+	IMODB,
+	IANDB,
+	IBEQB,
+	IBNEB,
+	IBLTB,
+	IBLEB,
+	IBGTB,
+	IBGEB,
+
+	-1
+};
+
+char *instname[256] = {
+	"nop",
+	"alt",
+	"nbalt",
+	"goto",
+	"call",
+	"frame",
+	"spawn",
+	"runt",
+	"load",
+	"mcall",
+	"mspawn",
+	"mframe",
+	"ret",
+	"jmp",
+	"case",
+	"exit",
+	"new",
+	"newa",
+	"newcb",
+	"newcw",
+	"newcf",
+	"newcp",
+	"newcm",
+	"newcmp",
+	"send",
+	"recv",
+	"consb",
+	"consw",
+	"consp",
+	"consf",
+	"consm",
+	"consmp",
+	"headb",
+	"headw",
+	"headp",
+	"headf",
+	"headm",
+	"headmp",
+	"tail",
+	"lea",
+	"indx",
+	"movp",
+	"movm",
+	"movmp",
+	"movb",
+	"movw",
+	"movf",
+	"cvtbw",
+	"cvtwb",
+	"cvtfw",
+	"cvtwf",
+	"cvtca",
+	"cvtac",
+	"cvtwc",
+	"cvtcw",
+	"cvtfc",
+	"cvtcf",
+	"addb",
+	"addw",
+	"addf",
+	"subb",
+	"subw",
+	"subf",
+	"mulb",
+	"mulw",
+	"mulf",
+	"divb",
+	"divw",
+	"divf",
+	"modw",
+	"modb",
+	"andb",
+	"andw",
+	"orb",
+	"orw",
+	"xorb",
+	"xorw",
+	"shlb",
+	"shlw",
+	"shrb",
+	"shrw",
+	"insc",
+	"indc",
+	"addc",
+	"lenc",
+	"lena",
+	"lenl",
+	"beqb",
+	"bneb",
+	"bltb",
+	"bleb",
+	"bgtb",
+	"bgeb",
+	"beqw",
+	"bnew",
+	"bltw",
+	"blew",
+	"bgtw",
+	"bgew",
+	"beqf",
+	"bnef",
+	"bltf",
+	"blef",
+	"bgtf",
+	"bgef",
+	"beqc",
+	"bnec",
+	"bltc",
+	"blec",
+	"bgtc",
+	"bgec",
+	"slicea",
+	"slicela",
+	"slicec",
+	"indw",
+	"indf",
+	"indb",
+	"negf",
+	"movl",
+	"addl",
+	"subl",
+	"divl",
+	"modl",
+	"mull",
+	"andl",
+	"orl",
+	"xorl",
+	"shll",
+	"shrl",
+	"bnel",
+	"bltl",
+	"blel",
+	"bgtl",
+	"bgel",
+	"beql",
+	"cvtlf",
+	"cvtfl",
+	"cvtlw",
+	"cvtwl",
+	"cvtlc",
+	"cvtcl",
+	"headl",
+	"consl",
+	"newcl",
+	"casec",
+	"indl",
+	"movpc",
+	"tcmp",
+	"mnewz",
+	"cvtrf",
+	"cvtfr",
+	"cvtws",
+	"cvtsw",
+	"lsrw",
+	"lsrl",
+	"eclr",
+	"newz",
+	"newaz",
+	"raise",
+	"casel",
+	"mulx",
+	"divx",
+	"cvtxx",
+	"mulx0",
+	"divx0",
+	"cvtxx0",
+	"mulx1",
+	"divx1",
+	"cvtxx1",
+	"cvtfx",
+	"cvtxf",
+	"expw",
+	"expl",
+	"expf",
+	"self",
+};
--- /dev/null
+++ b/limbo/optim.c
@@ -1,0 +1,1803 @@
+#include "limbo.h"
+
+#define	bzero	bbzero	/* bsd name space pollution */
+/* 
+	(r, s) := f();	=> r, s have def on same pc
+	s = g();	=> this def kills previous r def (and s def)
+	solution: r has def pc, s has def pc+1 and next instruction has pc pc+2
+*/
+
+#define BLEN	(8*sizeof(ulong))
+#define BSHIFT	5	/* assumes ulong 4 */
+#define BMASK	(BLEN-1)
+
+#define	SIGN(n)		(1<<(n-1))
+#define	MSK(n)		(SIGN(n)|(SIGN(n)-1))
+#define	MASK(a, b)	(MSK((b)-(a)+1)<<(a))
+
+#define isnilsrc(s)	((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0)
+
+#define limbovar(d)	((d)->sym->name[0] != '.')
+#define structure(t)	((t)->kind == Tadt || (t)->kind == Ttuple)
+
+enum
+{
+	Bclr,
+	Band,
+	Bandinv,
+	Bstore,
+	Bandrev,
+	Bnoop,
+	Bxor,
+	Bor,
+	Bnor,
+	Bequiv,
+	Binv,
+	Bimpby,
+	Brev,
+	Bimp,
+	Bnand,
+	Bset,
+};
+
+enum
+{
+	Suse = 1,
+	Muse = 2,
+	Duse = 4,
+	Sdef = 8,
+	Mdef = 16,
+	Ddef = 32,
+	Tuse1 = 64,	/* fixed point temporary */
+	Tuse2 = 128,	/* fixed point temporary */
+	Mduse = 256,	/* D used if M nil */
+
+	None = 0,
+	Unop = Suse|Ddef,
+	Cunop = Muse|Ddef,
+	Threop = Suse|Muse|Ddef,
+	Binop = Suse|Muse|Ddef|Mduse,
+	Mbinop = Suse|Mdef|Duse,	/* strange */
+	Abinop=Suse|Duse|Ddef,
+	Mabinop = Suse|Muse|Duse|Ddef,
+	Use1 = Suse,
+	Use2 = Suse|Duse,
+	Use3 = Suse|Muse|Duse,
+};
+
+enum
+{
+	Sshift = 10,
+	Mshift = 5,
+	Dshift = 0,
+};
+
+#define S(x)	((x)<<Sshift)
+#define M(x)	((x)<<Mshift)
+#define D(x)	((x)<<Dshift)
+
+#define SS(x)	(((x)>>Sshift)&0x1f)
+#define SM(x)	(((x)>>Mshift)&0x1f)
+#define SD(x)	(((x)>>Dshift)&0x1f)
+
+enum
+{
+	I = 0,		/* ignore */
+	B = 1,	/* byte */
+	W = 4,	/* int */
+	P = 4,	/* pointer */
+	A = 4,	/* array */
+	C = 4,	/* string */
+	X = 4,	/* fixed */
+	R = 4,	/* float */
+	L = 8,	/* big */
+	F = 8,	/* real */
+	Sh = 2,	/* short */
+	Pc = 4,	/* pc */
+	Mp = 16,	/* memory */
+
+	Bop2 = S(B)|D(B),
+	Bop = S(B)|M(B)|D(B),
+	Bopb = S(B)|M(B)|D(Pc),
+	Wop2 = S(W)|D(W),
+	Wop = S(W)|M(W)|D(W),
+	Wopb = S(W)|M(W)|D(Pc),
+	Lop2 = S(L)|D(L),
+	Lop = S(L)|M(L)|D(L),
+	Lopb = S(L)|M(L)|D(Pc),
+	Cop2 = Wop2,
+	Cop = Wop,
+	Copb = Wopb,
+	Fop2 = Lop2,
+	Fop = Lop,
+	Fopb = Lopb,
+	Xop = Wop,
+};
+
+typedef struct Array Array;
+typedef struct Bits Bits;
+typedef struct Blist Blist;
+typedef struct Block Block;
+typedef struct Idlist Idlist;
+typedef struct Optab Optab;
+
+struct Array
+{
+	int	n;
+	int	m;
+	Block	**a;
+};
+
+struct Bits
+{
+	int	n;
+	ulong	*b;
+};
+
+struct Blist
+{
+	Block	*block;
+	Blist	*next;
+};
+
+struct Block
+{
+	int	dfn;
+	int	flags;
+	Inst	*first;
+	Inst	*last;
+	Block	*prev;
+	Block	*next;
+	Blist	*pred;
+	Blist *succ;
+	Bits	kill;
+	Bits	gen;
+	Bits	in;
+	Bits	out;
+};
+
+struct Idlist
+{
+	int id;
+	Idlist *next;
+};
+
+struct Optab
+{
+	short flags;
+	short size;
+};
+
+Block	zblock;
+Decl	*regdecls;
+Idlist *frelist;
+Idlist *deflist;
+Idlist *uselist;
+
+static void
+addlist(Idlist **hd, int id)
+{
+	Idlist *il;
+
+	if(frelist == nil)
+		il = (Idlist*)malloc(sizeof(Idlist));
+	else{
+		il = frelist;
+		frelist = frelist->next;
+	}
+	il->id = id;
+	il->next = *hd;
+	*hd = il;
+}
+
+static void
+freelist(Idlist **hd)
+{
+	Idlist *il;
+
+	for(il = *hd; il != nil && il->next != nil; il = il->next)
+		;
+	if(il != nil){
+		il->next = frelist;
+		frelist = *hd;
+		*hd = nil;
+	}
+}
+	
+Optab opflags[] = {
+	/* INOP */	None,	0,
+	/* IALT */	Unop,	S(Mp)|D(W),
+	/* INBALT */	Unop,	S(Mp)|D(W),
+	/* IGOTO */	Use2,	S(W)|D(I),
+	/* ICALL */	Use2,	S(P)|D(Pc),
+	/* IFRAME */	Unop,	S(W)|D(P),
+	/* ISPAWN */	Use2,	S(P)|D(Pc),
+	/* IRUNT */	None,	0,
+	/* ILOAD */	Threop,	S(C)|M(P)|D(P),
+	/* IMCALL */	Use3,	S(P)|M(W)|D(P),
+	/* IMSPAWN */	Use3,	S(P)|M(W)|D(P),
+	/* IMFRAME */	Threop,	S(P)|M(W)|D(P),
+	/* IRET */	None,	0,
+	/* IJMP */	Duse,	D(Pc),
+	/* ICASE */	Use2,	S(W)|D(I),
+	/* IEXIT */	None,	0,
+	/* INEW */	Unop,	S(W)|D(P),
+	/* INEWA */	Threop,	S(W)|M(W)|D(P),
+	/* INEWCB */	Cunop,	M(W)|D(P),
+	/* INEWCW */	Cunop,	M(W)|D(P),
+	/* INEWCF */	Cunop,	M(W)|D(P),
+	/* INEWCP */	Cunop,	M(W)|D(P),
+	/* INEWCM */	Threop,	S(W)|M(W)|D(P),
+	/* INEWCMP */	Threop,	S(W)|M(W)|D(P),
+	/* ISEND */	Use2,	S(Mp)|D(P),
+	/* IRECV */	Unop,	S(P)|D(Mp),
+	/* ICONSB */	Abinop,	S(B)|D(P),
+	/* ICONSW */	Abinop,	S(W)|D(P),
+	/* ICONSP */	Abinop,	S(P)|D(P),
+	/* ICONSF */	Abinop,	S(F)|D(P),
+	/* ICONSM */	Mabinop,	S(Mp)|M(W)|D(P),
+	/* ICONSMP */	Mabinop,	S(Mp)|M(W)|D(P),
+	/* IHEADB */	Unop,	S(P)|D(B),
+	/* IHEADW */	Unop,	S(P)|D(W),
+	/* IHEADP */	Unop,	S(P)|D(P),
+	/* IHEADF */	Unop,	S(P)|D(F),
+	/* IHEADM */	Threop,	S(P)|M(W)|D(Mp),
+	/* IHEADMP */	Threop,	S(P)|M(W)|D(Mp),
+	/* ITAIL */	Unop,	S(P)|D(P),
+	/* ILEA */	Ddef,	S(Mp)|D(P),	/* S done specially cos of ALT */
+	/* IINDX */	Mbinop,	S(P)|M(P)|D(W),
+	/* IMOVP */	Unop,	S(P)|D(P),
+	/* IMOVM */	Threop,	S(Mp)|M(W)|D(Mp),
+	/* IMOVMP */	Threop,	S(Mp)|M(W)|D(Mp),
+	/* IMOVB */	Unop,	Bop2,
+	/* IMOVW */	Unop,	Wop2,
+	/* IMOVF */	Unop,	Fop2,
+	/* ICVTBW */	Unop,	S(B)|D(W),
+	/* ICVTWB */	Unop,	S(W)|D(B),
+	/* ICVTFW */	Unop,	S(F)|D(W),
+	/* ICVTWF */	Unop,	S(W)|D(F),
+	/* ICVTCA */	Unop,	S(C)|D(A),
+	/* ICVTAC */	Unop,	S(A)|D(C),
+	/* ICVTWC */	Unop,	S(W)|D(C),
+	/* ICVTCW */	Unop,	S(C)|D(W),
+	/* ICVTFC */	Unop,	S(F)|D(C),
+	/* ICVTCF */	Unop,	S(C)|D(F),
+	/* IADDB */	Binop,	Bop,
+	/* IADDW */	Binop,	Wop,
+	/* IADDF */	Binop,	Fop,
+	/* ISUBB */	Binop,	Bop,
+	/* ISUBW */	Binop,	Wop,
+	/* ISUBF */	Binop,	Fop,
+	/* IMULB */	Binop,	Bop,
+	/* IMULW */	Binop,	Wop,
+	/* IMULF */	Binop,	Fop,
+	/* IDIVB */	Binop,	Bop,
+	/* IDIVW */	Binop,	Wop,
+	/* IDIVF */	Binop,	Fop,
+	/* IMODW */	Binop,	Wop,
+	/* IMODB */	Binop,	Bop,
+	/* IANDB */	Binop,	Bop,
+	/* IANDW */	Binop,	Wop,
+	/* IORB */	Binop,	Bop,
+	/* IORW */	Binop,	Wop,
+	/* IXORB */	Binop,	Bop,
+	/* IXORW */	Binop,	Wop,
+	/* ISHLB */	Binop,	S(W)|M(B)|D(B),
+	/* ISHLW */	Binop,	Wop,
+	/* ISHRB */	Binop,	S(W)|M(B)|D(B),
+	/* ISHRW */	Binop,	Wop,
+	/* IINSC */	Mabinop,	S(W)|M(W)|D(C),
+	/* IINDC */	Threop,	S(C)|M(W)|D(W),
+	/* IADDC */	Binop,	Cop,
+	/* ILENC */	Unop,	S(C)|D(W),
+	/* ILENA */	Unop,	S(A)|D(W),
+	/* ILENL */	Unop,	S(P)|D(W),
+	/* IBEQB */	Use3,	Bopb,
+	/* IBNEB */	Use3,	Bopb,
+	/* IBLTB */	Use3,	Bopb,
+	/* IBLEB */	Use3,	Bopb,
+	/* IBGTB */	Use3,	Bopb,
+	/* IBGEB */	Use3,	Bopb,
+	/* IBEQW */	Use3,	Wopb,
+	/* IBNEW */	Use3,	Wopb,
+	/* IBLTW */	Use3,	Wopb,
+	/* IBLEW */	Use3,	Wopb,
+	/* IBGTW */	Use3,	Wopb,
+	/* IBGEW */	Use3,	Wopb,
+	/* IBEQF */	Use3,	Fopb,
+	/* IBNEF */	Use3,	Fopb,
+	/* IBLTF */	Use3,	Fopb,
+	/* IBLEF */	Use3,	Fopb,
+	/* IBGTF */	Use3,	Fopb,
+	/* IBGEF */	Use3,	Fopb,
+	/* IBEQC */	Use3,	Copb,
+	/* IBNEC */	Use3,	Copb,
+	/* IBLTC */	Use3,	Copb,
+	/* IBLEC */	Use3,	Copb,
+	/* IBGTC */	Use3,	Copb,
+	/* IBGEC */	Use3,	Copb,
+	/* ISLICEA */	Mabinop,	S(W)|M(W)|D(P),
+	/* ISLICELA */	Use3,	S(P)|M(W)|D(P),
+	/* ISLICEC */	Mabinop,	S(W)|M(W)|D(C),
+	/* IINDW */	Mbinop,	S(P)|M(P)|D(W),
+	/* IINDF */	Mbinop,	S(P)|M(P)|D(W),
+	/* IINDB */	Mbinop,	S(P)|M(P)|D(W),
+	/* INEGF */	Unop,	Fop2,
+	/* IMOVL */	Unop,	Lop2,
+	/* IADDL */	Binop,	Lop,
+	/* ISUBL */	Binop,	Lop,
+	/* IDIVL */	Binop,	Lop,
+	/* IMODL */	Binop,	Lop,
+	/* IMULL */	Binop,	Lop,
+	/* IANDL */	Binop,	Lop,
+	/* IORL */	Binop,	Lop,
+	/* IXORL */	Binop,	Lop,
+	/* ISHLL */	Binop,	S(W)|M(L)|D(L),
+	/* ISHRL */	Binop,	S(W)|M(L)|D(L),
+	/* IBNEL */	Use3,	Lopb,
+	/* IBLTL */	Use3,	Lopb,
+	/* IBLEL */	Use3,	Lopb,
+	/* IBGTL */	Use3,	Lopb,
+	/* IBGEL */	Use3,	Lopb,
+	/* IBEQL */	Use3,	Lopb,
+	/* ICVTLF */	Unop,	S(L)|D(F),
+	/* ICVTFL */	Unop,	S(F)|D(L),
+	/* ICVTLW */	Unop,	S(L)|D(W),
+	/* ICVTWL */	Unop,	S(W)|D(L),
+	/* ICVTLC */	Unop,	S(L)|D(C),
+	/* ICVTCL */	Unop,	S(C)|D(L),
+	/* IHEADL */	Unop,	S(P)|D(L),
+	/* ICONSL */	Abinop,	S(L)|D(P),
+	/* INEWCL */	Cunop,	M(W)|D(P),
+	/* ICASEC */	Use2,	S(C)|D(I),
+	/* IINDL */	Mbinop,	S(P)|M(P)|D(W),
+	/* IMOVPC */	Unop,	S(W)|D(P),
+	/* ITCMP */	Use2,	S(P)|D(P),
+	/* IMNEWZ */	Threop,	S(P)|M(W)|D(P),
+	/* ICVTRF */	Unop,	S(R)|D(F),
+	/* ICVTFR */	Unop,	S(F)|D(R),
+	/* ICVTWS */	Unop,	S(W)|D(Sh),
+	/* ICVTSW */	Unop,	S(Sh)|D(W),
+	/* ILSRW */	Binop,	Wop,
+	/* ILSRL */	Binop,	S(W)|M(L)|D(L),
+	/* IECLR */	None,	0,
+	/* INEWZ */	Unop,	S(W)|D(P),
+	/* INEWAZ */	Threop,	S(W)|M(W)|D(P),
+	/* IRAISE */	Use1,	S(P),
+	/* ICASEL */	Use2,	S(L)|D(I),
+	/* IMULX */	Binop|Tuse2,	Xop,
+	/* IDIVX */	Binop|Tuse2,	Xop,
+	/* ICVTXX */	Threop,	Xop,
+	/* IMULX0 */	Binop|Tuse1|Tuse2,	Xop,
+	/* IDIVX0 */	Binop|Tuse1|Tuse2,	Xop,
+	/* ICVTXX0 */	Threop|Tuse1,	Xop,
+	/* IMULX1 */	Binop|Tuse1|Tuse2,	Xop,
+	/* IDIVX1 */	Binop|Tuse1|Tuse2,	Xop,
+	/* ICVTXX1 */	Threop|Tuse1,	Xop,
+	/* ICVTFX */	Threop,	S(F)|M(F)|D(X),
+	/* ICVTXF */	Threop,	S(X)|M(F)|D(F),
+	/* IEXPW */	Binop,	S(W)|M(W)|D(W),
+	/* IEXPL */	Binop,	S(W)|M(L)|D(L),
+	/* IEXPF */	Binop,	S(W)|M(F)|D(F),
+	/* ISELF */	Ddef,	D(P),
+	/* IEXC */		None,		0,
+	/* IEXC0 */	None,		0,
+	/* INOOP */	None,		0,
+};
+
+/*
+static int
+pop(int i)
+{
+	i = (i & 0x55555555) + ((i>>1) & 0x55555555);
+	i = (i & 0x33333333) + ((i>>2) & 0x33333333);
+	i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
+	i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF);
+	i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF);
+	return i;
+}
+*/
+
+static int
+bitc(uint x)
+{
+	uint n;
+
+	n = (x>>1)&0x77777777;
+	x -= n;
+	n = (n>>1)&0x77777777;
+	x -= n;
+	n = (n>>1)&0x77777777;
+	x -= n;
+	x = (x+(x>>4))&0x0f0f0f0f;
+	x *= 0x01010101;
+	return x>>24;
+}
+
+/*
+static int
+top(uint x)
+{
+	int i;
+
+	for(i = -1; x; i++)
+		x >>= 1;
+	return i;
+}
+*/
+
+static int
+topb(uint x)
+{
+	int i;
+
+	if(x == 0)
+		return -1;
+	i = 0;
+	if(x&0xffff0000){
+		i |= 16;
+		x >>= 16;
+	}
+	if(x&0xff00){
+		i |= 8;
+		x >>= 8;
+	}
+	if(x&0xf0){
+		i |= 4;
+		x >>= 4;
+	}
+	if(x&0xc){
+		i |= 2;
+		x >>= 2;
+	}
+	if(x&0x2)
+		i |= 1;
+	return i;
+}
+
+/*
+static int
+lowb(uint x)
+{
+	int i;
+
+	if(x == 0)
+		return -1;
+	for(i = BLEN; x; i--)
+		x <<= 1;
+	return i;
+}
+*/
+
+static int
+lowb(uint x)
+{
+	int i;
+
+	if(x == 0)
+		return -1;
+	i = 0;
+	if((x&0xffff) == 0){
+		i |= 16;
+		x >>= 16;
+	}
+	if((x&0xff) == 0){
+		i |= 8;
+		x >>= 8;
+	}
+	if((x&0xf) == 0){
+		i |= 4;
+		x >>= 4;
+	}
+	if((x&0x3) == 0){
+		i |= 2;
+		x >>= 2;
+	}
+	return i+1-(x&1);
+}
+		
+static void
+pbit(int x, int n)
+{
+	int i, m;
+
+	m = 1;
+	for(i = 0; i < BLEN; i++){
+		if(x&m)
+			print("%d ", i+n);
+		m <<= 1;
+	}
+}
+
+static ulong
+bop(int o, ulong s, ulong d)
+{
+	switch(o){
+	case Bclr:	return 0;
+	case Band:	return s & d;
+	case Bandinv:	return s & ~d;
+	case Bstore:	return s;
+	case Bandrev:	return ~s & d;
+	case Bnoop:	return d;
+	case Bxor:	return s ^ d;
+	case Bor:	return s | d;
+	case Bnor:	return ~(s | d);
+	case Bequiv:	return ~(s ^ d);
+	case Binv:	return ~d;
+	case Bimpby:	return s | ~d;
+	case Brev:	return ~s;
+	case Bimp:	return ~s | d;
+	case Bnand:	return ~(s & d);
+	case Bset:	return 0xffffffff;
+	}
+	return 0;
+}
+
+static Bits
+bnew(int n, int bits)
+{
+	Bits b;
+
+	if(bits)
+		b.n = (n+BLEN-1)>>BSHIFT;
+	else
+		b.n = n;
+	b.b = allocmem(b.n*sizeof(ulong));
+	memset(b.b, 0, b.n*sizeof(ulong));
+	return b;
+}
+
+static void
+bfree(Bits b)
+{
+	free(b.b);
+}
+
+static void
+bset(Bits b, int n)
+{
+	b.b[n>>BSHIFT] |= 1<<(n&BMASK);
+}
+
+static void
+bclr(Bits b, int n)
+{
+	b.b[n>>BSHIFT] &= ~(1<<(n&BMASK));
+}
+
+static int
+bmem(Bits b, int n)
+{
+	return b.b[n>>BSHIFT] & (1<<(n&BMASK));
+}
+
+static void
+bsets(Bits b, int m, int n)
+{
+	int i, c1, c2;
+
+	c1 = m>>BSHIFT;
+	c2 = n>>BSHIFT;
+	m &= BMASK;
+	n &= BMASK;
+	if(c1 == c2){
+		b.b[c1] |= MASK(m, n);
+		return;
+	}
+	for(i = c1+1; i < c2; i++)
+		b.b[i] = 0xffffffff;
+	b.b[c1] |= MASK(m, BLEN-1);
+	b.b[c2] |= MASK(0, n);
+}
+
+static void
+bclrs(Bits b, int m, int n)
+{
+	int i, c1, c2;
+
+	if(n < 0)
+		n = (b.n<<BSHIFT)-1;
+	c1 = m>>BSHIFT;
+	c2 = n>>BSHIFT;
+	m &= BMASK;
+	n &= BMASK;
+	if(c1 == c2){
+		b.b[c1] &= ~MASK(m, n);
+		return;
+	}
+	for(i = c1+1; i < c2; i++)
+		b.b[i] = 0;
+	b.b[c1] &= ~MASK(m, BLEN-1);
+	b.b[c2] &= ~MASK(0, n);
+}
+	
+/* b = a op b */
+static Bits
+boper(int o, Bits a, Bits b)
+{
+	int i, n;
+
+	n = a.n;
+	if(b.n != n)
+		fatal("boper %d %d %d", o, a.n, b.n);
+	for(i = 0; i < n; i++)
+		b.b[i] = bop(o, a.b[i], b.b[i]);
+	return b;
+}
+
+static int
+beq(Bits a, Bits b)
+{
+	int i, n;
+
+	n = a.n;
+	for(i = 0; i < n; i++)
+		if(a.b[i] != b.b[i])
+			return 0;
+	return 1;
+}
+
+static int
+bzero(Bits b)
+{
+	int i, n;
+
+	n = b.n;
+	for(i = 0; i < n; i++)
+		if(b.b[i] != 0)
+			return 0;
+	return 1;
+}
+
+static int
+bitcnt(Bits b)
+{
+	int i, m, n;
+
+	m = b.n;
+	n = 0;
+	for(i = 0; i < m; i++)
+		n += bitc(b.b[i]);
+	return n;
+}
+
+static int
+topbit(Bits b)
+{
+	int i, n;
+
+	n = b.n;
+	for(i = n-1; i >= 0; i--)
+		if(b.b[i] != 0)
+			return (i<<BSHIFT)+topb(b.b[i]);
+	return -1;
+}
+
+static int
+lowbit(Bits b)
+{
+	int i, n;
+
+	n = b.n;
+	for(i = 0; i < n; i++)
+		if(b.b[i] != 0)
+			return (i<<BSHIFT)+lowb(b.b[i]);
+	return -1;
+}
+
+static void
+pbits(Bits b)
+{
+	int i, n;
+
+	n = b.n;
+	for(i = 0; i < n; i++)
+		pbit(b.b[i], i<<BSHIFT);
+}
+
+static char*
+decname(Decl *d)
+{
+	if(d->sym == nil)
+		return "<nil>";
+	return d->sym->name;
+}
+
+static void
+warning(Inst *i, char *s, Decl *d, Decl *sd)
+{
+	int n;
+	char *f;
+	Decl *ds;
+
+	n = 0;
+	for(ds = sd; ds != nil; ds = ds->next)
+		if(ds->link == d)
+			n += strlen(ds->sym->name)+1;
+	if(n == 0){
+		warn(i->src.start, "%s: %s", d->sym->name, s);
+		return;
+	}
+	n += strlen(d->sym->name);
+	f = malloc(n+1);
+	strcpy(f, d->sym->name);
+	for(ds = sd; ds != nil; ds = ds->next){
+		if(ds->link == d){
+			strcat(f, "/");
+			strcat(f, ds->sym->name);
+		}
+	}
+	warn(i->src.start, "%s: %s", f, s);
+	free(f);
+}
+
+static int
+inspc(Inst *in)
+{
+	int n;
+	Inst *i;
+
+	n = 0;
+	for(i = in; i != nil; i = i->next)
+		i->pc = n++;
+	return n;
+}
+
+static Inst*
+pc2i(Block *b, int pc)
+{
+	Inst *i;
+
+	for( ; b != nil; b = b->next){
+		if(pc > b->last->pc)
+			continue;
+		for(i = b->first; ; i = i->next){
+			if(i->pc == pc)
+				return i;
+			if(i == b->last)
+				fatal("pc2i a");
+		}
+	}
+	fatal("pc2i b");
+	return nil;
+}
+
+static void
+padr(int am, Addr *a, Inst *br)
+{
+	long reg;
+
+	if(br != nil){
+		print("$%ld", br->pc);
+		return;
+	}
+	reg = a->reg;
+	if(a->decl != nil && am != Adesc)
+		reg += a->decl->offset;
+	switch(am){
+	case Anone:
+		print("-");
+		break;
+	case Aimm:
+	case Apc:
+	case Adesc:
+		print("$%ld", a->offset);
+		break;
+	case Aoff:
+		print("$%ld", a->decl->iface->offset);
+		break;
+	case Anoff:
+		print("-$%ld", a->decl->iface->offset);
+		break;
+	case Afp:
+		print("%ld(fp)", reg);
+		break;
+	case Afpind:
+		print("%ld(%ld(fp))", a->offset, reg);
+		break;
+	case Amp:
+		print("%ld(mp)", reg);
+		break;
+	case Ampind:
+		print("%ld(%ld(mp))", a->offset, reg);
+		break;
+	case Aldt:
+		print("$%ld", reg);
+		break;
+	case Aerr:
+	default:
+		print("%ld(%ld(?%d?))", a->offset, reg, am);
+		break;
+	}
+}
+
+static void
+pins(Inst *i)
+{
+	/* print("%L		%ld	", i->src.start, i->pc); */
+	print("		%ld	", i->pc);
+	if(i->op < MAXDIS)
+		print("%s", instname[i->op]);
+	else
+		print("noop");
+	print("	");
+	padr(i->sm, &i->s, nil);
+	print(", ");
+	padr(i->mm, &i->m, nil);
+	print(", ");
+	padr(i->dm, &i->d, i->branch);
+	print("\n");
+}
+
+static void
+blfree(Blist *bl)
+{
+	Blist *nbl;
+
+	for( ; bl != nil; bl = nbl){
+		nbl = bl->next;
+		free(bl);
+	}
+}
+
+static void
+freebits(Bits *bs, int nv)
+{
+	int i;
+
+	for(i = 0; i < nv; i++)
+		bfree(bs[i]);
+	free(bs);
+}
+
+static void
+freeblks(Block *b)
+{
+	Block *nb;
+
+	for( ; b != nil; b = nb){
+		blfree(b->pred);
+		blfree(b->succ);
+		bfree(b->kill);
+		bfree(b->gen);
+		bfree(b->in);
+		bfree(b->out);
+		nb = b->next;
+		free(b);
+	}
+}
+
+static int
+len(Decl *d)
+{
+	int n;
+
+	n = 0;
+	for( ; d != nil; d = d->next)
+		n++;
+	return n;
+}
+
+static Bits*
+allocbits(int nv, int npc)
+{
+	int i;
+	Bits *defs;
+
+	defs = (Bits*)allocmem(nv*sizeof(Bits));
+	for(i = 0; i < nv; i++)
+		defs[i] = bnew(npc, 1);
+	return defs;
+}
+
+static int
+bitcount(Bits *bs, int nv)
+{
+	int i, n;
+
+	n = 0;
+	for(i = 0; i < nv; i++)
+		n += bitcnt(bs[i]);
+	return n;
+}
+
+static Block*
+mkblock(Inst *i)
+{
+	Block *b;
+
+	b = allocmem(sizeof(Block));
+	*b = zblock;
+	b->first = b->last = i;
+	return b;
+}
+
+static Blist*
+mkblist(Block *b, Blist *nbl)
+{
+	Blist *bl;
+
+	bl = allocmem(sizeof(Blist));
+	bl->block = b;
+	bl->next = nbl;
+	return bl;
+}
+
+static void
+leader(Inst *i, Array *ab)
+{
+	int m, n;
+	Block *b, **a;
+
+	if(i != nil && i->pc == 0){
+		if((n = ab->n) == (m = ab->m)){
+			a = ab->a;
+			ab->a = allocmem(2*m*sizeof(Block*));
+			memcpy(ab->a, a, m*sizeof(Block*));
+			ab->m = 2*m;
+			free(a);
+		}
+		b = mkblock(i);
+		b->dfn = n;
+		ab->a[n] = b;
+		i->pc = ab->n = n+1;
+	}
+}
+
+static Block*
+findb(Inst *i, Array *ab)
+{
+	if(i == nil)
+		return nil;
+	if(i->pc <= 0)
+		fatal("pc <= 0 in findb");
+	return ab->a[i->pc-1];
+}
+
+static int
+memb(Block *b, Blist *bl)
+{
+	for( ; bl != nil; bl = bl->next)
+		if(bl->block == b)
+			return 1;
+	return 0;
+}
+
+static int
+canfallthrough(Inst *i)
+{
+	if(i == nil)
+		return 0;
+	switch(i->op){
+	case IGOTO:
+	case ICASE:
+	case ICASEL:
+	case ICASEC:
+	case IRET:
+	case IEXIT:
+	case IRAISE:
+	case IJMP:
+		return 0;
+	case INOOP:
+		return i->branch != nil;
+	}
+	return 1;
+}
+
+static void
+predsucc(Block *b1, Block *b2)
+{
+	if(b1 == nil || b2 == nil)
+		return;
+	if(!memb(b1, b2->pred))
+		b2->pred = mkblist(b1, b2->pred);
+	if(!memb(b2, b1->succ))
+		b1->succ = mkblist(b2, b1->succ);
+}
+	
+static Block*
+mkblocks(Inst *in, int *nb)
+{
+	Inst *i;
+	Block *b, *firstb, *lastb;
+	Label *lab;
+	Array *ab;
+	int j, n;
+
+	ab = allocmem(sizeof(Array));
+	ab->n = 0;
+	ab->m = 16;
+	ab->a = allocmem(ab->m*sizeof(Block*));
+	leader(in, ab);
+	for(i = in; i != nil; i = i->next){
+		switch(i->op){
+		case IGOTO:
+		case ICASE:
+		case ICASEL:
+		case ICASEC:
+		case INOOP:
+			if(i->op == INOOP && i->branch != nil){
+				leader(i->branch, ab);
+				leader(i->next, ab);
+				break;
+			}
+			leader(i->d.decl->ty->cse->iwild, ab);
+			lab = i->d.decl->ty->cse->labs;
+			n = i->d.decl->ty->cse->nlab;
+			for(j = 0; j < n; j++)
+				leader(lab[j].inst, ab);
+			leader(i->next, ab);
+			break;
+		case IRET:
+		case IEXIT:
+		case IRAISE:
+			leader(i->next, ab);
+			break;
+		case IJMP:
+			leader(i->branch, ab);
+			leader(i->next, ab);
+			break;
+		default:
+			if(i->branch != nil){
+				leader(i->branch, ab);
+				leader(i->next, ab);
+			}
+			break;
+		}
+	}
+	firstb = lastb = mkblock(nil);
+	for(i = in; i != nil; i = i->next){
+		if(i->pc != 0){
+			b = findb(i, ab);
+			b->prev = lastb;
+			lastb->next = b;
+			if(canfallthrough(lastb->last))
+				predsucc(lastb, b);
+			lastb = b;
+		}
+		else
+			lastb->last = i;
+		switch(i->op){
+		case IGOTO:
+		case ICASE:
+		case ICASEL:
+		case ICASEC:
+		case INOOP:
+			if(i->op == INOOP && i->branch != nil){
+				b = findb(i->next, ab);
+				predsucc(lastb, b);
+				b = findb(i->branch, ab);
+				predsucc(lastb, b);
+				break;
+			}
+			b = findb(i->d.decl->ty->cse->iwild, ab);
+			predsucc(lastb, b);
+			lab = i->d.decl->ty->cse->labs;
+			n = i->d.decl->ty->cse->nlab;
+			for(j = 0; j < n; j++){
+				b = findb(lab[j].inst, ab);
+				predsucc(lastb, b);
+			}
+			break;
+		case IRET:
+		case IEXIT:
+		case IRAISE:
+			break;
+		case IJMP:
+			b = findb(i->branch, ab);
+			predsucc(lastb, b);
+			break;
+		default:
+			if(i->branch != nil){
+				b = findb(i->next, ab);
+				predsucc(lastb, b);
+				b = findb(i->branch, ab);
+				predsucc(lastb, b);
+			}
+			break;
+		}
+	}
+	*nb = ab->n;
+	free(ab->a);
+	free(ab);
+	b = firstb->next;
+	b->prev = nil;
+	return b;
+}
+
+static int
+back(Block *b1, Block *b2)
+{
+	return b1->dfn >= b2->dfn;
+}
+
+static void
+pblocks(Block *b, int nb)
+{
+	Inst *i;
+	Blist *bl;
+
+	print("--------------------%d blocks--------------------\n", nb);
+	print("------------------------------------------------\n");
+	for( ; b != nil; b = b->next){
+		print("dfn=%d\n", b->dfn);
+		print("    pred	");
+		for(bl = b->pred; bl != nil; bl = bl->next)
+			print("%d%s ", bl->block->dfn, back(bl->block, b) ? "*" : "");
+		print("\n");
+		print("    succ	");
+		for(bl = b->succ; bl != nil; bl = bl->next)
+			print("%d%s ", bl->block->dfn, back(b, bl->block) ? "*" : "");
+		print("\n");
+		for(i = b->first; i != nil; i = i->next){
+			// print("	%I\n", i);
+			pins(i);
+			if(i == b->last)
+				break;
+		}
+	}
+	print("------------------------------------------------\n");
+}
+
+static void
+ckblocks(Inst *in, Block *b, int nb)
+{
+	int n;
+	Block *lastb;
+
+	if(b->first != in)
+		fatal("A - %d", b->dfn);
+	n = 0;
+	lastb = nil;
+	for( ; b != nil; b = b->next){
+		n++;
+		if(b->prev != lastb)
+			fatal("a - %d\n", b->dfn);
+		if(b->prev != nil && b->prev->next != b)
+			fatal("b - %d\n", b->dfn);
+		if(b->next != nil && b->next->prev != b)
+			fatal("c - %d\n", b->dfn);
+
+		if(b->prev != nil && b->prev->last->next != b->first)
+			fatal("B - %d\n", b->dfn);
+		if(b->next != nil && b->last->next != b->next->first)
+			fatal("C - %d\n", b->dfn);
+		if(b->next == nil && b->last->next != nil)
+			fatal("D - %d\n", b->dfn);
+
+		if(b->last->branch != nil && b->succ->block->first != b->last->branch)
+			fatal("0 - %d\n", b->dfn);
+
+		lastb = b;
+	}
+	if(n != nb)
+		fatal("N - %d %d\n", n, nb);
+}
+
+static void
+dfs0(Block *b, int *n)
+{
+	Block *s;
+	Blist *bl;
+
+	b->flags = 1;
+	for(bl = b->succ; bl != nil; bl = bl->next){
+		s = bl->block;
+		if(s->flags == 0)
+			dfs0(s, n);
+	}
+	b->dfn = --(*n);
+}
+
+static int
+dfs(Block *b, int nb)
+{
+	int n, u;
+	Block *b0;
+
+	b0 = b;
+	n = nb;
+	dfs0(b0, &n);
+	u = 0;
+	for(b = b0; b != nil; b = b->next){
+		if(b->flags == 0){	/* unreachable: see foldbranch */
+			fatal("found unreachable code");
+			u++;
+			b->prev->next = b->next;
+			if(b->next){
+				b->next->prev = b->prev;
+				b->prev->last->next = b->next->first;
+			}
+			else
+				b->prev->last->next = nil;
+		}
+		b->flags = 0;
+	}
+	if(u){
+		for(b = b0; b != nil; b = b->next)
+			b->dfn -= u;
+	}
+	return nb-u;
+}
+
+static void
+loop0(Block *b)
+{
+	Block *p;
+	Blist *bl;
+
+	b->flags = 1;
+	for(bl = b->pred; bl != nil; bl = bl->next){
+		p = bl->block;
+		if(p->flags == 0)
+			loop0(p);
+	}
+}
+
+/* b1->b2 a back edge */
+static void
+loop(Block *b, Block *b1, Block *b2)
+{
+	if(0 && debug['o'])
+		print("back edge %d->%d\n", b1->dfn, b2->dfn);
+	b2->flags = 1;
+	if(b1->flags == 0)
+		loop0(b1);
+	if(0 && debug['o'])
+		print("	loop	");
+	for( ; b != nil; b = b->next){
+		if(b->flags && 0 && debug['o'])
+			print("%d ", b->dfn);
+		b->flags = 0;
+	}
+	if(0 && debug['o'])
+		print("\n");
+}
+
+static void
+loops(Block *b)
+{
+	Block *b0;
+	Blist *bl;
+
+	b0 = b;
+	for( ; b != nil; b = b->next){
+		for(bl = b->succ; bl != nil; bl = bl->next){
+			if(back(b, bl->block))
+				loop(b0, b, bl->block);
+		}
+	}
+}
+
+static int
+imm(int m, Addr *a)
+{
+	if(m == Aimm)
+		return a->offset;
+	fatal("bad immediate value");
+	return -1;
+}
+
+static int
+desc(int m, Addr *a)
+{
+	if(m == Adesc)
+		return a->decl->desc->size;
+	fatal("bad descriptor value");
+	return -1;
+}
+
+static int
+fpoff(int m, Addr *a)
+{
+	int off;
+	Decl *d;
+
+	if(m == Afp || m == Afpind){
+		off = a->reg;
+		if((d = a->decl) != nil)
+			off += d->offset;
+		return off;
+	}
+	return -1;
+}
+
+static int
+size(Inst *i)
+{
+	switch(i->op){
+	case ISEND:
+	case IRECV:
+	case IALT:
+	case INBALT:
+	case ILEA:
+		return i->m.offset;
+	case IMOVM:
+	case IHEADM:
+	case ICONSM:
+		return imm(i->mm, &i->m);
+	case IMOVMP:
+	case IHEADMP:
+	case ICONSMP:
+		return desc(i->mm, &i->m);
+		break;
+	}
+	fatal("bad op in size");
+	return -1;
+}
+
+static Decl*
+mkdec(int o)
+{
+	Decl *d;
+
+	d = mkdecl(&nosrc, Dlocal, tint);
+	d->offset = o;
+	return d;
+}
+
+static void
+mkdecls(void)
+{
+	regdecls = mkdec(REGRET*IBY2WD);
+	regdecls->next = mkdec(STemp);
+	regdecls->next->next = mkdec(DTemp);
+}
+
+static Decl*
+sharedecls(Decl *d)
+{
+	Decl *ld;
+
+	ld = d;
+	for(d = d->next ; d != nil; d = d->next){
+		if(d->offset <= ld->offset)
+			break;
+		ld = d;
+	}
+	return d;
+}
+
+static int
+finddec(int o, int s, Decl *vars, int *nv, Inst *i)
+{
+	int m, n;
+	Decl *d;
+
+	n = 0;
+	for(d = vars; d != nil; d = d->next){
+		if(o >= d->offset && o < d->offset+d->ty->size){
+			m = 1;
+			while(o+s > d->offset+d->ty->size){
+				m++;
+				d = d->next;
+			}
+			*nv = m;
+			return n;
+		}
+		n++;
+	}
+	// print("%d %d missing\n", o, s);
+	pins(i);
+	fatal("missing decl");
+	return -1;
+}
+
+static void
+setud(Bits *b, int id, int n, int pc)
+{
+	if(id < 0)
+		return;
+	while(--n >= 0)
+		bset(b[id++], pc);
+}
+
+static void
+ud(Inst *i, Decl *vars, Bits *uses, Bits *defs)
+{
+	ushort f;
+	int id, j, nv, pc, sz, s, m, d, ss, sm, sd;
+	Optab *t;
+	Idlist *l;
+
+	pc = i->pc;
+	ss = 0;
+	t = &opflags[i->op];
+	f = t->flags;
+	sz = t->size;
+	s = fpoff(i->sm, &i->s);
+	m = fpoff(i->mm, &i->m);
+	d = fpoff(i->dm, &i->d);
+	if(f&Mduse && i->mm == Anone)
+		f |= Duse;
+	if(s >= 0){
+		if(i->sm == Afp){
+			ss = SS(sz);
+			if(ss == Mp)
+				ss = size(i);
+		}
+		else
+			ss = IBY2WD;
+		id = finddec(s, ss, vars, &nv, i);
+		if(f&Suse)
+			setud(uses, id, nv, pc);
+		if(f&Sdef){
+			if(i->sm == Afp)
+				setud(defs, id, nv, pc);
+			else
+				setud(uses, id, nv, pc);
+		}
+	}
+	if(m >= 0){
+		if(i->mm == Afp){
+			sm = SM(sz);
+			if(sm == Mp)
+				sm = size(i);
+		}
+		else
+			sm = IBY2WD;
+		id = finddec(m, sm, vars, &nv, i);
+		if(f&Muse)
+			setud(uses, id, nv, pc);
+		if(f&Mdef){
+			if(i->mm == Afp)
+				setud(defs, id, nv, pc);
+			else
+				setud(uses, id, nv, pc);
+		}
+	}
+	if(d >= 0){
+		if(i->dm == Afp){
+			sd = SD(sz);
+			if(sd == Mp)
+				sd = size(i);
+		}
+		else
+			sd = IBY2WD;
+		id = finddec(d, sd, vars, &nv, i);
+		if(f&Duse)
+			setud(uses, id, nv, pc);
+		if(f&Ddef){
+			if(i->dm == Afp)
+				setud(defs, id, nv, pc);
+			else
+				setud(uses, id, nv, pc);
+		}
+	}
+	if(f&Tuse1){
+		id = finddec(STemp, IBY2WD, vars, &nv, i);
+		setud(uses, id, nv, pc);
+	}
+	if(f&Tuse2){
+		id = finddec(DTemp, IBY2WD, vars, &nv, i);
+		setud(uses, id, nv, pc);
+	}
+	if(i->op == ILEA){
+		if(s >= 0){
+			id = finddec(s, ss, vars, &nv, i);
+			if(i->sm == Afp && i->m.reg == 0)
+				setud(defs, id, nv, pc);
+			else
+				setud(uses, id, nv, pc);
+		}
+	}
+	if(0)
+	switch(i->op){
+	case ILEA:
+		if(s >= 0){
+			id = finddec(s, ss, vars, &nv, i);
+			if(id < 0)
+				break;
+			for(j = 0; j < nv; j++){
+				if(i->sm == Afp && i->m.reg == 0)
+					addlist(&deflist, id++);
+				else
+					addlist(&uselist, id++);
+			}
+		}
+		break;
+	case IALT:
+	case INBALT:
+	case ICALL:
+	case IMCALL:
+		for(l = deflist; l != nil; l = l->next){
+			id = l->id;
+			bset(defs[id], pc);
+		}
+		for(l = uselist; l != nil; l = l->next){
+			id = l->id;
+			bset(uses[id], pc);
+		}
+		freelist(&deflist);
+		freelist(&uselist);
+		break;
+	}
+}
+
+static void
+usedef(Inst *in, Decl *vars, Bits *uses, Bits *defs)
+{
+	Inst *i;
+
+	for(i = in; i != nil; i = i->next)
+		ud(i, vars, uses, defs);
+}
+
+static void
+pusedef(Bits *ud, int nv, Decl *d, char *s)
+{
+	int i;
+
+	print("%s\n", s);
+	for(i = 0; i < nv; i++){
+		if(!bzero(ud[i])){
+			print("\t%s(%ld):	", decname(d), d->offset);
+			pbits(ud[i]);
+			print("\n");
+		}
+		d = d->next;
+	}
+}
+
+static void
+dummydefs(Bits *defs, int nv, int npc)
+{
+	int i;
+
+	for(i = 0; i < nv; i++)
+		bset(defs[i], npc++);
+}
+
+static void
+dogenkill(Block *b, Bits *defs, int nv)
+{
+	int i, n, t;
+	Bits v;
+
+	n = defs[0].n;
+	v = bnew(n, 0);
+	for( ; b != nil; b = b->next){
+		b->gen = bnew(n, 0);
+		b->kill = bnew(n, 0);
+		b->in = bnew(n, 0);
+		b->out = bnew(n, 0);
+		for(i = 0; i < nv; i++){
+			boper(Bclr, v, v);
+			bsets(v, b->first->pc, b->last->pc);
+			boper(Band, defs[i], v);
+			t = topbit(v);
+			if(t >= 0)
+				bset(b->gen, t);
+			else
+				continue;
+			boper(Bclr, v, v);
+			bsets(v, b->first->pc, b->last->pc);
+			boper(Binv, v, v);
+			boper(Band, defs[i], v);
+			boper(Bor, v, b->kill);
+		}
+	}
+	bfree(v);
+}
+
+static void
+udflow(Block *b, int nv, int npc)
+{
+	int iter;
+	Block *b0, *p;
+	Blist *bl;
+	Bits newin;
+
+	b0 = b;
+	for(b = b0; b != nil; b = b->next)
+		boper(Bstore, b->gen, b->out);
+	newin = bnew(b0->in.n, 0);
+	iter = 1;
+	while(iter){
+		iter = 0;
+		for(b = b0; b != nil; b = b->next){
+			boper(Bclr, newin, newin);
+			for(bl = b->pred; bl != nil; bl = bl->next){
+				p = bl->block;
+				boper(Bor, p->out, newin);
+			}
+			if(b == b0)
+				bsets(newin, npc, npc+nv-1);
+			if(!beq(b->in, newin))
+				iter = 1;
+			boper(Bstore, newin, b->in);
+			boper(Bstore, b->in, b->out);
+			boper(Bandrev, b->kill, b->out);
+			boper(Bor, b->gen, b->out);
+		}
+	}
+	bfree(newin);
+}
+
+static void
+pflows(Block *b)
+{
+	for( ; b != nil; b = b->next){
+		print("block %d\n", b->dfn);
+		print("	gen:	"); pbits(b->gen); print("\n");
+		print("	kill:	"); pbits(b->kill); print("\n");
+		print("	in:	"); pbits(b->in); print("\n");
+		print("	out:	"); pbits(b->out); print("\n");
+	}
+}
+
+static int
+set(Decl *d)
+{
+	if(d->store == Darg)
+		return 1;
+	if(d->sym == nil)	/* || d->sym->name[0] == '.') */
+		return 1;
+	if(tattr[d->ty->kind].isptr || d->ty->kind == Texception)
+		return 1;
+	return 0;
+}
+
+static int
+used(Decl *d)
+{
+	if(d->sym == nil )	/* || d->sym->name[0] == '.') */
+		return 1;
+	return 0;
+}
+
+static void
+udchain(Block *b, Decl *ds, int nv, int npc, Bits *defs, Bits *uses, Decl *sd)
+{
+	int i, n, p, q;
+	Bits d, u, dd, ud;
+	Block *b0;
+	Inst *in;
+
+	b0 = b;
+	n = defs[0].n;
+	u = bnew(n, 0);
+	d = bnew(n, 0);
+	dd = bnew(n, 0);
+	ud = bnew(n, 0);
+	for(i = 0; i < nv; i++){
+		boper(Bstore, defs[i], ud);
+		bclr(ud, npc+i);
+		for(b = b0 ; b != nil; b = b->next){
+			boper(Bclr, u, u);
+			bsets(u, b->first->pc, b->last->pc);
+			boper(Band, uses[i], u);
+			boper(Bclr, d, d);
+			bsets(d, b->first->pc, b->last->pc);
+			boper(Band, defs[i], d);
+			for(;;){
+				p = topbit(u);
+				if(p < 0)
+					break;
+				bclr(u, p);
+				bclrs(d, p, -1);
+				q = topbit(d);
+				if(q >= 0){
+					bclr(ud, q);
+					if(debug['o'])
+						print("udc b=%d v=%d(%s/%ld) u=%d d=%d\n", b->dfn, i, decname(ds), ds->offset, p, q);
+				}
+				else{
+					boper(Bstore, defs[i], dd);
+					boper(Band, b->in, dd);
+					boper(Bandrev, dd, ud);
+					if(!bzero(dd)){
+						if(debug['o']){
+							print("udc b=%d v=%d(%s/%ld) u=%d d=", b->dfn, i, decname(ds), ds->offset, p);
+							pbits(dd);
+							print("\n");
+						}
+						if(bmem(dd, npc+i) && !set(ds))
+							warning(pc2i(b0, p), "used and not set", ds, sd);
+					}
+					else
+						fatal("no defs in udchain");
+				}
+			}
+		}
+		for(;;){
+			p = topbit(ud);
+			if(p < 0)
+				break;
+			bclr(ud, p);
+			if(!used(ds)){
+				in = pc2i(b0, p);
+				if(isnilsrc(&in->src))	/* nilling code */
+					in->op = INOOP;	/* elim p from bitmaps ? */
+				else if(limbovar(ds) && !structure(ds->ty))
+					warning(in, "set and not used", ds, sd);
+			}
+		}
+		ds = ds->next;
+	}
+	bfree(u);
+	bfree(d);
+	bfree(dd);
+	bfree(ud);
+}
+
+static void
+ckflags(void)
+{
+	int i, j, k, n;
+	Optab *o;
+
+	n = nelem(opflags);
+	o = opflags;
+	for(i = 0; i < n; i++){
+		j = (o->flags&(Suse|Sdef)) != 0;
+		k = SS(o->size) != 0;
+		if(j != k){
+			if(!(j == 0 && k == 1 && i == ILEA))
+				fatal("S %ld %s\n", o-opflags, instname[i]);
+		}
+		j = (o->flags&(Muse|Mdef)) != 0;
+		k = SM(o->size) != 0;
+		if(j != k)
+			fatal("M %ld %s\n", o-opflags, instname[i]);
+		j = (o->flags&(Duse|Ddef)) != 0;
+		k = SD(o->size) != 0;
+		if(j != k){
+			if(!(j == 1 && k == 0 && (i == IGOTO || i == ICASE || i == ICASEC || i == ICASEL)))
+				fatal("D %ld %s\n", o-opflags, instname[i]);
+		}
+		o++;
+	}
+}
+
+void
+optim(Inst *in, Decl *d)
+{
+	int nb, npc, nv, nd, nu;
+	Block *b;
+	Bits *uses, *defs;
+	Decl *sd;
+
+	ckflags();
+	if(debug['o'])
+		print("************************************************\nfunction %s\n************************************************\n", d->sym->name);
+	if(in == nil || errors > 0)
+		return;
+	d = d->ty->ids;
+	if(regdecls == nil)
+		mkdecls();
+	regdecls->next->next->next = d;
+	d = regdecls;
+	sd = sharedecls(d);
+	if(debug['o'])
+		printdecls(d);
+	b = mkblocks(in, &nb);
+	ckblocks(in, b, nb);
+	npc = inspc(in);
+	nb = dfs(b, nb);
+	if(debug['o'])
+		pblocks(b, nb);
+	loops(b);
+	nv = len(d);
+	uses = allocbits(nv, npc+nv);
+	defs = allocbits(nv, npc+nv);
+	dummydefs(defs, nv, npc);
+	usedef(in, d, uses, defs);
+	if(debug['o']){
+		pusedef(uses, nv, d, "uses");
+		pusedef(defs, nv, d, "defs");
+	}
+	nu = bitcount(uses, nv);
+	nd = bitcount(defs, nv);
+	dogenkill(b, defs, nv);
+	udflow(b, nv, npc);
+	if(debug['o'])
+		pflows(b);
+	udchain(b, d, nv, npc, defs, uses, sd);
+	freeblks(b);
+	freebits(uses, nv);
+	freebits(defs, nv);
+	if(debug['o'])
+		print("nb=%d npc=%d nv=%d nd=%d nu=%d\n", nb, npc, nv, nd, nu);
+}
+
--- /dev/null
+++ b/limbo/sbl.c
@@ -1,0 +1,352 @@
+#include "limbo.h"
+
+static char sbltname[Tend] =
+{
+	/* Tnone */	'n',
+	/* Tadt */	'a',
+	/* Tadtpick */	'a',
+	/* Tarray */	'A',
+	/* Tbig */	'B',
+	/* Tbyte */	'b',
+	/* Tchan */	'C',
+	/* Treal */	'f',
+	/* Tfn */	'F',
+	/* Tint */	'i',
+	/* Tlist */	'L',
+	/* Tmodule */	'm',
+	/* Tref */	'R',
+	/* Tstring */	's',
+	/* Ttuple */	't',
+	/* Texception */	't',
+	/* Tfix */	'i',
+	/* Tpoly */	'P',
+
+	/* Tainit */	'?',
+	/* Talt */	'?',
+	/* Tany */	'N',
+	/* Tarrow */	'?',
+	/* Tcase */	'?',
+	/* Tcasel */	'?',
+	/* Tcasec */	'?',
+	/* Tdot */	'?',
+	/* Terror */	'?',
+	/* Tgoto */	'?',
+	/* Tid */	'?',
+	/* Tiface */	'?',
+	/* Texcept */	'?',
+	/* Tinst */	'?',
+};
+int	sbltadtpick = 'p';
+
+static	Sym	*sfiles;
+static	Sym	*ftail;
+static	int	nsfiles;
+static	int	blockid;
+static	int	lastf;
+static	int	lastline;
+
+static	void	sbltype(Type*, int);
+static	void	sbldecl(Decl*, int);
+static	void	sblftype(Type*);
+static	void	sblfdecl(Decl*, int);
+
+void
+sblmod(Decl *m)
+{
+	Bprint(bsym, "limbo .sbl 2.1\n");
+	Bprint(bsym, "%s\n", m->sym->name);
+
+	blockid = 0;
+	nsfiles = 0;
+	sfiles = ftail = nil;
+	lastf = 0;
+	lastline = 0;
+}
+
+static int
+sblfile(char *name)
+{
+	Sym *s;
+	int i;
+
+	i = 0;
+	for(s = sfiles; s != nil; s = s->next){
+		if(strcmp(s->name, name) == 0)
+			return i;
+		i++;
+	}
+	s = allocmem(sizeof(Sym));
+	s->name = name;
+	s->next = nil;
+	if(sfiles == nil)
+		sfiles = s;
+	else
+		ftail->next = s;
+	ftail = s;
+	nsfiles = i + 1;
+	return i;
+}
+
+static char *
+filename(char *s)
+{
+	char *t;
+
+	t = strrchr(s, '/');
+	if(t != nil)
+		s = t + 1;
+	t = strrchr(s, '\\');
+	if(t != nil)
+		s = t+1;
+	t = strrchr(s, ' ');
+	if(t != nil)
+		s = t + 1;
+	return s;
+}
+
+void
+sblfiles(void)
+{
+	Sym *s;
+	int i;
+
+	for(i = 0; i < nfiles; i++)
+		files[i]->sbl = sblfile(files[i]->name);
+	Bprint(bsym, "%d\n", nsfiles);
+	for(s = sfiles; s != nil; s = s->next)
+		Bprint(bsym, "%s\n", filename(s->name));
+}
+
+static char*
+sblsrcconv(char *buf, char *end, Src *src)
+{
+	Fline fl;
+	File *startf, *stopf;
+	char *s;
+	int startl, stopl;
+
+	s = buf;
+
+	fl = fline(src->start.line);
+	startf = fl.file;
+	startl = fl.line;
+	fl = fline(src->stop.line);
+	stopf = fl.file;
+	stopl = fl.line;
+	if(lastf != startf->sbl)
+		s = seprint(s, end, "%d:", startf->sbl);
+	if(lastline != startl)
+		s = seprint(s, end, "%d.", startl);
+	s = seprint(s, end, "%d,", src->start.pos);
+	if(startf->sbl != stopf->sbl)
+		s = seprint(s, end, "%d:", stopf->sbl);
+	if(startl != stopl)
+		s = seprint(s, end, "%d.", stopl);
+	seprint(s, end, "%d ", src->stop.pos);
+	lastf = stopf->sbl;
+	lastline = stopl;
+	return buf;
+}
+
+#define isnilsrc(s)	((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0)
+#define isnilstopsrc(s)	((s)->stop.line == 0 && (s)->stop.pos == 0)
+
+void
+sblinst(Inst *inst, long ninst)
+{
+	Inst *in;
+	char buf[StrSize];
+	int *sblblocks, i, b;
+	Src src;
+
+	Bprint(bsym, "%ld\n", ninst);
+	sblblocks = allocmem(nblocks * sizeof *sblblocks);
+	for(i = 0; i < nblocks; i++)
+		sblblocks[i] = -1;
+	src = nosrc;
+	for(in = inst; in != nil; in = in->next){
+		if(in->op == INOOP)
+			continue;
+		if(in->src.start.line < 0)
+			fatal("no file specified for %I", in);
+		b = sblblocks[in->block];
+		if(b < 0)
+			sblblocks[in->block] = b = blockid++;
+		if(isnilsrc(&in->src))
+			in->src = src;
+		else if(isnilstopsrc(&in->src)){	/* how does this happen ? */
+			in->src.stop = in->src.start;
+			in->src.stop.pos++;
+		}
+		Bprint(bsym, "%s%d\n", sblsrcconv(buf, buf+sizeof(buf), &in->src), b);
+		src = in->src;
+	}
+	free(sblblocks);
+}
+
+void
+sblty(Decl **tys, int ntys)
+{
+	Decl *d;
+	int i;
+
+	Bprint(bsym, "%d\n", ntys);
+	for(i = 0; i < ntys; i++){
+		d = tys[i];
+		d->ty->sbl = i;
+	}
+	for(i = 0; i < ntys; i++){
+		d = tys[i];
+		sbltype(d->ty, 1);
+	}
+}
+
+void
+sblfn(Decl **fns, int nfns)
+{
+	Decl *f;
+	int i;
+
+	Bprint(bsym, "%d\n", nfns);
+	for(i = 0; i < nfns; i++){
+		f = fns[i];
+		if(ispoly(f))
+			rmfnptrs(f);
+		if(f->dot != nil && f->dot->ty->kind == Tadt)
+			Bprint(bsym, "%ld:%s.%s\n", f->pc->pc, f->dot->sym->name, f->sym->name);
+		else
+			Bprint(bsym, "%ld:%s\n", f->pc->pc, f->sym->name);
+		sbldecl(f->ty->ids, Darg);
+		sbldecl(f->locals, Dlocal);
+		sbltype(f->ty->tof, 0);
+	}
+}
+
+void
+sblvar(Decl *vars)
+{
+	sbldecl(vars, Dglobal);
+}
+
+static int
+isvis(Decl *id)
+{
+	if(!tattr[id->ty->kind].vis
+	|| id->sym == nil
+	|| id->sym->name == nil		/*????*/
+	|| id->sym->name[0] == '.')
+		return 0;
+	if(id->ty == tstring && id->init != nil && id->init->op == Oconst)
+		return 0;
+	if(id->src.start.line < 0 || id->src.stop.line < 0)
+		return 0;
+	return 1;
+}
+
+static void
+sbldecl(Decl *ids, int store)
+{
+	Decl *id;
+	char buf[StrSize];
+	int n;
+
+	n = 0;
+	for(id = ids; id != nil; id = id->next){
+		if(id->store != store || !isvis(id))
+			continue;
+		n++;
+	}
+	Bprint(bsym, "%d\n", n);
+	for(id = ids; id != nil; id = id->next){
+		if(id->store != store || !isvis(id))
+			continue;
+		Bprint(bsym, "%ld:%s:%s", id->offset, id->sym->name, sblsrcconv(buf, buf+sizeof(buf), &id->src));
+		sbltype(id->ty, 0);
+		Bprint(bsym, "\n");
+	}
+}
+
+static void
+sbltype(Type *t, int force)
+{
+	Type *lastt;
+	Decl *tg, *d;
+	char buf[StrSize];
+
+	if(t->kind == Tadtpick)
+		t = t->decl->dot->ty;
+
+	d = t->decl;
+	if(!force && d != nil && d->ty->sbl >= 0){
+		Bprint(bsym, "@%d\n", d->ty->sbl);
+		return;
+	}
+
+	switch(t->kind){
+	default:
+		fatal("bad type %T in sbltype", t);
+		break;
+	case Tnone:
+	case Tany:
+	case Tint:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+	case Tstring:
+	case Tfix:
+	case Tpoly:
+		Bprint(bsym, "%c", sbltname[t->kind]);
+		break;
+	case Tfn:
+		Bprint(bsym, "%c", sbltname[t->kind]);
+		sbldecl(t->ids, Darg);
+		sbltype(t->tof, 0);
+		break;
+	case Tarray:
+	case Tlist:
+	case Tchan:
+	case Tref:
+		Bprint(bsym, "%c", sbltname[t->kind]);
+		if(t->kind == Tref && t->tof->kind == Tfn){
+			tattr[Tany].vis = 1;
+			sbltype(tfnptr, 0);
+			tattr[Tany].vis = 0;
+		}
+		else
+			sbltype(t->tof, 0);
+		break;
+	case Ttuple:
+	case Texception:
+		Bprint(bsym, "%c%ld.", sbltname[t->kind], t->size);
+		sbldecl(t->ids, Dfield);
+		break;
+	case Tadt:
+		if(t->tags != nil)
+			Bputc(bsym, sbltadtpick);
+		else
+			Bputc(bsym, sbltname[t->kind]);
+		if(d->dot != nil && !isimpmod(d->dot->sym))
+			Bprint(bsym, "%s->", d->dot->sym->name);
+		Bprint(bsym, "%s %s%ld\n", d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src), d->ty->size);
+		sbldecl(t->ids, Dfield);
+		if(t->tags != nil){
+			Bprint(bsym, "%d\n", t->decl->tag);
+			lastt = nil;
+			for(tg = t->tags; tg != nil; tg = tg->next){
+				Bprint(bsym, "%s:%s", tg->sym->name, sblsrcconv(buf, buf+sizeof(buf), &tg->src));
+				if(lastt == tg->ty){
+					Bputc(bsym, '\n');
+				}else{
+					Bprint(bsym, "%ld\n", tg->ty->size);
+					sbldecl(tg->ty->ids, Dfield);
+				}
+				lastt = tg->ty;
+			}
+		}
+		break;
+	case Tmodule:
+		Bprint(bsym, "%c%s\n%s", sbltname[t->kind], d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src));
+		sbldecl(t->ids, Dglobal);
+		break;
+	}
+}
--- /dev/null
+++ b/limbo/stubs.c
@@ -1,0 +1,590 @@
+#include "limbo.h"
+
+static long	stubalign(long offset, int a, char** b, char *e);
+static void		pickadtstub(Type *t);
+
+void
+emit(Decl *globals)
+{
+	Decl *m, *d, *id;
+
+	for(m = globals; m != nil; m = m->next){
+		if(m->store != Dtype || m->ty->kind != Tmodule)
+			continue;
+		m->ty = usetype(m->ty);
+		for(d = m->ty->ids; d != nil; d = d->next){
+			d->ty = usetype(d->ty);
+			if(d->store == Dglobal || d->store == Dfn)
+				modrefable(d->ty);
+			if(d->store == Dtype && d->ty->kind == Tadt){
+				for(id = d->ty->ids; id != nil; id = id->next){
+					id->ty = usetype(id->ty);
+					modrefable(d->ty);
+				}
+			}
+		}
+	}
+	if(emitstub){
+		adtstub(globals);
+		modstub(globals);
+	}
+	if(emittab != nil)
+		modtab(globals);
+	if(emitcode)
+		modcode(globals);
+}
+
+static char*
+lowercase(char *f)
+{
+	char *s = f;
+
+	for( ; *s != 0; s++)
+		if(*s >= 'A' && *s <= 'Z')
+			*s += 'a' - 'A';
+	return f;
+}
+
+void
+modcode(Decl *globals)
+{
+	Decl *d, *id;
+	char buf[32];
+
+	if(emitdyn){
+		strcpy(buf, emitcode);
+		lowercase(buf);
+		print("#include \"%s.h\"\n", buf);
+	}
+	else{
+		print("#include <lib9.h>\n");
+		print("#include <isa.h>\n");
+		print("#include <interp.h>\n");
+		print("#include \"%smod.h\"\n", emitcode);
+	}
+	print("\n");
+
+	for(d = globals; d != nil; d = d->next)
+		if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emitcode) == 0)
+			break;
+
+	if(d == nil)
+		return;
+	
+	/*
+	 * stub types
+	 */
+	for(id = d->ty->ids; id != nil; id = id->next){
+		if(id->store == Dtype && id->ty->kind == Tadt){
+			id->ty = usetype(id->ty);
+			print("Type*\tT_%s;\n", id->sym->name);
+		}
+	}
+
+	/*
+	 * type maps
+	 */
+	if(emitdyn){
+		for(id = d->ty->ids; id != nil; id = id->next)
+			if(id->store == Dtype && id->ty->kind == Tadt)
+				print("uchar %s_map[] = %s_%s_map;\n",
+					id->sym->name, emitcode, id->sym->name);
+	}
+	
+	/*
+	  * heap allocation and garbage collection for a type
+	 */
+	if(emitdyn){
+		for(id = d->ty->ids; id != nil; id = id->next)
+			if(id->store == Dtype && id->ty->kind == Tadt){
+				print("\n%s_%s*\n%salloc%s(void)\n{\n\tHeap *h;\n\n\th = heap(T_%s);\n\treturn H2D(%s_%s*, h);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, emitcode, id->sym->name);
+				print("\nvoid\n%sfree%s(Heap *h, int swept)\n{\n\t%s_%s *d;\n\n\td = H2D(%s_%s*, h);\n\tfreeheap(h, swept);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name);
+			}
+	}
+
+	/*
+	 * initialization function
+	 */
+	if(emitdyn)
+		print("\nvoid\n%sinit(void)\n{\n", emitcode);
+	else{
+		print("\nvoid\n%smodinit(void)\n{\n", emitcode);
+		print("\tbuiltinmod(\"$%s\", %smodtab, %smodlen);\n", emitcode, emitcode, emitcode);
+	}
+	for(id = d->ty->ids; id != nil; id = id->next){
+		if(id->store == Dtype && id->ty->kind == Tadt){
+			if(emitdyn)
+				print("\tT_%s = dtype(%sfree%s, %s_%s_size, %s_map, sizeof(%s_map));\n",
+					id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, id->sym->name);
+			else
+				print("\tT_%s = dtype(freeheap, sizeof(%s), %smap, sizeof(%smap));\n",
+					id->sym->name, id->sym->name, id->sym->name, id->sym->name);
+		}
+	}
+	print("}\n");
+
+	/*
+	 * end function
+	 */
+	if(emitdyn){
+		print("\nvoid\n%send(void)\n{\n", emitcode);
+		for(id = d->ty->ids; id != nil; id = id->next)
+			if(id->store == Dtype && id->ty->kind == Tadt)
+				print("\tfreetype(T_%s);\n", id->sym->name);
+		print("}\n");
+	}
+
+	/*
+	 * stub functions
+	 */
+	for(id = d->ty->tof->ids; id != nil; id = id->next){
+		print("\nvoid\n%s_%s(void *fp)\n{\n\tF_%s_%s *f = fp;\n",
+			id->dot->sym->name, id->sym->name,
+			id->dot->sym->name, id->sym->name);
+		if(id->ty->tof != tnone && tattr[id->ty->tof->kind].isptr){
+			print("\tvoid *r;\n");
+			print("\n\tr = *f->ret;\n\t*f->ret = H;\n\tdestroy(r);\n");
+		}
+		print("}\n");
+	}
+
+	if(emitdyn)
+		print("\n#include \"%smod.h\"\n", buf);
+}
+
+void
+modtab(Decl *globals)
+{
+	int n;
+	Desc *md;
+	Decl *d, *id;
+
+	print("typedef struct{char *name; long sig; void (*fn)(void*); int size; int np; uchar map[16];} Runtab;\n");
+	for(d = globals; d != nil; d = d->next){
+		if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emittab) == 0){
+			n = 0;
+			print("Runtab %smodtab[]={\n", d->sym->name);
+			for(id = d->ty->tof->ids; id != nil; id = id->next){
+				n++;
+				print("\t\"");
+				if(id->dot != d)
+					print("%s.", id->dot->sym->name);
+				print("%s\",0x%lux,%s_%s,", id->sym->name, sign(id),
+					id->dot->sym->name, id->sym->name);
+				if(id->ty->varargs)
+					print("0,0,{0},");
+				else{
+					md = mkdesc(idoffsets(id->ty->ids, MaxTemp, MaxAlign), id->ty->ids);
+					print("%ld,%ld,%M,", md->size, md->nmap, md);
+				}
+				print("\n");
+			}
+			print("\t0\n};\n");
+			print("#define %smodlen	%d\n", d->sym->name, n);
+		}
+	}
+}
+
+/*
+ * produce activation records for all the functions in modules
+ */
+void
+modstub(Decl *globals)
+{
+	Type *t;
+	Decl *d, *id, *m;
+	char buf[StrSize*2], *p;
+	long offset;
+	int arg;
+
+	for(d = globals; d != nil; d = d->next){
+		if(d->store != Dtype || d->ty->kind != Tmodule)
+			continue;
+		arg = 0;
+		for(id = d->ty->tof->ids; id != nil; id = id->next){
+			if(emitdyn && id->dot->dot != nil)
+				seprint(buf, buf+sizeof(buf), "%s_%s_%s", id->dot->dot->sym->name, id->dot->sym->name, id->sym->name);
+			else
+				seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name);
+			print("void %s(void*);\ntypedef struct F_%s F_%s;\nstruct F_%s\n{\n",
+				buf, buf, buf, buf);
+			print("	WORD	regs[NREG-1];\n");
+			if(id->ty->tof != tnone)
+				print("	%R*	ret;\n", id->ty->tof);
+			else
+				print("	WORD	noret;\n");
+			print("	uchar	temps[%d];\n", MaxTemp-NREG*IBY2WD);
+			offset = MaxTemp;
+			for(m = id->ty->ids; m != nil; m = m->next){
+				if(m->sym != nil)
+					p = m->sym->name;
+				else{
+					seprint(buf, buf+sizeof(buf), "arg%d", arg);
+					p = buf;
+				}
+
+				/*
+				 * explicit pads for structure alignment
+				 */
+				t = m->ty;
+				offset = stubalign(offset, t->align, nil, nil);
+				if(offset != m->offset)
+					yyerror("module stub must not contain data objects");
+					// fatal("modstub bad offset");
+				print("	%R	%s;\n", t, p);
+				arg++;
+				offset += t->size;
+			}
+			if(id->ty->varargs)
+				print("	WORD	vargs;\n");
+			print("};\n");
+		}
+		for(id = d->ty->ids; id != nil; id = id->next)
+			if(id->store == Dconst)
+				constub(id);
+	}
+}
+
+static void
+chanstub(char *in, Decl *id)
+{
+	Desc *desc;
+
+	print("typedef %R %s_%s;\n", id->ty->tof, in, id->sym->name);
+	desc = mktdesc(id->ty->tof);
+	print("#define %s_%s_size %ld\n", in, id->sym->name, desc->size);
+	print("#define %s_%s_map %M\n", in, id->sym->name, desc);
+}
+
+/*
+ * produce c structs for all adts
+ */
+void
+adtstub(Decl *globals)
+{
+	Type *t, *tt;
+	Desc *desc;
+	Decl *m, *d, *id;
+	char buf[2*StrSize];
+	long offset;
+
+	for(m = globals; m != nil; m = m->next){
+		if(m->store != Dtype || m->ty->kind != Tmodule)
+			continue;
+		for(d = m->ty->ids; d != nil; d = d->next){
+			if(d->store != Dtype)
+				continue;
+			t = usetype(d->ty);
+			d->ty = t;
+			dotprint(buf, buf+sizeof(buf), d->ty->decl, '_');
+			switch(d->ty->kind){
+			case Tadt:
+				print("typedef struct %s %s;\n", buf, buf);
+				break;
+			case Tint:
+			case Tbyte:
+			case Treal:
+			case Tbig:
+			case Tfix:
+				print("typedef %T %s;\n", t, buf);
+				break;
+			}
+		}
+	}
+	for(m = globals; m != nil; m = m->next){
+		if(m->store != Dtype || m->ty->kind != Tmodule)
+			continue;
+		for(d = m->ty->ids; d != nil; d = d->next){
+			if(d->store != Dtype)
+				continue;
+			t = d->ty;
+			if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){
+				if(t->tags != nil){
+					pickadtstub(t);
+					continue;
+				}
+				dotprint(buf, buf+sizeof(buf), t->decl, '_');
+				print("struct %s\n{\n", buf);
+				offset = 0;
+				for(id = t->ids; id != nil; id = id->next){
+					if(id->store == Dfield){
+						tt = id->ty;
+						offset = stubalign(offset, tt->align, nil, nil);
+						if(offset != id->offset)
+							fatal("adtstub bad offset");
+						print("	%R	%s;\n", tt, id->sym->name);
+						offset += tt->size;
+					}
+				}
+				if(t->ids == nil){
+					print("	char	dummy[1];\n");
+					offset = 1;
+				}
+				offset = stubalign(offset, t->align, nil ,nil);
+				offset = stubalign(offset, IBY2WD, nil , nil);
+				if(offset != t->size && t->ids != nil)
+					fatal("adtstub: bad size");
+				print("};\n");
+
+				for(id = t->ids; id != nil; id = id->next)
+					if(id->store == Dconst)
+						constub(id);
+
+				for(id = t->ids; id != nil; id = id->next)
+					if(id->ty->kind == Tchan)
+						chanstub(buf, id);
+
+				desc = mktdesc(t);
+				if(offset != desc->size && t->ids != nil)
+					fatal("adtstub: bad desc size");
+				print("#define %s_size %ld\n", buf, offset);
+				print("#define %s_map %M\n", buf, desc);
+if(0)
+				print("struct %s_check {int s[2*(sizeof(%s)==%s_size)-1];};\n", buf, buf, buf);
+			}else if(t->kind == Tchan)
+				chanstub(m->sym->name, d);
+		}
+	}
+}
+
+/*
+ * emit an expicit pad field for aligning emitted c structs
+ * according to limbo's definition
+ */
+static long
+stubalign(long offset, int a, char **buf, char *end)
+{
+	long x;
+
+	x = offset & (a-1);
+	if(x == 0)
+		return offset;
+	x = a - x;
+	if(buf == nil)
+		print("\tuchar\t_pad%ld[%ld];\n", offset, x);
+	else
+		*buf = seprint(*buf, end, "uchar\t_pad%ld[%ld]; ", offset, x);
+	offset += x;
+	if((offset & (a-1)) || x >= a)
+		fatal("compiler stub misalign");
+	return offset;
+}
+
+void
+constub(Decl *id)
+{
+	char buf[StrSize*2];
+
+	seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name);
+	switch(id->ty->kind){
+	case Tbyte:
+		print("#define %s %d\n", buf, (int)id->init->val & 0xff);
+		break;
+	case Tint:
+	case Tfix:
+		print("#define %s %ld\n", buf, (long)id->init->val);
+		break;
+	case Tbig:
+		print("#define %s %ld\n", buf, (long)id->init->val);
+		break;
+	case Treal:
+		print("#define %s %g\n", buf, id->init->rval);
+		break;
+	case Tstring:
+		print("#define %s \"%s\"\n", buf, id->init->decl->sym->name);
+		break;
+	}
+}
+
+int
+mapconv(Fmt *f)
+{
+	Desc *d;
+	char *s, *e, buf[1024];
+	int i;
+
+	d = va_arg(f->args, Desc*);
+	e = buf+sizeof(buf);
+	s = buf;
+	s = secpy(s, e, "{");
+	for(i = 0; i < d->nmap; i++)
+		s = seprint(s, e, "0x%x,", d->map[i]);
+	if(i == 0)
+		s = seprint(s, e, "0");
+	seprint(s, e, "}");
+	return fmtstrcpy(f, buf);
+}
+
+char*
+dotprint(char *buf, char *end, Decl *d, int dot)
+{
+	if(d->dot != nil){
+		buf = dotprint(buf, end, d->dot, dot);
+		if(buf < end)
+			*buf++ = dot;
+	}
+	if(d->sym == nil)
+		return buf;
+	return seprint(buf, end, "%s", d->sym->name);
+}
+
+char *ckindname[Tend] =
+{
+	/* Tnone */	"void",
+	/* Tadt */	"struct",
+	/* Tadtpick */	"?adtpick?",
+	/* Tarray */	"Array*",
+	/* Tbig */	"LONG",
+	/* Tbyte */	"BYTE",
+	/* Tchan */	"Channel*",
+	/* Treal */	"REAL",
+	/* Tfn */	"?fn?",
+	/* Tint */	"WORD",
+	/* Tlist */	"List*",
+	/* Tmodule */	"Modlink*",
+	/* Tref */	"?ref?",
+	/* Tstring */	"String*",
+	/* Ttuple */	"?tuple?",
+	/* Texception */	"?exception",
+	/* Tfix */		"WORD",
+	/* Tpoly */	"void*",
+
+	/* Tainit */	"?ainit?",
+	/* Talt */	"?alt?",
+	/* Tany */	"void*",
+	/* Tarrow */	"?arrow?",
+	/* Tcase */	"?case?",
+	/* Tcasel */	"?casel",
+	/* Tcasec */	"?casec?",
+	/* Tdot */	"?dot?",
+	/* Terror */	"?error?",
+	/* Tgoto */	"?goto?",
+	/* Tid */	"?id?",
+	/* Tiface */	"?iface?",
+	/* Texcept */	"?except?",
+	/* Tinst */	"?inst?",
+};
+
+char*
+ctprint(char *buf, char *end, Type *t)
+{
+	Decl *id;
+	Type *tt;
+	long offset;
+
+	if(t == nil)
+		return secpy(buf, end, "void");
+	switch(t->kind){
+	case Tref:
+		return seprint(buf, end, "%R*", t->tof);
+	case Tarray:
+	case Tlist:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tchan:
+	case Tmodule:
+	case Tfix:
+	case Tpoly:
+		return seprint(buf, end, "%s", ckindname[t->kind]);
+	case Tadtpick:
+		return ctprint(buf, end, t->decl->dot->ty);
+	case Tadt:
+	case Ttuple:
+		if(t->decl->sym != anontupsym)
+			return dotprint(buf, end, t->decl, '_');
+		offset = 0;
+		buf = secpy(buf, end, "struct{ ");
+		for(id = t->ids; id != nil; id = id->next){
+			tt = id->ty;
+			offset = stubalign(offset, tt->align, &buf, end);
+			if(offset != id->offset)
+				fatal("ctypeconv tuple bad offset");
+			buf = seprint(buf, end, "%R %s; ", tt, id->sym->name);
+			offset += tt->size;
+		}
+		offset = stubalign(offset, t->align, &buf, end);
+		if(offset != t->size)
+			fatal("ctypeconv tuple bad t=%T size=%ld offset=%ld", t, t->size, offset);
+		return secpy(buf, end, "}");
+	default:
+		if(t->kind >= Tend)
+			yyerror("no C equivalent for type %d", t->kind);
+		else
+			yyerror("no C equivalent for type %s", kindname[t->kind]);
+		break;
+	}
+	return buf;
+}
+
+static void
+pickadtstub(Type *t)
+{
+	Type *tt;
+	Desc *desc;
+	Decl *id, *tg;
+	char buf[2*StrSize];
+	int ok;
+	long offset, tgoffset;
+
+	dotprint(buf, buf+sizeof(buf), t->decl, '_');
+	offset = 0;
+	for(tg = t->tags; tg != nil; tg = tg->next)
+		print("#define %s_%s %ld\n", buf, tg->sym->name, offset++);
+	print("struct %s\n{\n", buf);
+	print("	int	pick;\n");
+	offset = IBY2WD;
+	for(id = t->ids; id != nil; id = id->next){
+		if(id->store == Dfield){
+			tt = id->ty;
+			offset = stubalign(offset, tt->align, nil, nil);
+			if(offset != id->offset)
+				fatal("pickadtstub bad offset");
+			print("	%R	%s;\n", tt, id->sym->name);
+			offset += tt->size;
+		}
+	}
+	print("	union{\n");
+	for(tg = t->tags; tg != nil; tg = tg->next){
+		tgoffset = offset;
+		print("		struct{\n");
+		for(id = tg->ty->ids; id != nil; id = id->next){
+			if(id->store == Dfield){
+				tt = id->ty;
+				tgoffset = stubalign(tgoffset, tt->align, nil, nil);
+				if(tgoffset != id->offset)
+					fatal("pickadtstub bad offset");
+				print("			%R	%s;\n", tt, id->sym->name);
+				tgoffset += tt->size;
+			}
+		}
+		if(tg->ty->ids == nil)
+			print("			char	dummy[1];\n");
+		print("		} %s;\n", tg->sym->name);
+	}
+	print("	} u;\n");
+	print("};\n");
+
+	for(id = t->ids; id != nil; id = id->next)
+		if(id->store == Dconst)
+			constub(id);
+
+	for(id = t->ids; id != nil; id = id->next)
+		if(id->ty->kind == Tchan)
+			chanstub(buf, id);
+
+	for(tg = t->tags; tg != nil; tg = tg->next){
+		ok = tg->ty->tof->ok;
+		tg->ty->tof->ok = OKverify;
+		sizetype(tg->ty->tof);
+		tg->ty->tof->ok = OKmask;
+		desc = mktdesc(tg->ty->tof);
+		tg->ty->tof->ok = ok;
+		print("#define %s_%s_size %ld\n", buf, tg->sym->name, tg->ty->size);
+		print("#define %s_%s_map %M\n", buf, tg->sym->name, desc);
+	}
+}
--- /dev/null
+++ b/limbo/typecheck.c
@@ -1,0 +1,3627 @@
+#include "limbo.h"
+#include "y.tab.h"
+
+Node	**labstack;
+int	labdep;
+static Node* inexcept;
+static Decl* fndec;
+
+void checkraises(Node *n);
+
+static void
+increfs(Decl *id)
+{
+	for( ; id != nil; id = id->link)
+		id->refs++;
+}
+
+static int
+fninline(Decl *d)
+{
+	Node *n, *left, *right;
+
+	left = right = nil;
+	n = d->init;
+	if(dontinline || d->caninline < 0 || d->locals != nil || ispoly(d) || n->ty->tof->kind == Tnone || nodes(n) >= 100)
+		return 0;
+	n = n->right;
+	if(n->op == Oseq && n->right == nil)
+		n = n->left;
+	/* 
+	  *	inline
+	  *		(a) return e;
+	  *		(b) if(c) return e1; else return e2;
+	  *		(c) if(c) return e1; return e2;
+	  */
+	switch(n->op){
+	case Oret:
+		break;
+	case Oif:
+		right = n->right;
+		if(right->right == nil || right->left->op != Oret || right->right->op != Oret || !tequal(right->left->left->ty, right->right->left->ty))
+			return 0;
+		break;
+	case Oseq:
+		left = n->left;
+		right = n->right;
+		if(left->op != Oif || left->right->right != nil || left->right->left->op != Oret || right->op != Oseq || right->right != nil || right->left->op != Oret || !tequal(left->right->left->left->ty, right->left->left->ty))
+			return 0;
+		break;
+	default:
+		return 0;
+	}
+	if(occurs(d, n) || hasasgns(n))
+		return 0;
+	if(n->op == Oseq){
+		left->right->right = right->left;
+		n = left;
+		right = n->right;
+		d->init->right->right = nil;
+	}
+	if(n->op == Oif){
+		n->ty = right->ty = right->left->left->ty;
+		right->left = right->left->left;
+		right->right = right->right->left;
+		d->init->right->left = mkunary(Oret, n);
+	}
+	return 1;
+}
+
+static int
+isfnrefty(Type *t)
+{
+	return t->kind == Tref && t->tof->kind == Tfn;
+}
+
+static int
+isfnref(Decl *d)
+{
+	switch(d->store){
+	case Dglobal:
+	case Darg:
+	case Dlocal:
+	case Dfield:
+	case Dimport:
+		return isfnrefty(d->ty);
+	}
+	return 0;
+}
+
+int
+argncompat(Node *n, Decl *f, Node *a)
+{
+	for(; a != nil; a = a->right){
+		if(f == nil){
+			nerror(n, "%V: too many function arguments", n->left);
+			return 0;
+		}
+		f = f->next;
+	}
+	if(f != nil){
+		nerror(n, "%V: too few function arguments", n->left);
+		return 0;
+	}
+	return 1;
+}
+
+static void
+rewind(Node *n)
+{
+	Node *r, *nn;
+
+	r = n;
+	nn = n->left;
+	for(n = n->right; n != nil; n = n->right){
+		if(n->right == nil){
+			r->left = nn;
+			r->right = n->left;
+		}
+		else
+			nn = mkbin(Oindex, nn, n->left);
+	}
+}
+
+static void
+ckmod(Node *n, Decl *id)
+{
+	Type *t;
+	Decl *d, *idc;
+	Node *mod;
+
+	if(id == nil)
+		fatal("can't find function: %n", n);
+	idc = nil;
+	mod = nil;
+	if(n->op == Oname){
+		idc = id;
+		mod = id->eimport;
+	}
+	else if(n->op == Omdot)
+		mod = n->left;
+	else if(n->op == Odot){
+		idc = id->dot;
+		t = n->left->ty;
+		if(t->kind == Tref)
+			t = t->tof;
+		if(t->kind == Tadtpick)
+			t = t->decl->dot->ty;
+		d = t->decl;
+		while(d != nil && d->link != nil)
+			d = d->link;
+		if(d != nil && d->timport != nil)
+			mod = d->timport->eimport;
+		n->right->left = mod;
+	}
+	if(mod != nil && mod->ty->kind != Tmodule){
+		nerror(n, "cannot use %V as a function reference", n);
+		return;
+	}
+	if(mod != nil){
+		if(valistype(mod)){
+			nerror(n, "cannot use %V as a function reference because %V is a module interface", n, mod);
+			return;
+		}
+	}else if(idc != nil && idc->dot != nil && !isimpmod(idc->dot->sym)){
+		nerror(n, "cannot use %V without importing %s from a variable", n, idc->sym->name);
+		return;
+	}
+	if(mod != nil)
+		modrefable(n->ty);
+}
+
+static void
+addref(Node *n)
+{
+	Node *nn;
+
+	nn = mkn(0, nil, nil);
+	*nn = *n;
+	n->op = Oref;
+	n->left = nn;
+	n->right = nil;
+	n->decl = nil;
+	n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, nn->ty, nil));
+}
+
+static void
+fnref(Node *n, Decl *id)
+{
+	id->caninline = -1;
+	ckmod(n, id);
+	addref(n);
+	while(id->link != nil)
+		id = id->link;
+	if(ispoly(id) && encpolys(id) != nil)
+		nerror(n, "cannot have a polymorphic adt function reference %s", id->sym->name);
+}
+
+Decl*
+typecheck(int checkimp)
+{
+	Decl *entry, *m, *d;
+	Sym *s;
+	int i;
+
+	if(errors)
+		return nil;
+
+	/*
+	 * generate the set of all functions
+	 * compile one function at a time
+	 */
+	gdecl(tree);
+	gbind(tree);
+	fns = allocmem(nfns * sizeof(Decl));
+	i = gcheck(tree, fns, 0);
+	if(i != nfns)
+		fatal("wrong number of functions found in gcheck");
+
+	maxlabdep = 0;
+	for(i = 0; i < nfns; i++){
+		d = fns[i];
+		if(d != nil)
+			fndec = d;
+		if(d != nil)
+			fncheck(d);
+		fndec = nil;
+	}
+
+	if(errors)
+		return nil;
+
+	entry = nil;
+	if(checkimp){
+		Decl *im;
+		Dlist *dm;
+
+		if(impmods == nil){
+			yyerror("no implementation module");
+			return nil;
+		}
+		for(im = impmods; im != nil; im = im->next){
+			for(dm = impdecls; dm != nil; dm = dm->next)
+				if(dm->d->sym == im->sym)
+					break;
+			if(dm == nil || dm->d->ty == nil){
+				yyerror("no definition for implementation module %s", im->sym->name);
+				return nil;
+			}
+		}
+	
+		/*
+		 * can't check the module spec until all types and imports are determined,
+		 * which happens in scheck
+		 */
+		for(dm = impdecls; dm != nil; dm = dm->next){
+			im = dm->d;
+			im->refs++;
+			im->ty = usetype(im->ty);
+			if(im->store != Dtype || im->ty->kind != Tmodule){
+				error(im->src.start, "cannot implement %K", im);
+				return nil;
+			}
+		}
+	
+		/* now check any multiple implementations */
+		impdecl = modimp(impdecls, impmods);
+
+		s = enter("init", 0);
+		entry = nil;
+		for(dm = impdecls; dm != nil; dm = dm->next){
+			im = dm->d;
+			for(m = im->ty->ids; m != nil; m = m->next){
+				m->ty = usetype(m->ty);
+				m->refs++;
+	
+				if(m->sym == s && m->ty->kind == Tfn && entry == nil)
+					entry = m;
+	
+				if(m->store == Dglobal || m->store == Dfn)
+					modrefable(m->ty);
+	
+				if(m->store == Dtype && m->ty->kind == Tadt){
+					for(d = m->ty->ids; d != nil; d = d->next){
+						d->ty = usetype(d->ty);
+						modrefable(d->ty);
+						d->refs++;
+					}
+				}
+			}
+			checkrefs(im->ty->ids);
+		}
+	}
+
+	if(errors)
+		return nil;
+	gsort(tree);
+	tree = nil;
+	return entry;
+}
+	
+/*
+ * introduce all global declarations
+ * also adds all fields to adts and modules
+ * note the complications due to nested Odas expressions
+ */
+void
+gdecl(Node *n)
+{
+	for(;;){
+		if(n == nil)
+			return;
+		if(n->op != Oseq)
+			break;
+		gdecl(n->left);
+		n = n->right;
+	}
+	switch(n->op){
+	case Oimport:
+		importdecled(n);
+		gdasdecl(n->right);
+		break;
+	case Oadtdecl:
+		adtdecled(n);
+		break;
+	case Ocondecl:
+		condecled(n);
+		gdasdecl(n->right);
+		break;
+	case Oexdecl:
+		exdecled(n);
+		break;
+	case Omoddecl:
+		moddecled(n);
+		break;
+	case Otypedecl:
+		typedecled(n);
+		break;
+	case Ovardecl:
+		vardecled(n);
+		break;
+	case Ovardecli:
+		vardecled(n->left);
+		gdasdecl(n->right);
+		break;
+	case Ofunc:
+		fndecled(n);
+		break;
+	case Oas:
+	case Odas:
+	case Onothing:
+		gdasdecl(n);
+		break;
+	default:
+		fatal("can't deal with %O in gdecl", n->op);
+	}
+}
+
+/*
+ * bind all global type ids,
+ * including those nested inside modules
+ * this needs to be done, since we may use such
+ * a type later in a nested scope, so if we bound
+ * the type ids then, the type could get bound
+ * to a nested declaration
+ */
+void
+gbind(Node *n)
+{
+	Decl *d, *ids;
+
+	for(;;){
+		if(n == nil)
+			return;
+		if(n->op != Oseq)
+			break;
+		gbind(n->left);
+		n = n->right;
+	}
+	switch(n->op){
+	case Oas:
+	case Ocondecl:
+	case Odas:
+	case Oexdecl:
+	case Ofunc:
+	case Oimport:
+	case Onothing:
+	case Ovardecl:
+	case Ovardecli:
+		break;
+	case Ofielddecl:
+		bindtypes(n->decl->ty);
+		break;
+	case Otypedecl:
+		bindtypes(n->decl->ty);
+		if(n->left != nil)
+			gbind(n->left);
+		break;
+	case Opickdecl:
+		gbind(n->left);
+		d = n->right->left->decl;
+		bindtypes(d->ty);
+		repushids(d->ty->ids);
+		gbind(n->right->right);
+		/* get new ids for undefined types; propagate outwards */
+		ids = popids(d->ty->ids);
+		if(ids != nil)
+			installids(Dundef, ids);
+		break;
+	case Oadtdecl:
+	case Omoddecl:
+		bindtypes(n->ty);
+		if(n->ty->polys != nil)
+			repushids(n->ty->polys);
+		repushids(n->ty->ids);
+		gbind(n->left);
+		/* get new ids for undefined types; propagate outwards */
+		ids = popids(n->ty->ids);
+		if(ids != nil)
+			installids(Dundef, ids);
+		if(n->ty->polys != nil)
+			popids(n->ty->polys);
+		break;
+	default:
+		fatal("can't deal with %O in gbind", n->op);
+	}
+}
+
+/*
+ * check all of the > declarations
+ * bind all type ids referred to within types at the global level
+ * record decls for defined functions
+ */
+int
+gcheck(Node *n, Decl **fns, int nfns)
+{
+	Ok rok;
+	Decl *d;
+
+	for(;;){
+		if(n == nil)
+			return nfns;
+		if(n->op != Oseq)
+			break;
+		nfns = gcheck(n->left, fns, nfns);
+		n = n->right;
+	}
+
+	switch(n->op){
+	case Ofielddecl:
+		if(n->decl->ty->u.eraises)
+			raisescheck(n->decl->ty);
+		break;
+	case Onothing:
+	case Opickdecl:
+		break;
+	case Otypedecl:
+		tcycle(n->ty);
+		break;
+	case Oadtdecl:
+	case Omoddecl:
+		if(n->ty->polys != nil)
+			repushids(n->ty->polys);
+		repushids(n->ty->ids);
+		if(gcheck(n->left, nil, 0))
+			fatal("gcheck fn decls nested in modules or adts");
+		if(popids(n->ty->ids) != nil)
+			fatal("gcheck installs new ids in a module or adt");
+		if(n->ty->polys != nil)
+			popids(n->ty->polys);
+		break;
+	case Ovardecl:
+		varcheck(n, 1);
+		break;
+	case Ocondecl:
+		concheck(n, 1);
+		break;
+	case Oexdecl:
+		excheck(n, 1);
+		break;
+	case Oimport:
+		importcheck(n, 1);
+		break;
+	case Ovardecli:
+		varcheck(n->left, 1);
+		rok = echeck(n->right, 0, 1, nil);
+		if(rok.ok){
+			if(rok.allok)
+				n->right = fold(n->right);
+			globalas(n->right->left, n->right->right, rok.allok);
+		}
+		break;
+	case Oas:
+	case Odas:
+		rok = echeck(n, 0, 1, nil);
+		if(rok.ok){
+			if(rok.allok)
+				n = fold(n);
+			globalas(n->left, n->right, rok.allok);
+		}
+		break;
+	case Ofunc:
+		rok = echeck(n->left, 0, 1, n);
+		if(rok.ok && n->ty->u.eraises)
+			raisescheck(n->ty);
+		d = nil;
+		if(rok.ok)
+			d = fnchk(n);
+		fns[nfns++] = d;
+		break;
+	default:
+		fatal("can't deal with %O in gcheck", n->op);
+	}
+	return nfns;
+}
+
+/*
+ * check for unused expression results
+ * make sure the any calculated expression has
+ * a destination
+ */
+Node*
+checkused(Node *n)
+{
+	Type *t;
+	Node *nn;
+
+	/*
+	 * only nil; and nil = nil; should have type tany
+	 */
+	if(n->ty == tany){
+		if(n->op == Oname)
+			return n;
+		if(n->op == Oas)
+			return checkused(n->right);
+		fatal("line %L checkused %n", n->src.start, n);
+	}
+
+	if(n->op == Ocall && n->left->ty->kind == Tfn && n->left->ty->tof != tnone){
+		n = mkunary(Oused, n);
+		n->ty = n->left->ty;
+		return n;
+	}
+	if(n->op == Ocall && isfnrefty(n->left->ty)){
+		if(n->left->ty->tof->tof != tnone){
+			n = mkunary(Oused, n);
+			n->ty = n->left->ty;
+		}
+		return n;
+	}
+	if(isused[n->op] && (n->op != Ocall || n->left->ty->kind == Tfn))
+		return n;
+	t = n->ty;
+	if(t->kind == Tfn)
+		nerror(n, "function %V not called", n);
+	else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+		nerror(n, "expressions cannot have type %T", t);
+	else if(n->op == Otuple){
+		for(nn = n->left; nn != nil; nn = nn->right)
+			checkused(nn->left);
+	}
+	else
+		nwarn(n, "result of expression %V not used", n);
+	n = mkunary(Oused, n);
+	n->ty = n->left->ty;
+	return n;
+}
+
+void
+fncheck(Decl *d)
+{
+	Node *n;
+	Decl *adtp;
+
+	n = d->init;
+	if(debug['t'])
+		print("typecheck tree: %n\n", n);
+
+	fndecls = nil;
+	adtp = outerpolys(n->left);
+	if(n->left->op == Odot)
+		repushids(adtp);
+	if(d->ty->polys != nil)
+		repushids(d->ty->polys);
+	repushids(d->ty->ids);
+
+	labdep = 0;
+	labstack = allocmem(maxlabdep * sizeof *labstack);
+	n->right = scheck(n->right, d->ty->tof, Sother);
+	if(labdep != 0)
+		fatal("unbalanced label stack in fncheck");
+	free(labstack);
+
+	d->locals = appdecls(popids(d->ty->ids), fndecls);
+	if(d->ty->polys != nil)
+		popids(d->ty->polys);
+	if(n->left->op == Odot)
+		popids(adtp);
+	fndecls = nil;
+
+	checkrefs(d->ty->ids);
+	checkrefs(d->ty->polys);
+	checkrefs(d->locals);
+
+	checkraises(n);
+
+	d->caninline = fninline(d);
+}
+
+Node*
+scheck(Node *n, Type *ret, int kind)
+{
+	Node *left, *right, *last, *top;
+	Decl *d;
+	Sym *s;
+	Ok rok;
+	int i;
+
+	top = n;
+	last = nil;
+	for(; n != nil; n = n->right){
+		left = n->left;
+		right = n->right;
+		switch(n->op){
+		case Ovardecl:
+			vardecled(n);
+			varcheck(n, 0);
+			if (nested() && tmustzero(n->decl->ty))
+				decltozero(n);
+/*
+			else if (inloop() && tmustzero(n->decl->ty))
+				decltozero(n);
+*/
+			return top;
+		case Ovardecli:
+			vardecled(left);
+			varcheck(left, 0);
+			echeck(right, 0, 0, nil);
+			if (nested() && tmustzero(left->decl->ty))
+				decltozero(left);
+			return top;
+		case Otypedecl:
+			typedecled(n);
+			bindtypes(n->ty);
+			tcycle(n->ty);
+			return top;
+		case Ocondecl:
+			condecled(n);
+			concheck(n, 0);
+			return top;
+		case Oexdecl:
+			exdecled(n);
+			excheck(n, 0);
+			return top;
+		case Oimport:
+			importdecled(n);
+			importcheck(n, 0);
+			return top;
+		case Ofunc:
+			fatal("scheck func");
+		case Oscope:
+			pushscope(n, kind == Sother ? Sscope : kind);
+			if (left != nil)
+				fatal("Oscope has left field");
+			echeck(left, 0, 0, nil);
+			n->right = scheck(right, ret, Sother);
+			d = popscope();
+			fndecls = appdecls(fndecls, d);
+			return top;
+		case Olabel:
+			echeck(left, 0, 0, nil);
+			n->right = scheck(right, ret, Sother);
+			return top;
+		case Oseq:
+			n->left = scheck(left, ret, Sother);
+			/* next time will check n->right */
+			break;
+		case Oif:
+			rok = echeck(left, 0, 0, nil);
+			if(rok.ok && left->op != Onothing && left->ty != tint)
+				nerror(n, "if conditional must be an int, not %Q", left);
+			right->left = scheck(right->left, ret, Sother);
+			/* next time will check n->right->right */
+			n = right;
+			break;
+		case Ofor:
+			rok = echeck(left, 0, 0, nil);
+			if(rok.ok && left->op != Onothing && left->ty != tint)
+				nerror(n, "for conditional must be an int, not %Q", left);
+			/*
+			 * do the continue clause before the body
+			 * this reflects the ordering of declarations
+			 */
+			pushlabel(n);
+			right->right = scheck(right->right, ret, Sother);
+			right->left = scheck(right->left, ret, Sloop);
+			labdep--;
+			if(n->decl != nil && !n->decl->refs)
+				nwarn(n, "label %s never referenced", n->decl->sym->name);
+			return top;
+		case Odo:
+			rok = echeck(left, 0, 0, nil);
+			if(rok.ok && left->op != Onothing && left->ty != tint)
+				nerror(n, "do conditional must be an int, not %Q", left);
+			pushlabel(n);
+			n->right = scheck(n->right, ret, Sloop);
+			labdep--;
+			if(n->decl != nil && !n->decl->refs)
+				nwarn(n, "label %s never referenced", n->decl->sym->name);
+			return top;
+		case Oalt:
+		case Ocase:
+		case Opick:
+		case Oexcept:
+			pushlabel(n);
+			switch(n->op){
+			case Oalt:
+				altcheck(n, ret);
+				break;
+			case Ocase:
+				casecheck(n, ret);
+				break;
+			case Opick:
+				pickcheck(n, ret);
+				break;
+			case Oexcept:
+				exccheck(n, ret);
+				break;
+			}
+			labdep--;
+			if(n->decl != nil && !n->decl->refs)
+				nwarn(n, "label %s never referenced", n->decl->sym->name);
+			return top;
+		case Oret:
+			rok = echeck(left, 0, 0, nil);
+			if(!rok.ok)
+				return top;
+			if(left == nil){
+				if(ret != tnone)
+					nerror(n, "return of nothing from a fn of %T", ret);
+			}else if(ret == tnone){
+				if(left->ty != tnone)
+					nerror(n, "return %Q from a fn with no return type", left);
+			}else if(!tcompat(ret, left->ty, 0))
+				nerror(n, "return %Q from a fn of %T", left, ret);
+			return top;
+		case Obreak:
+		case Ocont:
+			s = nil;
+			if(n->decl != nil)
+				s = n->decl->sym;
+			for(i = 0; i < labdep; i++){
+				if(s == nil || labstack[i]->decl != nil && labstack[i]->decl->sym == s){
+					if(n->op == Ocont
+					&& labstack[i]->op != Ofor && labstack[i]->op != Odo)
+						continue;
+					if(s != nil)
+						labstack[i]->decl->refs++;
+					return top;
+				}
+			}
+			nerror(n, "no appropriate target for %V", n);
+			return top;
+		case Oexit:
+		case Onothing:
+			return top;
+		case Oexstmt:
+			fndec->handler = 1;
+			n->left = scheck(left, ret, Sother);
+			n->right = scheck(right, ret, Sother);
+			return top;
+		default:
+			rok = echeck(n, 0, 0, nil);
+			if(rok.allok)
+				n = checkused(n);
+			if(last == nil)
+				return n;
+			last->right = n;
+			return top;
+		}
+		last = n;
+	}
+	return top;
+}
+
+void
+pushlabel(Node *n)
+{
+	Sym *s;
+	int i;
+
+	if(labdep >= maxlabdep){
+		maxlabdep += MaxScope;
+		labstack = reallocmem(labstack, maxlabdep * sizeof *labstack);
+	}
+	if(n->decl != nil){
+		s = n->decl->sym;
+		n->decl->refs = 0;
+		for(i = 0; i < labdep; i++)
+			if(labstack[i]->decl != nil && labstack[i]->decl->sym == s)
+				nerror(n, "label %s duplicated on line %L", s->name, labstack[i]->decl->src.start);
+	}
+	labstack[labdep++] = n;
+}
+
+void
+varcheck(Node *n, int isglobal)
+{
+	Type *t;
+	Decl *ids, *last;
+
+	t = validtype(n->ty, nil);
+	t = topvartype(t, n->decl, isglobal, 0);
+	last = n->left->decl;
+	for(ids = n->decl; ids != last->next; ids = ids->next){
+		ids->ty = t;
+		shareloc(ids);
+	}
+	if(t->u.eraises)
+		raisescheck(t);
+}
+
+void
+concheck(Node *n, int isglobal)
+{
+	Decl *ids, *last;
+	Type *t;
+	Node *init;
+	Ok rok;
+	int i;
+
+	pushscope(nil, Sother);
+	installids(Dconst, iota);
+	rok = echeck(n->right, 0, isglobal, nil);
+	popscope();
+
+	init = n->right;
+	if(!rok.ok){
+		t = terror;
+	}else{
+		t = init->ty;
+		if(!tattr[t->kind].conable){
+			nerror(init, "cannot have a %T constant", t);
+			rok.allok = 0;
+		}
+	}
+
+	last = n->left->decl;
+	for(ids = n->decl; ids != last->next; ids = ids->next)
+		ids->ty = t;
+
+	if(!rok.allok)
+		return;
+
+	i = 0;
+	for(ids = n->decl; ids != last->next; ids = ids->next){
+		if(rok.ok){
+			iota->init->val = i;
+			ids->init = dupn(0, &nosrc, init);
+			if(!varcom(ids))
+				rok.ok = 0;
+		}
+		i++;
+	}
+}
+
+static char*
+exname(Decl *d)
+{
+	int n;
+	Sym *m;
+	char *s;
+	char buf[16];
+
+	n = 0;
+	sprint(buf, "%d", scope-ScopeGlobal);
+	m = nil;
+	if(d->dot)
+		m = d->dot->sym;
+	else if(impmods)
+		m = impmods->sym;
+	if(m)
+		n += strlen(m->name)+1;
+	if(fndec)
+		n += strlen(fndec->sym->name)+1;
+	n += strlen(buf)+1+strlen(d->sym->name)+1;
+	s = malloc(n);
+	strcpy(s, "");
+	if(m){
+		strcat(s, m->name);
+		strcat(s, ".");
+	}
+	if(fndec){
+		strcat(s, fndec->sym->name);
+		strcat(s, ".");
+	}
+	strcat(s, buf);
+	strcat(s, ".");
+	strcat(s, d->sym->name);
+	return s;
+}
+
+void
+excheck(Node *n, int isglobal)
+{
+	char *nm;
+	Type *t;
+	Decl *ids, *last;
+
+	t = validtype(n->ty, nil);
+	t = topvartype(t, n->decl, isglobal, 0);
+	last = n->left->decl;
+	for(ids = n->decl; ids != last->next; ids = ids->next){
+		ids->ty = t;
+		nm = exname(ids);
+		ids->init = mksconst(&n->src, enterstring(nm, strlen(nm)));
+		/* ids->init = mksconst(&n->src, enterstring(strdup(ids->sym->name), strlen(ids->sym->name))); */
+	}
+}
+
+void
+importcheck(Node *n, int isglobal)
+{
+	Node *m;
+	Decl *id, *last, *v;
+	Type *t;
+	Ok rok;
+
+	rok = echeck(n->right, 1, isglobal, nil);
+	if(!rok.ok)
+		return;
+
+	m = n->right;
+	if(m->ty->kind != Tmodule || m->op != Oname){
+		nerror(n, "cannot import from %Q", m);
+		return;
+	}
+
+	last = n->left->decl;
+	for(id = n->decl; id != last->next; id = id->next){
+		v = namedot(m->ty->ids, id->sym);
+		if(v == nil){
+			error(id->src.start, "%s is not a member of %V", id->sym->name, m);
+			id->store = Dwundef;
+			continue;
+		}
+		id->store = v->store;
+		v->ty = validtype(v->ty, nil);
+		id->ty = t = v->ty;
+		if(id->store == Dtype && t->decl != nil){
+			id->timport = t->decl->timport;
+			t->decl->timport = id;
+		}
+		id->init = v->init;
+		id->importid = v;
+		id->eimport = m;
+	}
+}
+
+static Decl*
+rewcall(Node *n, Decl *d)
+{
+	/* put original function back now we're type checked */
+	while(d->link != nil)
+		d = d->link;
+	if(n->op == Odot)
+		n->right->decl = d;
+	else if(n->op == Omdot){
+		n->right->right->decl = d;
+		n->right->right->ty = d->ty;
+	}
+	else
+		fatal("bad op in Ocall rewcall");
+	n->ty = n->right->ty = d->ty;
+	d->refs++;
+	usetype(d->ty);
+	return d;
+}
+
+/*
+ * annotate the expression with types
+ */
+Ok
+echeck(Node *n, int typeok, int isglobal, Node *par)
+{
+	Type *t, *tt;
+	Node *left, *right, *mod, *nn;
+	Decl *tg, *id, *callee;
+	Sym *s;
+	int max, nocheck;
+	Ok ok, rok, kidsok;
+	static int tagopt;
+
+	ok.ok = ok.allok = 1;
+	if(n == nil)
+		return ok;
+	
+	/* avoid deep recursions */
+	if(n->op == Oseq){
+		for( ; n != nil && n->op == Oseq; n = n->right){
+			rok = echeck(n->left, typeok == 2, isglobal, n);
+			ok.ok &= rok.ok;
+			ok.allok &= rok.allok;
+			n->ty = tnone;
+		}
+		if(n == nil)
+			return ok;
+	}
+
+	left = n->left;
+	right = n->right;
+
+	nocheck = 0;
+	if(n->op == Odot || n->op == Omdot || n->op == Ocall || n->op == Oref || n->op == Otagof || n->op == Oindex)
+		nocheck = 1;
+	if(n->op != Odas		/* special case */
+	&& n->op != Oload)		/* can have better error recovery */
+		ok = echeck(left, nocheck, isglobal, n);
+	if(n->op != Odas		/* special case */
+	&& n->op != Odot		/* special check */
+	&& n->op != Omdot		/* special check */
+	&& n->op != Ocall		/* can have better error recovery */
+	&& n->op != Oindex){
+		rok = echeck(right, 0, isglobal, n);
+		ok.ok &= rok.ok;
+		ok.allok &= rok.allok;
+	}
+	if(!ok.ok){
+		n->ty = terror;
+		ok.allok = 0;
+		return ok;
+	}
+
+	switch(n->op){
+	case Odas:
+		kidsok = echeck(right, 0, isglobal, n);
+		if(!kidsok.ok)
+			right->ty = terror;
+		if(!isglobal && !dasdecl(left)){
+			kidsok.ok = 0;
+		}else if(!specific(right->ty) || !declasinfer(left, right->ty)){
+			nerror(n, "cannot declare %V from %Q", left, right);
+			declaserr(left);
+			kidsok.ok = 0;
+		}
+		if(right->ty->kind == Texception)
+			left->ty = n->ty = mkextuptype(right->ty);
+		else{
+			left->ty = n->ty = right->ty;
+			usedty(n->ty);
+		}
+		kidsok.allok &= kidsok.ok;
+		if (nested() && tmustzero(left->ty))
+			decltozero(left);
+		return kidsok;
+	case Oseq:
+	case Onothing:
+		n->ty = tnone;
+		break;
+	case Owild:
+		n->ty = tint;
+		break;
+	case Ocast:
+		t = usetype(n->ty);
+		n->ty = t;
+		tt = left->ty;
+		if(tcompat(t, tt, 0)){
+			left->ty = t;
+			break;
+		}
+		if(tt->kind == Tarray){
+			if(tt->tof == tbyte && t == tstring)
+				break;
+		}else if(t->kind == Tarray){
+			if(t->tof == tbyte && tt == tstring)
+				break;
+		}else if(casttab[tt->kind][t->kind]){
+			break;
+		}
+		nerror(n, "cannot make a %T from %Q", n->ty, left);
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Ochan:
+		n->ty = usetype(n->ty);
+		if(left && left->ty->kind != Tint){
+			nerror(n, "channel size %Q is not an int", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Oload:
+		n->ty = usetype(n->ty);
+		kidsok = echeck(left, 0, isglobal, n);
+		if(n->ty->kind != Tmodule){
+			nerror(n, "cannot load a %T, ", n->ty);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(!kidsok.allok){
+			ok.allok = 0;
+			break;
+		}
+		if(left->ty != tstring){
+			nerror(n, "cannot load a module from %Q", left);
+			ok.allok = 0;
+			break;
+		}
+if(n->ty->tof->decl->refs != 0)
+n->ty->tof->decl->refs++;
+n->ty->decl->refs++;
+		usetype(n->ty->tof);
+		break;
+	case Oref:
+		t = left->ty;
+		if(t->kind != Tadt && t->kind != Tadtpick && t->kind != Tfn && t->kind != Ttuple){
+			nerror(n, "cannot make a ref from %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(!tagopt && t->kind == Tadt && t->tags != nil && valistype(left)){
+			nerror(n, "instances of ref %V must be qualified with a pick tag", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(t->kind == Tadtpick)
+			t->tof = usetype(t->tof);
+		n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
+		break;
+	case Oarray:
+		max = 0;
+		if(right != nil){
+			max = assignindices(n);
+			if(max < 0){
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			if(!specific(right->left->ty)){
+				nerror(n, "type for array not specific");
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			n->ty = mktype(&n->src.start, &n->src.stop, Tarray, right->left->ty, nil);
+		}
+		n->ty = usetype(n->ty);
+
+		if(left->op == Onothing)
+			n->left = left = mkconst(&n->left->src, max);
+
+		if(left->ty->kind != Tint){
+			nerror(n, "array size %Q is not an int", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Oelem:
+		n->ty = right->ty;
+		break;
+	case Orange:
+		if(left->ty != right->ty
+		|| left->ty != tint && left->ty != tstring){
+			nerror(left, "range %Q to %Q is not an int or string range", left, right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		n->ty = left->ty;
+		break;
+	case Oname:
+		id = n->decl;
+		if(id == nil){
+			nerror(n, "name with no declaration");
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id->store == Dunbound){
+			s = id->sym;
+			id = s->decl;
+			if(id == nil)
+				id = undefed(&n->src, s);
+			/* save a little space */
+			s->unbound = nil;
+			n->decl = id;
+			id->refs++;
+		}
+		n->ty = id->ty = usetype(id->ty);
+		switch(id->store){
+		case Dfn:
+		case Dglobal:
+		case Darg:
+		case Dlocal:
+		case Dimport:
+		case Dfield:
+		case Dtag:
+			break;
+		case Dundef:
+			nerror(n, "%s is not declared", id->sym->name);
+			id->store = Dwundef;
+			ok.ok = ok.allok = 0;
+			return ok;
+		case Dwundef:
+			ok.ok = ok.allok = 0;
+			return ok;
+		case Dconst:
+			if(id->init == nil){
+				nerror(n, "%s's value cannot be determined", id->sym->name);
+				id->store = Dwundef;
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			break;
+		case Dtype:
+			if(typeok)
+				break;
+			nerror(n, "%K is not a variable", id);
+			ok.ok = ok.allok = 0;
+			return ok;
+		default:
+			fatal("echeck: unknown symbol storage");
+		}
+		
+		if(n->ty == nil){
+			nerror(n, "%K's type is not fully defined", id);
+			id->store = Dwundef;
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id->importid != nil && valistype(id->eimport)
+		&& id->store != Dconst && id->store != Dtype && id->store != Dfn){
+			nerror(n, "cannot use %V because %V is a module interface", n, id->eimport);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(n->ty->kind == Texception && !n->ty->cons && par != nil && par->op != Oraise && par->op != Odot){
+			nn = mkn(0, nil, nil);
+			*nn = *n;
+			n->op = Ocast;
+			n->left = nn;
+			n->decl = nil;
+			n->ty = usetype(mkextuptype(n->ty));
+		}
+		/* function name as function reference */
+		if(id->store == Dfn && (par == nil || (par->op != Odot && par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+			fnref(n, id);
+		break;
+	case Oconst:
+		if(n->ty == nil){
+			nerror(n, "no type in %V", n);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Oas:
+		t = right->ty;
+		if(t->kind == Texception)
+			t = mkextuptype(t);
+		if(!tcompat(left->ty, t, 1)){
+			nerror(n, "type clash in %Q = %Q", left, right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(t == tany)
+			t = left->ty;
+		n->ty = t;
+		left->ty = t;
+		if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+		if(left->ty->kind != Tadtpick || right->ty->kind != Tadtpick)
+			nerror(n, "expressions cannot have type %T", t);
+		if(left->ty->kind == Texception){
+			nerror(n, "cannot assign to an exception");
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(islval(left))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Osnd:
+		if(left->ty->kind != Tchan){
+			nerror(n, "cannot send on %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(!tcompat(left->ty->tof, right->ty, 0)){
+			nerror(n, "type clash in %Q <-= %Q", left, right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		t = right->ty;
+		if(t == tany)
+			t = left->ty->tof;
+		n->ty = t;
+		break;
+	case Orcv:
+		t = left->ty;
+		if(t->kind == Tarray)
+			t = t->tof;
+		if(t->kind != Tchan){
+			nerror(n, "cannot receive on %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(left->ty->kind == Tarray)
+			n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil,
+					mkids(&n->src, nil, tint, mkids(&n->src, nil, t->tof, nil))));
+		else
+			n->ty = t->tof;
+		break;
+	case Ocons:
+		if(right->ty->kind != Tlist && right->ty != tany){
+			nerror(n, "cannot :: to %Q", right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		n->ty = right->ty;
+		if(right->ty == tany)
+			n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, left->ty, nil));
+		else if(!tcompat(right->ty->tof, left->ty, 0)){
+			t = tparent(right->ty->tof, left->ty);
+			if(!tcompat(t, left->ty, 0)){
+				nerror(n, "type clash in %Q :: %Q", left, right);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			else
+				n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, t, nil));
+		}
+		break;
+	case Ohd:
+	case Otl:
+		if(left->ty->kind != Tlist || left->ty->tof == nil){
+			nerror(n, "cannot %O %Q", n->op, left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(n->op == Ohd)
+			n->ty = left->ty->tof;
+		else
+			n->ty = left->ty;
+		break;
+	case Otuple:
+		n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(left)));
+		break;
+	case Ospawn:
+		if(left->op != Ocall || left->left->ty->kind != Tfn && !isfnrefty(left->left->ty)){
+			nerror(left, "cannot spawn %V", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(left->ty != tnone){
+			nerror(left, "cannot spawn functions which return values, such as %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Oraise:
+		if(left->op == Onothing){
+			if(inexcept == nil){
+				nerror(n, "%V: empty raise not in exception handler", n);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			n->left = dupn(1, &n->src, inexcept);
+			break;
+		}
+		if(left->ty != tstring && left->ty->kind != Texception){
+			nerror(n, "%V: raise argument %Q is not a string or exception", n, left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if((left->op != Ocall || left->left->ty->kind == Tfn) && left->ty->ids != nil && left->ty->cons){
+			nerror(n, "too few exception arguments");
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Ocall:{
+		int pure;
+
+		kidsok = echeck(right, 0, isglobal, nil);
+		t = left->ty;
+		usedty(t);
+		pure = 1;
+		if(t->kind == Tref){
+			pure = 0;
+			t = t->tof;
+		}
+		if(t->kind != Tfn)
+			return callcast(n, kidsok.allok, ok.allok);
+		n->ty = t->tof;
+		if(!kidsok.allok){
+			ok.allok = 0;
+			break;
+		}
+
+		/*
+		 * get the name to call and any associated module
+		 */
+		mod = nil;
+		callee = nil;
+		id = nil;
+		tt = nil;
+		if(left->op == Odot){
+			Decl *dd;
+			Type *ttt;
+
+			callee = left->right->decl;
+			id = callee->dot;
+			right = passimplicit(left, right);
+			n->right = right;
+			tt = left->left->ty;
+			if(tt->kind == Tref)
+				tt = tt->tof;
+			ttt = tt;
+			if(tt->kind == Tadtpick)
+				ttt = tt->decl->dot->ty;
+			dd = ttt->decl;
+			while(dd != nil && dd->link != nil)
+				dd = dd->link;
+			if(dd != nil && dd->timport != nil)
+				mod = dd->timport->eimport;
+			/*
+			 * stash the import module under a rock,
+			 * because we won't be able to get it later
+			 * after scopes are popped
+			 */
+			left->right->left = mod;
+		}else if(left->op == Omdot){
+			if(left->right->op == Odot){
+				callee = left->right->right->decl;
+				right = passimplicit(left->right, right);
+				n->right = right;
+				tt = left->right->left->ty;
+				if(tt->kind == Tref)
+					tt = tt->tof;
+			}else
+				callee = left->right->decl;
+			mod = left->left;
+		}else if(left->op == Oname){
+			callee = left->decl;
+			id = callee;
+			mod = id->eimport;
+		}else if(pure){
+			nerror(left, "%V is not a function name", left);
+			ok.allok = 0;
+			break;
+		}
+		if(pure && callee == nil)
+			fatal("can't find called function: %n", left);
+		if(callee != nil && callee->store != Dfn && !isfnref(callee)){
+			nerror(left, "%V is not a function or function reference", left);
+			ok.allok = 0;
+			break;
+		}
+		if(mod != nil && mod->ty->kind != Tmodule){
+			nerror(left, "cannot call %V", left);
+			ok.allok = 0;
+			break;
+		}
+		if(mod != nil){
+			if(valistype(mod)){
+				nerror(left, "cannot call %V because %V is a module interface", left, mod);
+				ok.allok = 0;
+				break;
+			}
+		}else if(id != nil && id->dot != nil && !isimpmod(id->dot->sym)){
+			nerror(left, "cannot call %V without importing %s from a variable", left, id->sym->name);
+			ok.allok = 0;
+			break;
+		}
+		if(mod != nil)
+			modrefable(left->ty);
+		if(callee != nil && callee->store != Dfn)
+			callee = nil;
+		if(t->varargs != 0){
+			t = mkvarargs(left, right);
+			if(left->ty->kind == Tref)
+				left->ty = usetype(mktype(&t->src.start, &t->src.stop, Tref, t, nil));
+			else
+				left->ty = t;
+		}
+		else if(ispoly(callee) || isfnrefty(left->ty) && left->ty->tof->polys != nil){
+			Tpair *tp;
+
+			unifysrc = n->src;
+			if(!argncompat(n, t->ids, right))
+				ok.allok = 0;
+			else if(!tunify(left->ty, calltype(left->ty, right, n->ty), &tp)){
+				nerror(n, "function call type mismatch");
+				ok.allok = 0;
+			}
+			else{
+				n->ty = usetype(expandtype(n->ty, nil, nil, &tp));
+				if(ispoly(callee) && tt != nil && (tt->kind == Tadt || tt->kind == Tadtpick) && (tt->flags&INST))
+					callee = rewcall(left, callee);
+				n->right = passfns(&n->src, callee, left, right, tt, tp);
+			}
+		}
+		else if(!argcompat(n, t->ids, right))
+			ok.allok = 0;
+		break;
+	}
+	case Odot:
+		t = left->ty;
+		if(t->kind == Tref)
+			t = t->tof;
+		switch(t->kind){
+		case Tadt:
+		case Tadtpick:
+		case Ttuple:
+		case Texception:
+		case Tpoly:
+			id = namedot(t->ids, right->decl->sym);
+			if(id == nil){
+				id = namedot(t->tags, right->decl->sym);
+				if(id != nil && !valistype(left)){
+					nerror(n, "%V is not a type", left);
+					ok.ok = ok.allok = 0;
+					return ok;
+				}
+			}
+			if(id == nil){
+				id = namedot(t->polys, right->decl->sym);
+				if(id != nil && !valistype(left)){
+					nerror(n, "%V is not a type", left);
+					ok.ok = ok.allok = 0;
+					return ok;
+				}
+			}
+			if(id == nil && t->kind == Tadtpick)
+				id = namedot(t->decl->dot->ty->ids, right->decl->sym);
+			if(id == nil){
+				for(tg = t->tags; tg != nil; tg = tg->next){
+					id = namedot(tg->ty->ids, right->decl->sym);
+					if(id != nil)
+						break;
+				}
+				if(id != nil){
+					nerror(n, "cannot yet index field %s of %Q", right->decl->sym->name, left);
+					ok.ok = ok.allok = 0;
+					return ok;
+				}
+			}
+			if(id == nil)
+				break;
+			if(id->store == Dfield && valistype(left)){
+				nerror(n, "%V is not a value", left);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			id->ty = validtype(id->ty, t->decl);
+			id->ty = usetype(id->ty);
+			break;
+		default:
+			nerror(left, "%Q cannot be qualified with .", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id == nil){
+			nerror(n, "%V is not a member of %Q", right, left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id->ty == tunknown){
+			nerror(n, "illegal forward reference to %V", n);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+
+		increfs(id);
+		right->decl = id;
+		n->ty = id->ty;
+		if((id->store == Dconst || id->store == Dtag) && hasside(left, 1))
+			nwarn(left, "result of expression %Q ignored", left);
+		/* function name as function reference */
+		if(id->store == Dfn && (par == nil || (par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+			fnref(n, id);
+		break;
+	case Omdot:
+		t = left->ty;
+		if(t->kind != Tmodule){
+			nerror(left, "%Q cannot be qualified with ->", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		id = nil;
+		if(right->op == Oname){
+			id = namedot(t->ids, right->decl->sym);
+		}else if(right->op == Odot){
+			kidsok = echeck(right, 0, isglobal, n);
+			ok.ok = kidsok.ok;
+			ok.allok &= kidsok.allok;
+			if(!ok.ok){
+				ok.allok = 0;
+				return ok;
+			}
+			tt = right->left->ty;
+			if(tt->kind == Tref)
+				tt = tt->tof;
+			if(right->ty->kind == Tfn
+			&& tt->kind == Tadt
+			&& tt->decl->dot == t->decl)
+				id = right->right->decl;
+		}
+		if(id == nil){
+			nerror(n, "%V is not a member of %Q", right, left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id->store != Dconst && id->store != Dtype && id->store != Dtag){
+			if(valistype(left)){
+				nerror(n, "%V is not a value", left);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+		}else if(hasside(left, 1))
+			nwarn(left, "result of expression %Q ignored", left);
+		if(!typeok && id->store == Dtype){
+			nerror(n, "%V is a type, not a value", n);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(id->ty == tunknown){
+			nerror(n, "illegal forward reference to %V", n);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		id->refs++;
+		right->decl = id;
+		n->ty = id->ty = usetype(id->ty);
+		if(id->store == Dglobal)
+			modrefable(id->ty);
+		/* function name as function reference */
+		if(id->store == Dfn && (par == nil || (par->op != Ocall && par->op != Ofunc)))
+			fnref(n, id);
+		break;
+	case Otagof:
+		n->ty = tint;
+		t = left->ty;
+		if(t->kind == Tref)
+			t = t->tof;
+		id = nil;
+		switch(left->op){
+		case Oname:
+			id = left->decl;
+			break;
+		case Odot:
+			id = left->right->decl;
+			break;
+		case Omdot:
+			if(left->right->op == Odot)
+				id = left->right->right->decl;
+			break;
+		}
+		if(id != nil && id->store == Dtag
+		|| id != nil && id->store == Dtype && t->kind == Tadt && t->tags != nil)
+			n->decl = id;
+		else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+			n->decl = nil;
+		else{
+			nerror(n, "cannot get the tag value for %Q", left);
+			ok.ok = 1;
+			ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Oind:
+		t = left->ty;
+		if(t->kind != Tref || (t->tof->kind != Tadt && t->tof->kind != Tadtpick && t->tof->kind != Ttuple)){
+			nerror(n, "cannot * %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		n->ty = t->tof;
+		for(tg = t->tof->tags; tg != nil; tg = tg->next)
+			tg->ty->tof = usetype(tg->ty->tof);
+		break;
+	case Oindex:
+		if(valistype(left)){
+			tagopt = 1;
+			kidsok = echeck(right, 2, isglobal, n);
+			tagopt = 0;
+			if(!kidsok.allok){
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			if((t = exptotype(n)) == nil){
+				nerror(n, "%V is not a type list", right);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			if(!typeok){
+				nerror(n, "%Q is not a variable", left);
+				ok.ok = ok.allok = 0;
+				return ok;
+			}
+			*n = *(n->left);
+			n->ty = usetype(t);
+			break;
+		}
+		if(0 && right->op == Oseq){	/* a[e1, e2, ...] */
+			/* array creation to do before we allow this */
+			rewind(n);
+			return echeck(n, typeok, isglobal, par);
+		}
+		t = left->ty;
+		kidsok = echeck(right, 0, isglobal, n);
+		if(t->kind != Tarray && t != tstring){
+			nerror(n, "cannot index %Q", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(t == tstring){
+			n->op = Oinds;
+			n->ty = tint;
+		}else{
+			n->ty = t->tof;
+		}
+		if(!kidsok.allok){
+			ok.allok = 0;
+			break;
+		}
+		if(right->ty != tint){
+			nerror(n, "cannot index %Q with %Q", left, right);
+			ok.allok = 0;
+			break;
+		}
+		break;
+	case Oslice:
+		t = n->ty = left->ty;
+		if(t->kind != Tarray && t != tstring){
+			nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(right->left->ty != tint && right->left->op != Onothing
+		|| right->right->ty != tint && right->right->op != Onothing){
+			nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+			ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Olen:
+		t = left->ty;
+		n->ty = tint;
+		if(t->kind != Tarray && t->kind != Tlist && t != tstring){
+			nerror(n, "len requires an array, string or list in %Q", left);
+			ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Ocomp:
+	case Onot:
+	case Oneg:
+		n->ty = left->ty;
+		usedty(n->ty);
+		switch(left->ty->kind){
+		case Tint:
+			return ok;
+		case Treal:
+		case Tfix:
+			if(n->op == Oneg)
+				return ok;
+			break;
+		case Tbig:
+		case Tbyte:
+			if(n->op == Oneg || n->op == Ocomp)
+				return ok;
+			break;
+		}
+		nerror(n, "cannot apply %O to %Q", n->op, left);
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oinc:
+	case Odec:
+	case Opreinc:
+	case Opredec:
+		n->ty = left->ty;
+		switch(left->ty->kind){
+		case Tint:
+		case Tbig:
+		case Tbyte:
+		case Treal:
+			break;
+		default:
+			nerror(n, "cannot apply %O to %Q", n->op, left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(islval(left))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oadd:
+	case Odiv:
+	case Omul:
+	case Osub:
+		if(mathchk(n, 1))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oexp:
+	case Oexpas:
+		n->ty = left->ty;
+		if(n->ty != tint && n->ty != tbig && n->ty != treal){
+			nerror(n, "exponend %Q is not int, big or real", left);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(right->ty != tint){
+			nerror(n, "exponent %Q is not int", right);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		if(n->op == Oexpas && !islval(left)){
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		break;
+	case Olsh:
+	case Orsh:
+		if(shiftchk(n))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oandand:
+	case Ooror:
+		if(left->ty != tint){
+			nerror(n, "%O's left operand is not an int: %Q", n->op, left);
+			ok.allok = 0;
+		}
+		if(right->ty != tint){
+			nerror(n, "%O's right operand is not an int: %Q", n->op, right);
+			ok.allok = 0;
+		}
+		n->ty = tint;
+		break;
+	case Oand:
+	case Omod:
+	case Oor:
+	case Oxor:
+		if(mathchk(n, 0))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oaddas:
+	case Odivas:
+	case Omulas:
+	case Osubas:
+		if(mathchk(n, 1) && islval(left))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Olshas:
+	case Orshas:
+		if(shiftchk(n) && islval(left))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Oandas:
+	case Omodas:
+	case Oxoras:
+	case Ooras:
+		if(mathchk(n, 0) && islval(left))
+			break;
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Olt:
+	case Oleq:
+	case Ogt:
+	case Ogeq:
+		if(!mathchk(n, 1)){
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		n->ty = tint;
+		break;
+	case Oeq:
+	case Oneq:
+		switch(left->ty->kind){
+		case Tint:
+		case Tbig:
+		case Tbyte:
+		case Treal:
+		case Tstring:
+		case Tref:
+		case Tlist:
+		case Tarray:
+		case Tchan:
+		case Tany:
+		case Tmodule:
+		case Tfix:
+		case Tpoly:
+			if(!tcompat(left->ty, right->ty, 0) && !tcompat(right->ty, left->ty, 0))
+				break;
+			t = left->ty;
+			if(t == tany)
+				t = right->ty;
+			if(t == tany)
+				t = tint;
+			if(left->ty == tany)
+				left->ty = t;
+			if(right->ty == tany)
+				right->ty = t;
+			n->ty = tint;
+			return ok;
+		}
+		nerror(n, "cannot compare %Q to %Q", left, right);
+		usedty(n->ty);
+		ok.ok = ok.allok = 0;
+		return ok;
+	case Otype:
+		if(!typeok){
+			nerror(n, "%Q is not a variable", n);
+			ok.ok = ok.allok = 0;
+			return ok;
+		}
+		n->ty = usetype(n->ty);
+		break;
+	default:
+		fatal("unknown op in typecheck: %O", n->op);
+	}
+	usedty(n->ty);
+	return ok;
+}
+
+/*
+ * n is syntactically a call, but n->left is not a fn
+ * check if it's the contructor for an adt
+ */
+Ok
+callcast(Node *n, int kidsok, int allok)
+{
+	Node *left, *right;
+	Decl *id;
+	Type *t, *tt;
+	Ok ok;
+
+	left = n->left;
+	right = n->right;
+	id = nil;
+	switch(left->op){
+	case Oname:
+		id = left->decl;
+		break;
+	case Omdot:
+		if(left->right->op == Odot)
+			id = left->right->right->decl;
+		else
+			id = left->right->decl;
+		break;
+	case Odot:
+		id = left->right->decl;
+		break;
+	}
+/*
+	(chan of int)(nil) looks awkward since both sets of brackets needed
+	if(id == nil && right != nil && right->right == nil && (t = exptotype(left)) != nil){
+		n->op = Ocast;
+		n->left = right->left;
+		n->right = nil;
+		n->ty = t;
+		return echeck(n, 0, 0, nil);
+	}
+*/
+	if(id == nil || (id->store != Dtype && id->store != Dtag && id->ty->kind != Texception)){
+		nerror(left, "%V is not a function or type name", left);
+		ok.ok = ok.allok = 0;
+		return ok;
+	}
+	if(id->store == Dtag)
+		return tagcast(n, left, right, id, kidsok, allok);
+	t = left->ty;
+	n->ty = t;
+	if(!kidsok){
+		ok.ok = 1;
+		ok.allok = 0;
+		return ok;
+	}
+
+	if(t->kind == Tref)
+		t = t->tof;
+	tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(right));
+	if(t->kind == Tadt && tcompat(t, tt, 1)){
+		if(right == nil)
+			*n = *n->left;
+		ok.ok = 1;
+		ok.allok = allok;
+		return ok;
+	}
+
+	/* try an exception with args */
+	tt = mktype(&n->src.start, &n->src.stop, Texception, nil, tuplefields(right));
+	tt->cons = 1;
+	if(t->kind == Texception && t->cons && tcompat(t, tt, 1)){
+		if(right == nil)
+			*n = *n->left;
+		ok.ok = 1;
+		ok.allok = allok;
+		return ok;
+	}
+
+	/* try a cast */
+	if(t->kind != Texception && right != nil && right->right == nil){	/* Oseq but single expression */
+		right = right->left;
+		n->op = Ocast;
+		n->left = right;
+		n->right = nil;
+		n->ty = mkidtype(&n->src, id->sym);
+		return echeck(n, 0, 0, nil);
+	}
+
+	nerror(left, "cannot make a %V from '(%v)'", left, right);
+	ok.ok = ok.allok = 0;
+	return ok;
+}
+
+Ok
+tagcast(Node *n, Node *left, Node *right, Decl *id, int kidsok, int allok)
+{
+	Type *tt;
+	Ok ok;
+
+	left->ty = id->ty;
+	if(left->op == Omdot)
+		left->right->ty = id->ty;
+	n->ty = id->ty;
+	if(!kidsok){
+		ok.ok = 1;
+		ok.allok = 0;
+		return ok;
+	}
+	id->ty->tof = usetype(id->ty->tof);
+	if(right != nil)
+		right->ty = id->ty->tof;
+	tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, mkids(&nosrc, nil, tint, tuplefields(right)));
+	tt->ids->store = Dfield;
+	if(tcompat(id->ty->tof, tt, 1)){
+		ok.ok = 1;
+		ok.allok = allok;
+		return ok;
+	}
+
+	nerror(left, "cannot make a %V from '(%v)'", left, right);
+	ok.ok = ok.allok = 0;
+	return ok;
+}
+
+int
+valistype(Node *n)
+{
+	switch(n->op){
+	case Oname:
+		if(n->decl->store == Dtype)
+			return 1;
+		break;
+	case Omdot:
+		return valistype(n->right);
+	}
+	return 0;
+}
+
+int
+islval(Node *n)
+{
+	int s;
+
+	s = marklval(n);
+	if(s == 1)
+		return 1;
+	if(s == 0)
+		nerror(n, "cannot assign to %V", n);
+	else
+		circlval(n, n);
+	return 0;
+}
+
+/*
+ * check to see if n is an lval
+ * mark the lval name as set
+ */
+int
+marklval(Node *n)
+{
+	Decl *id;
+	Node *nn;
+	int s;
+
+	if(n == nil)
+		return 0;
+	switch(n->op){
+	case Oname:
+		return storespace[n->decl->store] && n->ty->kind != Texception; /*ZZZZ && n->decl->tagged == nil;*/
+	case Odot:
+		if(n->right->decl->store != Dfield)
+			return 0;
+		if(n->right->decl->cycle && !n->right->decl->cyc)
+			return -1;
+		if(n->left->ty->kind != Tref && marklval(n->left) == 0)
+			nwarn(n, "assignment to %Q ignored", n);
+		return 1;
+	case Omdot:
+		if(n->right->decl->store == Dglobal)
+			return 1;
+		return 0;
+	case Oind:
+		for(id = n->ty->ids; id != nil; id = id->next)
+			if(id->cycle && !id->cyc)
+				return -1;
+		return 1;
+	case Oslice:
+		if(n->right->right->op != Onothing || n->ty == tstring)
+			return 0;
+		return 1;
+	case Oinds:
+		/*
+		 * make sure we don't change a string constant
+		 */
+		switch(n->left->op){
+		case Oconst:
+			return 0;
+		case Oname:
+			return storespace[n->left->decl->store];
+		case Odot:
+		case Omdot:
+			if(n->left->right->decl != nil)
+				return storespace[n->left->right->decl->store];
+			break;
+		}
+		return 1;
+	case Oindex:
+	case Oindx:
+		return 1;
+	case Otuple:
+		for(nn = n->left; nn != nil; nn = nn->right){
+			s = marklval(nn->left);
+			if(s != 1)
+				return s;
+		}
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * n has a circular field assignment.
+ * find it and print an error message.
+ */
+int
+circlval(Node *n, Node *lval)
+{
+	Decl *id;
+	Node *nn;
+	int s;
+
+	if(n == nil)
+		return 0;
+	switch(n->op){
+	case Oname:
+		break;
+	case Odot:
+		if(oldcycles && n->right->decl->cycle && !n->right->decl->cyc){
+			nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+				lval, n->right->decl->sym->name, n->left, n->left);
+			return -1;
+		}
+		return 1;
+	case Oind:
+		if(!oldcycles)
+			return 1;
+		for(id = n->ty->ids; id != nil; id = id->next){
+			if(id->cycle && !id->cyc){
+				nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+					lval, id->sym->name, n, n);
+				return -1;
+			}
+		}
+		return 1;
+	case Oslice:
+		if(n->right->right->op != Onothing || n->ty == tstring)
+			return 0;
+		return 1;
+	case Oindex:
+	case Oinds:
+	case Oindx:
+		return 1;
+	case Otuple:
+		for(nn = n->left; nn != nil; nn = nn->right){
+			s = circlval(nn->left, lval);
+			if(s != 1)
+				return s;
+		}
+		return 1;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+int
+mathchk(Node *n, int realok)
+{
+	Type *tr, *tl;
+
+	tl = n->left->ty;
+	tr = n->right->ty;
+	if(tr != tl && !tequal(tl, tr)){
+		nerror(n, "type clash in %Q %O %Q", n->left, n->op, n->right);
+		return 0;
+	}
+	n->ty = tr;
+	switch(tr->kind){
+	case Tint:
+	case Tbig:
+	case Tbyte:
+		return 1;
+	case Tstring:
+		switch(n->op){
+		case Oadd:
+		case Oaddas:
+		case Ogt:
+		case Ogeq:
+		case Olt:
+		case Oleq:
+			return 1;
+		}
+		break;
+	case Treal:
+	case Tfix:
+		if(realok)
+			return 1;
+		break;
+	}
+	nerror(n, "cannot %O %Q and %Q", n->op, n->left, n->right);
+	return 0;
+}
+
+int
+shiftchk(Node *n)
+{
+	Node *left, *right;
+
+	right = n->right;
+	left = n->left;
+	n->ty = left->ty;
+	switch(n->ty->kind){
+	case Tint:
+	case Tbyte:
+	case Tbig:
+		if(right->ty->kind != Tint){
+			nerror(n, "shift %Q is not an int", right);
+			return 0;
+		}
+		return 1;
+	}
+	nerror(n, "cannot %Q %O %Q", left, n->op, right);
+	return 0;
+}
+
+/*
+ * check for any tany's in t
+ */
+int
+specific(Type *t)
+{
+	Decl *d;
+
+	if(t == nil)
+		return 0;
+	switch(t->kind){
+	case Terror:
+	case Tnone:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Tbyte:
+	case Treal:
+	case Tfn:
+	case Tadt:
+	case Tadtpick:
+	case Tmodule:
+	case Tfix:
+		return 1;
+	case Tany:
+		return 0;
+	case Tpoly:
+		return 1;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		return specific(t->tof);
+	case Ttuple:
+	case Texception:
+		for(d = t->ids; d != nil; d = d->next)
+			if(!specific(d->ty))
+				return 0;
+		return 1;
+	}
+	fatal("unknown type %T in specific", t);
+	return 0;
+}
+
+/*
+ * infer the type of all variable in n from t
+ * n is the left-hand exp of a := exp
+ */
+int
+declasinfer(Node *n, Type *t)
+{
+	Decl *ids;
+	int ok;
+
+	if(t->kind == Texception){
+		if(t->cons)
+			return 0;
+		t = mkextuptype(t);
+	}
+	switch(n->op){
+	case Otuple:
+		if(t->kind != Ttuple && t->kind != Tadt && t->kind != Tadtpick)
+			return 0;
+		ok = 1;
+		n->ty = t;
+		n = n->left;
+		ids = t->ids;
+		if(t->kind == Tadtpick)
+			ids = t->tof->ids->next;
+		for(; n != nil && ids != nil; ids = ids->next){
+			if(ids->store != Dfield)
+				continue;
+			ok &= declasinfer(n->left, ids->ty);
+			n = n->right;
+		}
+		for(; ids != nil; ids = ids->next)
+			if(ids->store == Dfield)
+				break;
+		if(n != nil || ids != nil)
+			return 0;
+		return ok;
+	case Oname:
+		topvartype(t, n->decl, 0, 0);
+		if(n->decl == nildecl)
+			return 1;
+		n->decl->ty = t;
+		n->ty = t;
+		shareloc(n->decl);
+		return 1;
+	}
+	fatal("unknown op %n in declasinfer", n);
+	return 0;
+}
+
+/*
+ * an error occured in declaring n;
+ * set all decl identifiers to Dwundef
+ * so further errors are squashed.
+ */
+void
+declaserr(Node *n)
+{
+	switch(n->op){
+	case Otuple:
+		for(n = n->left; n != nil; n = n->right)
+			declaserr(n->left);
+		return;
+	case Oname:
+		if(n->decl != nildecl)
+			n->decl->store = Dwundef;
+		return;
+	}
+	fatal("unknown op %n in declaserr", n);
+}
+
+int
+argcompat(Node *n, Decl *f, Node *a)
+{
+	for(; a != nil; a = a->right){
+		if(f == nil){
+			nerror(n, "%V: too many function arguments", n->left);
+			return 0;
+		}
+		if(!tcompat(f->ty, a->left->ty, 0)){
+			nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+				n->left, f->ty, a->left);
+			return 0;
+		}
+		if(a->left->ty == tany)
+			a->left->ty = f->ty;
+		f = f->next;
+	}
+	if(f != nil){
+		nerror(n, "%V: too few function arguments", n->left);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * fn is Odot(adt, methid)
+ * pass adt implicitly if needed
+ * if not, any side effect of adt will be ignored
+ */
+Node*
+passimplicit(Node *fn, Node *args)
+{
+	Node *n;
+	Type *t;
+
+	t = fn->ty;
+	n = fn->left;
+	if(t->ids == nil || !t->ids->implicit){
+		if(!isfnrefty(t) && hasside(n, 1))
+			nwarn(fn, "result of expression %V ignored", n);
+		return args;
+	}
+	if(n->op == Oname && n->decl->store == Dtype){
+		nerror(n, "%V is a type and cannot be a self argument", n);
+		n = mkn(Onothing, nil, nil);
+		n->src = fn->src;
+		n->ty = t->ids->ty;
+	}
+	args = mkn(Oseq, n, args);
+	args->src = n->src;
+	return args;
+}
+
+static int
+mem(Type *t, Decl *d)
+{
+	for( ; d != nil; d = d->next)
+		if(d->ty == t)	/* was if(d->ty == t || tequal(d->ty, t)) */
+			return 1;
+	return 0;
+}
+
+static int
+memp(Type *t, Decl *f)
+{
+	return mem(t, f->ty->polys) || mem(t, encpolys(f));
+}
+
+static void
+passfns0(Src *src, Decl *fn, Node *args0, Node **args, Node **a, Tpair *tp, Decl *polys)
+{
+	Decl *id, *idt, *idf, *dot;
+	Type *tt;
+	Sym *sym;
+	Node *n, *na, *mod;
+	Tpair *p;
+
+if(debug['w']){
+	print("polys: ");
+	for(id=polys; id!=nil; id=id->next) print("%s ", id->sym->name);
+	print("\nmap: ");
+	for(p=tp; p!=nil; p=p->nxt) print("%T -> %T ", p->t1, p->t2);
+	print("\n");
+}
+	for(idt = polys; idt != nil; idt = idt->next){
+		tt = valtmap(idt->ty, tp);
+		if(tt->kind == Tpoly && fndec != nil && !memp(tt, fndec))
+			error(src->start, "cannot determine the instantiated type of %T", tt);
+		for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+			sym = idf->sym;
+			id = fnlookup(sym, tt, &mod);
+			while(id != nil && id->link != nil)
+				id = id->link;
+if(debug['v']) print("fnlookup: %p\n", id);
+			if(id == nil)	/* error flagged already */
+				continue;
+			id->refs++;
+			id->caninline = -1;
+			if(tt->kind == Tmodule){	/* mod an actual parameter */
+				for(;;){
+					if(args0 != nil && tequal(tt, args0->left->ty)){
+						mod = args0->left;
+						break;
+					}
+					if(args0 == nil)
+						break;
+					args0 = args0->right;
+				}
+			}
+			if(mod == nil && (dot = module(id)) != nil && !isimpmod(dot->sym))
+				error(src->start, "cannot use %s without importing %s from a variable", id->sym->name, id->dot->sym->name);
+
+if(debug['U']) print("fp: %s %s %s\n", fn->sym->name, mod ? mod->decl->sym->name : "nil", id->sym->name);
+			n = mkn(Ofnptr, mod, mkdeclname(src, id));
+			n->src = *src;
+			n->decl = fn;
+			if(tt->kind == Tpoly)
+				n->flags = FNPTRA;
+			else
+				n->flags = 0;
+			na = mkn(Oseq, n, nil);
+			if(*a == nil)
+				*args = na;
+			else
+				(*a)->right = na;
+			
+			n = mkn(Ofnptr, mod, mkdeclname(src, id));
+			n->src = *src;
+			n->decl = fn;
+			if(tt->kind == Tpoly)
+				n->flags = FNPTRA|FNPTR2;
+			else
+				n->flags = FNPTR2;
+			*a = na->right = mkn(Oseq, n, nil);
+		}
+		if(args0 != nil)
+			args0 = args0->right;
+	}
+}
+
+Node*
+passfns(Src *src, Decl *fn, Node *left, Node *args, Type *adt, Tpair *tp)
+{
+	Node *a, *args0;
+
+	a = nil;
+	args0 = args;
+	if(args != nil)
+		for(a = args; a->right != nil; a = a->right)
+			;
+	passfns0(src, fn, args0, &args, &a, tp, ispoly(fn) ? fn->ty->polys : left->ty->tof->polys);
+	if(adt != nil)
+		passfns0(src, fn, args0, &args, &a, adt->u.tmap, ispoly(fn) ? encpolys(fn) : nil);
+	return args;	
+}
+
+/*
+ * check the types for a function with a variable number of arguments
+ * last typed argument must be a constant string, and must use the
+ * print format for describing arguments.
+ */
+Type*
+mkvarargs(Node *n, Node *args)
+{
+	Node *s, *a;
+	Decl *f, *last, *va;
+	Type *nt;
+
+	nt = copytypeids(n->ty);
+	n->ty = nt;
+	f = n->ty->ids;
+	last = nil;
+	if(f == nil){
+		nerror(n, "%V's type is illegal", n);
+		return nt;
+	}
+	s = args;
+	for(a = args; a != nil; a = a->right){
+		if(f == nil)
+			break;
+		if(!tcompat(f->ty, a->left->ty, 0)){
+			nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+				n, f->ty, a->left);
+			return nt;
+		}
+		if(a->left->ty == tany)
+			a->left->ty = f->ty;
+		last = f;
+		f = f->next;
+		s = a;
+	}
+	if(f != nil){
+		nerror(n, "%V: too few function arguments", n);
+		return nt;
+	}
+
+	s->left = fold(s->left);
+	s = s->left;
+	if(s->ty != tstring || s->op != Oconst){
+		nerror(args, "%V: format argument %Q is not a string constant", n, s);
+		return nt;
+	}
+	fmtcheck(n, s, a);
+	va = tuplefields(a);
+	if(last == nil)
+		nt->ids = va;
+	else
+		last->next = va;
+	return nt;
+}
+
+/*
+ * check that a print style format string matches its arguments
+ */
+void
+fmtcheck(Node *f, Node *fmtarg, Node *va)
+{
+	Sym *fmt;
+	Rune r;
+	char *s, flags[10];
+	int i, c, n1, n2, dot, verb, flag, ns, lens, fmtstart;
+	Type *ty;
+
+	fmt = fmtarg->decl->sym;
+	s = fmt->name;
+	lens = fmt->len;
+	ns = 0;
+	while(ns < lens){
+		c = s[ns++];
+		if(c != '%')
+			continue;
+
+		verb = -1;
+		n1 = 0;
+		n2 = 0;
+		dot = 0;
+		flag = 0;
+		fmtstart = ns - 1;
+		while(ns < lens && verb < 0){
+			c = s[ns++];
+			switch(c){
+			default:
+				chartorune(&r, &s[ns-1]);
+				nerror(f, "%V: invalid character %C in format '%.*s'", f, r, ns-fmtstart, &s[fmtstart]);
+				return;
+			case '.':
+				if(dot){
+					nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+					return;
+				}
+				n1 = 1;
+				dot = 1;
+				continue;
+			case '*':
+				if(!n1)
+					n1 = 1;
+				else if(!n2 && dot)
+					n2 = 1;
+				else{
+					nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+					return;
+				}
+				if(va == nil){
+					nerror(f, "%V: too few arguments for format '%.*s'",
+						f, ns-fmtstart, &s[fmtstart]);
+					return;
+				}
+				if(va->left->ty->kind != Tint){
+					nerror(f, "%V: format '%.*s' incompatible with argument %Q",
+						f, ns-fmtstart, &s[fmtstart], va->left);
+					return;
+				}
+				va = va->right;
+				break;
+			case '0': case '1': case '2': case '3': case '4':
+			case '5': case '6': case '7': case '8': case '9':
+				while(ns < lens && s[ns] >= '0' && s[ns] <= '9')
+					ns++;
+				if(!n1)
+					n1 = 1;
+				else if(!n2 && dot)
+					n2 = 1;
+				else{
+					nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+					return;
+				}
+				break;
+			case '+':
+			case '-':
+			case '#':
+			case ',':
+			case 'b':
+			case 'u':
+				for(i = 0; i < flag; i++){
+					if(flags[i] == c){
+						nerror(f, "%V: duplicate flag %c in format '%.*s'",
+							f, c, ns-fmtstart, &s[fmtstart]);
+						return;
+					}
+				}
+				flags[flag++] = c;
+				if(flag >= sizeof flags){
+					nerror(f, "too many flags in format '%.*s'", ns-fmtstart, &s[fmtstart]);
+					return;
+				}
+				break;
+			case '%':
+			case 'r':
+				verb = Tnone;
+				break;
+			case 'H':
+				verb = Tany;
+				break;
+			case 'c':
+				verb = Tint;
+				break;
+			case 'd':
+			case 'o':
+			case 'x':
+			case 'X':
+				verb = Tint;
+				for(i = 0; i < flag; i++){
+					if(flags[i] == 'b'){
+						verb = Tbig;
+						break;
+					}
+				}
+				break;
+			case 'e':
+			case 'f':
+			case 'g':
+			case 'E':
+			case 'G':
+				verb = Treal;
+				break;
+			case 's':
+			case 'q':
+				verb = Tstring;
+				break;
+			}
+		}
+		if(verb != Tnone){
+			if(verb < 0){
+				nerror(f, "%V: incomplete format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+				return;
+			}
+			if(va == nil){
+				nerror(f, "%V: too few arguments for format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+				return;
+			}
+			ty = va->left->ty;
+			if(ty->kind == Texception)
+				ty = mkextuptype(ty);
+			switch(verb){
+			case Tint:
+				switch(ty->kind){
+				case Tstring:
+				case Tarray:
+				case Tref:
+				case Tchan:
+				case Tlist:
+				case Tmodule:
+					if(c == 'x' || c == 'X')
+						verb = ty->kind;
+					break;
+				}
+				break;
+			case Tany:
+				if(tattr[ty->kind].isptr)
+					verb = ty->kind;
+				break;
+			}
+			if(verb != ty->kind){
+				nerror(f, "%V: format '%.*s' incompatible with argument %Q", f, ns-fmtstart, &s[fmtstart], va->left);
+				return;
+			}
+			va = va->right;
+		}
+	}
+	if(va != nil)
+		nerror(f, "%V: more arguments than formats", f);
+}
+
+Decl*
+tuplefields(Node *n)
+{
+	Decl *d, *h, **last;
+
+	h = nil;
+	last = &h;
+	for(; n != nil; n = n->right){
+		d = mkdecl(&n->left->src, Dfield, n->left->ty);
+		*last = d;
+		last = &d->next;
+	}
+	return h;
+}
+
+/*
+ * make explicit indices for every element in an array initializer
+ * return the maximum index
+ * sort the indices and check for duplicates
+ */
+int
+assignindices(Node *ar)
+{
+	Node *wild, *off, *size, *inits, *n, *q;
+	Type *t;
+	Case *c;
+	int amax, max, last, nlab, ok;
+
+	amax = 0x7fffffff;
+	size = dupn(0, &nosrc, ar->left);
+	if(size->ty == tint){
+		size = fold(size);
+		if(size->op == Oconst)
+			amax = size->val;
+	}
+
+	inits = ar->right;
+	max = -1;
+	last = -1;
+	t = inits->left->ty;
+	wild = nil;
+	nlab = 0;
+	ok = 1;
+	for(n = inits; n != nil; n = n->right){
+		if(!tcompat(t,  n->left->ty, 0)){
+			t = tparent(t, n->left->ty);
+			if(!tcompat(t, n->left->ty, 0)){
+				nerror(n->left, "inconsistent types %T and %T and in array initializer", t, n->left->ty);
+				return -1;
+			}
+			else
+				inits->left->ty = t;
+		}
+		if(t == tany)
+			t = n->left->ty;
+
+		/*
+		 * make up an index if there isn't one
+		 */
+		if(n->left->left == nil)
+			n->left->left = mkn(Oseq, mkconst(&n->left->right->src, last + 1), nil);
+
+		for(q = n->left->left; q != nil; q = q->right){
+			off = q->left;
+			if(off->ty != tint){
+				nerror(off, "array index %Q is not an int", off);
+				ok = 0;
+				continue;
+			}
+			off = fold(off);
+			switch(off->op){
+			case Owild:
+				if(wild != nil)
+					nerror(off, "array index * duplicated on line %L", wild->src.start);
+				wild = off;
+				continue;
+			case Orange:
+				if(off->left->op != Oconst || off->right->op != Oconst){
+					nerror(off, "range %V is not constant", off);
+					off = nil;
+				}else if(off->left->val < 0 || off->right->val >= amax){
+					nerror(off, "array index %V out of bounds", off);
+					off = nil;
+				}else
+					last = off->right->val;
+				break;
+			case Oconst:
+				last = off->val;
+				if(off->val < 0 || off->val >= amax){
+					nerror(off, "array index %V out of bounds", off);
+					off = nil;
+				}
+				break;
+			case Onothing:
+				/* get here from a syntax error */
+				off = nil;
+				break;
+			default:
+				nerror(off, "array index %V is not constant", off);
+				off = nil;
+				break;
+			}
+
+			nlab++;
+			if(off == nil){
+				off = mkconst(&n->left->right->src, last);
+				ok = 0;
+			}
+			if(last > max)
+				max = last;
+			q->left = off;
+		}
+	}
+
+	/*
+	 * fix up types of nil elements
+	 */
+	for(n = inits; n != nil; n = n->right)
+		if(n->left->ty == tany)
+			n->left->ty = t;
+
+	if(!ok)
+		return -1;
+
+
+	c = checklabels(inits, tint, nlab, "array index");
+	t = mktype(&inits->src.start, &inits->src.stop, Tainit, nil, nil);
+	inits->ty = t;
+	t->cse = c;
+
+	return max + 1;
+}
+
+/*
+ * check the labels of a case statment
+ */
+void
+casecheck(Node *cn, Type *ret)
+{
+	Node *n, *q, *wild, *left, *arg;
+	Type *t;
+	Case *c;
+	Ok rok;
+	int nlab, ok, op;
+
+	rok = echeck(cn->left, 0, 0, nil);
+	cn->right = scheck(cn->right, ret, Sother);
+	if(!rok.ok)
+		return;
+	arg = cn->left;
+
+	t = arg->ty;
+	if(t != tint && t != tbig && t != tstring){
+		nerror(cn, "case argument %Q is not an int or big or string", arg);
+		return;
+	}
+
+	wild = nil;
+	nlab= 0;
+	ok = 1;
+	for(n = cn->right; n != nil; n = n->right){
+		q = n->left->left;
+		if(n->left->right->right == nil)
+			nwarn(q, "no body for case qualifier %V", q);
+		for(; q != nil; q = q->right){
+			left = fold(q->left);
+			q->left = left;
+			switch(left->op){
+			case Owild:
+				if(wild != nil)
+					nerror(left, "case qualifier * duplicated on line %L", wild->src.start);
+				wild = left;
+				break;
+			case Orange:
+				if(left->ty != t)
+					nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+				else if(left->left->op != Oconst || left->right->op != Oconst){
+					nerror(left, "case range %V is not constant", left);
+					ok = 0;
+				}
+				nlab++;
+				break;
+			default:
+				if(left->ty != t){
+					nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+					ok = 0;
+				}else if(left->op != Oconst){
+					nerror(left, "case qualifier %V is not constant", left);
+					ok = 0;
+				}
+				nlab++;
+				break;
+			}
+		}
+	}
+
+	if(!ok)
+		return;
+
+	c = checklabels(cn->right, t, nlab, "case qualifier");
+	op = Tcase;
+	if(t == tbig)
+		op = Tcasel;
+	else if(t == tstring)
+		op = Tcasec;
+	t = mktype(&cn->src.start, &cn->src.stop, op, nil, nil);
+	cn->ty = t;
+	t->cse = c;
+}
+
+/*
+ * check the labels and bodies of a pick statment
+ */
+void
+pickcheck(Node *n, Type *ret)
+{
+	Node *w, *arg, *qs, *q, *qt, *left, **tags;
+	Decl *id, *d;
+	Type *t, *argty;
+	Case *c;
+	Ok rok;
+	int ok, nlab;
+
+	arg = n->left->right;
+	rok = echeck(arg, 0, 0, nil);
+	if(!rok.allok)
+		return;
+	t = arg->ty;
+	if(t->kind == Tref)
+		t = t->tof;
+	if(arg->ty->kind != Tref || t->kind != Tadt || t->tags == nil){
+		nerror(arg, "pick argument %Q is not a ref adt with pick tags", arg);
+		return;
+	}
+	argty = usetype(mktype(&arg->ty->src.start, &arg->ty->src.stop, Tref, t, nil));
+
+	arg = n->left->left;
+	pushscope(nil, Sother);
+	dasdecl(arg);
+	arg->decl->ty = argty;
+	arg->ty = argty;
+
+	tags = allocmem(t->decl->tag * sizeof *tags);
+	memset(tags, 0, t->decl->tag * sizeof *tags);
+	w = nil;
+	ok = 1;
+	nlab = 0;
+	for(qs = n->right; qs != nil; qs = qs->right){
+		qt = nil;
+		for(q = qs->left->left; q != nil; q = q->right){
+			left = q->left;
+			switch(left->op){
+			case Owild:
+				/* left->ty = tnone; */
+				left->ty = t;
+				if(w != nil)
+					nerror(left, "pick qualifier * duplicated on line %L", w->src.start);
+				w = left;
+				break;
+			case Oname:
+				id = namedot(t->tags, left->decl->sym);
+				if(id == nil){
+					nerror(left, "pick qualifier %V is not a member of %Q", left, arg);
+					ok = 0;
+					continue;
+				}
+
+				left->decl = id;
+				left->ty = id->ty;
+
+				if(tags[id->tag] != nil){
+					nerror(left, "pick qualifier %V duplicated on line %L",
+						left, tags[id->tag]->src.start);
+					ok = 0;
+				}
+				tags[id->tag] = left;
+				nlab++;
+				break;
+			default:
+				fatal("pickcheck can't handle %n", q);
+				break;
+			}
+
+			if(qt == nil)
+				qt = left;
+			else if(!tequal(qt->ty, left->ty))
+				nerror(left, "type clash in pick qualifiers %Q and %Q", qt, left);
+		}
+
+		argty->tof = t;
+		if(qt != nil)
+			argty->tof = qt->ty;
+		qs->left->right = scheck(qs->left->right, ret, Sother);
+		if(qs->left->right == nil)
+			nwarn(qs->left->left, "no body for pick qualifier %V", qs->left->left);
+	}
+	argty->tof = t;
+	for(qs = n->right; qs != nil; qs = qs->right)
+		for(q = qs->left->left; q != nil; q = q->right)
+			q->left = fold(q->left);
+
+	d = popscope();
+	d->refs++;
+	if(d->next != nil)
+		fatal("pickcheck: installing more than one id");
+	fndecls = appdecls(fndecls, d);
+
+	if(!ok)
+		return;
+
+	c = checklabels(n->right, tint, nlab, "pick qualifier");
+	t = mktype(&n->src.start, &n->src.stop, Tcase, nil, nil);
+	n->ty = t;
+	t->cse = c;
+}
+
+void
+exccheck(Node *en, Type *ret)
+{
+	Decl *ed;
+	Node *n, *q, *wild, *left, *oinexcept;
+	Type *t, *qt;
+	Case *c;
+	int nlab, ok;
+	Ok rok;
+	char buf[32];
+	static int nexc;
+
+	pushscope(nil, Sother);
+	if(en->left == nil){
+		seprint(buf, buf+sizeof(buf), ".ex%d", nexc++);
+		en->left = mkdeclname(&en->src, mkids(&en->src, enter(buf, 0), texception, nil));
+	}
+	oinexcept = inexcept;
+	inexcept = en->left;
+	dasdecl(en->left);
+	en->left->ty = en->left->decl->ty = texception;
+	ed = en->left->decl;
+	/* en->right = scheck(en->right, ret, Sother); */
+	t = tstring;
+	wild = nil;
+	nlab = 0;
+	ok = 1;
+	for(n = en->right; n != nil; n = n->right){
+		qt = nil;
+		for(q = n->left->left; q != nil; q = q->right){
+			left = q->left;
+			switch(left->op){
+			case Owild:
+				left->ty = texception;
+				if(wild != nil)
+					nerror(left, "exception qualifier * duplicated on line %L", wild->src.start);
+				wild = left;
+				break;
+			case Orange:
+				left->ty = tnone;
+				nerror(left, "exception qualifier %V is illegal", left);
+				ok = 0;
+				break;
+			default:
+				rok = echeck(left, 0, 0, nil);
+				if(!rok.ok){
+					ok = 0;
+					break;
+				}
+				left = q->left = fold(left);
+				if(left->ty != t && left->ty->kind != Texception){
+					nerror(left, "exception qualifier %Q is not a string or exception", left);
+					ok = 0;
+				}else if(left->op != Oconst){
+					nerror(left, "exception qualifier %V is not constant", left);
+					ok = 0;
+				}
+				else if(left->ty != t)
+					left->ty = mkextype(left->ty);
+				nlab++;
+				break;
+			}
+
+			if(qt == nil)
+				qt = left->ty;
+			else if(!tequal(qt, left->ty))
+				qt = texception;
+		}
+
+		if(qt != nil)
+			ed->ty = qt;
+		n->left->right = scheck(n->left->right, ret, Sother);
+		if(n->left->right->right == nil)
+			nwarn(n->left->left, "no body for exception qualifier %V", n->left->left);
+	}
+	ed->ty = texception;
+	inexcept = oinexcept;
+	if(!ok)
+		return;
+	c = checklabels(en->right, texception, nlab, "exception qualifier");
+	t = mktype(&en->src.start, &en->src.stop, Texcept, nil, nil);
+	en->ty = t;
+	t->cse = c;
+	ed = popscope();
+	fndecls = appdecls(fndecls, ed);
+}
+
+/*
+ * check array and case labels for validity
+ */
+Case *
+checklabels(Node *inits, Type *ctype, int nlab, char *title)
+{
+	Node *n, *p, *q, *wild;
+	Label *labs, *aux;
+	Case *c;
+	char buf[256], buf1[256];
+	int i, e;
+
+	labs = allocmem(nlab * sizeof *labs);
+	i = 0;
+	wild = nil;
+	for(n = inits; n != nil; n = n->right){
+		for(q = n->left->left; q != nil; q = q->right){
+			switch(q->left->op){
+			case Oconst:
+				labs[i].start = q->left;
+				labs[i].stop = q->left;
+				labs[i++].node = n->left;
+				break;
+			case Orange:
+				labs[i].start = q->left->left;
+				labs[i].stop = q->left->right;
+				labs[i++].node = n->left;
+				break;
+			case Owild:
+				wild = n->left;
+				break;
+			default:
+				fatal("bogus index in checklabels");
+				break;
+			}
+		}
+	}
+
+	if(i != nlab)
+		fatal("bad label count: %d then %d", nlab, i);
+
+	aux = allocmem(nlab * sizeof *aux);
+	casesort(ctype, aux, labs, 0, nlab);
+	for(i = 0; i < nlab; i++){
+		p = labs[i].stop;
+		if(casecmp(ctype, labs[i].start, p) > 0)
+			nerror(labs[i].start, "unmatchable %s %V", title, labs[i].node);
+		for(e = i + 1; e < nlab; e++){
+			if(casecmp(ctype, labs[e].start, p) <= 0){
+				eprintlist(buf, buf+sizeof(buf), labs[e].node->left, " or ");
+				eprintlist(buf1, buf1+sizeof(buf1), labs[e-1].node->left, " or ");
+				nerror(labs[e].start,"%s '%s' overlaps with '%s' on line %L",
+					title, buf, buf1, p->src.start);
+			}
+
+			/*
+			 * check for merging case labels
+			 */
+			if(ctype != tint
+			|| labs[e].start->val != p->val+1
+			|| labs[e].node != labs[i].node)
+				break;
+			p = labs[e].stop;
+		}
+		if(e != i + 1){
+			labs[i].stop = p;
+			memmove(&labs[i+1], &labs[e], (nlab-e) * sizeof *labs);
+			nlab -= e - (i + 1);
+		}
+	}
+	free(aux);
+
+	c = allocmem(sizeof *c);
+	c->nlab = nlab;
+	c->nsnd = 0;
+	c->labs = labs;
+	c->wild = wild;
+
+	return c;
+}
+
+static int
+matchcmp(Node *na, Node *nb)
+{
+	Sym *a, *b;
+	int sa, sb;
+
+	a = na->decl->sym;
+	b = nb->decl->sym;
+	sa = a->len > 0 && a->name[a->len-1] == '*';
+	sb = b->len > 0 && b->name[b->len-1] == '*';
+	if(sa){
+		if(sb){
+			if(a->len == b->len)
+				return symcmp(a, b);
+			return b->len-a->len;
+		}
+		else
+			return 1;
+	}
+	else{
+		if(sb)
+			return -1;
+		else{
+			if(na->ty == tstring){
+				if(nb->ty == tstring)
+					return symcmp(a, b);
+				else
+					return 1;
+			}
+			else{
+				if(nb->ty == tstring)
+					return -1;
+				else
+					return symcmp(a, b);
+			}
+		}
+	}
+}
+
+int
+casecmp(Type *ty, Node *a, Node *b)
+{
+	if(ty == tint || ty == tbig){
+		if(a->val < b->val)
+			return -1;
+		if(a->val > b->val)
+			return 1;
+		return 0;
+	}
+	if(ty == texception)
+		return matchcmp(a, b);
+	return symcmp(a->decl->sym, b->decl->sym);
+}
+
+void
+casesort(Type *t, Label *aux, Label *labs, int start, int stop)
+{
+	int n, top, mid, base;
+
+	n = stop - start;
+	if(n <= 1)
+		return;
+	top = mid = start + n / 2;
+
+	casesort(t, aux, labs, start, top);
+	casesort(t, aux, labs, mid, stop);
+
+	/*
+	 * merge together two sorted label arrays, yielding a sorted array
+	 */
+	n = 0;
+	base = start;
+	while(base < top && mid < stop){
+		if(casecmp(t, labs[base].start, labs[mid].start) <= 0)
+			aux[n++] = labs[base++];
+		else
+			aux[n++] = labs[mid++];
+	}
+	if(base < top)
+		memmove(&aux[n], &labs[base], (top-base) * sizeof *aux);
+	else if(mid < stop)
+		memmove(&aux[n], &labs[mid], (stop-mid) * sizeof *aux);
+	memmove(&labs[start], &aux[0], (stop-start) * sizeof *labs);
+}
+
+/*
+ * binary search for the label corresponding to a given value
+ */
+int
+findlab(Type *ty, Node *v, Label *labs, int nlab)
+{
+	int l, r, m;
+
+	if(nlab <= 1)
+		return 0;
+	l = 1;
+	r = nlab - 1;
+	while(l <= r){
+		m = (r + l) / 2;
+		if(casecmp(ty, labs[m].start, v) <= 0)
+			l = m + 1;
+		else
+			r = m - 1;
+	}
+	m = l - 1;
+	if(casecmp(ty, labs[m].start, v) > 0
+	|| casecmp(ty, labs[m].stop, v) < 0)
+		fatal("findlab out of range");
+	return m;
+}
+
+void
+altcheck(Node *an, Type *ret)
+{
+	Node *n, *q, *left, *op, *wild;
+	Case *c;
+	int ok, nsnd, nrcv;
+
+	an->left = scheck(an->left, ret, Sother);
+
+	ok = 1;
+	nsnd = 0;
+	nrcv = 0;
+	wild = nil;
+	for(n = an->left; n != nil; n = n->right){
+		q = n->left->right->left;
+		if(n->left->right->right == nil)
+			nwarn(q, "no body for alt guard %V", q);
+		for(; q != nil; q = q->right){
+			left = q->left;
+			switch(left->op){
+			case Owild:
+				if(wild != nil)
+					nerror(left, "alt guard * duplicated on line %L", wild->src.start);
+				wild = left;
+				break;
+			case Orange:
+				nerror(left, "alt guard %V is illegal", left);
+				ok = 0;
+				break;
+			default:
+				op = hascomm(left);
+				if(op == nil){
+					nerror(left, "alt guard %V has no communication", left);
+					ok = 0;
+					break;
+				}
+				if(op->op == Osnd)
+					nsnd++;
+				else
+					nrcv++;
+				break;
+			}
+		}
+	}
+
+	if(!ok)
+		return;
+
+	c = allocmem(sizeof *c);
+	c->nlab = nsnd + nrcv;
+	c->nsnd = nsnd;
+	c->wild = wild;
+
+	an->ty = mktalt(c);
+}
+
+Node*
+hascomm(Node *n)
+{
+	Node *r;
+
+	if(n == nil)
+		return nil;
+	if(n->op == Osnd || n->op == Orcv)
+		return n;
+	r = hascomm(n->left);
+	if(r != nil)
+		return r;
+	return hascomm(n->right);
+}
+
+void
+raisescheck(Type *t)
+{
+	Node *n, *nn;
+	Ok ok;
+
+	if(t->kind != Tfn)
+		return;
+	n = t->u.eraises;
+	for(nn = n->left; nn != nil; nn = nn->right){
+		ok = echeck(nn->left, 0, 0, nil);
+		if(ok.ok && nn->left->ty->kind != Texception)
+			nerror(n, "%V: illegal raises expression", nn->left);
+	}
+}
+
+typedef struct Elist Elist;
+
+struct Elist{
+	Decl *d;
+	Elist *nxt;
+};
+
+static Elist*
+emerge(Elist *el1, Elist *el2)
+{
+	int f;
+	Elist *el, *nxt;
+
+	for( ; el1 != nil; el1 = nxt){
+		f = 0;
+		for(el = el2; el != nil; el = el->nxt){
+			if(el1->d == el->d){
+				f = 1;
+				break;
+			}
+		}
+		nxt = el1->nxt;
+		if(!f){
+			el1->nxt = el2;
+			el2 = el1;
+		}
+	}
+	return el2;
+}
+
+static Elist*
+equals(Node *n)
+{
+	Node *q, *nn;
+	Elist *e, *el;
+
+	el = nil;
+	for(q = n->left->left; q != nil; q = q->right){
+		nn = q->left;
+		if(nn->op == Owild)
+			return nil;
+		if(nn->ty->kind != Texception)
+			continue;
+		e = (Elist*)malloc(sizeof(Elist));
+		e->d = nn->decl;
+		e->nxt = el;
+		el = e;
+	}
+	return el;
+}
+
+static int
+caught(Decl *d, Node *n)
+{
+	Node *q, *nn;
+
+	for(n = n->right; n != nil; n = n->right){
+		for(q = n->left->left; q != nil; q = q->right){
+			nn = q->left;
+			if(nn->op == Owild)
+				return 1;
+			if(nn->ty->kind != Texception)
+				continue;
+			if(d == nn->decl)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static Elist*
+raisecheck(Node *n, Elist *ql)
+{
+	int exc;
+	Node *e;
+	Elist *el, *nel, *nxt;
+
+	if(n == nil)
+		return nil;
+	el = nil;
+	for(; n != nil; n = n->right){
+		switch(n->op){
+		case Oscope:
+			return raisecheck(n->right, ql);
+		case Olabel:
+		case Odo:
+			return raisecheck(n->right, ql);
+		case Oif:
+		case Ofor:
+			return emerge(raisecheck(n->right->left, ql),
+					        raisecheck(n->right->right, ql));
+		case Oalt:
+		case Ocase:
+		case Opick:
+		case Oexcept:
+			exc = n->op == Oexcept;
+			for(n = n->right; n != nil; n = n->right){
+				ql = nil;
+				if(exc)
+					ql = equals(n);
+				el = emerge(raisecheck(n->left->right, ql), el);
+			}
+			return el;
+		case Oseq:
+			el = emerge(raisecheck(n->left, ql), el);
+			break;
+		case Oexstmt:
+			el = raisecheck(n->left, ql);
+			nel = nil;
+			for( ; el != nil; el = nxt){
+				nxt = el->nxt;
+				if(!caught(el->d, n->right)){
+					el->nxt = nel;
+					nel = el;
+				}
+			}		
+			return emerge(nel, raisecheck(n->right, ql));
+		case Oraise:
+			e = n->left;
+			if(e->ty && e->ty->kind == Texception){
+				if(!e->ty->cons)
+					return ql;
+				if(e->op == Ocall)
+					e = e->left;
+				if(e->op == Omdot)
+					e = e->right;
+				if(e->op != Oname)
+					fatal("exception %n not a name", e);
+				el = (Elist*)malloc(sizeof(Elist));
+				el->d = e->decl;
+				el->nxt = nil;
+				return el;
+			}
+			return nil;
+		default:
+			return nil;
+		}
+	}
+	return el;
+}
+
+void
+checkraises(Node *n)
+{
+	int f;
+	Decl *d;
+	Elist *e, *el;
+	Node *es, *nn;
+
+	el = raisecheck(n->right, nil);
+	es = n->ty->u.eraises;
+	if(es != nil){
+		for(nn = es->left; nn != nil; nn = nn->right){
+			d = nn->left->decl;
+			f = 0;
+			for(e = el; e != nil; e = e->nxt){
+				if(d == e->d){
+					f = 1;
+					e->d = nil;
+					break;
+				}
+			}
+			if(!f)
+				nwarn(n, "function %V does not raise %s but declared", n->left, d->sym->name);
+		}
+	}
+	for(e = el; e != nil; e = e->nxt)
+		if(e->d != nil)
+			nwarn(n, "function %V raises %s but not declared", n->left, e->d->sym->name);
+}
+
+/* sort all globals in modules now that we've finished with 'last' pointers
+ * and before any code generation
+ */
+void
+gsort(Node *n)
+{
+	for(;;){
+		if(n == nil)
+			return;
+		if(n->op != Oseq)
+			break;
+		gsort(n->left);
+		n = n->right;
+	}
+	if(n->op == Omoddecl && n->ty->ok & OKverify){
+		n->ty->ids = namesort(n->ty->ids);
+		sizeids(n->ty->ids, 0);
+	}
+}
--- /dev/null
+++ b/limbo/types.c
@@ -1,0 +1,4745 @@
+#include "limbo.h"
+#include "mp.h"
+#include "libsec.h"
+
+char *kindname[Tend] =
+{
+	/* Tnone */	"no type",
+	/* Tadt */	"adt",
+	/* Tadtpick */	"adt",
+	/* Tarray */	"array",
+	/* Tbig */	"big",
+	/* Tbyte */	"byte",
+	/* Tchan */	"chan",
+	/* Treal */	"real",
+	/* Tfn */	"fn",
+	/* Tint */	"int",
+	/* Tlist */	"list",
+	/* Tmodule */	"module",
+	/* Tref */	"ref",
+	/* Tstring */	"string",
+	/* Ttuple */	"tuple",
+	/* Texception */	"exception",
+	/* Tfix */	"fixed point",
+	/* Tpoly */	"polymorphic",
+
+	/* Tainit */	"array initializers",
+	/* Talt */	"alt channels",
+	/* Tany */	"polymorphic type",
+	/* Tarrow */	"->",
+	/* Tcase */	"case int labels",
+	/* Tcasel */	"case big labels",
+	/* Tcasec */	"case string labels",
+	/* Tdot */	".",
+	/* Terror */	"type error",
+	/* Tgoto */	"goto labels",
+	/* Tid */	"id",
+	/* Tiface */	"module interface",
+	/* Texcept */	"exception handler table",
+	/* Tinst */	"instantiated type",
+};
+
+Tattr tattr[Tend] =
+{
+	/*		 isptr	refable	conable	big	vis */
+	/* Tnone */	{ 0,	0,	0,	0,	0, },
+	/* Tadt */	{ 0,	1,	1,	1,	1, },
+	/* Tadtpick */	{ 0,	1,	0,	1,	1, },
+	/* Tarray */	{ 1,	0,	0,	0,	1, },
+	/* Tbig */	{ 0,	0,	1,	1,	1, },
+	/* Tbyte */	{ 0,	0,	1,	0,	1, },
+	/* Tchan */	{ 1,	0,	0,	0,	1, },
+	/* Treal */	{ 0,	0,	1,	1,	1, },
+	/* Tfn */	{ 0,	1,	0,	0,	1, },
+	/* Tint */	{ 0,	0,	1,	0,	1, },
+	/* Tlist */	{ 1,	0,	0,	0,	1, },
+	/* Tmodule */	{ 1,	0,	0,	0,	1, },
+	/* Tref */	{ 1,	0,	0,	0,	1, },
+	/* Tstring */	{ 1,	0,	1,	0,	1, },
+	/* Ttuple */	{ 0,	1,	1,	1,	1, },
+	/* Texception */	{ 0,	0,	0,	1,	1, },
+	/* Tfix */	{ 0,	0,	1,	0,	1, },
+	/* Tpoly */	{ 1,	0,	0,	0,	1, },
+
+	/* Tainit */	{ 0,	0,	0,	1,	0, },
+	/* Talt */	{ 0,	0,	0,	1,	0, },
+	/* Tany */	{ 1,	0,	0,	0,	0, },
+	/* Tarrow */	{ 0,	0,	0,	0,	1, },
+	/* Tcase */	{ 0,	0,	0,	1,	0, },
+	/* Tcasel */	{ 0,	0,	0,	1,	0, },
+	/* Tcasec */	{ 0,	0,	0,	1,	0, },
+	/* Tdot */	{ 0,	0,	0,	0,	1, },
+	/* Terror */	{ 0,	1,	1,	0,	0, },
+	/* Tgoto */	{ 0,	0,	0,	1,	0, },
+	/* Tid */	{ 0,	0,	0,	0,	1, },
+	/* Tiface */	{ 0,	0,	0,	1,	0, },
+	/* Texcept */	{ 0,	0,	0,	1,	0, },
+	/* Tinst */	{ 0,	1,	1,	1,	1, },
+};
+
+static	Teq	*eqclass[Tend];
+
+static	Type	ztype;
+static	int	eqrec;
+static	int	eqset;
+static	int	tcomset;
+
+static	int	idcompat(Decl*, Decl*, int, int);
+static	int	rtcompat(Type *t1, Type *t2, int any, int);
+static	int	assumeteq(Type *t1, Type *t2);
+static	int	assumetcom(Type *t1, Type *t2);
+static	int	cleartcomrec(Type *t);
+static	int	rtequal(Type*, Type*);
+static	int	cleareqrec(Type*);
+static	int	idequal(Decl*, Decl*, int, int*);
+static	int	pyequal(Type*, Type*);
+static	int	rtsign(Type*, uchar*, int, int);
+static	int	clearrec(Type*);
+static	int	idsign(Decl*, int, uchar*, int, int);
+static	int	idsign1(Decl*, int, uchar*, int, int);
+static	int	raisessign(Node *n, uchar *sig, int lensig, int spos);
+static	void	ckfix(Type*, double);
+static	int	fnunify(Type*, Type*, Tpair**, int);
+static	int	rtunify(Type*, Type*, Tpair**, int);
+static	int	idunify(Decl*, Decl*, Tpair**, int);
+static	int	toccurs(Type*, Tpair**);
+static	int	fncleareqrec(Type*, Type*);
+static	Type*	comtype(Src*, Type*, Decl*);
+static	Type*	duptype(Type*);
+static	int	tpolys(Type*);
+
+static void
+addtmap(Type *t1, Type *t2, Tpair **tpp)
+{
+	Tpair *tp;
+
+	tp = allocmem(sizeof *tp);
+	tp->t1 = t1;
+	tp->t2 = t2;
+	tp->nxt = *tpp;
+	*tpp = tp;
+}
+
+Type*
+valtmap(Type *t, Tpair *tp)
+{
+	for( ; tp != nil; tp = tp->nxt)
+		if(tp->t1 == t)
+			return tp->t2;
+	return t;
+}
+
+Typelist*
+addtype(Type *t, Typelist *hd)
+{
+	Typelist *tl, *p;
+
+	tl = allocmem(sizeof(*tl));
+	tl->t = t;
+	tl->nxt = nil;
+	if(hd == nil)
+		return tl;
+	for(p = hd; p->nxt != nil; p = p->nxt)
+		;
+	p->nxt = tl;
+	return hd;
+}
+
+void
+typeinit(void)
+{
+	Decl *id;
+
+	anontupsym = enter(".tuple", 0);
+
+	ztype.sbl = -1;
+	ztype.ok = 0;
+	ztype.rec = 0;
+
+	tbig = mktype(&noline, &noline, Tbig, nil, nil);
+	tbig->size = IBY2LG;
+	tbig->align = IBY2LG;
+	tbig->ok = OKmask;
+
+	tbyte = mktype(&noline, &noline, Tbyte, nil, nil);
+	tbyte->size = 1;
+	tbyte->align = 1;
+	tbyte->ok = OKmask;
+
+	tint = mktype(&noline, &noline, Tint, nil, nil);
+	tint->size = IBY2WD;
+	tint->align = IBY2WD;
+	tint->ok = OKmask;
+
+	treal = mktype(&noline, &noline, Treal, nil, nil);
+	treal->size = IBY2FT;
+	treal->align = IBY2FT;
+	treal->ok = OKmask;
+
+	tstring = mktype(&noline, &noline, Tstring, nil, nil);
+	tstring->size = IBY2WD;
+	tstring->align = IBY2WD;
+	tstring->ok = OKmask;
+
+	texception = mktype(&noline, &noline, Texception, nil, nil);
+	texception->size = IBY2WD;
+	texception->align = IBY2WD;
+	texception->ok = OKmask;
+
+	tany = mktype(&noline, &noline, Tany, nil, nil);
+	tany->size = IBY2WD;
+	tany->align = IBY2WD;
+	tany->ok = OKmask;
+
+	tnone = mktype(&noline, &noline, Tnone, nil, nil);
+	tnone->size = 0;
+	tnone->align = 1;
+	tnone->ok = OKmask;
+
+	terror = mktype(&noline, &noline, Terror, nil, nil);
+	terror->size = 0;
+	terror->align = 1;
+	terror->ok = OKmask;
+
+	tunknown = mktype(&noline, &noline, Terror, nil, nil);
+	tunknown->size = 0;
+	tunknown->align = 1;
+	tunknown->ok = OKmask;
+
+	tfnptr = mktype(&noline, &noline, Ttuple, nil, nil);
+	id = tfnptr->ids = mkids(&nosrc, nil, tany, nil);
+	id->store = Dfield;
+	id->offset = 0;
+	id->sym = enter("t0", 0);
+	id->src = nosrc;
+	id = tfnptr->ids->next = mkids(&nosrc, nil, tint, nil);
+	id->store = Dfield;
+	id->offset = IBY2WD;
+	id->sym = enter("t1", 0);
+	id->src = nosrc;
+
+	rtexception = mktype(&noline, &noline, Tref, texception, nil);
+	rtexception->size = IBY2WD;
+	rtexception->align = IBY2WD;
+	rtexception->ok = OKmask;
+}
+
+void
+typestart(void)
+{
+	descriptors = nil;
+	nfns = 0;
+	nadts = 0;
+	selfdecl = nil;
+	if(tfnptr->decl != nil)
+		tfnptr->decl->desc = nil;
+
+	memset(eqclass, 0, sizeof eqclass);
+
+	typebuiltin(mkids(&nosrc, enter("int", 0), nil, nil), tint);
+	typebuiltin(mkids(&nosrc, enter("big", 0), nil, nil), tbig);
+	typebuiltin(mkids(&nosrc, enter("byte", 0), nil, nil), tbyte);
+	typebuiltin(mkids(&nosrc, enter("string", 0), nil, nil), tstring);
+	typebuiltin(mkids(&nosrc, enter("real", 0), nil, nil), treal);
+}
+
+Teq*
+modclass(void)
+{
+	return eqclass[Tmodule];
+}
+
+Type*
+mktype(Line *start, Line *stop, int kind, Type *tof, Decl *args)
+{
+	Type *t;
+
+	t = allocmem(sizeof *t);
+	*t = ztype;
+	t->src.start = *start;
+	t->src.stop = *stop;
+	t->kind = kind;
+	t->tof = tof;
+	t->ids = args;
+	return t;
+}
+
+Type*
+mktalt(Case *c)
+{
+	Type *t;
+	char buf[32];
+	static int nalt;
+
+	t = mktype(&noline, &noline, Talt, nil, nil);
+	t->decl = mkdecl(&nosrc, Dtype, t);
+	seprint(buf, buf+sizeof(buf), ".a%d", nalt++);
+	t->decl->sym = enter(buf, 0);
+	t->cse = c;
+	return usetype(t);
+}
+
+/*
+ * copy t and the top level of ids
+ */
+Type*
+copytypeids(Type *t)
+{
+	Type *nt;
+	Decl *id, *new, *last;
+
+	nt = allocmem(sizeof *nt);
+	*nt = *t;
+	last = nil;
+	for(id = t->ids; id != nil; id = id->next){
+		new = allocmem(sizeof *id);
+		*new = *id;
+		if(last == nil)
+			nt->ids = new;
+		else
+			last->next = new;
+		last = new;
+	}
+	return nt;
+}
+
+/*
+ * make each of the ids have type t
+ */
+Decl*
+typeids(Decl *ids, Type *t)
+{
+	Decl *id;
+
+	if(ids == nil)
+		return nil;
+
+	ids->ty = t;
+	for(id = ids->next; id != nil; id = id->next){
+		id->ty = t;
+	}
+	return ids;
+}
+
+void
+typebuiltin(Decl *d, Type *t)
+{
+	d->ty = t;
+	t->decl = d;
+	installids(Dtype, d);
+}
+
+Node *
+fielddecl(int store, Decl *ids)
+{
+	Node *n;
+
+	n = mkn(Ofielddecl, nil, nil);
+	n->decl = ids;
+	for(; ids != nil; ids = ids->next)
+		ids->store = store;
+	return n;
+}
+
+Node *
+typedecl(Decl *ids, Type *t)
+{
+	Node *n;
+
+	if(t->decl == nil)
+		t->decl = ids;
+	n = mkn(Otypedecl, nil, nil);
+	n->decl = ids;
+	n->ty = t;
+	for(; ids != nil; ids = ids->next)
+		ids->ty = t;
+	return n;
+}
+
+void
+typedecled(Node *n)
+{
+	installids(Dtype, n->decl);
+}
+
+Node *
+adtdecl(Decl *ids, Node *fields)
+{
+	Node *n;
+	Type *t;
+
+	n = mkn(Oadtdecl, nil, nil);
+	t = mktype(&ids->src.start, &ids->src.stop, Tadt, nil, nil);
+	n->decl = ids;
+	n->left = fields;
+	n->ty = t;
+	t->decl = ids;
+	for(; ids != nil; ids = ids->next)
+		ids->ty = t;
+	return n;
+}
+
+void
+adtdecled(Node *n)
+{
+	Decl *d, *ids;
+
+	d = n->ty->decl;
+	installids(Dtype, d);
+	if(n->ty->polys != nil){
+		pushscope(nil, Sother);
+		installids(Dtype, n->ty->polys);
+	}
+	pushscope(nil, Sother);
+	fielddecled(n->left);
+	n->ty->ids = popscope();
+	if(n->ty->polys != nil)
+		n->ty->polys = popscope();
+	for(ids = n->ty->ids; ids != nil; ids = ids->next)
+		ids->dot = d;
+}
+
+void
+fielddecled(Node *n)
+{
+	for(; n != nil; n = n->right){
+		switch(n->op){
+		case Oseq:
+			fielddecled(n->left);
+			break;
+		case Oadtdecl:
+			adtdecled(n);
+			return;
+		case Otypedecl:
+			typedecled(n);
+			return;
+		case Ofielddecl:
+			installids(Dfield, n->decl);
+			return;
+		case Ocondecl:
+			condecled(n);
+			gdasdecl(n->right);
+			return;
+		case Oexdecl:
+			exdecled(n);
+			return;
+		case Opickdecl:
+			pickdecled(n);
+			return;
+		default:
+			fatal("can't deal with %O in fielddecled", n->op);
+		}
+	}
+}
+
+int
+pickdecled(Node *n)
+{
+	Decl *d;
+	int tag;
+
+	if(n == nil)
+		return 0;
+	tag = pickdecled(n->left);
+	pushscope(nil, Sother);
+	fielddecled(n->right->right);
+	d = n->right->left->decl;
+	d->ty->ids = popscope();
+	installids(Dtag, d);
+	for(; d != nil; d = d->next)
+		d->tag = tag++;
+	return tag;
+}
+
+/*
+ * make the tuple type used to initialize adt t
+ */
+Type*
+mkadtcon(Type *t)
+{
+	Decl *id, *new, *last;
+	Type *nt;
+
+	nt = allocmem(sizeof *nt);
+	*nt = *t;
+	last = nil;
+	nt->ids = nil;
+	nt->kind = Ttuple;
+	for(id = t->ids; id != nil; id = id->next){
+		if(id->store != Dfield)
+			continue;
+		new = allocmem(sizeof *id);
+		*new = *id;
+		new->cyc = 0;
+		if(last == nil)
+			nt->ids = new;
+		else
+			last->next = new;
+		last = new;
+	}
+	last->next = nil;
+	return nt;
+}
+
+/*
+ * make the tuple type used to initialize t,
+ * an adt with pick fields tagged by tg
+ */
+Type*
+mkadtpickcon(Type *t, Type *tgt)
+{
+	Decl *id, *new, *last;
+	Type *nt;
+
+	last = mkids(&tgt->decl->src, nil, tint, nil);
+	last->store = Dfield;
+	nt = mktype(&t->src.start, &t->src.stop, Ttuple, nil, last);
+	for(id = t->ids; id != nil; id = id->next){
+		if(id->store != Dfield)
+			continue;
+		new = allocmem(sizeof *id);
+		*new = *id;
+		new->cyc = 0;
+		last->next = new;
+		last = new;
+	}
+	for(id = tgt->ids; id != nil; id = id->next){
+		if(id->store != Dfield)
+			continue;
+		new = allocmem(sizeof *id);
+		*new = *id;
+		new->cyc = 0;
+		last->next = new;
+		last = new;
+	}
+	last->next = nil;
+	return nt;
+}
+
+/*
+ * make an identifier type
+ */
+Type*
+mkidtype(Src *src, Sym *s)
+{
+	Type *t;
+
+	t = mktype(&src->start, &src->stop, Tid, nil, nil);
+	if(s->unbound == nil){
+		s->unbound = mkdecl(src, Dunbound, nil);
+		s->unbound->sym = s;
+	}
+	t->decl = s->unbound;
+	return t;
+}
+
+/*
+ * make a qualified type for t->s
+ */
+Type*
+mkarrowtype(Line *start, Line *stop, Type *t, Sym *s)
+{
+	Src src;
+
+	src.start = *start;
+	src.stop = *stop;
+	t = mktype(start, stop, Tarrow, t, nil);
+	if(s->unbound == nil){
+		s->unbound = mkdecl(&src, Dunbound, nil);
+		s->unbound->sym = s;
+	}
+	t->decl = s->unbound;
+	return t;
+}
+
+/*
+ * make a qualified type for t.s
+ */
+Type*
+mkdottype(Line *start, Line *stop, Type *t, Sym *s)
+{
+	Src src;
+
+	src.start = *start;
+	src.stop = *stop;
+	t = mktype(start, stop, Tdot, t, nil);
+	if(s->unbound == nil){
+		s->unbound = mkdecl(&src, Dunbound, nil);
+		s->unbound->sym = s;
+	}
+	t->decl = s->unbound;
+	return t;
+}
+
+Type*
+mkinsttype(Src* src, Type *tt, Typelist *tl)
+{
+	Type *t;
+
+	t = mktype(&src->start, &src->stop, Tinst, tt, nil);
+	t->u.tlist = tl;
+	return t;
+}
+
+/*
+ * look up the name f in the fields of a module, adt, or tuple
+ */
+Decl*
+namedot(Decl *ids, Sym *s)
+{
+	for(; ids != nil; ids = ids->next)
+		if(ids->sym == s)
+			return ids;
+	return nil;
+}
+
+/*
+ * complete the declaration of an adt
+ * methods frames get sized in module definition or during function definition
+ * place the methods at the end of the field list
+ */
+void
+adtdefd(Type *t)
+{
+	Decl *d, *id, *next, *aux, *store, *auxhd, *tagnext;
+	int seentags;
+
+	if(debug['x'])
+		print("adt %T defd\n", t);
+	d = t->decl;
+	tagnext = nil;
+	store = nil;
+	for(id = t->polys; id != nil; id = id->next){
+		id->store = Dtype;
+		id->ty = verifytypes(id->ty, d, nil);
+	}
+	for(id = t->ids; id != nil; id = next){
+		if(id->store == Dtag){
+			if(t->tags != nil)
+				error(id->src.start, "only one set of pick fields allowed");
+			tagnext = pickdefd(t, id);
+			next = tagnext;
+			if(store != nil)
+				store->next = next;
+			else
+				t->ids = next;
+			continue;
+		}else{
+			id->dot = d;
+			next = id->next;
+			store = id;
+		}
+	}
+	aux = nil;
+	store = nil;
+	auxhd = nil;
+	seentags = 0;
+	for(id = t->ids; id != nil; id = next){
+		if(id == tagnext)
+			seentags = 1;
+
+		next = id->next;
+		id->dot = d;
+		id->ty = topvartype(verifytypes(id->ty, d, nil), id, 1, 1);
+		if(id->store == Dfield && id->ty->kind == Tfn)
+			id->store = Dfn;
+		if(id->store == Dfn || id->store == Dconst){
+			if(store != nil)
+				store->next = next;
+			else
+				t->ids = next;
+			if(aux != nil)
+				aux->next = id;
+			else
+				auxhd = id;
+			aux = id;
+		}else{
+			if(seentags)
+				error(id->src.start, "pick fields must be the last data fields in an adt");
+			store = id;
+		}
+	}
+	if(aux != nil)
+		aux->next = nil;
+	if(store != nil)
+		store->next = auxhd;
+	else
+		t->ids = auxhd;
+
+	for(id = t->tags; id != nil; id = id->next){
+		id->ty = verifytypes(id->ty, d, nil);
+		if(id->ty->tof == nil)
+			id->ty->tof = mkadtpickcon(t, id->ty);
+	}
+}
+
+/*
+ * assemble the data structure for an adt with a pick clause.
+ * since the scoping rules for adt pick fields are strange,
+ * we have a customized check for overlapping definitions.
+ */
+Decl*
+pickdefd(Type *t, Decl *tg)
+{
+	Decl *id, *xid, *lasttg, *d;
+	Type *tt;
+	int tag;
+
+	lasttg = nil;
+	d = t->decl;
+	t->tags = tg;
+	tag = 0;
+	while(tg != nil){
+		tt = tg->ty;
+		if(tt->kind != Tadtpick || tg->tag != tag)
+			break;
+		tt->decl = tg;
+		lasttg = tg;
+		for(; tg != nil; tg = tg->next){
+			if(tg->ty != tt)
+				break;
+			tag++;
+			lasttg = tg;
+			tg->dot = d;
+		}
+		for(id = tt->ids; id != nil; id = id->next){
+			xid = namedot(t->ids, id->sym);
+			if(xid != nil)
+				error(id->src.start, "redeclaration of %K, previously declared as %k on line %L",
+					id, xid, xid->src.start);
+			id->dot = d;
+		}
+	}
+	if(lasttg == nil){
+		error(t->src.start, "empty pick field declaration in %T", t);
+		t->tags = nil;
+	}else
+		lasttg->next = nil;
+	d->tag = tag;
+	return tg;
+}
+
+Node*
+moddecl(Decl *ids, Node *fields)
+{
+	Node *n;
+	Type *t;
+
+	n = mkn(Omoddecl, mkn(Oseq, nil, nil), nil);
+	t = mktype(&ids->src.start, &ids->src.stop, Tmodule, nil, nil);
+	n->decl = ids;
+	n->left = fields;
+	n->ty = t;
+	return n;
+}
+
+void
+moddecled(Node *n)
+{
+	Decl *d, *ids, *im, *dot;
+	Type *t;
+	Sym *s;
+	char buf[StrSize];
+	int isimp;
+	Dlist *dm, *dl;
+
+	d = n->decl;
+	installids(Dtype, d);
+	isimp = 0;
+	for(ids = d; ids != nil; ids = ids->next){
+		for(im = impmods; im != nil; im = im->next){
+			if(ids->sym == im->sym){
+				isimp = 1;
+				d = ids;
+				dm = malloc(sizeof(Dlist));
+				dm->d = ids;
+				dm->next = nil;
+				if(impdecls == nil)
+					impdecls = dm;
+				else{
+					for(dl = impdecls; dl->next != nil; dl = dl->next)
+						;
+					dl->next = dm;
+				}
+			}
+		}
+		ids->ty = n->ty;
+	}
+	pushscope(nil, Sother);
+	fielddecled(n->left);
+
+	d->ty->ids = popscope();
+
+	/*
+	 * make the current module the -> parent of all contained decls->
+	 */
+	for(ids = d->ty->ids; ids != nil; ids = ids->next)
+		ids->dot = d;
+
+	t = d->ty;
+	t->decl = d;
+	if(debug['m'])
+		print("declare module %s\n", d->sym->name);
+
+	/*
+	 * add the iface declaration in case it's needed later
+	 */
+	seprint(buf, buf+sizeof(buf), ".m.%s", d->sym->name);
+	installids(Dglobal, mkids(&d->src, enter(buf, 0), tnone, nil));
+
+	if(isimp){
+		for(ids = d->ty->ids; ids != nil; ids = ids->next){
+			s = ids->sym;
+			if(s->decl != nil && s->decl->scope >= scope){
+				dot = s->decl->dot;
+				if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 0))
+					continue;
+				redecl(ids);
+				ids->old = s->decl->old;
+			}else
+				ids->old = s->decl;
+			s->decl = ids;
+			ids->scope = scope;
+		}
+	}
+}
+
+/*
+ * for each module in id,
+ * link by field ext all of the decls for
+ * functions needed in external linkage table
+ * collect globals and make a tuple for all of them
+ */
+Type*
+mkiface(Decl *m)
+{
+	Decl *iface, *last, *globals, *glast, *id, *d;
+	Type *t;
+	char buf[StrSize];
+
+	iface = last = allocmem(sizeof(Decl));
+	globals = glast = mkdecl(&m->src, Dglobal, mktype(&m->src.start, &m->src.stop, Tadt, nil, nil));
+	for(id = m->ty->ids; id != nil; id = id->next){
+		switch(id->store){
+		case Dglobal:
+			glast = glast->next = dupdecl(id);
+			id->iface = globals;
+			glast->iface = id;
+			break;
+		case Dfn:
+			id->iface = last = last->next = dupdecl(id);
+			last->iface = id;
+			break;
+		case Dtype:
+			if(id->ty->kind != Tadt)
+				break;
+			for(d = id->ty->ids; d != nil; d = d->next){
+				if(d->store == Dfn){
+					d->iface = last = last->next = dupdecl(d);
+					last->iface = d;
+				}
+			}
+			break;
+		}
+	}
+	last->next = nil;
+	iface = namesort(iface->next);
+
+	if(globals->next != nil){
+		glast->next = nil;
+		globals->ty->ids = namesort(globals->next);
+		globals->ty->decl = globals;
+		globals->sym = enter(".mp", 0);
+		globals->dot = m;
+		globals->next = iface;
+		iface = globals;
+	}
+
+	/*
+	 * make the interface type and install an identifier for it
+	 * the iface has a ref count if it is loaded
+	 */
+	t = mktype(&m->src.start, &m->src.stop, Tiface, nil, iface);
+	seprint(buf, buf+sizeof(buf), ".m.%s", m->sym->name);
+	id = enter(buf, 0)->decl;
+	t->decl = id;
+	id->ty = t;
+
+	/*
+	 * dummy node so the interface is initialized
+	 */
+	id->init = mkn(Onothing, nil, nil);
+	id->init->ty = t;
+	id->init->decl = id;
+	return t;
+}
+
+void
+joiniface(Type *mt, Type *t)
+{
+	Decl *id, *d, *iface, *globals;
+
+	iface = t->ids;
+	globals = iface;
+	if(iface != nil && iface->store == Dglobal)
+		iface = iface->next;
+	for(id = mt->tof->ids; id != nil; id = id->next){
+		switch(id->store){
+		case Dglobal:
+			for(d = id->ty->ids; d != nil; d = d->next)
+				d->iface->iface = globals;
+			break;
+		case Dfn:
+			id->iface->iface = iface;
+			iface = iface->next;
+			break;
+		default:
+			fatal("unknown store %k in joiniface", id);
+			break;
+		}
+	}
+	if(iface != nil)
+		fatal("join iface not matched");
+	mt->tof = t;
+}
+
+void
+addiface(Decl *m, Decl *d)
+{
+	Type *t;
+	Decl *id, *last, *dd, *lastorig;
+	Dlist *dl;
+
+	if(d == nil || !local(d))
+		return;
+	modrefable(d->ty);
+	if(m == nil){
+		if(impdecls->next != nil)
+			for(dl = impdecls; dl != nil; dl = dl->next)
+				if(dl->d->ty->tof != impdecl->ty->tof)	/* impdecl last */
+					addiface(dl->d, d);
+		addiface(impdecl, d);
+		return;
+	}
+	t = m->ty->tof;
+	last = nil;
+	lastorig = nil;
+	for(id = t->ids; id != nil; id = id->next){
+		if(d == id || d == id->iface)
+			return;
+		last = id;
+		if(id->tag == 0)
+			lastorig = id;
+	}
+	dd = dupdecl(d);
+	if(d->dot == nil)
+		d->dot = dd->dot = m;
+	d->iface = dd;
+	dd->iface = d;
+if(debug['v']) print("addiface %p %p\n", d, dd);
+	if(last == nil)
+		t->ids = dd;
+	else
+		last->next = dd;
+	dd->tag = 1;	/* mark so not signed */
+	if(lastorig == nil)
+		t->ids = namesort(t->ids);
+	else
+		lastorig->next = namesort(lastorig->next);
+}
+
+/*
+ * eliminate unused declarations from interfaces
+ * label offset within interface
+ */
+void
+narrowmods(void)
+{
+	Teq *eq;
+	Decl *id, *last;
+	Type *t;
+	long offset;
+
+	for(eq = modclass(); eq != nil; eq = eq->eq){
+		t = eq->ty->tof;
+
+		if(t->linkall == 0){
+			last = nil;
+			for(id = t->ids; id != nil; id = id->next){
+				if(id->refs == 0){
+					if(last == nil)
+						t->ids = id->next;
+					else
+						last->next = id->next;
+				}else
+					last = id;
+			}
+
+			/*
+			 * need to resize smaller interfaces
+			 */
+			resizetype(t);
+		}
+
+		offset = 0;
+		for(id = t->ids; id != nil; id = id->next)
+			id->offset = offset++;
+
+		/*
+		 * rathole to stuff number of entries in interface
+		 */
+		t->decl->init->val = offset;
+	}
+}
+
+/*
+ * check to see if any data field of module m if referenced.
+ * if so, mark all data in m
+ */
+void
+moddataref(void)
+{
+	Teq *eq;
+	Decl *id;
+
+	for(eq = modclass(); eq != nil; eq = eq->eq){
+		id = eq->ty->tof->ids;
+		if(id != nil && id->store == Dglobal && id->refs)
+			for(id = eq->ty->ids; id != nil; id = id->next)
+				if(id->store == Dglobal)
+					modrefable(id->ty);
+	}
+}
+
+/*
+ * move the global declarations in interface to the front
+ */
+Decl*
+modglobals(Decl *mod, Decl *globals)
+{
+	Decl *id, *head, *last;
+
+	/*
+	 * make a copy of all the global declarations
+	 * 	used for making a type descriptor for globals ONLY
+	 * note we now have two declarations for the same variables,
+	 * which is apt to cause problems if code changes
+	 *
+	 * here we fix up the offsets for the real declarations
+	 */
+	idoffsets(mod->ty->ids, 0, 1);
+
+	last = head = allocmem(sizeof(Decl));
+	for(id = mod->ty->ids; id != nil; id = id->next)
+		if(id->store == Dglobal)
+			last = last->next = dupdecl(id);
+
+	last->next = globals;
+	return head->next;
+}
+
+/*
+ * snap all id type names to the actual type
+ * check that all types are completely defined
+ * verify that the types look ok
+ */
+Type*
+validtype(Type *t, Decl *inadt)
+{
+	if(t == nil)
+		return t;
+	bindtypes(t);
+	t = verifytypes(t, inadt, nil);
+	cycsizetype(t);
+	teqclass(t);
+	return t;
+}
+
+Type*
+usetype(Type *t)
+{
+	if(t == nil)
+		return t;
+	t = validtype(t, nil);
+	reftype(t);
+	return t;
+}
+
+Type*
+internaltype(Type *t)
+{
+	bindtypes(t);
+	t->ok = OKverify;
+	sizetype(t);
+	t->ok = OKmask;
+	return t;
+}
+
+/*
+ * checks that t is a valid top-level type
+ */
+Type*
+topvartype(Type *t, Decl *id, int tyok, int polyok)
+{
+	if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+		error(id->src.start, "cannot declare %s with type %T", id->sym->name, t);
+	if(!tyok && t->kind == Tfn)
+		error(id->src.start, "cannot declare %s to be a function", id->sym->name);
+	if(!polyok && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
+		error(id->src.start, "cannot declare %s of a polymorphic type", id->sym->name);
+	return t;
+}
+
+Type*
+toptype(Src *src, Type *t)
+{
+	if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+		error(src->start, "%T, an adt with pick fields, must be used with ref", t);
+	if(t->kind == Tfn)
+		error(src->start, "data cannot have a fn type like %T", t);
+	return t;
+}
+
+static Type*
+comtype(Src *src, Type *t, Decl* adtd)
+{
+	if(adtd == nil && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
+		error(src->start, "polymorphic type %T illegal here", t);
+	return t;
+}
+
+void
+usedty(Type *t)
+{
+	if(t != nil && (t->ok | OKmodref) != OKmask)
+		fatal("used ty %t %2.2ux", t, t->ok);
+}
+
+void
+bindtypes(Type *t)
+{
+	Decl *id;
+	Typelist *tl;
+
+	if(t == nil)
+		return;
+	if((t->ok & OKbind) == OKbind)
+		return;
+	t->ok |= OKbind;
+	switch(t->kind){
+	case Tadt:
+		if(t->polys != nil){
+			pushscope(nil, Sother);
+			installids(Dtype, t->polys);
+		}
+		if(t->val != nil)
+			mergepolydecs(t);
+		if(t->polys != nil){
+			popscope();
+			for(id = t->polys; id != nil; id = id->next)
+				bindtypes(id->ty);
+		}
+		break;
+	case Tadtpick:
+	case Tmodule:
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tiface:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Texcept:
+	case Tfix:
+	case Tpoly:
+		break;
+	case Tarray:
+	case Tarrow:
+	case Tchan:
+	case Tdot:
+	case Tlist:
+	case Tref:
+		bindtypes(t->tof);
+		break;
+	case Tid:
+		id = t->decl->sym->decl;
+		if(id == nil)
+			id = undefed(&t->src, t->decl->sym);
+		/* save a little space */
+		id->sym->unbound = nil;
+		t->decl = id;
+		break;
+	case Ttuple:
+	case Texception:
+		for(id = t->ids; id != nil; id = id->next)
+			bindtypes(id->ty);
+		break;
+	case Tfn:
+		if(t->polys != nil){
+			pushscope(nil, Sother);
+			installids(Dtype, t->polys);
+		}
+		for(id = t->ids; id != nil; id = id->next)
+			bindtypes(id->ty);
+		bindtypes(t->tof);
+		if(t->val != nil)
+			mergepolydecs(t);
+		if(t->polys != nil){
+			popscope();
+			for(id = t->polys; id != nil; id = id->next)
+				bindtypes(id->ty);
+		}
+		break;
+	case Tinst:
+		bindtypes(t->tof);
+		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+			bindtypes(tl->t);
+		break;
+	default:
+		fatal("bindtypes: unknown type kind %d", t->kind);
+	}
+}
+
+/*
+ * walk the type checking for validity
+ */
+Type*
+verifytypes(Type *t, Decl *adtt, Decl *poly)
+{
+	Node *n;
+	Decl *id, *id1, *last;
+	char buf[32];
+	int i, cyc;
+	Ok ok, ok1;
+	double max;
+	Typelist *tl;
+
+	if(t == nil)
+		return nil;
+	if((t->ok & OKverify) == OKverify)
+		return t;
+	t->ok |= OKverify;
+if((t->ok & (OKverify|OKbind)) != (OKverify|OKbind))
+fatal("verifytypes bogus ok for %t", t);
+	cyc = t->flags&CYCLIC;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tiface:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Texcept:
+		break;
+	case Tfix:
+		n = t->val;
+		max = 0.0;
+		if(n->op == Oseq){
+			ok = echeck(n->left, 0, 0, n);
+			ok1 = echeck(n->right, 0, 0, n);
+			if(!ok.ok || !ok1.ok)
+				return terror;
+			if(n->left->ty != treal || n->right->ty != treal){
+				error(t->src.start, "fixed point scale/maximum not real");
+				return terror;
+			}
+			n->right = fold(n->right);
+			if(n->right->op != Oconst){
+				error(t->src.start, "fixed point maximum not constant");
+				return terror;
+			}
+			if((max = n->right->rval) <= 0){
+				error(t->src.start, "non-positive fixed point maximum");
+				return terror;
+			}
+			n = n->left;
+		}
+		else{
+			ok = echeck(n, 0, 0, nil);
+			if(!ok.ok)
+				return terror;
+			if(n->ty != treal){
+				error(t->src.start, "fixed point scale not real");
+				return terror;
+			}
+		}
+		n = t->val = fold(n);
+		if(n->op != Oconst){
+			error(t->src.start, "fixed point scale not constant");
+			return terror;
+		}
+		if(n->rval <= 0){
+			error(t->src.start, "non-positive fixed point scale");
+			return terror;
+		}
+		ckfix(t, max);
+		break;
+	case Tref:
+		t->tof = comtype(&t->src, verifytypes(t->tof, adtt, nil), adtt);
+		if(t->tof != nil && !tattr[t->tof->kind].refable){
+			error(t->src.start, "cannot have a ref %T", t->tof);
+			return terror;
+		}
+		if(0 && t->tof->kind == Tfn && t->tof->ids != nil && t->tof->ids->implicit)
+			error(t->src.start, "function references cannot have a self argument");
+		if(0 && t->tof->kind == Tfn && t->polys != nil)
+			error(t->src.start, "function references cannot be polymorphic");
+		break;
+	case Tchan:
+	case Tarray:
+	case Tlist:
+		t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt);
+		break;
+	case Tid:
+		t->ok &= ~OKverify;
+		t = verifytypes(idtype(t), adtt, nil);
+		break;
+	case Tarrow:
+		t->ok &= ~OKverify;
+		t = verifytypes(arrowtype(t, adtt), adtt, nil);
+		break;
+	case Tdot:
+		/*
+		 * verify the parent adt & lookup the tag fields
+		 */
+		t->ok &= ~OKverify;
+		t = verifytypes(dottype(t, adtt), adtt, nil);
+		break;
+	case Tadt:
+		/*
+		 * this is where Tadt may get tag fields added
+		 */
+		adtdefd(t);
+		break;
+	case Tadtpick:
+		for(id = t->ids; id != nil; id = id->next){
+			id->ty = topvartype(verifytypes(id->ty, id->dot, nil), id, 0, 1);
+			if(id->store == Dconst)
+				error(t->src.start, "pick fields cannot be a con like %s", id->sym->name);
+		}
+		verifytypes(t->decl->dot->ty, nil, nil);
+		break;
+	case Tmodule:
+		for(id = t->ids; id != nil; id = id->next){
+			id->ty = verifytypes(id->ty, nil, nil);
+			if(id->store == Dglobal && id->ty->kind == Tfn)
+				id->store = Dfn;
+			if(id->store != Dtype && id->store != Dfn)
+				topvartype(id->ty, id, 0, 0);
+		}
+ 		break;
+	case Ttuple:
+	case Texception:
+		if(t->decl == nil){
+			t->decl = mkdecl(&t->src, Dtype, t);
+			t->decl->sym = enter(".tuple", 0);
+		}
+		i = 0;
+		for(id = t->ids; id != nil; id = id->next){
+			id->store = Dfield;
+			if(id->sym == nil){
+				seprint(buf, buf+sizeof(buf), "t%d", i);
+				id->sym = enter(buf, 0);
+			}
+			i++;
+			id->ty = toptype(&id->src, verifytypes(id->ty, adtt, nil));
+			/* id->ty = comtype(&id->src, toptype(&id->src, verifytypes(id->ty, adtt, nil)), adtt); */
+		}
+		break;
+	case Tfn:
+		last = nil;
+		for(id = t->ids; id != nil; id = id->next){
+			id->store = Darg;
+			id->ty = topvartype(verifytypes(id->ty, adtt, nil), id, 0, 1);
+			if(id->implicit){
+				Decl *selfd;
+
+				selfd = poly ? poly : adtt;
+				if(selfd == nil)
+					error(t->src.start, "function is not a member of an adt, so can't use self");
+				else if(id != t->ids)
+					error(id->src.start, "only the first argument can use self");
+				else if(id->ty != selfd->ty && (id->ty->kind != Tref || id->ty->tof != selfd->ty))
+					error(id->src.start, "self argument's type must be %s or ref %s",
+						selfd->sym->name, selfd->sym->name);
+			}
+			last = id;
+		}
+		for(id = t->polys; id != nil; id = id->next){
+			if(adtt != nil){
+				for(id1 = adtt->ty->polys; id1 != nil; id1 = id1->next){
+					if(id1->sym == id->sym)
+						id->ty = id1->ty;
+				}
+			}
+			id->store = Dtype;
+			id->ty = verifytypes(id->ty, adtt, nil);
+		}
+		t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt);
+		if(t->varargs && (last == nil || last->ty != tstring))
+			error(t->src.start, "variable arguments must be preceded by a string");
+		if(t->varargs && t->polys != nil)
+			error(t->src.start, "polymorphic functions must not have variable arguments");
+		break;
+	case Tpoly:
+		for(id = t->ids; id != nil; id = id->next){
+			id->store = Dfn;
+			id->ty = verifytypes(id->ty, adtt, t->decl);
+		}
+		break;
+	case Tinst:
+		t->ok &= ~OKverify;
+		t->tof = verifytypes(t->tof, adtt, nil);
+		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+			tl->t = verifytypes(tl->t, adtt, nil);
+		t = verifytypes(insttype(t, adtt, nil), adtt, nil);
+		break;
+	default:
+		fatal("verifytypes: unknown type kind %d", t->kind);
+	}
+	if(cyc)
+		t->flags |= CYCLIC;
+	return t;
+}
+
+/*
+ * resolve an id type
+ */
+Type*
+idtype(Type *t)
+{
+	Decl *id;
+	Type *tt;
+
+	id = t->decl;
+	if(id->store == Dunbound)
+		fatal("idtype: unbound decl");
+	tt = id->ty;
+	if(id->store != Dtype && id->store != Dtag){
+		if(id->store == Dundef){
+			id->store = Dwundef;
+			error(t->src.start, "%s is not declared", id->sym->name);
+		}else if(id->store == Dimport){
+			id->store = Dwundef;
+			error(t->src.start, "%s's type cannot be determined", id->sym->name);
+		}else if(id->store != Dwundef)
+			error(t->src.start, "%s is not a type", id->sym->name);
+		return terror;
+	}
+	if(tt == nil){
+		error(t->src.start, "%t not fully defined", t);
+		return terror;
+	}
+	return tt;
+}
+
+/*
+ * resolve a -> qualified type
+ */
+Type*
+arrowtype(Type *t, Decl *adtt)
+{
+	Type *tt;
+	Decl *id;
+
+	id = t->decl;
+	if(id->ty != nil){
+		if(id->store == Dunbound)
+			fatal("arrowtype: unbound decl has a type");
+		return id->ty;
+	}
+
+	/*
+	 * special hack to allow module variables to derive other types
+	 */ 
+	tt = t->tof;
+	if(tt->kind == Tid){
+		id = tt->decl;
+		if(id->store == Dunbound)
+			fatal("arrowtype: Tid's decl unbound");
+		if(id->store == Dimport){
+			id->store = Dwundef;
+			error(t->src.start, "%s's type cannot be determined", id->sym->name);
+			return terror;
+		}
+
+		/*
+		 * forward references to module variables can't be resolved
+		 */
+		if(id->store != Dtype && !(id->ty->ok & OKbind)){
+			error(t->src.start, "%s's type cannot be determined", id->sym->name);
+			return terror;
+		}
+
+		if(id->store == Dwundef)
+			return terror;
+		tt = id->ty = verifytypes(id->ty, adtt, nil);
+		if(tt == nil){
+			error(t->tof->src.start, "%T is not a module", t->tof);
+			return terror;
+		}
+	}else
+		tt = verifytypes(t->tof, adtt, nil);
+	t->tof = tt;
+	if(tt == terror)
+		return terror;
+	if(tt->kind != Tmodule){
+		error(t->src.start, "%T is not a module", tt);
+		return terror;
+	}
+	id = namedot(tt->ids, t->decl->sym);
+	if(id == nil){
+		error(t->src.start, "%s is not a member of %T", t->decl->sym->name, tt);
+		return terror;
+	}
+	if(id->store == Dtype && id->ty != nil){
+		t->decl = id;
+		return id->ty;
+	}
+	error(t->src.start, "%T is not a type", t);
+	return terror;
+}
+
+/*
+ * resolve a . qualified type
+ */
+Type*
+dottype(Type *t, Decl *adtt)
+{
+	Type *tt;
+	Decl *id;
+
+	if(t->decl->ty != nil){
+		if(t->decl->store == Dunbound)
+			fatal("dottype: unbound decl has a type");
+		return t->decl->ty;
+	}
+	t->tof = tt = verifytypes(t->tof, adtt, nil);
+	if(tt == terror)
+		return terror;
+	if(tt->kind != Tadt){
+		error(t->src.start, "%T is not an adt", tt);
+		return terror;
+	}
+	id = namedot(tt->tags, t->decl->sym);
+	if(id != nil && id->ty != nil){
+		t->decl = id;
+		return id->ty;
+	}
+	error(t->src.start, "%s is not a pick tag of %T", t->decl->sym->name, tt);
+	return terror;
+}
+
+Type*
+insttype(Type *t, Decl *adtt, Tpair **tp)
+{
+	Type *tt;
+	Typelist *tl;
+	Decl *ids;
+	Tpair *tp1, *tp2;
+	Src src;
+
+	src = t->src;
+	if(tp == nil){
+		tp2 = nil;
+		tp = &tp2;
+	}
+	if(t->tof->kind != Tadt && t->tof->kind != Tadtpick){
+		error(src.start, "%T is not an adt", t->tof);
+		return terror;
+	}
+	if(t->tof->kind == Tadt)
+		ids = t->tof->polys;
+	else
+		ids = t->tof->decl->dot->ty->polys;
+	if(ids == nil){
+		error(src.start, "%T is not a polymorphic adt", t->tof);
+		return terror;
+	}
+	for(tl = t->u.tlist; tl != nil && ids != nil; tl = tl->nxt, ids = ids->next){
+		tt = tl->t;
+		if(!tattr[tt->kind].isptr){
+			error(src.start, "%T is not a pointer type", tt);
+			return terror;
+		}
+		unifysrc = src;
+		if(!tunify(ids->ty, tt, &tp1)){
+			error(src.start, "type %T does not match %T", tt, ids->ty);
+			return terror;
+		}
+		/* usetype(tt); */
+		tt = verifytypes(tt, adtt, nil);
+		addtmap(ids->ty, tt, tp);
+	}
+	if(tl != nil){
+		error(src.start, "too many actual types in instantiation");
+		return terror;
+	}
+	if(ids != nil){
+		error(src.start, "too few actual types in instantiation");
+		return terror;
+	}
+	tp1 = *tp;
+	tt = t->tof;
+	t = expandtype(tt, t, adtt, tp);
+	if(t == tt && adtt == nil)
+		t = duptype(t);
+	if(t != tt){
+		t->u.tmap = tp1;
+		if(debug['w']){
+			print("tmap for %T: ", t);
+			for( ; tp1!=nil; tp1=tp1->nxt)
+				print("%T -> %T ", tp1->t1, tp1->t2);
+			print("\n");
+		}
+	}
+	t->src = src;
+	return t;
+}
+
+/*
+ * walk a type, putting all adts, modules, and tuples into equivalence classes
+ */
+void
+teqclass(Type *t)
+{
+	Decl *id, *tg;
+	Teq *teq;
+
+	if(t == nil || (t->ok & OKclass) == OKclass)
+		return;
+	t->ok |= OKclass;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tiface:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Texcept:
+	case Tfix:
+	case Tpoly:
+		return;
+	case Tref:
+		teqclass(t->tof);
+		return;
+	case Tchan:
+	case Tarray:
+	case Tlist:
+		teqclass(t->tof);
+		if(!debug['Z'])
+			return;
+		break;
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+	case Texception:
+		for(id = t->ids; id != nil; id = id->next)
+			teqclass(id->ty);
+		for(tg = t->tags; tg != nil; tg = tg->next)
+			teqclass(tg->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			teqclass(id->ty);
+		break;
+	case Tmodule:
+		t->tof = mkiface(t->decl);
+		for(id = t->ids; id != nil; id = id->next)
+			teqclass(id->ty);
+		break;
+	case Tfn:
+		for(id = t->ids; id != nil; id = id->next)
+			teqclass(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			teqclass(id->ty);
+		teqclass(t->tof);
+		return;
+	default:
+		fatal("teqclass: unknown type kind %d", t->kind);
+		return;
+	}
+
+	/*
+	 * find an equivalent type
+	 * stupid linear lookup could be made faster
+	 */
+	if((t->ok & OKsized) != OKsized)
+		fatal("eqclass type not sized: %t", t);
+
+	for(teq = eqclass[t->kind]; teq != nil; teq = teq->eq){
+		if(t->size == teq->ty->size && tequal(t, teq->ty)){
+			t->eq = teq;
+			if(t->kind == Tmodule)
+				joiniface(t, t->eq->ty->tof);
+			return;
+		}
+	}
+
+	/*
+	 * if no equiv type, make one
+	 */
+	t->eq = allocmem(sizeof(Teq));
+	t->eq->id = 0;
+	t->eq->ty = t;
+	t->eq->eq = eqclass[t->kind];
+	eqclass[t->kind] = t->eq;
+}
+
+/*
+ * record that we've used the type
+ * using a type uses all types reachable from that type
+ */
+void
+reftype(Type *t)
+{
+	Decl *id, *tg;
+
+	if(t == nil || (t->ok & OKref) == OKref)
+		return;
+	t->ok |= OKref;
+	if(t->decl != nil && t->decl->refs == 0)
+		t->decl->refs++;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tiface:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Texcept:
+	case Tfix:
+	case Tpoly:
+		break;
+	case Tref:
+	case Tchan:
+	case Tarray:
+	case Tlist:
+		if(t->decl != nil){
+			if(nadts >= lenadts){
+				lenadts = nadts + 32;
+				adts = reallocmem(adts, lenadts * sizeof *adts);
+			}
+			adts[nadts++] = t->decl;
+		}
+		reftype(t->tof);
+		break;
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+	case Texception:
+		if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){
+			if(nadts >= lenadts){
+				lenadts = nadts + 32;
+				adts = reallocmem(adts, lenadts * sizeof *adts);
+			}
+			adts[nadts++] = t->decl;
+		}
+		for(id = t->ids; id != nil; id = id->next)
+			if(id->store != Dfn)
+				reftype(id->ty);
+		for(tg = t->tags; tg != nil; tg = tg->next)
+			reftype(tg->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			reftype(id->ty);
+		if(t->kind == Tadtpick)
+			reftype(t->decl->dot->ty);
+		break;
+	case Tmodule:
+		/*
+		 * a module's elements should get used individually
+		 * but do the globals for any sbl file
+		 */
+		if(bsym != nil)
+			for(id = t->ids; id != nil; id = id->next)
+				if(id->store == Dglobal)
+					reftype(id->ty);
+		break;
+	case Tfn:
+		for(id = t->ids; id != nil; id = id->next)
+			reftype(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			reftype(id->ty);
+		reftype(t->tof);
+		break;
+	default:
+		fatal("reftype: unknown type kind %d", t->kind);
+		break;
+	}
+}
+
+/*
+ * check all reachable types for cycles and illegal forward references
+ * find the size of all the types
+ */
+void
+cycsizetype(Type *t)
+{
+	Decl *id, *tg;
+
+	if(t == nil || (t->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+		return;
+	t->ok |= OKcycsize;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tiface:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Texcept:
+	case Tfix:
+	case Tpoly:
+		t->ok |= OKcyc;
+		sizetype(t);
+		break;
+	case Tref:
+	case Tchan:
+	case Tarray:
+	case Tlist:
+		cyctype(t);
+		sizetype(t);
+		cycsizetype(t->tof);
+		break;
+	case Tadt:
+	case Ttuple:
+	case Texception:
+		cyctype(t);
+		sizetype(t);
+		for(id = t->ids; id != nil; id = id->next)
+			cycsizetype(id->ty);
+		for(tg = t->tags; tg != nil; tg = tg->next){
+			if((tg->ty->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+				continue;
+			tg->ty->ok |= (OKcycsize|OKcyc|OKsized);
+			for(id = tg->ty->ids; id != nil; id = id->next)
+				cycsizetype(id->ty);
+		}
+		for(id = t->polys; id != nil; id = id->next)
+			cycsizetype(id->ty);
+		break;
+	case Tadtpick:
+		t->ok &= ~OKcycsize;
+		cycsizetype(t->decl->dot->ty);
+		break;
+	case Tmodule:
+		cyctype(t);
+		sizetype(t);
+		for(id = t->ids; id != nil; id = id->next)
+			cycsizetype(id->ty);
+		sizeids(t->ids, 0);
+		break;
+	case Tfn:
+		cyctype(t);
+		sizetype(t);
+		for(id = t->ids; id != nil; id = id->next)
+			cycsizetype(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			cycsizetype(id->ty);
+		cycsizetype(t->tof);
+		sizeids(t->ids, MaxTemp);
+		break;
+	default:
+		fatal("cycsizetype: unknown type kind %d", t->kind);
+		break;
+	}
+}
+
+/* check for circularity in type declarations
+ * - has to be called before verifytypes
+ */
+void
+tcycle(Type *t)
+{
+	Decl *id;
+	Type *tt;
+	Typelist *tl;
+
+	if(t == nil)
+		return;
+	switch(t->kind){
+	default:
+		break;
+	case Tchan:
+	case Tarray:
+	case Tref:
+	case Tlist:
+	case Tdot:
+		tcycle(t->tof);
+		break;
+	case Tfn:
+	case Ttuple:
+		tcycle(t->tof);
+		for(id = t->ids; id != nil; id = id->next)
+			tcycle(id->ty);
+		break;
+	case Tarrow:
+		if(t->rec&TRvis){
+			error(t->src.start, "circularity in definition of %T", t);
+			*t = *terror;	/* break the cycle */
+			return;
+		}
+		tt = t->tof;
+		t->rec |= TRvis;
+		tcycle(tt);
+		if(tt->kind == Tid)
+			tt = tt->decl->ty;
+		id = namedot(tt->ids, t->decl->sym);
+		if(id != nil)
+			tcycle(id->ty);
+		t->rec &= ~TRvis;
+		break;
+	case Tid:
+		if(t->rec&TRvis){
+			error(t->src.start, "circularity in definition of %T", t);
+			*t = *terror;	/* break the cycle */
+			return;
+		}
+		t->rec |= TRvis;
+		tcycle(t->decl->ty);
+		t->rec &= ~TRvis;
+		break;
+	case Tinst:
+		tcycle(t->tof);
+		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+			tcycle(tl->t);
+		break;
+	}
+}
+
+/*
+ * marks for checking for arcs
+ */
+enum
+{
+	ArcValue	= 1 << 0,
+	ArcList		= 1 << 1,
+	ArcArray	= 1 << 2,
+	ArcRef		= 1 << 3,
+	ArcCyc		= 1 << 4,		/* cycle found */
+	ArcPolycyc	= 1 << 5,
+};
+
+void
+cyctype(Type *t)
+{
+	Decl *id, *tg;
+
+	if((t->ok & OKcyc) == OKcyc)
+		return;
+	t->ok |= OKcyc;
+	t->rec |= TRcyc;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tfn:
+	case Tchan:
+	case Tarray:
+	case Tref:
+	case Tlist:
+	case Tfix:
+	case Tpoly:
+		break;
+	case Tadt:
+	case Tmodule:
+	case Ttuple:
+	case Texception:
+		for(id = t->ids; id != nil; id = id->next)
+			cycfield(t, id);
+		for(tg = t->tags; tg != nil; tg = tg->next){
+			if((tg->ty->ok & OKcyc) == OKcyc)
+				continue;
+			tg->ty->ok |= OKcyc;
+			for(id = tg->ty->ids; id != nil; id = id->next)
+				cycfield(t, id);
+		}
+		break;
+	default:
+		fatal("checktype: unknown type kind %d", t->kind);
+		break;
+	}
+	t->rec &= ~TRcyc;
+}
+
+void
+cycfield(Type *base, Decl *id)
+{
+	int arc;
+
+	if(!storespace[id->store])
+		return;
+	arc = cycarc(base, id->ty);
+
+	if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){
+		if(id->cycerr == 0)
+			error(base->src.start, "illegal type cycle without a reference in field %s of %t",
+				id->sym->name, base);
+		id->cycerr = 1;
+	}else if(arc & ArcCyc){
+		if((arc & ArcArray) && id->cyc == 0 && !(arc & ArcPolycyc)){
+			if(id->cycerr == 0)
+				error(base->src.start, "illegal circular reference to type %T in field %s of %t",
+					id->ty, id->sym->name, base);
+			id->cycerr = 1;
+		}
+		id->cycle = 1;
+	}else if(id->cyc != 0){
+		if(id->cycerr == 0)
+			error(id->src.start, "spurious cyclic qualifier for field %s of %t", id->sym->name, base);
+		id->cycerr = 1;
+	}
+}
+
+int
+cycarc(Type *base, Type *t)
+{
+	Decl *id, *tg;
+	int me, arc;
+
+	if(t == nil)
+		return 0;
+	if(t->rec & TRcyc){
+		if(tequal(t, base)){
+			if(t->kind == Tmodule)
+				return ArcCyc | ArcRef;
+			else
+				return ArcCyc | ArcValue;
+		}
+		return 0;
+	}
+	t->rec |= TRcyc;
+	me = 0;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tchan:
+	case Tfn:
+	case Tfix:
+	case Tpoly:
+		break;
+	case Tarray:
+		me = cycarc(base, t->tof) & ~ArcValue | ArcArray;
+		break;
+	case Tref:
+		me = cycarc(base, t->tof) & ~ArcValue | ArcRef;
+		break;
+	case Tlist:
+		me = cycarc(base, t->tof) & ~ArcValue | ArcList;
+		break;
+	case Tadt:
+	case Tadtpick:
+	case Tmodule:
+	case Ttuple:
+	case Texception:
+		me = 0;
+		for(id = t->ids; id != nil; id = id->next){
+			if(!storespace[id->store])
+				continue;
+			arc = cycarc(base, id->ty);
+			if((arc & ArcCyc) && id->cycerr == 0)
+				me |= arc;
+		}
+		for(tg = t->tags; tg != nil; tg = tg->next){
+			arc = cycarc(base, tg->ty);
+			if((arc & ArcCyc) && tg->cycerr == 0)
+				me |= arc;
+		}
+
+		if(t->kind == Tmodule)
+			me = me & ArcCyc | ArcRef | ArcPolycyc;
+		else
+			me &= ArcCyc | ArcValue | ArcPolycyc;
+		break;
+	default:
+		fatal("cycarc: unknown type kind %d", t->kind);
+		break;
+	}
+	t->rec &= ~TRcyc;
+	if(t->flags&CYCLIC)
+		me |= ArcPolycyc;
+	return me;
+}
+
+/*
+ * set the sizes and field offsets for t
+ * look only as deeply as needed to size this type.
+ * cycsize type will clean up the rest.
+ */
+void
+sizetype(Type *t)
+{
+	Decl *id, *tg;
+	Szal szal;
+	long sz, al, a;
+
+	if(t == nil)
+		return;
+	if((t->ok & OKsized) == OKsized)
+		return;
+	t->ok |= OKsized;
+if((t->ok & (OKverify|OKsized)) != (OKverify|OKsized))
+fatal("sizetype bogus ok for %t", t);
+	switch(t->kind){
+	default:
+		fatal("sizetype: unknown type kind %d", t->kind);
+		break;
+	case Terror:
+	case Tnone:
+	case Tbyte:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Tany:
+	case Treal:
+		fatal("%T should have a size", t);
+		break;
+	case Tref:
+	case Tchan:
+	case Tarray:
+	case Tlist:
+	case Tmodule:
+	case Tfix:
+	case Tpoly:
+		t->size = t->align = IBY2WD;
+		break;
+	case Ttuple:
+	case Tadt:
+	case Texception:
+		if(t->tags == nil){
+			if(!debug['z']){
+				szal = sizeids(t->ids, 0);
+				t->size = align(szal.size, szal.align);
+				t->align = szal.align;
+			}else{
+				szal = sizeids(t->ids, 0);
+				t->align = IBY2LG;
+				t->size = align(szal.size, IBY2LG);
+			}
+			return;
+		}
+		if(!debug['z']){
+			szal = sizeids(t->ids, IBY2WD);
+			sz = szal.size;
+			al = szal.align;
+			if(al < IBY2WD)
+				al = IBY2WD;
+		}else{
+			szal = sizeids(t->ids, IBY2WD);
+			sz = szal.size;
+			al = IBY2LG;
+		}
+		for(tg = t->tags; tg != nil; tg = tg->next){
+			if((tg->ty->ok & OKsized) == OKsized)
+				continue;
+			tg->ty->ok |= OKsized;
+			if(!debug['z']){
+				szal = sizeids(tg->ty->ids, sz);
+				a = szal.align;
+				if(a < al)
+					a = al;
+				tg->ty->size = align(szal.size, a);
+				tg->ty->align = a;
+			}else{
+				szal = sizeids(tg->ty->ids, sz);
+				tg->ty->size = align(szal.size, IBY2LG);
+				tg->ty->align = IBY2LG;
+			}			
+		}
+		break;
+	case Tfn:
+		t->size = 0;
+		t->align = 1;
+		break;
+	case Tainit:
+		t->size = 0;
+		t->align = 1;
+		break;
+	case Talt:
+		t->size = t->cse->nlab * 2*IBY2WD + 2*IBY2WD;
+		t->align = IBY2WD;
+		break;
+	case Tcase:
+	case Tcasec:
+		t->size = t->cse->nlab * 3*IBY2WD + 2*IBY2WD;
+		t->align = IBY2WD;
+		break;
+	case Tcasel:
+		t->size = t->cse->nlab * 6*IBY2WD + 3*IBY2WD;
+		t->align = IBY2LG;
+		break;
+	case Tgoto:
+		t->size = t->cse->nlab * IBY2WD + IBY2WD;
+		if(t->cse->iwild != nil)
+			t->size += IBY2WD;
+		t->align = IBY2WD;
+		break;
+	case Tiface:
+		sz = IBY2WD;
+		for(id = t->ids; id != nil; id = id->next){
+			sz = align(sz, IBY2WD) + IBY2WD;
+			sz += id->sym->len + 1;
+			if(id->dot->ty->kind == Tadt)
+				sz += id->dot->sym->len + 1;
+		}
+		t->size = sz;
+		t->align = IBY2WD;
+		break;
+	case Texcept:
+		t->size = 0;
+		t->align = IBY2WD;
+		break;
+	}
+}
+
+Szal
+sizeids(Decl *id, long off)
+{
+	Szal szal;
+	int a, al;
+
+	al = 1;
+	for(; id != nil; id = id->next){
+		if(storespace[id->store]){
+			sizetype(id->ty);
+			/*
+			 * alignment can be 0 if we have
+			 * illegal forward declarations.
+			 * just patch a; other code will flag an error
+			 */
+			a = id->ty->align;
+			if(a == 0)
+				a = 1;
+
+			if(a > al)
+				al = a;
+
+			off = align(off, a);
+			id->offset = off;
+			off += id->ty->size;
+		}
+	}
+	szal.size = off;
+	szal.align = al;
+	return szal;
+}
+
+long
+align(long off, int align)
+{
+	if(align == 0)
+		fatal("align 0");
+	while(off % align)
+		off++;
+	return off;
+}
+
+/*
+ * recalculate a type's size
+ */
+void
+resizetype(Type *t)
+{
+	if((t->ok & OKsized) == OKsized){
+		t->ok &= ~OKsized;
+		cycsizetype(t);
+	}
+}
+
+/*
+ * check if a module is accessable from t
+ * if so, mark that module interface
+ */
+void
+modrefable(Type *t)
+{
+	Decl *id, *m, *tg;
+
+	if(t == nil || (t->ok & OKmodref) == OKmodref)
+		return;
+	if((t->ok & OKverify) != OKverify)
+		fatal("modrefable unused type %t", t);
+	t->ok |= OKmodref;
+	switch(t->kind){
+	case Terror:
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tnone:
+	case Tany:
+	case Tfix:
+	case Tpoly:
+		break;
+	case Tchan:
+	case Tref:
+	case Tarray:
+	case Tlist:
+		modrefable(t->tof);
+		break;
+	case Tmodule:
+		t->tof->linkall = 1;
+		t->decl->refs++;
+		for(id = t->ids; id != nil; id = id->next){
+			switch(id->store){
+			case Dglobal:
+			case Dfn:
+				modrefable(id->ty);
+				break;
+			case Dtype:
+				if(id->ty->kind != Tadt)
+					break;
+				for(m = id->ty->ids; m != nil; m = m->next)
+					if(m->store == Dfn)
+						modrefable(m->ty);
+				break;
+			}
+		}
+		break;
+	case Tfn:
+	case Tadt:
+	case Ttuple:
+	case Texception:
+		for(id = t->ids; id != nil; id = id->next)
+			if(id->store != Dfn)
+				modrefable(id->ty);
+		for(tg = t->tags; tg != nil; tg = tg->next){
+/*
+			if((tg->ty->ok & OKmodref) == OKmodref)
+				continue;
+*/
+			tg->ty->ok |= OKmodref;
+			for(id = tg->ty->ids; id != nil; id = id->next)
+				modrefable(id->ty);
+		}
+		for(id = t->polys; id != nil; id = id->next)
+			modrefable(id->ty);
+		modrefable(t->tof);
+		break;
+	case Tadtpick:
+		modrefable(t->decl->dot->ty);
+		break;
+	default:
+		fatal("unknown type kind %d", t->kind);
+		break;
+	}
+}
+
+Desc*
+gendesc(Decl *d, long size, Decl *decls)
+{
+	Desc *desc;
+
+	if(debug['D'])
+		print("generate desc for %D\n", d);
+	if(ispoly(d))
+		addfnptrs(d, 0);
+	desc = usedesc(mkdesc(size, decls));
+	return desc;
+}
+
+Desc*
+mkdesc(long size, Decl *d)
+{
+	uchar *pmap;
+	long len, n;
+
+	len = (size+8*IBY2WD-1) / (8*IBY2WD);
+	pmap = allocmem(len);
+	memset(pmap, 0, len);
+	n = descmap(d, pmap, 0);
+	if(n >= 0)
+		n = n / (8*IBY2WD) + 1;
+	else
+		n = 0;
+	if(n > len)
+		fatal("wrote off end of decl map: %ld %ld", n, len);
+	return enterdesc(pmap, size, n);
+}
+
+Desc*
+mktdesc(Type *t)
+{
+	Desc *d;
+	uchar *pmap;
+	long len, n;
+
+usedty(t);
+	if(debug['D'])
+		print("generate desc for %T\n", t);
+	if(t->decl == nil){
+		t->decl = mkdecl(&t->src, Dtype, t);
+		t->decl->sym = enter("_mktdesc_", 0);
+	}
+	if(t->decl->desc != nil)
+		return t->decl->desc;
+	len = (t->size+8*IBY2WD-1) / (8*IBY2WD);
+	pmap = allocmem(len);
+	memset(pmap, 0, len);
+	n = tdescmap(t, pmap, 0);
+	if(n >= 0)
+		n = n / (8*IBY2WD) + 1;
+	else
+		n = 0;
+	if(n > len)
+		fatal("wrote off end of type map for %T: %ld %ld 0x%2.2ux", t, n, len, t->ok);
+	d = enterdesc(pmap, t->size, n);
+	t->decl->desc = d;
+	if(debug['j']){
+		uchar *m, *e;
+
+		print("generate desc for %T\n", t);
+		print("\tdesc\t$%d,%lud,\"", d->id, d->size);
+		e = d->map + d->nmap;
+		for(m = d->map; m < e; m++)
+			print("%.2x", *m);
+		print("\"\n");
+	}
+	return d;
+}
+
+Desc*
+enterdesc(uchar *map, long size, long nmap)
+{
+	Desc *d, *last;
+	int c;
+
+	last = nil;
+	for(d = descriptors; d != nil; d = d->next){
+		if(d->size > size || d->size == size && d->nmap > nmap)
+			break;
+		if(d->size == size && d->nmap == nmap){
+			c = memcmp(d->map, map, nmap);
+			if(c == 0){
+				free(map);
+				return d;
+			}
+			if(c > 0)
+				break;
+		}
+		last = d;
+	}
+	d = allocmem(sizeof *d);
+	d->id = -1;
+	d->used = 0;
+	d->map = map;
+	d->size = size;
+	d->nmap = nmap;
+	if(last == nil){
+		d->next = descriptors;
+		descriptors = d;
+	}else{
+		d->next = last->next;
+		last->next = d;
+	}
+	return d;
+}
+
+Desc*
+usedesc(Desc *d)
+{
+	d->used = 1;
+	return d;
+}
+
+/*
+ * create the pointer description byte map for every type in decls
+ * each bit corresponds to a word, and is 1 if occupied by a pointer
+ * the high bit in the byte maps the first word
+ */
+long
+descmap(Decl *decls, uchar *map, long start)
+{
+	Decl *d;
+	long last, m;
+
+	if(debug['D'])
+		print("descmap offset %ld\n", start);
+	last = -1;
+	for(d = decls; d != nil; d = d->next){
+		if(d->store == Dtype && d->ty->kind == Tmodule
+		|| d->store == Dfn
+		|| d->store == Dconst)
+			continue;
+		if(d->store == Dlocal && d->link != nil)
+			continue;
+		m = tdescmap(d->ty, map, d->offset + start);
+		if(debug['D']){
+			if(d->sym != nil)
+				print("descmap %s type %T offset %ld returns %ld\n",
+					d->sym->name, d->ty, d->offset+start, m);
+			else
+				print("descmap type %T offset %ld returns %ld\n", d->ty, d->offset+start, m);
+		}
+		if(m >= 0)
+			last = m;
+	}
+	return last;
+}
+
+long
+tdescmap(Type *t, uchar *map, long offset)
+{
+	Label *lab;
+	long i, e, m;
+	int bit;
+
+	if(t == nil)
+		return -1;
+
+	m = -1;
+	if(t->kind == Talt){
+		lab = t->cse->labs;
+		e = t->cse->nlab;
+		offset += IBY2WD * 2;
+		for(i = 0; i < e; i++){
+			if(lab[i].isptr){
+				bit = offset / IBY2WD % 8;
+				map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+				m = offset;
+			}
+			offset += 2*IBY2WD;
+		}
+		return m;
+	}
+	if(t->kind == Tcasec){
+		e = t->cse->nlab;
+		offset += IBY2WD;
+		for(i = 0; i < e; i++){
+			bit = offset / IBY2WD % 8;
+			map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+			offset += IBY2WD;
+			bit = offset / IBY2WD % 8;
+			map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+			m = offset;
+			offset += 2*IBY2WD;
+		}
+		return m;
+	}
+
+	if(tattr[t->kind].isptr){
+		bit = offset / IBY2WD % 8;
+		map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
+		return offset;
+	}
+	if(t->kind == Tadtpick)
+		t = t->tof;
+	if(t->kind == Ttuple || t->kind == Tadt || t->kind == Texception){
+		if(debug['D'])
+			print("descmap adt offset %ld\n", offset);
+		if(t->rec != 0)
+			fatal("illegal cyclic type %t in tdescmap", t);
+		t->rec = 1;
+		offset = descmap(t->ids, map, offset);
+		t->rec = 0;
+		return offset;
+	}
+
+	return -1;
+}
+
+/*
+ * can a t2 be assigned to a t1?
+ * any means Tany matches all types,
+ * not just references
+ */
+int
+tcompat(Type *t1, Type *t2, int any)
+{
+	int ok, v;
+
+	if(t1 == t2)
+		return 1;
+	if(t1 == nil || t2 == nil)
+		return 0;
+	if(t2->kind == Texception && t1->kind != Texception)
+		t2 = mkextuptype(t2);
+	tcomset = 0;
+	ok = rtcompat(t1, t2, any, 0);
+	v = cleartcomrec(t1) + cleartcomrec(t2);
+	if(v != tcomset)
+		fatal("recid t1 %t and t2 %t not balanced in tcompat: %d v %d", t1, t2, v, tcomset);
+	return ok;
+}
+
+static int
+rtcompat(Type *t1, Type *t2, int any, int inaorc)
+{
+	if(t1 == t2)
+		return 1;
+	if(t1 == nil || t2 == nil)
+		return 0;
+	if(t1->kind == Terror || t2->kind == Terror)
+		return 1;
+	if(t2->kind == Texception && t1->kind != Texception)
+		t2 = mkextuptype(t2);
+
+	if(debug['x'])
+		print("rtcompat: %t and %t\n", t1, t2);
+
+	t1->rec |= TRcom;
+	t2->rec |= TRcom;
+	switch(t1->kind){
+	default:
+		fatal("unknown type %t v %t in rtcompat", t1, t2);
+	case Tstring:
+		return t2->kind == Tstring || t2->kind == Tany;
+	case Texception:
+		if(t2->kind == Texception && t1->cons == t2->cons){
+			if(assumetcom(t1, t2))
+				return 1;
+			return idcompat(t1->ids, t2->ids, 0, inaorc);
+		}
+		return 0;
+	case Tnone:
+	case Tint:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+		return t1->kind == t2->kind;
+	case Tfix:
+		return t1->kind == t2->kind && sametree(t1->val, t2->val);
+	case Tany:
+		if(tattr[t2->kind].isptr)
+			return 1;
+		return any;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		if(t1->kind != t2->kind){
+			if(t2->kind == Tany)
+				return 1;
+			return 0;
+		}
+		if(t1->kind != Tref && assumetcom(t1, t2))
+			return 1;
+		return rtcompat(t1->tof, t2->tof, 0, t1->kind == Tarray || t1->kind == Tchan || inaorc);
+	case Tfn:
+		break;
+	case Ttuple:
+		if(t2->kind == Tadt && t2->tags == nil
+		|| t2->kind == Ttuple){
+			if(assumetcom(t1, t2))
+				return 1;
+			return idcompat(t1->ids, t2->ids, any, inaorc);
+		}
+		if(t2->kind == Tadtpick){
+			t2->tof->rec |= TRcom;
+			if(assumetcom(t1, t2->tof))
+				return 1;
+			return idcompat(t1->ids, t2->tof->ids->next, any, inaorc);
+		}
+		return 0;
+	case Tadt:
+		if(t2->kind == Ttuple && t1->tags == nil){
+			if(assumetcom(t1, t2))
+				return 1;
+			return idcompat(t1->ids, t2->ids, any, inaorc);
+		}
+		if(t1->tags != nil && t2->kind == Tadtpick && !inaorc)
+			t2 = t2->decl->dot->ty;
+		break;
+	case Tadtpick:
+/*
+		if(t2->kind == Ttuple)
+			return idcompat(t1->tof->ids->next, t2->ids, any, inaorc);
+*/
+		break;
+	case Tmodule:
+		if(t2->kind == Tany)
+			return 1;
+		break;
+	case Tpoly:
+		if(t2->kind == Tany)
+			return 1;
+		break;
+	}
+	return tequal(t1, t2);
+}
+
+/*
+ * add the assumption that t1 and t2 are compatable
+ */
+static int
+assumetcom(Type *t1, Type *t2)
+{
+	Type *r1, *r2;
+
+	if(t1->tcom == nil && t2->tcom == nil){
+		tcomset += 2;
+		t1->tcom = t2->tcom = t1;
+	}else{
+		if(t1->tcom == nil){
+			r1 = t1;
+			t1 = t2;
+			t2 = r1;
+		}
+		for(r1 = t1->tcom; r1 != r1->tcom; r1 = r1->tcom)
+			;
+		for(r2 = t2->tcom; r2 != nil && r2 != r2->tcom; r2 = r2->tcom)
+			;
+		if(r1 == r2)
+			return 1;
+		if(r2 == nil)
+			tcomset++;
+		t2->tcom = t1;
+		for(; t2 != r1; t2 = r2){
+			r2 = t2->tcom;
+			t2->tcom = r1;
+		}
+	}
+	return 0;
+}
+
+static int
+cleartcomrec(Type *t)
+{
+	Decl *id;
+	int n;
+
+	n = 0;
+	for(; t != nil && (t->rec & TRcom) == TRcom; t = t->tof){
+		t->rec &= ~TRcom;
+		if(t->tcom != nil){
+			t->tcom = nil;
+			n++;
+		}
+		if(t->kind == Tadtpick)
+			n += cleartcomrec(t->tof);
+		if(t->kind == Tmodule)
+			t = t->tof;
+		for(id = t->ids; id != nil; id = id->next)
+			n += cleartcomrec(id->ty);
+		for(id = t->tags; id != nil; id = id->next)
+			n += cleartcomrec(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			n += cleartcomrec(id->ty);
+	}
+	return n;
+}
+
+/*
+ * id1 and id2 are the fields in an adt or tuple
+ * simple structural check; ignore names
+ */
+static int
+idcompat(Decl *id1, Decl *id2, int any, int inaorc)
+{
+	for(; id1 != nil; id1 = id1->next){
+		if(id1->store != Dfield)
+			continue;
+		while(id2 != nil && id2->store != Dfield)
+			id2 = id2->next;
+		if(id2 == nil
+		|| id1->store != id2->store
+		|| !rtcompat(id1->ty, id2->ty, any, inaorc))
+			return 0;
+		id2 = id2->next;
+	}
+	while(id2 != nil && id2->store != Dfield)
+		id2 = id2->next;
+	return id2 == nil;
+}
+
+int
+tequal(Type *t1, Type *t2)
+{
+	int ok, v;
+
+	eqrec = 0;
+	eqset = 0;
+	ok = rtequal(t1, t2);
+	v = cleareqrec(t1) + cleareqrec(t2);
+	if(v != eqset && 0)
+		fatal("recid t1 %t and t2 %t not balanced in tequal: %d %d", t1, t2, v, eqset);
+	eqset = 0;
+	return ok;
+}
+
+/*
+ * structural equality on types
+ */
+static int
+rtequal(Type *t1, Type *t2)
+{
+	/*
+	 * this is just a shortcut
+	 */
+	if(t1 == t2)
+		return 1;
+
+	if(t1 == nil || t2 == nil)
+		return 0;
+	if(t1->kind == Terror || t2->kind == Terror)
+		return 1;
+
+	if(t1->kind != t2->kind)
+		return 0;
+
+	if(t1->eq != nil && t2->eq != nil)
+		return t1->eq == t2->eq;
+
+	if(debug['x'])
+		print("rtequal: %t and %t\n", t1, t2);
+
+	t1->rec |= TReq;
+	t2->rec |= TReq;
+	switch(t1->kind){
+	default:
+		fatal("unknown type %t v %t in rtequal", t1, t2);
+	case Tnone:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+	case Tint:
+	case Tstring:
+		/*
+		 * this should always be caught by t1 == t2 check
+		 */
+		fatal("bogus value type %t vs %t in rtequal", t1, t2);
+		return 1;
+	case Tfix:
+		return sametree(t1->val, t2->val);
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		if(t1->kind != Tref && assumeteq(t1, t2))
+			return 1;
+		return rtequal(t1->tof, t2->tof);
+	case Tfn:
+		if(t1->varargs != t2->varargs)
+			return 0;
+		if(!idequal(t1->ids, t2->ids, 0, storespace))
+			return 0;
+		/* if(!idequal(t1->polys, t2->polys, 1, nil)) */
+		if(!pyequal(t1, t2))
+			return 0;
+		return rtequal(t1->tof, t2->tof);
+	case Ttuple:
+	case Texception:
+		if(t1->kind != t2->kind || t1->cons != t2->cons)
+			return 0;
+		if(assumeteq(t1, t2))
+			return 1;
+		return idequal(t1->ids, t2->ids, 0, storespace);
+	case Tadt:
+	case Tadtpick:
+	case Tmodule:
+		if(assumeteq(t1, t2))
+			return 1;
+		/*
+		 * compare interfaces when comparing modules
+		 */
+		if(t1->kind == Tmodule)
+			return idequal(t1->tof->ids, t2->tof->ids, 1, nil);
+
+		/*
+		 * picked adts; check parent,
+		 * assuming equiv picked fields,
+		 * then check picked fields are equiv
+		 */
+		if(t1->kind == Tadtpick && !rtequal(t1->decl->dot->ty, t2->decl->dot->ty))
+			return 0;
+
+		/*
+		 * adts with pick tags: check picked fields for equality
+		 */
+		if(!idequal(t1->tags, t2->tags, 1, nil))
+			return 0;
+
+		/* if(!idequal(t1->polys, t2->polys, 1, nil)) */
+		if(!pyequal(t1, t2))
+			return 0;
+		return idequal(t1->ids, t2->ids, 1, storespace);
+	case Tpoly:
+		if(assumeteq(t1, t2))
+			return 1;
+		if(t1->decl->sym != t2->decl->sym)
+			return 0;
+		return idequal(t1->ids, t2->ids, 1, nil);
+	}
+}
+
+static int
+assumeteq(Type *t1, Type *t2)
+{
+	Type *r1, *r2;
+
+	if(t1->teq == nil && t2->teq == nil){
+		eqrec++;
+		eqset += 2;
+		t1->teq = t2->teq = t1;
+	}else{
+		if(t1->teq == nil){
+			r1 = t1;
+			t1 = t2;
+			t2 = r1;
+		}
+		for(r1 = t1->teq; r1 != r1->teq; r1 = r1->teq)
+			;
+		for(r2 = t2->teq; r2 != nil && r2 != r2->teq; r2 = r2->teq)
+			;
+		if(r1 == r2)
+			return 1;
+		if(r2 == nil)
+			eqset++;
+		t2->teq = t1;
+		for(; t2 != r1; t2 = r2){
+			r2 = t2->teq;
+			t2->teq = r1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * checking structural equality for adts, tuples, and fns
+ */
+static int
+idequal(Decl *id1, Decl *id2, int usenames, int *storeok)
+{
+	/*
+	 * this is just a shortcut
+	 */
+	if(id1 == id2)
+		return 1;
+
+	for(; id1 != nil; id1 = id1->next){
+		if(storeok != nil && !storeok[id1->store])
+			continue;
+		while(id2 != nil && storeok != nil && !storeok[id2->store])
+			id2 = id2->next;
+		if(id2 == nil
+		|| usenames && id1->sym != id2->sym
+		|| id1->store != id2->store
+		|| id1->implicit != id2->implicit
+		|| id1->cyc != id2->cyc
+		|| (id1->dot == nil) != (id2->dot == nil)
+		|| id1->dot != nil && id2->dot != nil && id1->dot->ty->kind != id2->dot->ty->kind
+		|| !rtequal(id1->ty, id2->ty))
+			return 0;
+		id2 = id2->next;
+	}
+	while(id2 != nil && storeok != nil && !storeok[id2->store])
+		id2 = id2->next;
+	return id1 == nil && id2 == nil;
+}
+
+static int
+pyequal(Type *t1, Type *t2)
+{
+	Type *pt1, *pt2;
+	Decl *id1, *id2;
+
+	if(t1 == t2)
+		return 1;
+	id1 = t1->polys;
+	id2 = t2->polys;
+	for(; id1 != nil; id1 = id1->next){
+		if(id2 == nil)
+			return 0;
+		pt1 = id1->ty;
+		pt2 = id2->ty;
+		if(!rtequal(pt1, pt2)){
+			if(t1->u.tmap != nil)
+				pt1 = valtmap(pt1, t1->u.tmap);
+			if(t2->u.tmap != nil)
+				pt2 = valtmap(pt2, t2->u.tmap);
+			if(!rtequal(pt1, pt2))
+				return 0;
+		}
+		id2 = id2->next;
+	}
+	return id1 == nil && id2 == nil;
+}
+
+static int
+cleareqrec(Type *t)
+{
+	Decl *id;
+	int n;
+
+	n = 0;
+	for(; t != nil && (t->rec & TReq) == TReq; t = t->tof){
+		t->rec &= ~TReq;
+		if(t->teq != nil){
+			t->teq = nil;
+			n++;
+		}
+		if(t->kind == Tadtpick)
+			n += cleareqrec(t->decl->dot->ty);
+		if(t->kind == Tmodule)
+			t = t->tof;
+		for(id = t->ids; id != nil; id = id->next)
+			n += cleareqrec(id->ty);
+		for(id = t->tags; id != nil; id = id->next)
+			n += cleareqrec(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			n += cleareqrec(id->ty);
+	}
+	return n;
+}
+
+int
+raisescompat(Node *n1, Node *n2)
+{
+	if(n1 == n2)
+		return 1;
+	if(n2 == nil)
+		return 1;	/* no need to repeat in definition if given in declaration */
+	if(n1 == nil)
+		return 0;
+	for(n1 = n1->left, n2 = n2->left; n1 != nil && n2 != nil; n1 = n1->right, n2 = n2->right){
+		if(n1->left->decl != n2->left->decl)
+			return 0;
+	}
+	return n1 == n2;
+}
+
+/* t1 a polymorphic type */
+static int
+fnunify(Type *t1, Type *t2, Tpair **tp, int swapped)
+{
+	Decl *id, *ids;
+	Sym *sym;
+
+	for(ids = t1->ids; ids != nil; ids = ids->next){
+		sym = ids->sym;
+		id = fnlookup(sym, t2, nil);
+		if(id != nil)
+			usetype(id->ty);
+		if(id == nil){
+			if(dowarn)
+				error(unifysrc.start, "type %T does not have a '%s' function", t2, sym->name);
+			return 0;
+		}
+		else if(id->ty->kind != Tfn){
+			if(dowarn)
+				error(unifysrc.start, "%T is not a function", id->ty);
+			return 0;
+		}
+		else if(!rtunify(ids->ty, id->ty, tp, !swapped)){
+			if(dowarn)
+				error(unifysrc.start, "%T and %T are not compatible wrt %s", ids->ty, id->ty, sym->name);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int
+fncleareqrec(Type *t1, Type *t2)
+{
+	Decl *id, *ids;
+	int n;
+
+	n = 0;
+	n += cleareqrec(t1);
+	n += cleareqrec(t2);
+	for(ids = t1->ids; ids != nil; ids = ids->next){
+		id = fnlookup(ids->sym, t2, nil);
+		if(id == nil)
+			continue;
+		else{
+			n += cleareqrec(ids->ty);
+			n += cleareqrec(id->ty);
+		}
+	}
+	return n;
+}
+int
+tunify(Type *t1, Type *t2, Tpair **tp)
+{
+	int ok, v;
+	Tpair *p;
+
+	*tp = nil;
+	eqrec = 0;
+	eqset = 0;
+	ok = rtunify(t1, t2, tp, 0);
+	v = cleareqrec(t1) + cleareqrec(t2);
+	for(p = *tp; p != nil; p = p->nxt)
+		v += fncleareqrec(p->t1, p->t2);
+	if(0 && v != eqset)
+		fatal("recid t1 %t and t2 %t not balanced in tunify: %d %d", t1, t2, v, eqset);
+	return ok;
+}
+
+static int
+rtunify(Type *t1, Type *t2, Tpair **tp, int swapped)
+{
+	Type *tmp;
+
+if(debug['w']) print("rtunifya - %T %T\n", t1, t2);
+	t1 = valtmap(t1, *tp);
+	t2 = valtmap(t2, *tp);
+if(debug['w']) print("rtunifyb - %T %T\n", t1, t2);
+	if(t1 == t2)
+		return 1;
+	if(t1 == nil || t2 == nil)
+		return 0;
+	if(t1->kind == Terror || t2->kind == Terror)
+		return 1;
+	if(t1->kind != Tpoly && t2->kind == Tpoly){
+		tmp = t1;
+		t1 = t2;
+		t2 = tmp;
+		swapped = !swapped;
+	}
+	if(t1->kind == Tpoly){
+/*
+		if(typein(t1, t2))
+			 return 0;
+*/
+		if(!tattr[t2->kind].isptr)
+			return 0;
+		if(t2->kind != Tany)
+			addtmap(t1, t2, tp);
+		return fnunify(t1, t2, tp, swapped);
+	}
+	if(t1->kind != Tany && t2->kind == Tany){
+		tmp = t1;
+		t1 = t2;
+		t2 = tmp;
+		swapped = !swapped;
+	}
+	if(t1->kind == Tadt && t1->tags != nil && t2->kind == Tadtpick && !swapped)
+		t2 = t2->decl->dot->ty;
+	if(t2->kind == Tadt && t2->tags != nil && t1->kind == Tadtpick && swapped)
+		t1 = t1->decl->dot->ty;
+	if(t1->kind != Tany && t1->kind != t2->kind)
+		return 0;
+	t1->rec |= TReq;
+	t2->rec |= TReq;
+	switch(t1->kind){
+	default:
+		return tequal(t1, t2);
+	case Tany:
+		return tattr[t2->kind].isptr;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		if(t1->kind != Tref && assumeteq(t1, t2))
+			return 1;
+		return rtunify(t1->tof, t2->tof, tp, swapped);
+	case Tfn:
+		if(!idunify(t1->ids, t2->ids, tp, swapped))
+			return 0;
+		if(!idunify(t1->polys, t2->polys, tp, swapped))
+			return 0;
+		return rtunify(t1->tof, t2->tof, tp, swapped);
+	case Ttuple:
+		if(assumeteq(t1, t2))
+			return 1;
+		return idunify(t1->ids, t2->ids, tp, swapped);
+	case Tadt:
+	case Tadtpick:
+		if(assumeteq(t1, t2))
+			return 1;
+		if(!idunify(t1->polys, t2->polys, tp, swapped))
+			return 0;
+		if(!idunify(t1->tags, t2->tags, tp, swapped))
+			return 0;
+		return idunify(t1->ids, t2->ids, tp, swapped);
+	case Tmodule:
+		if(assumeteq(t1, t2))
+			return 1;
+		return idunify(t1->tof->ids, t2->tof->ids, tp, swapped);
+	case Tpoly:
+		return t1 == t2;
+	}
+}
+
+static int
+idunify(Decl *id1, Decl *id2, Tpair **tp, int swapped)
+{
+	if(id1 == id2)
+		return 1;
+	for(; id1 != nil; id1 = id1->next){
+		if(id2 == nil || !rtunify(id1->ty, id2->ty, tp, swapped))
+			return 0;
+		id2 = id2->next;
+	}
+	return id1 == nil && id2 == nil;
+}
+
+int
+polyequal(Decl *id1, Decl *id2)
+{
+	int ck2;
+	Decl *d;
+
+	/* allow id2 list to have an optional for clause */
+	ck2 = 0;
+	for(d = id2; d != nil; d = d->next)
+		if(d->ty->ids != nil)
+			ck2 = 1;
+	for( ; id1 != nil; id1 = id1->next){
+		if(id2 == nil
+		|| id1->sym != id2->sym
+		|| id1->ty->decl != nil && id2->ty->decl != nil && id1->ty->decl->sym != id2->ty->decl->sym)
+			return 0;
+		if(ck2 && !idequal(id1->ty->ids, id2->ty->ids, 1, nil))
+			return 0;
+		id2 = id2->next;
+	}
+	return id1 == nil && id2 == nil;
+}
+
+Type*
+calltype(Type *f, Node *a, Type *rt)
+{
+	Type *t;
+	Decl *id, *first, *last;
+
+	first = last = nil;
+	t = mktype(&f->src.start, &f->src.stop, Tfn, rt, nil);
+	t->polys = f->kind == Tref ? f->tof->polys : f->polys;
+	for( ; a != nil; a = a->right){
+		id = mkdecl(&f->src, Darg, a->left->ty);
+		if(last == nil)
+			first = id;
+		else
+			last->next = id;
+		last = id;
+	}
+	t->ids = first;
+	if(f->kind == Tref)
+		t = mktype(&f->src.start, &f->src.stop, Tref, t, nil);
+	return t;
+}
+
+static Type*
+duptype(Type *t)
+{
+	Type *nt;
+
+	nt = allocmem(sizeof(*nt));
+	*nt = *t;
+	nt->ok &= ~(OKverify|OKref|OKclass|OKsized|OKcycsize|OKcyc);
+	nt->flags |= INST;
+	nt->eq = nil;
+	nt->sbl = -1;
+	if(t->decl != nil && (nt->kind == Tadt || nt->kind == Tadtpick || nt->kind == Ttuple)){
+		nt->decl = dupdecl(t->decl);
+		nt->decl->ty = nt;
+		nt->decl->link = t->decl;
+		if(t->decl->dot != nil){
+			nt->decl->dot = dupdecl(t->decl->dot);
+			nt->decl->dot->link = t->decl->dot;
+		}
+	}
+	else
+		nt->decl = nil;
+	return nt;
+}
+
+static int
+dpolys(Decl *ids)
+{
+	Decl *p;
+
+	for(p = ids; p != nil; p = p->next)
+		if(tpolys(p->ty))
+			return 1;
+	return 0;
+}
+
+static int
+tpolys(Type *t)
+{
+	int v;
+	Typelist *tl;
+
+	if(t == nil)
+		return 0;
+	if(t->flags&(POLY|NOPOLY))
+		return t->flags&POLY;
+	switch(t->kind){
+	default:
+		v = 0;
+		break;
+	case Tarrow:
+	case Tdot:
+	case Tpoly:
+		v = 1;
+		break;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		v = tpolys(t->tof);
+		break;
+	case Tid:
+		v = tpolys(t->decl->ty);
+		break;
+	case Tinst:
+		v = 0;
+		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+			if(tpolys(tl->t)){
+				v = 1;
+				break;
+			}
+		if(v == 0)
+			v = tpolys(t->tof);
+		break;
+	case Tfn:
+	case Tadt:
+	case Tadtpick:
+	case Ttuple:
+	case Texception:
+		if(t->polys != nil){
+			v = 1;
+			break;
+		}
+		if(t->rec&TRvis)
+			return 0;
+		t->rec |= TRvis;
+		v = tpolys(t->tof) || dpolys(t->polys) || dpolys(t->ids) || dpolys(t->tags);
+		t->rec &= ~TRvis;
+		if(t->kind == Tadtpick && v == 0)
+			v = tpolys(t->decl->dot->ty);
+		break;
+	}
+	if(v)
+		t->flags |= POLY;
+	else
+		t->flags |= NOPOLY;
+	return v;
+}
+
+static int
+doccurs(Decl *ids, Tpair **tp)
+{
+	Decl *p;
+
+	for(p = ids; p != nil; p = p->next)
+		if(toccurs(p->ty, tp))
+			return 1;
+	return 0;
+}
+
+static int
+toccurs(Type *t, Tpair **tp)
+{
+	int o;
+	Typelist *tl;
+
+	if(t == nil)
+		return 0;
+	if(!(t->flags&(POLY|NOPOLY)))
+		tpolys(t);
+	if(t->flags&NOPOLY)
+		return 0;
+	switch(t->kind){
+		default:
+			fatal("unknown type %t in toccurs", t);
+		case Tnone:
+		case Tbig:
+		case Tbyte:
+		case Treal:
+		case Tint:
+		case Tstring:
+		case Tfix:
+		case Tmodule:
+		case Terror:
+			return 0;
+		case Tarrow:
+		case Tdot:
+			return 1;
+		case Tpoly:
+			return valtmap(t, *tp) != t;
+		case Tref:
+		case Tlist:
+		case Tarray:
+		case Tchan:
+			return toccurs(t->tof, tp);
+		case Tid:
+			return toccurs(t->decl->ty, tp);
+		case Tinst:
+			for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+				if(toccurs(tl->t, tp))
+					return 1;
+			return toccurs(t->tof, tp);
+		case Tfn:
+		case Tadt:
+		case Tadtpick:
+		case Ttuple:
+		case Texception:
+			if(t->rec&TRvis)
+				return 0;
+			t->rec |= TRvis;
+			o = toccurs(t->tof, tp) || doccurs(t->polys, tp) || doccurs(t->ids, tp) || doccurs(t->tags, tp);
+			t->rec &= ~TRvis;
+			if(t->kind == Tadtpick && o == 0)
+				o = toccurs(t->decl->dot->ty, tp);
+			return o;
+	}
+}
+
+static Decl*
+expandids(Decl *ids, Decl *adtt, Tpair **tp, int sym)
+{
+	Decl *p, *q, *nids, *last;
+
+	nids = last = nil;
+	for(p = ids; p != nil; p = p->next){
+		q = dupdecl(p);
+		q->ty = expandtype(p->ty, nil, adtt, tp);
+		if(sym && q->ty->decl != nil)
+			q->sym = q->ty->decl->sym;
+		if(q->store == Dfn){
+if(debug['v']) print("%p->link = %p\n", q, p);
+			q->link = p;
+		}
+		if(nids == nil)
+			nids = q;
+		else
+			last->next = q;
+		last = q;
+	}
+	return nids;
+}
+
+Type*
+expandtype(Type *t, Type *instt, Decl *adtt, Tpair **tp)
+{
+	Type *nt;
+	Decl *ids;
+
+	if(t == nil)
+		return nil;
+if(debug['w']) print("expandtype %d %lux %T\n", t->kind, (ulong)t, t);
+	if(!toccurs(t, tp))
+		return t;
+if(debug['w']) print("\texpanding\n");
+	switch(t->kind){
+		default:
+			fatal("unknown type %t in expandtype", t);
+		case Tpoly:
+			return valtmap(t, *tp);
+		case Tref:
+		case Tlist:
+		case Tarray:
+		case Tchan:
+			nt = duptype(t);
+			nt->tof = expandtype(t->tof, nil, adtt, tp);
+			return nt;
+		case Tid:
+			return expandtype(idtype(t), nil, adtt, tp);
+		case Tdot:
+			return expandtype(dottype(t, adtt), nil, adtt, tp);
+		case Tarrow:
+			return expandtype(arrowtype(t, adtt), nil, adtt, tp);
+		case Tinst:
+			if((nt = valtmap(t, *tp)) != t)
+				return nt;
+			return expandtype(insttype(t, adtt, tp), nil, adtt, tp);
+		case Tfn:
+		case Tadt:
+		case Tadtpick:
+		case Ttuple:
+		case Texception:
+			if((nt = valtmap(t, *tp)) != t)
+				return nt;
+			if(t->kind == Tadt)
+				adtt = t->decl;
+			nt = duptype(t);
+			addtmap(t, nt, tp);
+			if(instt != nil)
+				addtmap(instt, nt, tp);
+			nt->tof = expandtype(t->tof, nil, adtt, tp);
+			nt->polys = expandids(t->polys, adtt, tp, 1);
+			nt->ids = expandids(t->ids, adtt, tp, 0);
+			nt->tags = expandids(t->tags, adtt, tp, 0);
+			if(t->kind == Tadt){
+				for(ids = nt->tags; ids != nil; ids = ids->next)
+					ids->ty->decl->dot = nt->decl;
+			}
+			if(t->kind == Tadtpick){
+				nt->decl->dot->ty = expandtype(t->decl->dot->ty, nil, adtt, tp);
+			}
+			if((t->kind == Tadt || t->kind == Tadtpick) && t->u.tmap != nil){
+				Tpair *p;
+
+				nt->u.tmap = nil;
+				for(p = t->u.tmap; p != nil; p = p->nxt)
+					addtmap(valtmap(p->t1, *tp), valtmap(p->t2, *tp), &nt->u.tmap);
+				if(debug['w']){
+					print("new tmap for %T->%T: ", t, nt);
+					for(p=nt->u.tmap;p!=nil;p=p->nxt)print("%T -> %T ", p->t1, p->t2);
+					print("\n");
+				}
+			}
+			return nt;
+	}
+}
+
+/*
+ * create type signatures
+ * sign the same information used
+ * for testing type equality
+ */
+ulong
+sign(Decl *d)
+{
+	Type *t;
+	uchar *sig, md5sig[MD5dlen];
+	char buf[StrSize];
+	int i, sigend, sigalloc, v;
+
+	t = d->ty;
+	if(t->sig != 0)
+		return t->sig;
+
+	if(ispoly(d))
+		rmfnptrs(d);
+
+	sig = 0;
+	sigend = -1;
+	sigalloc = 1024;
+	while(sigend < 0 || sigend >= sigalloc){
+		sigalloc *= 2;
+		sig = reallocmem(sig, sigalloc);
+		eqrec = 0;
+		sigend = rtsign(t, sig, sigalloc, 0);
+		v = clearrec(t);
+		if(v != eqrec)
+			fatal("recid not balanced in sign: %d %d", v, eqrec);
+		eqrec = 0;
+	}
+	sig[sigend] = '\0';
+
+	if(signdump != nil){
+		seprint(buf, buf+sizeof(buf), "%D", d);
+		if(strcmp(buf, signdump) == 0){
+			print("sign %D len %d\n", d, sigend);
+			print("%s\n", (char*)sig);
+		}
+	}
+
+	md5(sig, sigend, md5sig, nil);
+	for(i = 0; i < MD5dlen; i += 4)
+		t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24);
+	if(debug['S'])
+		print("signed %D type %T len %d sig %#lux\n", d, t, sigend, t->sig);
+	free(sig);
+	return t->sig;
+}
+
+enum
+{
+	SIGSELF =	'S',
+	SIGVARARGS =	'*',
+	SIGCYC =	'y',
+	SIGREC =	'@'
+};
+
+static int sigkind[Tend] =
+{
+	/* Tnone */	'n',
+	/* Tadt */	'a',
+	/* Tadtpick */	'p',
+	/* Tarray */	'A',
+	/* Tbig */	'B',
+	/* Tbyte */	'b',
+	/* Tchan */	'C',
+	/* Treal */	'r',
+	/* Tfn */	'f',
+	/* Tint */	'i',
+	/* Tlist */	'L',
+	/* Tmodule */	'm',
+	/* Tref */	'R',
+	/* Tstring */	's',
+	/* Ttuple */	't',
+	/* Texception */	'e',
+	/* Tfix */	'x',
+	/* Tpoly */	'P',
+};
+
+static int
+rtsign(Type *t, uchar *sig, int lensig, int spos)
+{
+	Decl *id, *tg;
+	char name[32];
+	int kind, lenname;
+
+	if(t == nil)
+		return spos;
+
+	if(spos < 0 || spos + 8 >= lensig)
+		return -1;
+
+	if(t->eq != nil && t->eq->id){
+		if(t->eq->id < 0 || t->eq->id > eqrec)
+			fatal("sign rec %T %d %d", t, t->eq->id, eqrec);
+
+		sig[spos++] = SIGREC;
+		seprint(name, name+sizeof(name), "%d", t->eq->id);
+		lenname = strlen(name);
+		if(spos + lenname > lensig)
+			return -1;
+		strcpy((char*)&sig[spos], name);
+		spos += lenname;
+		return spos;
+	}
+	if(t->eq != nil){
+		eqrec++;
+		t->eq->id = eqrec;
+	}
+
+	kind = sigkind[t->kind];
+	sig[spos++] = kind;
+	if(kind == 0)
+		fatal("no sigkind for %t", t);
+
+	t->rec = 1;
+	switch(t->kind){
+	default:
+		fatal("bogus type %t in rtsign", t);
+		return -1;
+	case Tnone:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+	case Tint:
+	case Tstring:
+	case Tpoly:
+		return spos;
+	case Tfix:
+		seprint(name, name+sizeof(name), "%g", t->val->rval);
+		lenname = strlen(name);
+		if(spos+lenname-1 >= lensig)
+			return -1;
+		strcpy((char*)&sig[spos], name);
+		spos += lenname;
+		return spos;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		return rtsign(t->tof, sig, lensig, spos);
+	case Tfn:
+		if(t->varargs != 0)
+			sig[spos++] = SIGVARARGS;
+		if(t->polys != nil)
+			spos = idsign(t->polys, 0, sig, lensig, spos);
+		spos = idsign(t->ids, 0, sig, lensig, spos);
+		if(t->u.eraises)
+			spos = raisessign(t->u.eraises, sig, lensig, spos);
+		return rtsign(t->tof, sig, lensig, spos);
+	case Ttuple:
+		return idsign(t->ids, 0, sig, lensig, spos);
+	case Tadt:
+		/*
+		 * this is a little different than in rtequal,
+		 * since we flatten the adt we used to represent the globals
+		 */
+		if(t->eq == nil){
+			if(strcmp(t->decl->sym->name, ".mp") != 0)
+				fatal("no t->eq field for %t", t);
+			spos--;
+			for(id = t->ids; id != nil; id = id->next){
+				spos = idsign1(id, 1, sig, lensig, spos);
+				if(spos < 0 || spos >= lensig)
+					return -1;
+				sig[spos++] = ';';
+			}
+			return spos;
+		}
+		if(t->polys != nil)
+			spos = idsign(t->polys, 0, sig, lensig, spos);
+		spos = idsign(t->ids, 1, sig, lensig, spos);
+		if(spos < 0 || t->tags == nil)
+			return spos;
+
+		/*
+		 * convert closing ')' to a ',', then sign any tags
+		 */
+		sig[spos-1] = ',';
+		for(tg = t->tags; tg != nil; tg = tg->next){
+			lenname = tg->sym->len;
+			if(spos + lenname + 2 >= lensig)
+				return -1;
+			strcpy((char*)&sig[spos], tg->sym->name);
+			spos += lenname;
+			sig[spos++] = '=';
+			sig[spos++] = '>';
+
+			spos = rtsign(tg->ty, sig, lensig, spos);
+			if(spos < 0 || spos >= lensig)
+				return -1;
+
+			if(tg->next != nil)
+				sig[spos++] = ',';
+		}
+		if(spos >= lensig)
+			return -1;
+		sig[spos++] = ')';
+		return spos;
+	case Tadtpick:
+		spos = idsign(t->ids, 1, sig, lensig, spos);
+		if(spos < 0)
+			return spos;
+		return rtsign(t->decl->dot->ty, sig, lensig, spos);
+	case Tmodule:
+		if(t->tof->linkall == 0)
+			fatal("signing a narrowed module");
+
+		if(spos >= lensig)
+			return -1;
+		sig[spos++] = '{';
+		for(id = t->tof->ids; id != nil; id = id->next){
+			if(id->tag)
+				continue;
+			if(strcmp(id->sym->name, ".mp") == 0){
+				spos = rtsign(id->ty, sig, lensig, spos);
+				if(spos < 0)
+					return -1;
+				continue;
+			}
+			spos = idsign1(id, 1, sig, lensig, spos);
+			if(spos < 0 || spos >= lensig)
+				return -1;
+			sig[spos++] = ';';
+		}
+		if(spos >= lensig)
+			return -1;
+		sig[spos++] = '}';
+		return spos;
+	}
+}
+
+static int
+idsign(Decl *id, int usenames, uchar *sig, int lensig, int spos)
+{
+	int first;
+
+	if(spos >= lensig)
+		return -1;
+	sig[spos++] = '(';
+	first = 1;
+	for(; id != nil; id = id->next){
+		if(id->store == Dlocal)
+			fatal("local %s in idsign", id->sym->name);
+
+		if(!storespace[id->store])
+			continue;
+
+		if(!first){
+			if(spos >= lensig)
+				return -1;
+			sig[spos++] = ',';
+		}
+
+		spos = idsign1(id, usenames, sig, lensig, spos);
+		if(spos < 0)
+			return -1;
+		first = 0;
+	}
+	if(spos >= lensig)
+		return -1;
+	sig[spos++] = ')';
+	return spos;
+}
+
+static int
+idsign1(Decl *id, int usenames, uchar *sig, int lensig, int spos)
+{
+	char *name;
+	int lenname;
+
+	if(usenames){
+		name = id->sym->name;
+		lenname = id->sym->len;
+		if(spos + lenname + 1 >= lensig)
+			return -1;
+		strcpy((char*)&sig[spos], name);
+		spos += lenname;
+		sig[spos++] = ':';
+	}
+
+	if(spos + 2 >= lensig)
+		return -1;
+
+	if(id->implicit != 0)
+		sig[spos++] = SIGSELF;
+
+	if(id->cyc != 0)
+		sig[spos++] = SIGCYC;
+
+	return rtsign(id->ty, sig, lensig, spos);
+}
+
+static int
+raisessign(Node *n, uchar *sig, int lensig, int spos)
+{
+	int m;
+	char *s;
+	Node *nn;
+
+	if(spos >= lensig)
+		return -1;
+	sig[spos++] = '(';
+	for(nn = n->left; nn != nil; nn = nn->right){
+		s = nn->left->decl->sym->name;
+		m = nn->left->decl->sym->len;
+		if(spos+m-1 >= lensig)
+			return -1;
+		strcpy((char*)&sig[spos], s);
+		spos += m;
+		if(nn->right != nil){
+			if(spos >= lensig)
+				return -1;
+			sig[spos++] = ',';
+		}
+	}
+	if(spos >= lensig)
+		return -1;
+	sig[spos++] = ')';
+	return spos;
+}
+
+static int
+clearrec(Type *t)
+{
+	Decl *id;
+	int n;
+
+	n = 0;
+	for(; t != nil && t->rec; t = t->tof){
+		t->rec = 0;
+		if(t->eq != nil && t->eq->id != 0){
+			t->eq->id = 0;
+			n++;
+		}
+		if(t->kind == Tmodule){
+			for(id = t->tof->ids; id != nil; id = id->next)
+				n += clearrec(id->ty);
+			return n;
+		}
+		if(t->kind == Tadtpick)
+			n += clearrec(t->decl->dot->ty);
+		for(id = t->ids; id != nil; id = id->next)
+			n += clearrec(id->ty);
+		for(id = t->tags; id != nil; id = id->next)
+			n += clearrec(id->ty);
+		for(id = t->polys; id != nil; id = id->next)
+			n += clearrec(id->ty);
+	}
+	return n;
+}
+
+/* must a variable of the given type be zeroed ? (for uninitialized declarations inside loops) */
+int
+tmustzero(Type *t)
+{
+	if(t==nil)
+		return 0;
+	if(tattr[t->kind].isptr)
+		return 1;
+	if(t->kind == Tadtpick)
+		t = t->tof;
+	if(t->kind == Ttuple || t->kind == Tadt)
+		return mustzero(t->ids);
+	return 0;
+}
+
+int
+mustzero(Decl *decls)
+{
+	Decl *d;
+
+	for (d = decls; d != nil; d = d->next)
+		if (tmustzero(d->ty))
+			return 1;
+	return 0;
+}
+
+int
+typeconv(Fmt *f)
+{
+	Type *t;
+	char *p, buf[1024];
+
+	t = va_arg(f->args, Type*);
+	if(t == nil){
+		p = "nothing";
+	}else{
+		p = buf;
+		buf[0] = 0;
+		tprint(buf, buf+sizeof(buf), t);
+	}
+	return fmtstrcpy(f, p);
+}
+
+int
+stypeconv(Fmt *f)
+{
+	Type *t;
+	char *p, buf[1024];
+
+	t = va_arg(f->args, Type*);
+	if(t == nil){
+		p = "nothing";
+	}else{
+		p = buf;
+		buf[0] = 0;
+		stprint(buf, buf+sizeof(buf), t);
+	}
+	return fmtstrcpy(f, p);
+}
+
+int
+ctypeconv(Fmt *f)
+{
+	Type *t;
+	char buf[1024];
+
+	t = va_arg(f->args, Type*);
+	buf[0] = 0;
+	ctprint(buf, buf+sizeof(buf), t);
+	return fmtstrcpy(f, buf);
+}
+
+char*
+tprint(char *buf, char *end, Type *t)
+{
+	Decl *id;
+	Typelist *tl;
+
+	if(t == nil)
+		return buf;
+	if(t->kind >= Tend)
+		return seprint(buf, end, "kind %d", t->kind);
+	switch(t->kind){
+	case Tarrow:
+		buf = seprint(buf, end, "%T->%s", t->tof, t->decl->sym->name);
+		break;
+	case Tdot:
+		buf = seprint(buf, end, "%T.%s", t->tof, t->decl->sym->name);
+		break;
+	case Tid:
+	case Tpoly:
+		buf = seprint(buf, end, "%s", t->decl->sym->name);
+		break;
+	case Tinst:
+		buf = tprint(buf, end, t->tof);
+		buf = secpy(buf ,end, "[");
+		for(tl = t->u.tlist; tl != nil; tl = tl->nxt){
+			buf = tprint(buf, end, tl->t);
+			if(tl->nxt != nil)
+				buf = secpy(buf, end, ", ");
+		}
+		buf = secpy(buf, end, "]");
+		break;
+	case Tint:
+	case Tbig:
+	case Tstring:
+	case Treal:
+	case Tbyte:
+	case Tany:
+	case Tnone:
+	case Terror:
+	case Tainit:
+	case Talt:
+	case Tcase:
+	case Tcasel:
+	case Tcasec:
+	case Tgoto:
+	case Tiface:
+	case Texception:
+	case Texcept:
+		buf = secpy(buf, end, kindname[t->kind]);
+		break;
+	case Tfix:
+		buf = seprint(buf, end, "%s(%v)", kindname[t->kind], t->val);
+		break;
+	case Tref:
+		buf = secpy(buf, end, "ref ");
+		buf = tprint(buf, end, t->tof);
+		break;
+	case Tchan:
+	case Tarray:
+	case Tlist:
+		buf = seprint(buf, end, "%s of ", kindname[t->kind]);
+		buf = tprint(buf, end, t->tof);
+		break;
+	case Tadtpick:
+		buf = seprint(buf, end, "%s.%s", t->decl->dot->sym->name, t->decl->sym->name);
+		break;
+	case Tadt:
+		if(t->decl->dot != nil && !isimpmod(t->decl->dot->sym))
+			buf = seprint(buf, end, "%s->%s", t->decl->dot->sym->name, t->decl->sym->name);
+		else
+			buf = seprint(buf, end, "%s", t->decl->sym->name);
+		if(t->polys != nil){
+			buf = secpy(buf ,end, "[");
+			for(id = t->polys; id != nil; id = id->next){
+				if(t->u.tmap != nil)
+					buf = tprint(buf, end, valtmap(id->ty, t->u.tmap));
+				else
+					buf = seprint(buf, end, "%s", id->sym->name);
+				if(id->next != nil)
+					buf = secpy(buf, end, ", ");
+			}
+			buf = secpy(buf, end, "]");
+		}
+		break;
+	case Tmodule:
+		buf = seprint(buf, end, "%s", t->decl->sym->name);
+		break;
+	case Ttuple:
+		buf = secpy(buf, end, "(");
+		for(id = t->ids; id != nil; id = id->next){
+			buf = tprint(buf, end, id->ty);
+			if(id->next != nil)
+				buf = secpy(buf, end, ", ");
+		}
+		buf = secpy(buf, end, ")");
+		break;
+	case Tfn:
+		buf = secpy(buf, end, "fn");
+		if(t->polys != nil){
+			buf = secpy(buf, end, "[");
+			for(id = t->polys; id != nil; id = id->next){
+				buf = seprint(buf, end, "%s", id->sym->name);
+				if(id->next != nil)
+					buf = secpy(buf, end, ", ");
+			}
+			buf = secpy(buf, end, "]");
+		}
+		buf = secpy(buf, end, "(");
+		for(id = t->ids; id != nil; id = id->next){
+			if(id->sym == nil)
+				buf = secpy(buf, end, "nil: ");
+			else
+				buf = seprint(buf, end, "%s: ", id->sym->name);
+			if(id->implicit)
+				buf = secpy(buf, end, "self ");
+			buf = tprint(buf, end, id->ty);
+			if(id->next != nil)
+				buf = secpy(buf, end, ", ");
+		}
+		if(t->varargs && t->ids != nil)
+			buf = secpy(buf, end, ", *");
+		else if(t->varargs)
+			buf = secpy(buf, end, "*");
+		if(t->tof != nil && t->tof->kind != Tnone){
+			buf = secpy(buf, end, "): ");
+			buf = tprint(buf, end, t->tof);
+			break;
+		}
+		buf = secpy(buf, end, ")");
+		break;
+	default:
+		yyerror("tprint: unknown type kind %d", t->kind);
+		break;
+	}
+	return buf;
+}
+
+char*
+stprint(char *buf, char *end, Type *t)
+{
+	if(t == nil)
+		return buf;
+	switch(t->kind){
+	case Tid:
+		return seprint(buf, end, "id %s", t->decl->sym->name);
+	case Tadt:
+	case Tadtpick:
+	case Tmodule:
+		buf = secpy(buf, end, kindname[t->kind]);
+		buf = secpy(buf, end, " ");
+		return tprint(buf, end, t);
+	}
+	return tprint(buf, end, t);
+}
+
+/* generalize ref P.A, ref P.B to ref P */
+
+/*
+Type*
+tparentx(Type *t1, Type* t2)
+{
+	if(t1 == nil || t2 == nil || t1->kind != Tref || t2->kind != Tref)
+		return t1;
+	t1 = t1->tof;
+	t2 = t2->tof;
+	if(t1 == nil || t2 == nil || t1->kind != Tadtpick || t2->kind != Tadtpick)
+		return t1;
+	t1 = t1->decl->dot->ty;
+	t2 = t2->decl->dot->ty;
+	if(tequal(t1, t2))
+		return mktype(&t1->src.start, &t1->src.stop, Tref, t1, nil);
+	return t1;
+}
+*/
+
+static int
+tparent0(Type *t1, Type *t2)
+{
+	Decl *id1, *id2;
+
+	if(t1 == t2)
+		return 1;
+	if(t1 == nil || t2 == nil)
+		return 0;
+	if(t1->kind == Tadt && t2->kind == Tadtpick)
+		t2 = t2->decl->dot->ty;
+	if(t1->kind == Tadtpick && t2->kind == Tadt)
+		t1 = t1->decl->dot->ty;
+	if(t1->kind != t2->kind)
+		return 0;
+	switch(t1->kind){
+	default:
+		fatal("unknown type %t v %t in tparent", t1, t2);
+		break;
+	case Terror:
+	case Tstring:
+	case Tnone:
+	case Tint:
+	case Tbig:
+	case Tbyte:
+	case Treal:
+	case Tany:
+		return 1;
+	case Texception:
+	case Tfix:
+	case Tfn:
+	case Tadt:
+	case Tmodule:
+	case Tpoly:
+		return tcompat(t1, t2, 0);
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		return tparent0(t1->tof, t2->tof);
+	case Ttuple:
+		for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next)
+			if(!tparent0(id1->ty, id2->ty))
+				return 0;
+		return id1 == nil && id2 == nil;
+	case Tadtpick:
+		return tequal(t1->decl->dot->ty, t2->decl->dot->ty);
+	}
+	return 0;
+}
+
+static Type*
+tparent1(Type *t1, Type *t2)
+{
+	Type *t, *nt;
+	Decl *id, *id1, *id2, *idt;
+
+	if(t1->kind == Tadt && t2->kind == Tadtpick)
+		t2 = t2->decl->dot->ty;
+	if(t1->kind == Tadtpick && t2->kind == Tadt)
+		t1 = t1->decl->dot->ty;
+	switch(t1->kind){
+	default:
+		return t1;
+	case Tref:
+	case Tlist:
+	case Tarray:
+	case Tchan:
+		t = tparent1(t1->tof, t2->tof);
+		if(t == t1->tof)
+			return t1;
+		return mktype(&t1->src.start, &t1->src.stop, t1->kind, t, nil);
+	case Ttuple:
+		nt = nil;
+		id = nil;
+		for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next){
+			t = tparent1(id1->ty, id2->ty);
+			if(t != id1->ty){
+				if(nt == nil){
+					nt = mktype(&t1->src.start, &t1->src.stop, Ttuple, nil, dupdecls(t1->ids));
+					for(id = nt->ids, idt = t1->ids; idt != id1; id = id->next, idt = idt->next)
+						;
+				}
+				id->ty = t;
+			}
+			if(id != nil)
+				id = id->next;
+		}
+		if(nt == nil)
+			return t1;
+		return nt;
+	case Tadtpick:
+		if(tequal(t1, t2))
+			return t1;
+		return t1->decl->dot->ty;
+	}
+}
+
+Type*
+tparent(Type *t1, Type *t2)
+{
+	if(tparent0(t1, t2))
+		return tparent1(t1, t2);
+	return t1;
+}
+
+/*
+ * make the tuple type used to initialize an exception type
+ */
+Type*
+mkexbasetype(Type *t)
+{
+	Decl *id, *new, *last;
+	Type *nt;
+
+	if(!t->cons)
+		fatal("mkexbasetype on non-constant");
+	last = mkids(&t->decl->src, nil, tstring, nil);
+	last->store = Dfield;
+	nt = mktype(&t->src.start, &t->src.stop, Texception, nil, last);
+	nt->cons = 0;
+	new = mkids(&t->decl->src, nil, tint, nil);
+	new->store = Dfield;
+	last->next = new;
+	last = new;
+	for(id = t->ids; id != nil; id = id->next){
+		new = allocmem(sizeof *id);
+		*new = *id;
+		new->cyc = 0;
+		last->next = new;
+		last = new;
+	}
+	last->next = nil;
+	return usetype(nt);
+}
+
+/*
+ * make an instantiated exception type
+ */
+Type*
+mkextype(Type *t)
+{
+	Type *nt;
+
+	if(!t->cons)
+		fatal("mkextype on non-constant");
+	if(t->tof != nil)
+		return t->tof;
+	nt = copytypeids(t);
+	nt->cons = 0;
+	t->tof = usetype(nt);
+	return t->tof;
+}
+
+/*
+ * convert an instantiated exception type to its underlying type
+ */
+Type*
+mkextuptype(Type *t)
+{
+	Decl *id;
+	Type *nt;
+
+	if(t->cons)
+		return t;
+	if(t->tof != nil)
+		return t->tof;
+	id = t->ids;
+	if(id == nil)
+		nt = t;
+	else if(id->next == nil)
+		nt = id->ty;
+	else{
+		nt = copytypeids(t);
+		nt->cons = 0;
+		nt->kind = Ttuple;
+	}
+	t->tof = usetype(nt);
+	return t->tof;
+}
+
+static void
+ckfix(Type *t, double max)
+{
+	int p;
+	vlong k, x;
+	double s;
+
+	s = t->val->rval;
+	if(max == 0.0)
+		k = ((vlong)1<<32)-1;
+	else
+		k = 2*(vlong)(max/s+0.5)+1;
+	x = 1;
+	for(p = 0; k > x; p++)
+		x *= 2;
+	if(p == 0 || p > 32){
+		error(t->src.start, "cannot fit fixed type into an int");
+		return;
+	}
+	if(p < 32)
+		t->val->rval /= (double)(1<<(32-p));
+}
+
+double
+scale(Type *t)
+{
+	Node *n;
+
+	if(t->kind == Tint || t->kind == Treal)
+		return 1.0;
+	if(t->kind != Tfix)
+		fatal("scale() on non fixed point type");
+	n = t->val;
+	if(n->op != Oconst)
+		fatal("non constant scale");
+	if(n->ty != treal)
+		fatal("non real scale");
+	return n->rval;
+}
+
+double
+scale2(Type *f, Type *t)
+{
+	return scale(f)/scale(t);
+}
+
+#define I(x)	((int)(x))
+#define V(x)	((Long)(x))
+#define D(x)	((double)(x))
+
+/* put x in normal form */
+static int
+nf(double x, int *mant)
+{
+	int p;
+	double m;
+
+	p = 0;
+	m = x;
+	while(m >= 1){
+		p++;
+		m /= 2;
+	}
+	while(m < 0.5){
+		p--;
+		m *= 2;
+	}
+	m *= D(1<<16)*D(1<<15);
+	if(m >= D(0x7fffffff) - 0.5){
+		*mant = 0x7fffffff;
+		return p;
+	}
+	*mant = I(m+0.5);
+	return p;
+}
+
+static int
+ispow2(double x)
+{
+	int m;
+
+	nf(x, &m);
+	if(m != 1<<30)
+		return 0;
+	return 1;
+}
+
+static int
+fround(double x, int n, int *m)
+{
+	if(n != 31)
+		fatal("not 31 in fround");
+	return nf(x, m);
+}
+
+static int
+fixmul2(double sx, double sy, double sr, int *rp, int *ra)
+{
+	int k, n, a;
+	double alpha;
+
+	alpha = (sx*sy)/sr;
+	n = 31;
+	k = fround(1/alpha, n, &a);
+	*rp = 1-k;
+	*ra = 0;
+	return IMULX;
+}
+
+static int
+fixdiv2(double sx, double sy, double sr, int *rp, int *ra)
+{
+	int k, n, b;
+	double beta;
+
+	beta = sx/(sy*sr);
+	n = 31;
+	k = fround(beta, n, &b);
+	*rp = k-1;
+	*ra = 0;
+	return IDIVX;
+}
+
+static int
+fixmul(double sx, double sy, double sr, int *rp, int *ra)
+{
+	int k, m, n, a, v;
+	vlong W;
+	double alpha, eps;
+
+	alpha = (sx*sy)/sr;
+	if(ispow2(alpha))
+		return fixmul2(sx, sy, sr, rp, ra);
+	n = 31;
+	k = fround(1/alpha, n, &a);
+	m = n-k;
+	if(m < -n-1)
+		return IMOVW;	/* result is zero whatever the values */
+	v = 0;
+	W = 0;
+	eps = D(1<<m)/(alpha*D(a)) - 1;
+	if(eps < 0){
+		v = a-1;
+		eps = -eps;
+	}
+	if(m < 0 && D(1<<n)*eps*D(a) >= D(a)-1+D(1<<m))
+		W = (V(1)<<(-m)) - 1;
+	if(v != 0 || W != 0)
+		m = m<<2|(v != 0)<<1|(W != 0);
+	*rp = m;
+	*ra = a;
+	return v == 0 && W == 0 ? IMULX0: IMULX1;
+}
+
+static int
+fixdiv(double sx, double sy, double sr, int *rp, int *ra)
+{
+	int k, m, n, b, v;
+	vlong W;
+	double beta, eps;
+
+	beta = sx/(sy*sr);
+	if(ispow2(beta))
+		return fixdiv2(sx, sy, sr, rp, ra);
+	n = 31;
+	k = fround(beta, n, &b);
+	m = k-n;
+	if(m <= -2*n)
+		return IMOVW;	/* result is zero whatever the values */
+	v = 0;
+	W = 0;
+	eps = (D(1<<m)*D(b))/beta - 1;
+	if(eps < 0)
+		v = 1;
+	if(m < 0)
+		W = (V(1)<<(-m)) - 1;
+	if(v != 0 || W != 0)
+		m = m<<2|(v != 0)<<1|(W != 0);
+	*rp = m;
+	*ra = b;
+	return v == 0 && W == 0 ? IDIVX0: IDIVX1;
+}
+
+static int
+fixcast(double sx, double sr, int *rp, int *ra)
+{
+	int op;
+
+	op = fixmul(sx, 1.0, sr, rp, ra);
+	return op-IMULX+ICVTXX;
+}
+
+int
+fixop(int op, Type *tx, Type *ty, Type *tr, int *rp, int *ra)
+{
+	double sx, sy, sr;
+
+	sx = scale(tx);
+	sy = scale(ty);
+	sr = scale(tr);
+	if(op == IMULX)
+		op = fixmul(sx, sy, sr, rp, ra);
+	else if(op == IDIVX)
+		op = fixdiv(sx, sy, sr, rp, ra);
+	else
+		op = fixcast(sx, sr, rp, ra);
+	return op;
+}
+
+int
+ispoly(Decl *d)
+{
+	Type *t;
+
+	if(d == nil)
+		return 0;
+	t = d->ty;
+	if(t->kind == Tfn){
+		if(t->polys != nil)
+			return 1;
+		if((d = d->dot) == nil)
+			return 0;
+		t = d->ty;
+		return t->kind == Tadt && t->polys != nil;
+	}
+	return 0;
+}
+
+int
+ispolyadt(Type *t)
+{
+	return (t->kind == Tadt || t->kind == Tadtpick) && t->polys != nil && !(t->flags & INST);
+}
+
+Decl*
+polydecl(Decl *ids)
+{
+	Decl *id;
+	Type *t;
+
+	for(id = ids; id != nil; id = id->next){
+		t = mktype(&id->src.start, &id->src.stop, Tpoly, nil, nil);
+		id->ty = t;
+		t->decl = id;
+	}
+	return ids;
+}
+
+/* try to convert an expression tree to a type */
+Type*
+exptotype(Node *n)
+{
+	Type *t, *tt;
+	Decl *d;
+	Typelist *tl;
+	Src *src;
+
+	if(n == nil)
+		return nil;
+	t = nil;
+	switch(n->op){
+		case Oname:
+			if((d = n->decl) != nil && d->store == Dtype)
+				t = d->ty;
+			break;
+		case Otype:
+		case Ochan:
+			t = n->ty;
+			break;
+		case Oref:
+			t = exptotype(n->left);
+			if(t != nil)
+				t = mktype(&n->src.start, &n->src.stop, Tref, t, nil);
+			break;
+		case Odot:
+			t = exptotype(n->left);
+			if(t != nil){
+				d = namedot(t->tags, n->right->decl->sym);
+				if(d == nil)
+					t = nil;
+				else
+					t = d->ty;
+			}
+			if(t == nil)
+				t = exptotype(n->right);
+			break;
+		case Omdot:
+			t = exptotype(n->right);
+			break;
+		case Oindex:
+			t = exptotype(n->left);
+			if(t != nil){
+				src = &n->src;
+				tl = nil;
+				for(n = n->right; n != nil; n = n->right){
+					if(n->op == Oseq)
+						tt = exptotype(n->left);
+					else
+						tt = exptotype(n);
+					if(tt == nil)
+						return nil;
+					tl = addtype(tt, tl);
+					if(n->op != Oseq)
+						break;
+				}
+				t = mkinsttype(src, t, tl);
+			}
+			break;
+	}
+	return t;
+}
+
+static char*
+uname(Decl *im)
+{
+	Decl *p;
+	int n;
+	char *s;
+
+	n = 0;
+	for(p = im; p != nil; p = p->next)
+		n += strlen(p->sym->name)+1;
+	s = allocmem(n);
+	strcpy(s, "");
+	for(p = im; p != nil; p = p->next){
+		strcat(s, p->sym->name);
+		if(p->next != nil)
+			strcat(s, "+");
+	}
+	return s;
+}
+
+/* check all implementation modules have consistent declarations 
+ * and create their union if needed
+ */
+Decl*
+modimp(Dlist *dl, Decl *im)
+{
+	Decl *u, *d, *dd, *ids, *dot, *last;
+	Sym *s;
+	Dlist *dl0;
+	long sg, sg0;
+	char buf[StrSize], *un;
+
+	if(dl->next == nil)
+		return dl->d;
+	dl0 = dl;
+	sg0 = 0;
+	un = uname(im);
+	seprint(buf, buf+sizeof(buf), ".m.%s", un);
+	installids(Dglobal, mkids(&dl->d->src, enter(buf, 0), tnone, nil));
+	u = dupdecl(dl->d);
+	u->sym = enter(un, 0);
+	u->sym->decl = u;
+	u->ty = mktype(&u->src.start, &u->src.stop, Tmodule, nil, nil);
+	u->ty->decl = u;
+	last = nil;
+	for( ; dl != nil; dl = dl->next){
+		d = dl->d;
+		ids = d->ty->tof->ids;	/* iface */
+		if(ids != nil && ids->store == Dglobal)	/* .mp */
+			sg = sign(ids);
+		else
+			sg = 0;
+		if(dl == dl0)
+			sg0 = sg;
+		else if(sg != sg0)
+			error(d->src.start, "%s's module data not consistent with that of %s\n", d->sym->name, dl0->d->sym->name);
+		for(ids = d->ty->ids; ids != nil; ids = ids->next){
+			s = ids->sym;
+			if(s->decl != nil && s->decl->scope >= scope){
+				if(ids == s->decl){
+					dd = dupdecl(ids);
+					if(u->ty->ids == nil)
+						u->ty->ids = dd;
+					else
+						last->next = dd;
+					last = dd;
+					continue;
+				}
+				dot = s->decl->dot;
+				if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 1))
+					ids->refs = s->decl->refs;
+				else
+					redecl(ids);
+				ids->init = s->decl->init;
+			}
+		}
+	}
+	u->ty = usetype(u->ty);
+	return u;
+}
+
+static void
+modres(Decl *d)
+{
+	Decl *ids, *id, *n, *i;
+	Type *t;
+
+	for(ids = d->ty->ids; ids != nil; ids = ids->next){
+		id = ids->sym->decl;
+		if(ids != id){
+			n = ids->next;
+			i = ids->iface;
+			t = ids->ty;
+			*ids = *id;
+			ids->next = n;
+			ids->iface = i;
+			ids->ty = t;
+		}
+	}
+}
+
+/* update the fields of duplicate declarations in other implementation modules
+ * and their union
+ */	
+void
+modresolve(void)
+{
+	Dlist *dl;
+
+	dl = impdecls;
+	if(dl->next == nil)
+		return;
+	for( ; dl != nil; dl = dl->next)
+		modres(dl->d);
+	modres(impdecl);
+}
--- /dev/null
+++ b/scripts/startinferno
@@ -1,0 +1,3 @@
+#!/usr/bin/env sh
+emu -g1280x1024 wm/wm
+
--- /dev/null
+++ b/services/httpd/httpd.rewrite
@@ -1,0 +1,10 @@
+# syntax: pattern replacement
+# The most specific pattern wins, and is applied once (no rescanning).
+# Leave off trailing slash if pattern is a directory.
+# Replacements starting with http:// return "Permanently moved" 
+# message.
+# e.g the following line aliases requests for / to requests for
+# /usr/mig/html
+
+# /   /usr/mig/html
+
--- /dev/null
+++ b/services/httpd/httpd.suff
@@ -1,0 +1,110 @@
+#suffix		generic type	specific type	encoding
+.C		text		plain		-		# C++ program
+.Z		-		-		x-compress
+.a		application	octet-stream	-		# [Mosaic]
+.ada		text		plain		-		# ada program
+.ai		application	postscript	-		# [Mosaic]
+.aif		audio		x-aiff		-
+.aifc		audio		x-aiff		-
+.aiff		audio		x-aiff		-
+.au		audio		basic		-		# sun audio
+.avi		video		x-msvideo	-		# [Mosaic]
+.awk		text		plain		-		# awk program
+.bas		text		plain		-		# basic program
+.bbl		text		plain		-		# BibTex output
+.bcpio		application	x-bcpio		-		# [Mosaic]
+.bib		text		plain		-		# BibTex input
+.c		text		plain		-		# C program
+.c++		text		plain		-		# C++ program
+.cc		text		plain		-		# [Mosaic]
+.cdf		application	x-netcdf	-
+.cpio		application	x-cpio		-
+.cpp		text		plain		-		# DOS C++ program
+.dat		text		plain		-		# AMPL et al.
+.diff		text		plain		-
+.dvi		application	x-dvi		-		# TeX output
+.enc		application	octet-stream	-		# encrypted file
+.eps		application	postscript	-
+.etx		text		x-setext	-		# [Mosaic]
+.exe		application	octet-stream	-		# DOS executable
+.executable	application	octet-stream	-		# DOS executable
+.exz		application	octet-stream	x-gzip		# gziped DOS executable
+.f		text		plain		-		# fortran-77 program
+.flc		video		x-flc		-
+.fli		video		x-fli		-
+.gif		image		gif		-
+.gtar		application	x-gtar		-		# [Mosaic]
+.gz		-		-		x-gzip		# gziped file
+.h		text		plain		-		# C header file
+.hdf		application	x-hdf		-
+.hqx		application	octet-stream	-		# Mac BinHex
+.htm		text		html		-
+.html		text		html		-
+.ief		image		ief		-		# [Mosaic]
+.jfif		image		jpeg		-		# [Mosaic]
+.jfif-tbnl	image		jpeg		-		# [Mosaic]
+.jpe		image		jpeg		-		# [Mosaic]
+.jpeg		image		jpeg		-
+.jpg		image		jpeg		-
+.latex		application	x-latex		-		# [Mosaic]
+.ltx		application	x-latex		-
+.man		application	x-troff-man	-		# [Mosaic]
+.me		application	x-troff-me	-		# [Mosaic]
+.mime		message		rfc822		-		# [Mosaic]
+.mod		text		plain		-		# AMPL et al.
+.mov		video		quicktime	-		# [Mosaic]
+.movie		video		x-sgi-movie	-		# [Mosaic]
+.mpe		video		mpeg		-		# [Mosaic]
+.mpeg		video		mpeg		-
+.mpg		video		mpeg		-
+.ms		application	x-troff-ms	-		# [Mosaic]
+.mv		video		x-sgi-movie	-		# [Mosaic]
+.nc		application	x-netcdf	-		# [Mosaic]
+.o		application	octet-stream	-		# [Mosaic]
+.oda		application	oda		-		# [Mosaic]
+.pbm		image		x-portable-bitmap	-	# [Mosaic]
+.pdf		application	pdf		-		# Adobe Portable Document Format
+.pgm		image		x-portable-graymap	-	# [Mosaic]
+.pl		text		plain		-		# [Mosaic]
+.pnm		image		x-portable-anymap	-	# [Mosaic]
+.ppm		image		x-portable-pixmap	-	# [Mosaic]
+.ps		application	postscript	-
+.qt		video		quicktime	-		# [Mosaic]
+.r		text		plain		-		# ratfor program
+.ras		image		x-cmu-rast	-		# [Mosaic]
+.rc		text		plain		-		# rc
+.rfr		text		plain		-		# refer
+.rgb		image		x-rgb		-		# [Mosaic]
+.roff		application	x-troff		-		# [Mosaic]
+.rtf		application	rtf		-		# [Mosaic]
+.rtx		text		richtext 	-	# MIME richtext	  [Mosaic]
+.sh		application	x-shar		-
+.shar		application	x-shar		-
+.snd		audio		basic		-
+.sv4cpio	application	x-sv4cpio	-		# [Mosaic]
+.sv4crc		application	x-sv4crc	-		# [Mosaic]
+.t		application	x-troff		-		# [Mosaic]
+.tar		application	x-tar		-		# [Mosaic]
+.taz		application	x-tar		x-compress
+.tcl		application	x-tcl		-
+.tex		application	x-tex		-		# Tex input
+.texi		application	x-texinfo	-		# [Mosaic]
+.texinfo	application	x-texinfo	-		# [Mosaic]
+.text		text		plain		-		# [Mosaic]
+.tgz		application	x-tar		x-gzip
+.tif		image		tiff		-
+.tiff		image		tiff		-
+.toc		text		plain		-		# table of contents
+.tr		application	x-troff		-		# [Mosaic]
+.trz		application	x-tar		x-compress
+.tsv		text		tab-separated-values	-	# [Mosaic]
+.txt		text		plain		-
+.ustar		application	x-ustar		-		# [Mosaic]
+.wav		audio		x-wav		-
+.wsrc		application	x-wais-source	-		# [Mosaic]
+.xbm		image		x-xbitmap	-		# X bitmap
+.xpm		image		x-xpixmap	-		# [Mosaic]
+.xwd		image		x-xwindowdump	-		# [Mosaic]
+.z		-		-		x-compress
+.Z		-		-		x-compress
+.zip		application	zip		-
--- /dev/null
+++ b/services/webget/help.html
@@ -1,0 +1,58 @@
+<HTML>
+<HEAD>
+<TITLE>Browser Help</TITLE>
+</HEAD>
+<BODY BGCOLOR="#90EE90">
+
+<P>
+
+Click on the buttons to travel around during a browsing session.
+<P>
+
+<IMG SRC="file:///icons/charon/redleft.bit"> Goes back to the previous page.
+<P>
+<IMG SRC="file:///icons/charon/redright.bit"> Goes forward one page beyond the page that you've gone back to.
+<P>
+<IMG SRC="file:///icons/charon/circarrow.bit"> Reloads the current page.
+<P>
+<IMG SRC="file:///icons/charon/stop.bit"> Stops loading the current page.
+<P>
+<IMG SRC="file:///icons/charon/home.bit"> Returns to your home page.
+<P>
+The location field displays your current location on the internet. When you are accessing a web site, the location field
+will also include a status bar that tells you what's happening with your web site connection.
+<P>
+The <b>History</b> is a list of web sites that you have visited since you opened the browser.
+<P>
+To view the History touch
+<IMG SRC="file:///icons/charon/history.bit">
+and then touch the desired web page to return to that page.
+<P>
+<b>Bookmarks</b> are saved URLs (web site addresses).
+<P>
+To save a bookmark, access the desired web page and touch
+<IMG SRC="file:///icons/charon/edit.bit"> .
+<P>
+To view saved bookmarks, touch
+<IMG SRC="file:///icons/charon/bookmark.bit"> .
+<P>
+To access a bookmark, touch 
+<IMG SRC="file:///icons/charon/bookmark.bit"> 
+and then touch the desired bookmark to go to that page.
+<P>
+<b>
+User Preferences</b> allow you to make changes in how your web browser performs.
+<P>
+Touch 
+<IMG SRC="file:///icons/charon/conf.bit"> 
+to change the User Preferences.
+<P>
+Check Automatically Load Images if you want images on web sites to be
+loaded. Enter the URL you want for your home page. Touch Ok to save the information.
+<P>
+<IMG SRC="file:///icons/charon/help.bit"> Displays this help file.
+<P>
+Touch a down arrow to scroll down a line in a web site. Touch an up arrow to scroll up in a web site.
+You may also use the PgUp and PgDn keys on your keyboard to scroll a full page at a time. The Home and End keys allow you to jump to the beginning and end of a web page. 
+</BODY>
+</HTML>
binary files /dev/null b/services/webget/inferno.gif differ
--- /dev/null
+++ b/services/webget/start.html
@@ -1,0 +1,17 @@
+<head><title>Welcome to Inferno</title></head>
+<body bgcolor=#ff5500 link=#000000>
+<font color=#ffffff>
+<br><br>
+<center>
+<table bgcolor=#000000>
+<tr>
+<td valign=middle><img src="vnlogo.gif">
+<td valign=middle><img src="inferno.gif">
+</table>
+<br><br>
+<h2>brought to you by 
+<a href="http://www.vitanuova.com">
+Vita Nuova
+</a></h2>
+</center>
+</body>
binary files /dev/null b/services/webget/vnlogo.gif differ
--- /dev/null
+++ b/tools/NOTICE
@@ -1,0 +1,23 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 2000-2007 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License (`LGPL') as published by
+the Free Software Foundation; either version 2 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 Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--- /dev/null
+++ b/tools/db/infdb.c
@@ -1,0 +1,998 @@
+//
+//	infdb - NT data base daemon for Inferno
+//
+//	Copyright 1997 Lucent Technologies
+//
+//	May 1997
+//
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef WIN32
+#include <winsock.h>
+#include <SQL.h>
+#include <SQLEXT.h>
+#else
+#include <unistd.h>
+#include <sql.h>
+#include <sqlext.h>
+#define max(a, b)        ((a) > (b) ? (a) : (b))
+#define strnicmp strncasecmp
+#endif
+
+#define MAXCOLS	100
+#define BUFSIZE	8192
+#define REQ_HEADER_SIZE 18
+#define RES_HEADER_SIZE 22
+#define OFFSET_LENGTH 2
+#define OFFSET_STREAM 14
+#define OFFSET_REQ_DATA 18
+#define OFFSET_RETURN 18
+#define OFFSET_RES_DATA 22
+
+#define	CONN_ALLOC_FAIL		1
+#define STREAM_ALLOC_FAIL	2
+#define STREAM_BAD_ID		3
+#define LAST_ERROR_NO		4
+
+
+//
+//	Deal with one connection.  Use stdin and stdout to read and write messages.
+//  Each incoming message is answered before reading the next incoming message.
+//
+
+typedef int STATUS;
+#define			OK		 0
+#define			WARN	-1
+#define			ERR		-2
+
+typedef struct {
+	int		state;
+#define			SQLC_FREE	0
+#define			SQLC_INUSE	1
+	int		connid;
+	int		refcount;
+	UCHAR	user[48];
+	UCHAR	passwd[48];
+	UCHAR	dbname[48];
+	UCHAR	errmsg[256];
+	HDBC	hdbc;
+} SQLConn;
+
+
+typedef struct {
+	int		state;
+#define			SQLS_FREE	0
+#define			SQLS_INUSE	1
+	int		streamid;
+	int		connid;
+	HSTMT	hstmt;
+	UCHAR	errmsg[256];
+	UCHAR	colname[MAXCOLS][32];
+	SWORD	coltype[MAXCOLS];
+	SWORD	colnamelen;
+	SWORD	nullable;
+	UDWORD	collen[MAXCOLS];
+	SWORD	scale;
+	SDWORD	outlen[MAXCOLS];
+	UCHAR	*data[MAXCOLS];
+	SWORD	nresultcols;
+	SDWORD	rowcount;
+	SWORD	rownum;
+	RETCODE	rc;
+	UCHAR	*setdata[MAXCOLS];
+	SDWORD	setdatalen[MAXCOLS];
+} SQLStream;
+
+
+typedef struct {
+	HENV		henv;
+	int			maxconn;
+	int			numconn;
+	SQLConn		**scarray;
+	int			maxstream;
+	int			numstream;
+	SQLStream	**ssarray;
+} SQLEnv;
+
+
+typedef struct {
+	char	mtype;
+	char	version;
+	int		nbytes;
+	int		sstream;
+	int		retcode;
+	int		bytesNotRead;
+	char	*data;
+} DBMSG, *DBMSGP;
+
+
+int			getCommand		(DBMSGP msgp, UCHAR *buf, int bufsiz);
+void		sendResponse	(char type, int lendata, int sstream, int retcode, char *data);
+void		sendError		(char *errmsg, int sstream);
+void		print_err		(SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz);
+UDWORD		display_size	(SWORD coltype, UDWORD collen, UCHAR *colname);
+
+STATUS		newSqlEnv		(SQLEnv **sqle);
+STATUS		freeSqlEnv		(SQLEnv **sqle);
+
+STATUS		newSqlConn		(SQLEnv *sqle, char *info, int *connid);
+STATUS		mapSqlConn		(SQLEnv *sqle, int connid, SQLConn **sqlc);
+STATUS		freeSqlConn		(SQLEnv *sqle, int connid);
+
+STATUS		newSqlStream	(SQLEnv *sqle, int connid, int *streamid);
+STATUS		mapSqlStream	(SQLEnv *sqle, int streamid, SQLStream **sqls);
+STATUS		freeSqlStream	(SQLEnv *sqle, int streamid);
+
+STATUS		parseConnInfo	(SQLConn *sqlc, char *info);
+
+char	*iError[] = {
+	"INFDB: DB connection allocation failed",
+	"INFDB: couldn't allocate SQL stream",
+	"INFDB: bad SQL stream identifier"
+};
+
+int
+main(int argc, char *argv[])
+{
+	int			i;
+	int			notdone = 1;
+	int			infErrno;
+	DBMSG		msg;
+	SQLEnv		*sqle = NULL;
+	SQLStream	*sqls;
+	char		buf[BUFSIZE];
+	char		outbuf[BUFSIZE];
+	char		errmsg[256];
+	STATUS		rc;
+
+ 	//	We just have to talk to stdin and stdout.  However, stdout may be open
+	//	in text mode, which is bad for data.  Set it to binary mode.
+
+#ifdef WIN32
+	_setmode(0, _O_BINARY);
+	_setmode(1, _O_BINARY);
+#endif
+
+	rc = newSqlEnv(&sqle);
+	if ( rc != OK ) {
+		sendError("INFDB: Failed to allocate SQL environment.", -1);
+		return -1;
+	}
+
+    while ( notdone ) {
+		int		bytesRead;
+
+		bytesRead = 0;
+		if ( (bytesRead = getCommand(&msg, buf, sizeof(buf))) <= 0 ) {
+			continue;
+		}
+		msg.retcode = 0;
+		infErrno = 0;
+
+		switch ( msg.mtype ) {
+		  // Initiate a new connection.
+		  case 'I':
+		  {
+			int connid;
+
+			rc = newSqlConn(sqle, msg.data, &connid);
+			if ( rc != OK ) {
+				infErrno = CONN_ALLOC_FAIL;
+				break;
+			}
+			/*
+			// Need a new SQLStream to make subsequent requests.
+			rc = newSqlStream(sqle, connid, &streamid);
+			if ( rc != OK )	{
+				infErrno = STREAM_ALLOC_FAIL;
+				break;
+		    }
+
+			sprintf(outbuf, "%d", streamid);
+			*/
+			sprintf(outbuf, "%d", connid);
+			sendResponse('i', strlen(outbuf), msg.sstream, 0, outbuf);
+			break;
+		  }
+
+
+		  case 'O':
+		  {
+			// open an SQL stream.
+			int connid, streamid;
+
+			connid = atoi(msg.data);
+			rc = newSqlStream(sqle, connid, &streamid);
+			if (rc != OK) {
+				infErrno = STREAM_ALLOC_FAIL;
+				break;
+			}
+
+			sprintf(outbuf, "%d", streamid);
+			sendResponse('o', strlen(outbuf), msg.sstream, 0, outbuf);
+			break;
+		  }
+
+
+		  case 'K':
+			// klose an SQL stream
+			rc = freeSqlStream(sqle, msg.sstream);
+			sendResponse('k', 0, msg.sstream, 0, "");
+			break;
+
+
+		  case 'C': 
+			// request number of columns
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+
+			sprintf(outbuf, "%d", sqls->nresultcols);
+			sendResponse('c', strlen(outbuf), msg.sstream, 0, outbuf);
+			break;
+
+
+		  case 'N':																		 
+			// fetch next row
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			sqls->errmsg[0] = '\0';
+			rc = SQLFetch(sqls->hstmt);
+			if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO ) {
+				sqls->rownum++;
+				//	if ( rc == SQL_SUCCESS_WITH_INFO ) {
+					//	fprintf(stderr, "SQLFetch got SQL_SUCCESS_WITH_INFO\n");
+				//	}
+				/*		Get the data for all columns	*/
+				for ( i = 0; i < sqls->nresultcols; i++ ) {
+					rc = SQLGetData(sqls->hstmt, (UWORD)(i+1),
+									(sqls->coltype[i] == SQL_LONGVARBINARY ||
+									 sqls->coltype[i] == SQL_LONGVARCHAR) ? SQL_C_DEFAULT :
+																			SQL_C_CHAR,
+									sqls->data[i], sqls->collen[i], &sqls->outlen[i]);
+					if ( rc == SQL_SUCCESS_WITH_INFO &&
+											(UDWORD) sqls->outlen[i] > sqls->collen[i] ) {
+						UCHAR	*tmp;
+
+						tmp = (UCHAR *) realloc(sqls->data[i], sqls->outlen[i]+1);
+						if ( tmp != NULL ) {
+							SDWORD	dummy;
+							sqls->data[i] = tmp;
+							rc = SQLGetData(sqls->hstmt, (UWORD)(i+1), SQL_C_DEFAULT,
+											&tmp[sqls->collen[i]], sqls->outlen[i], &dummy);
+							sqls->collen[i] = sqls->outlen[i];
+							sqls->data[i][sqls->outlen[i]] = 0;
+						}
+					}
+					else if ( rc != SQL_SUCCESS ) {
+						sprintf(sqls->errmsg, "Problem retrieving data from data base, col %d", i+1);
+						msg.retcode = 2;
+					}
+				}
+			}
+			else if ( rc == SQL_NO_DATA_FOUND ) {
+				sqls->rownum = 0;
+				msg.retcode = 1;
+			}
+			else {
+				sqls->rownum = -1;
+				//	Probably should get some status from ODBC for message
+				sprintf(sqls->errmsg, "Error occurred in fetching data");
+			}
+			if ( sqls->rownum < 0 ) {
+				sendError(errmsg, msg.sstream);
+			}
+			else {
+				sprintf(outbuf, "%d", sqls->rownum);
+				//	rownum should be <= rowcount
+				sendResponse('n', strlen(outbuf), msg.sstream, msg.retcode, outbuf);
+			}
+			break;
+
+
+		  case 'H':
+			// request an error message, if any
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			sendError(sqls->errmsg, msg.sstream);
+			sqls->errmsg[0] = 0;
+			break;
+
+
+		  case 'P':
+			// request write data
+			// in Inferno, param nums start at 0; in ODBC/SQL, they start at 1
+			// This leaves outdatalen[0] unused, hence available as final SQLBindParameter arg.
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			sqls->errmsg[0] = 0;
+			if ( (i = atoi(msg.data) + 1) < 1 || i >= MAXCOLS ) {
+				sendError("Illegal param number", msg.sstream);
+			}
+			else {
+				int		len;
+				char	*p = msg.data + 4;		// data points to param number
+
+				len = msg.nbytes - 4;			// number of data chars
+				if ( len < 0 ) {
+					sendError("Write phase error II", msg.sstream);
+					break;
+				}
+				if ( sqls->setdata[i] != NULL ) {
+					free(sqls->setdata[i]);
+				}
+				sqls->setdata[i] = (char *) malloc(len + 1);
+				if ( sqls->setdata[i] == NULL ) {
+					sendError("Allocation error in server", msg.sstream);
+					break;
+				}
+				//	Copy data we have into buffer, and if we don't have it all yet,
+				//	try to get the rest.
+				sqls->setdatalen[i] = len++;		// adjust len for trailing \n
+				bytesRead = &buf[bytesRead] - p;	// number data bytes we have read
+				memcpy(sqls->setdata[i], p, bytesRead);
+				len -= bytesRead;					// number bytes still to read,
+				while ( len > 0 ) {
+					int	n;
+
+					if ( (n = read(0, sqls->setdata[i] + bytesRead, len)) <= 0 ) {
+						break;
+					}
+					bytesRead += n;
+					len -= n;
+				}
+				if ( len > 0 ) {
+					sendError("Couldn't read all of parameter", msg.sstream);
+					break;
+				}
+				rc = SQLBindParameter(sqls->hstmt, (UWORD)i, SQL_PARAM_INPUT,
+									SQL_C_BINARY, SQL_LONGVARBINARY,
+									sqls->setdatalen[i], 0, (PTR) i, 0, sqls->setdatalen);
+				if ( rc != SQL_SUCCESS ) {
+					sendError("BindParameter failed: maybe not supported", msg.sstream);
+					break;
+				}
+				sqls->setdatalen[0] = SQL_LEN_DATA_AT_EXEC(0);
+				sprintf(outbuf, "%d", bytesRead - 1);
+				sendResponse('p', strlen(outbuf), msg.sstream, 0, outbuf);
+			}
+			break;
+
+
+		  case 'R':
+			// request read data
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols || sqls->rownum <= 0 ) {
+				sendError(sqls->rownum <= 0 ? "No current row" : "Illegal column number", msg.sstream);
+			}
+			else if ( sqls->outlen[i] == SQL_NULL_DATA || sqls->outlen[i] == SQL_NO_TOTAL ) {
+				sendResponse('r', 0, msg.sstream, 0, "");
+			}
+			else {
+				if ( sqls->coltype[i] == SQL_VARCHAR ) {
+					sqls->outlen[i] = strlen(sqls->data[i]);
+				}
+				sendResponse('r', sqls->outlen[i], msg.sstream, 0, sqls->data[i]);
+			}
+			break;
+
+
+		  case 'T':
+			// request column title
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			if ( (i = atoi(msg.data)) < 0 || i >= sqls->nresultcols ) {
+				sendError("Illegal column number", msg.sstream);
+			}
+			else {
+				sendResponse('t', strlen(sqls->colname[i]), msg.sstream, 0, sqls->colname[i]);
+			}
+			break;
+
+
+		  case 'W':	    
+			// execute command
+			rc = mapSqlStream(sqle, msg.sstream, &sqls);
+			if ( rc != OK ) {
+				infErrno = STREAM_BAD_ID;
+				break;
+			}
+		
+			if ( sqls->hstmt ) {
+				SQLFreeStmt(sqls->hstmt, SQL_CLOSE);
+				SQLFreeStmt(sqls->hstmt, SQL_UNBIND);
+			}
+			//	Look for special extensions
+			if ( strnicmp(msg.data, "commit", 6) == 0 ) {
+				SQLConn	*sqlc;
+
+				rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+				rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_COMMIT);
+			}
+			else if ( strnicmp(msg.data, "rollback", 8) == 0 ) {
+				SQLConn	*sqlc;
+
+				rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+				rc = SQLTransact(SQL_NULL_HENV, sqlc->hdbc, SQL_ROLLBACK);
+			}
+
+			else if ( strnicmp(msg.data, "tables", 6) == 0 ) {
+				rc = SQLTables(sqls->hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
+			}
+			else if ( strnicmp(msg.data, "columns", 7) == 0 ) {
+				UCHAR	*tbl;
+				
+				for ( tbl = msg.data+8; *tbl == ' ' || *tbl == '\t'; tbl++ ) { }
+
+				rc = SQLColumns(sqls->hstmt, NULL, 0, NULL, 0, tbl, SQL_NTS, NULL, 0);
+			}
+			else {
+				rc = SQLExecDirect(sqls->hstmt, msg.data, SQL_NTS);
+			}
+			outbuf[0] = '\0';
+			while ( rc == SQL_NEED_DATA ) {
+				PTR	pToken;
+//				SDWORD	pnum;
+
+				rc = SQLParamData(sqls->hstmt, &pToken);
+//				pnum = (SDWORD) pToken;
+#define pnum (int)pToken
+				if ( rc == SQL_NEED_DATA ) {
+					int	retcode;
+
+					if ( sqls->setdata[pnum] == NULL || sqls->setdatalen[pnum] <= 0 ) {
+						sprintf(outbuf, "Parameter %d not set\n", pnum);
+						break;
+					}
+					for ( i = 0; i < sqls->setdatalen[pnum]; ) {
+						int		togo;
+
+						togo = 1024;
+						if ( sqls->setdatalen[pnum] - i < 1024 ) {
+							togo = sqls->setdatalen[pnum] - i;
+						}
+						retcode = SQLPutData(sqls->hstmt,
+													sqls->setdata[pnum] + i, togo);
+						i += togo;
+						if ( retcode != SQL_SUCCESS ) {
+							print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+							break;
+						}
+					}
+					if ( retcode != SQL_SUCCESS /* && retcode != SQL_SUCCESS_WITH_INFO */) {
+						break;
+					}
+				}
+			}
+			if ( rc != SQL_SUCCESS ) {
+				strcat(outbuf, "Command execution failed\n");
+				switch ( rc ) {
+				case SQL_SUCCESS_WITH_INFO:
+					strcat(outbuf, ": SQL_SUCCESS_WITH_INFO");
+					print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+					break;
+				case SQL_ERROR:
+					strcat(outbuf, ": SQL_ERROR");
+					print_err(sqle, -1, msg.sstream, &outbuf[strlen(outbuf)], sizeof (outbuf) - strlen(outbuf));
+					break;
+				case SQL_NEED_DATA:
+					strcat(outbuf, ": SQL_NEED_DATA");
+					break;
+				case SQL_STILL_EXECUTING:
+					strcat(outbuf, ": SQL_STILL_EXECUTING");
+					break;
+				case SQL_INVALID_HANDLE:
+					strcat(outbuf, ": SQL_INVALID_HANDLE");
+					break;
+				}
+				sendError(outbuf, msg.sstream);
+				break;
+			}
+			SQLNumResultCols(sqls->hstmt, &sqls->nresultcols);
+			if ( sqls->nresultcols == 0 ) {	//	was not 'select' command
+				SQLRowCount(sqls->hstmt, &sqls->rowcount);	//	we don't use this, do we?
+			}
+			else {				//	get the column labels, save for later
+				for ( i = 0; i < sqls->nresultcols; i++ ) {
+					int	newlen;
+
+					SQLDescribeCol(sqls->hstmt, (UWORD) (i+1), sqls->colname[i],
+							(SWORD)sizeof(sqls->colname[i]),
+							&sqls->colnamelen, &sqls->coltype[i], &sqls->collen[i],
+							&sqls->scale, &sqls->nullable);
+					sqls->colname[i][sqls->colnamelen] = 0;
+					//	Adjust the length, since we are converting everything to strings.
+					if ( (newlen = display_size(sqls->coltype[i], sqls->collen[i],
+															sqls->colname[i])) != 0 ) {
+						sqls->collen[i] = newlen;
+					}
+					if ( sqls->collen[i] == 0 ) {
+						sqls->collen[i] = BUFSIZE;
+					}
+					sqls->data[i] = (UCHAR *) malloc(sqls->collen[i] + 1);
+				/*
+					SQLBindCol(sqls->hstmt, (UWORD) (i+1), newlen > 0 ? SQL_C_CHAR : SQL_C_DEFAULT,
+													sqls->data[i], sqls->collen[i], &sqls->outlen[i]);
+				 */
+				}
+				sqls->rownum = 0;
+			}
+			sendResponse('w', 0, msg.sstream, 0, "");
+			break;
+
+
+		  case 'X':
+			notdone = 0;
+			break;
+
+
+		  default:
+			sprintf(sqls->errmsg, "Unknown command: %c", msg.mtype);
+			sendError(sqls->errmsg, msg.sstream);
+			sqls->errmsg[0] = '\0';
+			break;
+		}		// end of switch (msg.mtype)
+		if ( infErrno > 0 && infErrno < LAST_ERROR_NO ) {
+			sendError(iError[infErrno - 1], msg.sstream);
+		}
+    }		// end of while (notdone)
+	rc = freeSqlEnv(&sqle);
+
+	return 0;
+}
+
+//
+//	All the incoming commands should end with a newline character.
+//	We read until we get one.  Then we verify that we have read as
+//  many bytes as the count in message says we should.
+//
+int
+getCommand(DBMSGP msgp, UCHAR *buf, int bufsiz)
+{
+	int		bytesRead = 0;
+	int		rc = 0;
+
+	msgp->mtype = '\0';
+	while ( bufsiz > 0 && (rc = read(0, &buf[bytesRead], bufsiz)) > 0 ) {
+		bytesRead += rc;
+		bufsiz -= rc;
+		msgp->bytesNotRead -= rc;
+		if ( msgp->mtype == '\0' && bytesRead >= REQ_HEADER_SIZE ) {
+			if ( (msgp->version = buf[1]) != '1' ) {	// wrong version, give up
+				char	*wrong_version = "Message has wrong version number";
+				sendResponse('h', strlen(wrong_version), 0, 0, wrong_version);
+				return -1;
+			}
+			msgp->mtype = buf[0];
+			msgp->nbytes = atoi(buf+OFFSET_LENGTH);
+			msgp->sstream = atoi(buf+OFFSET_STREAM);
+			msgp->data = buf+OFFSET_REQ_DATA;
+			msgp->bytesNotRead = REQ_HEADER_SIZE + msgp->nbytes + 1 - bytesRead;
+			if ( bufsiz > msgp->bytesNotRead ) {
+				bufsiz = msgp->bytesNotRead;
+			}
+		}
+	}
+	if ( rc < 0 ) {
+		//	log a problem
+		//	fprintf(stderr, "Problem reading from client\n");
+		return rc;
+	}
+	if ( msgp->bytesNotRead == 0 ) {
+		msgp->data[msgp->nbytes] = 0;		// discard final newline
+	}
+	return bytesRead;
+}
+
+
+void
+sendResponse(char type, int lendata, int sstream, int retcode, char *data)
+{
+	char	hdr[RES_HEADER_SIZE+2];
+
+	sprintf(hdr, "%c1%11d %3d %3d ", type, lendata, sstream, retcode);
+	write(1, hdr, RES_HEADER_SIZE);
+	write(1, data, lendata);
+	write(1, "\n", 1);
+}
+
+
+void
+sendError(char *errmsg, int sstream)
+{
+	sendResponse('h', strlen(errmsg), sstream, 0, errmsg);
+}
+
+
+void
+print_err(SQLEnv *sqle, int connid, int streamid, UCHAR * buf, int bufsiz)
+{
+	RETCODE		rc;
+	UCHAR		stateString[40];
+	SDWORD		native;
+	SWORD		msglen;
+	SQLConn		*sqlc;
+	SQLStream	*sqls;
+	HENV		*henv;
+	HDBC		*hdbc;
+	HSTMT		*hstmt;
+
+	henv = sqle->henv;
+
+	rc = mapSqlConn(sqle, connid, &sqlc);
+	hdbc = rc == OK ? sqlc->hdbc : SQL_NULL_HDBC;
+
+	rc = mapSqlStream(sqle, streamid, &sqls);
+	hstmt = rc == OK ? sqls->hstmt : SQL_NULL_HSTMT;
+	
+	rc = SQLError(henv, hdbc, hstmt, stateString, &native, buf, (SWORD) bufsiz, &msglen);
+}
+
+
+#define MAX_NUM_PRECISION 15
+
+/* Define max length of char string representation of number as:      */
+/*   =  max(precision) + leading sign + E + exp sign + max exp length */
+/*   =  15             + 1            + 1 + 1        + 2              */
+/*   =  15 + 5                                                        */
+
+#define MAX_NUM_STRING_SIZE (MAX_NUM_PRECISION + 5)
+
+UDWORD
+display_size(SWORD coltype, UDWORD collen, UCHAR *colname)
+{
+switch (coltype) {
+
+     case SQL_CHAR:
+     case SQL_VARCHAR:
+          return max(collen, strlen(colname));
+
+     case SQL_SMALLINT:
+	 case SQL_TINYINT:
+	 case SQL_BIT:
+          return max(6, strlen(colname));
+
+     case SQL_INTEGER:
+          return max(11, strlen(colname));
+
+	 case SQL_BIGINT:
+		  return max(30, strlen(colname));
+
+	 case SQL_DATE:
+	 case SQL_TIME:
+	 case SQL_TIMESTAMP:
+		  return max(50, strlen(colname));
+
+     case SQL_DECIMAL:
+     case SQL_NUMERIC:
+     case SQL_REAL:
+     case SQL_FLOAT:
+     case SQL_DOUBLE:
+          return(max(MAX_NUM_STRING_SIZE, strlen(colname)));
+
+	 case SQL_LONGVARBINARY:
+	 case SQL_LONGVARCHAR:
+		  return BUFSIZE;
+
+     /* Note that this function only supports the core data types. */
+	 /* For unknown data types, the caller should assume binary data */
+     default:
+          /* fprintf(stderr, "Unknown datatype, %d\n", coltype); */
+		  return 0;
+     }
+}
+
+
+STATUS
+newSqlEnv(SQLEnv **sqle)
+{
+	SQLEnv	*newenv;
+	STATUS	rc;
+
+	newenv = (SQLEnv *) calloc(1, sizeof(SQLEnv));
+	if (newenv == NULL) {
+		return ERR;
+	}
+
+	rc = SQLAllocEnv(&newenv->henv);
+	if ( rc != SQL_SUCCESS) {
+		free (newenv);
+		return ERR;
+	}
+
+	*sqle = newenv;
+	return OK;
+}
+
+
+STATUS
+freeSqlEnv(SQLEnv **sqle)
+{
+	int		i;
+	STATUS	rc;
+
+	for (i = 0; i < (*sqle)->maxstream; i++) {
+		// Free this stream.
+		// Connection will be freed automatically.
+		rc = freeSqlStream(*sqle, i);
+	}
+	// dealloc the stream structures
+	// dealloc the connect structures
+	SQLFreeEnv((*sqle)->henv);
+	// dealloc the env structure
+
+	return OK;
+}
+
+
+STATUS
+mapSqlConn(SQLEnv *sqle, int connid, SQLConn **sqlc)
+{
+	if ( connid >= 0 && connid < sqle->maxconn ) {
+		*sqlc = sqle->scarray[connid];
+		if ( (*sqlc)->state == SQLC_INUSE )
+			return OK;
+	}
+	return ERR;
+}
+
+
+STATUS
+newSqlConn(SQLEnv *sqle, char *info, int *connid)
+{
+	SQLConn		**newarray, *sqlc;
+	int			newid = -1, i;
+	STATUS		rc;
+
+	*connid = -1;
+
+	// Connect to the database.
+	// Search for an available connection structure to reuse
+	for ( i = 0; i < sqle->maxconn; i++ ) {
+		sqlc = sqle->scarray[i];
+		if ( sqlc != NULL && sqlc->state == SQLC_FREE ) {
+			newid = i;
+			break;
+		}
+	}
+	
+	if ( newid == -1 ) {
+		// Assign a new connection id
+		newid = sqle->maxconn++;
+
+		// Extend the connection pointer array
+		newarray = (SQLConn **) realloc((char *) sqle->scarray,
+			sqle->maxconn * sizeof(SQLConn*));
+		if ( newarray == NULL ) {
+			return ERR;
+		}
+		sqle->scarray = newarray;
+
+		// Allocate a new connection structure
+		sqlc = (SQLConn *) calloc(1, sizeof(SQLConn));
+		if ( sqlc == NULL ) {
+			return ERR;
+		}
+		sqle->scarray[newid] = sqlc;
+	}
+
+	// Ask ODBC for a new connection handle
+	rc = SQLAllocConnect(sqle->henv, &sqlc->hdbc);
+	if (rc == SQL_ERROR) {
+		return ERR;
+	}
+
+	sqlc->refcount = 0;
+	sqlc->state = SQLC_INUSE;
+	 
+	// Extract the username, password, and database name
+	rc = parseConnInfo(sqlc, info);
+	if ( rc != OK ) {
+		return ERR;
+	}
+
+	// Request an ODBC connection to the database
+    rc = SQLConnect(sqlc->hdbc, sqlc->dbname, SQL_NTS, sqlc->user, SQL_NTS,
+		sqlc->passwd, SQL_NTS);
+	if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
+		//	log error?
+		//	Should try to get something more specific from ODBC
+		sprintf(sqlc->errmsg, "Connect failed: user = %s, passwd = %s, dbname = %s",
+			sqlc->user, sqlc->passwd, sqlc->dbname);
+
+		SQLDisconnect(sqlc->hdbc);
+		SQLFreeConnect(sqlc->hdbc);
+		return ERR;
+	}
+	*connid = newid;
+
+	// Set connect option to disable auto commit
+	rc = SQLSetConnectOption(sqlc->hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
+	if ( rc != SQL_SUCCESS ) {
+		return WARN;
+	}
+
+	return OK;
+}
+
+
+STATUS
+freeSqlConn(SQLEnv *sqle, int connid)
+{
+	SQLConn	*sqlc;
+	STATUS	rc;
+
+	rc = mapSqlConn(sqle, connid, &sqlc);
+	if ( rc != OK ) {
+		return WARN;
+	}
+
+	SQLDisconnect(sqlc->hdbc);
+	SQLFreeConnect(sqlc->hdbc);
+	sqlc->state = SQLC_FREE;
+	return OK;
+}
+
+
+STATUS
+mapSqlStream(SQLEnv *sqle, int streamid, SQLStream **sqls)
+{
+	if ( streamid >= 0 && streamid < sqle->maxstream ) {
+		*sqls = sqle->ssarray[streamid];
+		if ( (*sqls)->state == SQLS_INUSE )
+			return OK;
+	}
+	return ERR;
+}
+
+
+STATUS
+newSqlStream(SQLEnv *sqle, int connid, int *streamid)
+{
+	HSTMT		hstmt;
+	SQLConn		*sqlc;
+	SQLStream	**newarray, *sqls;
+	int			newid = -1, i;
+	STATUS		rc;
+
+	rc = mapSqlConn(sqle, connid, &sqlc);
+	if (rc != OK) {
+		return ERR;
+	}
+
+	// Search for an available stream structure to reuse
+	for ( i = 0; i < sqle->maxstream; i++ ) {
+		sqls = sqle->ssarray[i];
+		if ( sqls != NULL && sqls->state == SQLS_FREE ) {
+			newid = i;
+			break;
+		}
+	}
+	
+	if ( newid == -1 ) {
+		// Assign a new stream id
+		newid = sqle->maxstream++;
+
+		// Extend the stream pointer array
+		newarray = (SQLStream **) realloc((char *) sqle->ssarray,
+			sqle->maxstream * sizeof(SQLStream*));
+		if ( newarray == NULL ) {
+			return ERR;
+		}
+		sqle->ssarray = newarray;
+
+		// Allocate a new stream structure
+		sqls = (SQLStream *) calloc(1, sizeof(SQLStream));
+		if ( sqls == NULL ) {
+			return ERR;
+		}
+		sqle->ssarray[newid] = sqls;
+	}
+
+	// Associate new stream with specified connection
+	sqls->connid = connid;
+	sqlc->refcount++;
+
+	// Ask ODBC to allocate a new statement handle
+	rc = SQLAllocStmt(sqlc->hdbc, &hstmt);
+	if (rc == SQL_ERROR) {
+		return ERR;
+	}
+	sqls->hstmt = hstmt;
+	sqls->state = SQLS_INUSE;
+
+	*streamid = newid;
+	return OK;
+}
+
+
+STATUS
+freeSqlStream(SQLEnv *sqle, int streamid)
+{
+	SQLConn		*sqlc;
+	SQLStream	*sqls;
+	STATUS		rc;
+
+	rc = mapSqlStream(sqle, streamid, &sqls);
+	if ( rc != OK ) {
+		return WARN;
+	}
+
+	sqls->state = SQLS_FREE;
+
+	rc = SQLFreeStmt(sqls->hstmt, SQL_DROP);
+
+	rc = mapSqlConn(sqle, sqls->connid, &sqlc);
+	if ( rc != OK ) {
+		return WARN;
+	}
+
+	if ( --sqlc->refcount == 0 )
+	{
+		rc = freeSqlConn(sqle, sqls->connid);
+		if (rc != OK) {
+			return WARN;
+		}
+	}
+	return OK;
+}
+
+
+STATUS
+parseConnInfo(SQLConn *sqlc, char *info)
+{
+	UCHAR	*temp;
+
+	// The argument 'info' points to a buffer containing a string
+	// of the form "username/password/dbname\n".  We will use 'strtok'
+	// to tokenize the string into the parts we need, keeping
+	// copies in the 'sqlc' structure.
+
+	temp = strtok(info, "/\n");
+	if ( temp == NULL ) {
+		return ERR;
+	}
+	strncpy(sqlc->user, temp, 48);
+
+	temp = strtok(NULL, "/\n");
+	if ( temp == NULL ) {
+		return ERR;
+	}
+	strncpy(sqlc->passwd, temp, 48);
+
+	temp = strtok(NULL, "/\n");
+	if ( temp == NULL ) {
+		return ERR;
+	}
+	strncpy(sqlc->dbname, temp, 48);
+
+	return OK;
+}
+
+
+
--- /dev/null
+++ b/tools/db/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=infdb
+
+OFILES=	infdb.$O
+
+LIBS= 	9\
+
+BIN=$ROOT/$OBJDIR/bin
+
+SYSLIBS= -liodbc
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/tools/libstyx/Nt.c
@@ -1,0 +1,172 @@
+#include <windows.h>
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+	fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+	WSADATA wsaData;
+	WORD wVersionRequired=MAKEWORD(1,1);
+	
+	int rv = WSAStartup(wVersionRequired, &wsaData);	
+
+	if(rv != 0){
+		fprint(2, "Unable to Find winsock.dll");
+		return -1;
+	}
+	if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1 ){
+	   	fprint(2, "Unable to find winsock.dll V1.1 or later");
+		return -1;
+	}
+	return 0;
+}
+
+void
+styxendsocket(void)
+{
+	WSACleanup( );
+}
+
+void
+styxclosesocket(int fd)
+{
+	closesocket(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+	struct sockaddr_in sin;
+	int s, one;
+
+	USED(server);
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if(s < 0)
+		return s;
+	one = 1;
+	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+		fprint(2, "setsockopt failed\n");
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = 0;
+	sin.sin_port = htons(atoi(port));
+	if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+		close(s);
+		return -1;
+	}
+	if(listen(s, 20) < 0){
+		close(s);
+		return -1;
+	}
+	return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+	struct sockaddr_in sin;
+	int len, s;
+
+	len = sizeof(sin);
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+	if(s < 0){
+		if(errno != EINTR)
+			fprint(2, "error in accept: %s\n", strerror(errno));
+	}
+	return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+	Fdset *fs;
+
+	server->priv = fs = malloc(sizeof(Fdset));
+	FD_ZERO(&fs->infds);
+	FD_ZERO(&fs->outfds);
+	FD_ZERO(&fs->excfds);
+	FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+	struct timeval seltime;
+	int nfds;
+	Fdset *fs;
+
+	fs = server->priv;
+	fs->r_infds = fs->infds;
+	fs->r_outfds = fs->outfds;
+	fs->r_excfds = fs->excfds;
+	seltime.tv_sec = 10;
+	seltime.tv_usec = 0L;
+	nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+	if(nfds < 0 && errno != EINTR)
+		return"error in select";
+	return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+	exit(n);
+}
--- /dev/null
+++ b/tools/libstyx/Plan9.c
@@ -1,0 +1,314 @@
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Listener Listener;
+typedef struct Reader Reader;
+typedef struct Union Union;
+
+struct Listener
+{
+	int fd;
+	char *dir;
+	Listener *next;
+};
+
+struct Reader
+{
+	int pid;
+	int fd;
+	int n;
+	char buf[MSGMAX];
+	char rbuf[MSGMAX];
+	Reader *next;
+};
+
+struct Union
+{
+	int	pid;
+	Lock	lock;
+	Listener *lr;
+	Reader *rr;
+};
+
+void xlock(Lock *l){ lock(l); }
+void xunlock(Lock *l){ unlock(l); }
+
+static Reader*
+findr(Styxserver *server, int fd)
+{
+	Reader *r;
+	Union *u;
+
+	u = server->priv;
+	xlock(&u->lock);
+	for(r = u->rr; r != nil; r = r->next)
+		if(r->fd == fd)
+			break;
+	xunlock(&u->lock);
+	return r;
+}
+
+int
+styxinitsocket(void)
+{
+	return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+	close(fd);
+}
+
+static void
+listener(Styxserver *server, int afd, char *adir)
+{
+	int s;
+	Listener *l;
+	Union *u;
+	char ld[40];
+
+	USED(afd);
+	u = server->priv;
+	for(;;){
+		s = listen(adir, ld);
+		/* fprint(2, "listen %d %s %s\n", s, adir, ld); */
+		if(s < 0){
+			u->pid = -1;
+			break;
+		}
+		l = malloc(sizeof(Listener));
+		l->fd = s;
+		l->dir = strdup(ld);
+		xlock(&u->lock);
+		l->next = u->lr;
+		u->lr = l;
+		xunlock(&u->lock);
+	}
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+	int s, pid;
+	Union *u;
+	char adr[32], adir[40];
+
+	server->priv = u = malloc(sizeof(Union));
+	u->lock.val = 0;
+	u->lr = nil;
+	u->rr = nil;
+	sprint(adr, "tcp!*!%s", port);
+	s = announce(adr, adir);
+	/* fprint(2, "announce %d %s %s\n", s, adr, adir); */
+	if(s < 0)
+		return s;
+	switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+	case 0:
+		listener(server, s, strdup(adir));
+		break;
+	default:
+		u->pid = pid;
+		break;
+	}
+	return s;
+}
+
+static void
+reader(Styxserver *server, Reader *r)
+{
+	int m, n, s;
+	Union *u;
+
+	u = server->priv;
+	s = r->fd;
+	for(;;){
+		n = r->n;
+		if(n < 0){
+			r->pid = -1;
+			exits(nil);
+		}
+		m = read(s, r->rbuf, MSGMAX-n);
+		xlock(&u->lock);
+		n = r->n;
+		if(m < 0)
+			r->n = n == 0 ? m : n;
+		else{
+			memmove(r->buf+n, r->rbuf, m);
+			r->n = m+n;	
+		}
+		xunlock(&u->lock);
+	}
+}
+
+int
+styxaccept(Styxserver *server)
+{
+	int s, fd, pid;
+	Reader *r;
+	Listener *l;
+	Union *u;
+	char *dir;
+
+	u = server->priv;
+	xlock(&u->lock);
+	if((l = u->lr) == nil){
+		xunlock(&u->lock);
+		return -1;
+	}
+	u->lr = l->next;
+	xunlock(&u->lock);
+	fd = l->fd;
+	dir = l->dir;
+	free(l);
+	s = accept(fd, dir);
+	/* fprint(2, "accept %d\n", s); */
+	free(dir);
+	if(s < 0)
+		return s;
+	r = malloc(sizeof(struct Reader));
+	r->fd = s;
+	r->n = 0;
+	xlock(&u->lock);
+	r->next = u->rr;
+	u->rr = r;
+	xunlock(&u->lock);
+	switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+	case 0:
+		reader(server, r);
+		break;
+	case 1:
+		r->pid = pid;
+		break;
+	}
+	return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+	USED(server);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+	Union *u;
+
+	u = server->priv;
+	return u->lr != nil;
+}
+
+int
+styxnewmsg(Styxserver *server, int fd)
+{
+	Reader *r;
+
+	r = findr(server, fd);
+	return r != nil && r->n != 0;
+}
+
+void
+styxnewclient(Styxserver *server, int fd)
+{
+	USED(server);
+	USED(fd);
+}
+
+void
+styxfreeclient(Styxserver *server, int fd)
+{
+	Reader *r, **rp;
+	Union *u;
+
+	u = server->priv;
+	r = findr(server, fd);
+	if(r == nil)
+		return;
+	xlock(&u->lock);
+	for(rp = &u->rr; *rp != nil; rp = &(*rp)->next)
+		if(r == *rp){
+			*rp = r->next;
+			break;
+		}
+	xunlock(&u->lock);
+	if(r->pid >= 0)
+		postnote(PNPROC, r->pid, "kill");
+	free(r);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+	int i;
+	Reader *r;
+	Union *u;
+
+	u = server->priv;
+	for(i = 0; i < 100; i++){
+		if(u->lr != nil)
+			return nil;
+		xlock(&u->lock);
+		for(r = u->rr; r != nil; r = r->next)
+			if(r->n != 0){
+				xunlock(&u->lock);
+				return nil;
+			}
+		xunlock(&u->lock);
+		sleep(100);
+	}
+	return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	Reader *r;
+	Union *u;
+	int rn;
+	char *rbuf;
+
+	USED(m);
+	r = findr(server, fd);
+	if(r == nil)
+		return -1;
+	u = server->priv;
+	xlock(&u->lock);
+	rn = r->n;
+	rbuf = r->buf;
+	if(rn < 0){
+		xunlock(&u->lock);
+		return rn;
+	}
+	if(n > rn)
+		n = rn;
+	memmove(buf, rbuf, n);
+	rn -= n;
+	memmove(rbuf, rbuf+n, rn);
+	r->n = rn;
+	xunlock(&u->lock);
+	return n;
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	USED(server);
+	USED(m);
+	return write(fd, buf, n);
+}
+
+void
+styxexit(int n)
+{
+	if(n)
+		exits("error");
+	else
+		exits(nil);
+}
--- /dev/null
+++ b/tools/libstyx/Posix.c
@@ -1,0 +1,165 @@
+#define	__EXTENSIONS__
+#define	_BSD_COMPAT
+#include <lib9.h>
+#include 	<sys/types.h>
+#include	<sys/time.h>
+#include	<sys/socket.h>
+#include	<netinet/in.h>
+
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+	fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+	return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+	close(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+	struct sockaddr_in sin;
+	int s, one;
+
+	USED(server);
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if(s < 0)
+		return s;
+	one = 1;
+	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+		fprint(2, "setsockopt failed\n");
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = 0;
+	sin.sin_port = htons(atoi(port));
+	if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+		close(s);
+		return -1;
+	}
+	if(listen(s, 20) < 0){
+		close(s);
+		return -1;
+	}
+	return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+	struct sockaddr_in sin;
+	int s;
+	socklen_t len;
+
+	len = sizeof(sin);
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+	if(s < 0){
+		if(errno != EINTR)
+			fprint(2, "error in accept: %s\n", strerror(errno));
+	}
+	return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+	Fdset *fs;
+
+	server->priv = fs = malloc(sizeof(Fdset));
+	FD_ZERO(&fs->infds);
+	FD_ZERO(&fs->outfds);
+	FD_ZERO(&fs->excfds);
+	FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+	Fdset *fs;
+
+	fs = server->priv;
+	return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+	struct timeval seltime;
+	int nfds;
+	Fdset *fs;
+
+	fs = server->priv;
+	fs->r_infds = fs->infds;
+	fs->r_outfds = fs->outfds;
+	fs->r_excfds = fs->excfds;
+	seltime.tv_sec = 10;
+	seltime.tv_usec = 0L;
+	nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+	if(nfds < 0 && errno != EINTR)
+		return"error in select";
+	return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+	return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+	exit(n);
+}
--- /dev/null
+++ b/tools/libstyx/mkfile
@@ -1,0 +1,11 @@
+<../../mkconfig
+
+LIB=libstyx.a
+
+OFILES=\
+	styxserver.$O\
+	$TARGMODEL.$O\
+
+HFILES=\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
--- /dev/null
+++ b/tools/libstyx/styxaux.h
@@ -1,0 +1,14 @@
+int styxinitsocket(void);
+void styxendsocket(void);
+void styxclosesocket(int);
+int styxannounce(Styxserver*, char *);
+void styxinitwait(Styxserver*);
+int styxnewcall(Styxserver*);
+int styxnewmsg(Styxserver*, int);
+int styxaccept(Styxserver *server);
+void styxnewclient(Styxserver*, int);
+void styxfreeclient(Styxserver*, int);
+char* styxwaitmsg(Styxserver*);
+int styxrecv(Styxserver*, int, char*, int, int);
+int styxsend(Styxserver*, int, char*, int, int);
+void styxexit(int);
--- /dev/null
+++ b/tools/libstyx/styxserver.c
@@ -1,0 +1,1073 @@
+#include <lib9.h>
+#include <styx.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+#define MAXSTAT	512
+#define EMSGLEN			256		/* %r */
+
+#define TABSZ	32	/* power of 2 */
+
+static	unsigned long		boottime;
+static	char*	eve = "inferno";
+static	int		Debug = 0;
+
+char Enomem[] =		"out of memory";
+char Eperm[] =			"permission denied";
+char Enodev[] =		"no free devices";
+char Ehungup[] =		"write to hungup channel";
+char	Eexist[] =			"file exists";
+char Enonexist[] =		"file does not exist";
+char Ebadcmd[] =		"bad command";
+char Ebadarg[] =			"bad arg in system call";
+char Enofid[] =			"no such fid";
+char Enotdir[] =		"not a directory";
+char	Eopen[] =			"already open";
+char	Ebadfid[] =		"bad fid";
+
+/* client state */
+enum{
+	CDISC = 01,
+	CNREAD = 02,
+	CRECV = 04,
+};
+
+typedef struct Walkqid Walkqid;
+
+struct Fid
+{
+	Client 	*client;
+	Fid *next;
+	short	fid;
+	ushort	open;
+	ushort	mode;	/* read/write */
+	ulong	offset;	/* in file */
+	int		dri;		/* dirread index */
+	Qid		qid;
+};
+
+struct Walkqid
+{
+	Fid	*clone;
+	int	nqid;
+	Qid	qid[1];
+};
+
+#define ASSERT(A,B) styxassert((int)A,B)
+
+static int hash(Path);
+static void deletefids(Client *);
+
+static void
+styxfatal(char *fmt, ...)
+{
+	char buf[1024], *out;
+	va_list arg;
+	out = seprint(buf, buf+sizeof(buf), "Fatal error: ");
+	va_start(arg, fmt);
+	out = vseprint(out, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	write(2, buf, out-buf);
+	styxexit(1);
+}
+
+static void
+styxassert(int vtrue, char *reason)
+{
+	if(!vtrue)
+		styxfatal("assertion failed: %s\n", reason);
+}
+
+void *
+styxmalloc(int bytes)
+{
+	char *m = malloc(bytes);
+	if(m == nil)
+		styxfatal(Enomem);
+	memset(m, 0, bytes);
+	return m;
+}
+
+void
+styxfree(void *p)
+{
+	free(p);
+}
+
+void
+styxdebug()
+{
+	Debug = 1;
+}
+
+static Client *
+newclient(Styxserver *server, int fd)
+{
+	Client *c = (Client *)styxmalloc(sizeof(Client));
+
+	if(Debug)
+		fprint(2, "New client at %lux\n", (ulong)c);
+	c->server = server;
+	c->fd = fd;
+	c->nread = 0;
+	c->nc = 0;
+	c->state = 0;
+	c->fids = nil;
+	c->uname = strdup(eve);
+	c->aname = strdup("");
+	c->next = server->clients;
+	server->clients = c;
+	if(server->ops->newclient)
+		server->ops->newclient(c);
+	return c;
+}
+
+static void
+freeclient(Client *c)
+{
+	Client **p;
+	Styxserver *server;
+
+	if(Debug)
+		fprint(2, "Freeing client at %lux\n", (ulong)c);
+	server = c->server;
+	if(server->ops->freeclient)
+		server->ops->freeclient(c);
+	for(p = &server->clients; *p; p = &(*p)->next)
+		if(*p == c){
+			styxclosesocket(c->fd);
+			*p = c->next;
+			deletefids(c);
+			free(c->uname);
+			free(c->aname);
+			styxfree(c);
+			return;
+		}
+}
+
+static int
+nbread(Client *c, int nr)
+{
+	int nb;
+
+	if(c->state&CDISC)
+		return -1;
+	nb = styxrecv(c->server, c->fd, c->msg + c->nread, nr, 0);
+	if(nb <= 0){
+		c->nread = 0;
+		c->state |= CDISC;
+		return -1;
+	}
+	c->nread += nb;
+	return 0;
+}
+
+static int
+rd(Client *c, Fcall *r)
+{
+	if(c->nc > 0){	/* last convM2S consumed nc bytes */
+		c->nread -= c->nc;
+		if((int)c->nread < 0){
+			r->ename = "negative size in rd";
+			return -1;
+		}
+		memmove(c->msg, c->msg+c->nc, c->nread);
+		c->nc = 0;
+	}
+	if(c->state&CRECV){
+		if(nbread(c, MSGMAX - c->nread) != 0){
+			r->ename = "unexpected EOF";
+			return -1;
+		}
+		c->state &= ~CRECV;
+	}
+	c->nc = convM2S((uchar*)(c->msg), c->nread, r);
+	if(c->nc < 0){
+		r->ename = "bad message format";
+		return -1;
+	}
+	if(c->nc == 0 && c->nread > 0){
+		c->nread = 0;
+		c->state &= ~CNREAD;
+		return 0;
+	}
+	if(c->nread > c->nc)
+		c->state |= CNREAD;
+	else
+		c->state &= ~CNREAD;
+	if(c->nc == 0)
+		return 0;
+	/* fprint(2, "rd: %F\n", r); */
+	return 1;
+}
+
+static int
+wr(Client *c, Fcall *r)
+{
+	int n;
+	char buf[MSGMAX];
+
+	n = convS2M(r, (uchar*)buf, sizeof(buf));
+	if(n < 0){
+		r->ename = "bad message type in wr";
+		return -1;
+	}
+	/* fprint(2, "wr: %F\n", r); */
+	return styxsend(c->server, c->fd, buf, n, 0);
+}
+
+static void
+sremove(Styxserver *server, Styxfile *f)
+{
+	Styxfile *s, *next, **p;
+
+	if(f == nil)
+		return;
+	if(Debug)
+		fprint(2, "Remove file %s Qid=%llx\n", f->d.name, f->d.qid.path);
+	if(f->d.qid.type&QTDIR)
+		for(s = f->child; s != nil; s = next){
+			next = s->sibling;
+			sremove(server, s);
+	}
+	for(p = &server->ftab[hash(f->d.qid.path)]; *p; p = &(*p)->next)
+		if(*p == f){
+			*p = f->next;
+			break;
+		}
+	for(p = &f->parent->child; *p; p = &(*p)->sibling)
+		if(*p == f){
+			*p = f->sibling;
+			break;
+		}
+	styxfree(f->d.name);
+	styxfree(f->d.uid);
+	styxfree(f->d.gid);
+	styxfree(f);
+}
+
+int
+styxrmfile(Styxserver *server, Path qid)
+{
+	Styxfile *f;
+
+	f = styxfindfile(server, qid);
+	if(f != nil){
+		if(f->parent == nil)
+			return -1;
+		sremove(server, f);
+		return 0;
+	}
+	return -1;
+}
+
+static void
+incref(Styxfile *f)
+{
+	if(f != nil)
+		f->ref++;
+}
+
+static void
+decref(Styxfile *f)
+{
+	if(f != nil)
+		--f->ref;
+}
+
+static void
+increff(Fid *f)
+{
+	incref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+decreff(Fid *f)
+{
+	decref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+incopen(Fid *f)
+{
+	Styxfile *file;
+
+	if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+		file->open++;
+}
+
+static void
+decopen(Fid *f)
+{
+	Styxfile *file;
+
+	if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+		file->open--;
+}
+
+int
+styxperm(Styxfile *f, char *uid, int mode)
+{
+	int m, p;
+
+	p = 0;
+	switch(mode&3){
+	case OREAD:	p = AREAD;	break;
+	case OWRITE:	p = AWRITE;	break;
+	case ORDWR:	p = AREAD+AWRITE;	break;
+	case OEXEC:	p = AEXEC;	break;
+	}
+	if(mode&OTRUNC)
+		p |= AWRITE;
+	m = f->d.mode&7;
+	if((p&m) == p)
+		return 1;
+	if(strcmp(f->d.uid, uid) == 0){
+		m |= (f->d.mode>>6)&7;
+		if((p&m) == p)
+			return 1;
+	}
+	if(strcmp(f->d.gid, uid) == 0){
+		m |= (f->d.mode>>3)&7;
+		if((p&m) == p)
+			return 1;
+	}
+	return 0;
+}
+
+static int
+hash(Path path)
+{
+	return path&(TABSZ-1);
+}
+
+Styxfile *
+styxfindfile(Styxserver *server, Path path)
+{
+	Styxfile *f;
+
+	for(f = server->ftab[hash(path)]; f != nil; f = f->next){
+		if(f->d.qid.path == path)
+			return f;
+	}
+	return nil;
+}
+
+static Fid *
+findfid(Client *c, short fid)
+{
+	Fid *f;
+	for(f = c->fids; f && f->fid != fid; f = f->next)
+		;
+	return f;
+}
+
+static void
+deletefid(Client *c, Fid *d)
+{
+	/* TODO: end any outstanding reads on this fid */
+	Fid **f;
+
+	for(f = &c->fids; *f; f = &(*f)->next)
+		if(*f == d){
+			decreff(d);
+			decopen(d);
+			*f = d->next;
+			styxfree(d);
+			return;
+		}
+}
+
+static void
+deletefids(Client *c)
+{
+	Fid *f, *g;
+
+	for(f = c->fids; f; f = g){
+		decreff(f);
+		decopen(f);
+		g = f->next;
+		styxfree(f);
+	}
+}
+
+Fid *
+newfid(Client *c, short fid, Qid qid){
+	Fid *f;
+
+	f = styxmalloc(sizeof(Fid));
+	ASSERT(f, "newfid");
+	f->client = c;
+	f->fid = fid;
+	f->open = 0;
+	f->dri = 0;
+	f->qid = qid;
+	f->next = c->fids;
+	c->fids = f;
+	increff(f);
+	return f;
+}
+
+static void
+flushtag(int oldtag)
+{
+	USED(oldtag);
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+	return a.path == b.path && a.vers == b.vers;
+}
+
+static Fid *
+fidclone(Fid *old, short fid)
+{
+	Fid *new;
+
+	new = newfid(old->client, fid, old->qid);
+	return new;
+}
+
+static Walkqid*
+devwalk(Client *c, Styxfile *file, Fid *fp, Fid *nfp, char **name, int nname, char **err)
+{
+	Styxserver *server;
+	long j;
+	Walkqid *wq;
+	char *n;
+	Styxfile *p, *f;
+	Styxops *ops;
+	Qid qid;
+
+	*err = nil;
+	server = c->server;
+	ops = server->ops;
+
+	wq = styxmalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+	wq->nqid = 0;
+
+	p = file;
+	qid = p != nil ? p->d.qid : fp->qid;
+	for(j = 0; j < nname; j++){
+		if(!(qid.type&QTDIR)){
+			if(j == 0)
+				styxfatal("devwalk error");
+			*err = Enotdir;
+			goto Done;
+		}
+		if(p != nil && !styxperm(p, c->uname, OEXEC)){
+			*err = Eperm;
+			goto Done;
+		}
+		n = name[j];
+		if(strcmp(n, ".") == 0){
+    Accept:
+			wq->qid[wq->nqid++] = nfp->qid;
+			continue;
+		}
+		if(p != nil && strcmp(n, "..") == 0 && p->parent){
+			decref(p);
+			nfp->qid.path = p->parent->d.qid.path;
+			nfp->qid.type = p->parent->d.qid.type;
+			nfp->qid.vers = 0;
+			incref(p->parent);
+			p = p->parent;
+			qid = p->d.qid;
+			goto Accept;
+		}
+		
+		if(ops->walk != nil){
+			char *e;
+
+			e = ops->walk(&qid, n);
+			if(e == nil){
+				decreff(nfp);
+				nfp->qid = qid;
+				increff(nfp);
+				p = styxfindfile(server, qid.path);
+				if(server->needfile && p == nil)
+					goto Done;
+				qid = p != nil ? p->d.qid : nfp->qid;
+				goto Accept;
+			}
+		}
+
+		if(p != nil)
+		for(f = p->child; f != nil; f = f->sibling){
+			if(strcmp(n, f->d.name) == 0){
+				decref(p);
+				nfp->qid.path = f->d.qid.path;
+				nfp->qid.type = f->d.qid.type;
+				nfp->qid.vers = 0;
+				incref(f);
+				p = f;
+				qid = p->d.qid;
+				goto Accept;
+			}
+		}
+		if(j == 0 && *err == nil)
+			*err = Enonexist;
+		goto Done;
+	}
+Done:
+	if(*err != nil){
+		styxfree(wq);
+		return nil;
+	}
+	return wq;
+}
+
+static long
+devdirread(Fid *fp, Styxfile *file, char *d, long n)
+{
+	long dsz, m;
+	Styxfile *f;
+	int i;
+
+	struct{
+		Dir d;
+		char slop[100];	/* TO DO */
+	}dir;
+
+	f = file->child;
+	for(i = 0; i < fp->dri; i++)
+		if(f == 0)
+			return 0;
+		else
+			f = f->sibling;
+	for(m = 0; m < n; fp->dri++){
+		if(f == nil)
+			break;
+		dir.d = f->d;
+		dsz = convD2M(&dir.d, (uchar*)d, n-m);
+		m += dsz;
+		d += dsz;
+		f = f->sibling;
+	}
+
+	return m;
+}
+
+static char*
+nilconv(char *s)
+{
+	if(s != nil && s[0] == '\0')
+		return nil;
+	return s;
+}
+
+static Styxfile *
+newfile(Styxserver *server, Styxfile *parent, int isdir, Path qid, char *name, int mode, char *owner)
+{
+	Styxfile *file;
+	Dir d;
+	int h;
+
+	if(qid == -1)
+		qid = server->qidgen++;
+	file = styxfindfile(server, qid);
+	if(file != nil)
+		return nil;
+	if(parent != nil){
+		for(file = parent->child; file != nil; file = file->sibling)
+			if(strcmp(name, file->d.name) == 0)
+				return nil;
+	}
+	file = (Styxfile *)styxmalloc(sizeof(Styxfile));
+	file->parent = parent;
+	file->child = nil;
+	h = hash(qid);
+	file->next = server->ftab[h];
+	server->ftab[h] = file;
+	if(parent){
+		file->sibling = parent->child;
+		parent->child = file;
+	}else
+		file->sibling = nil;
+
+	d.type = 'X';
+	d.dev = 'x';
+	d.qid.path = qid;
+	d.qid.type = 0;
+	d.qid.vers = 0;
+	d.mode = mode;
+	d.atime = time(0);
+	d.mtime = boottime;
+	d.length = 0;
+	d.name = strdup(name);
+	d.uid = strdup(owner);
+	d.gid = strdup(eve);
+	d.muid = "";
+
+	if(isdir){
+		d.qid.type |= QTDIR;
+		d.mode |= DMDIR;
+	}
+	else{
+		d.qid.type &= ~QTDIR;
+		d.mode &= ~DMDIR;
+	}
+
+	file->d = d;
+	file->ref = 0;
+	file->open = 0;
+	if(Debug)
+		fprint(2, "New file %s Qid=%llx\n", name, qid);
+	return file;
+}
+
+static void
+run(Client *c)
+{
+	Fcall f;
+	Fid *fp, *nfp;
+	int i, open, mode;
+	char ebuf[EMSGLEN];
+	Walkqid *wq;
+	Styxfile *file;
+	Dir dir;
+	Qid qid;
+	Styxops *ops;
+	char strs[128];
+
+	ebuf[0] = 0;
+	if(rd(c, &f) <= 0)
+		return;
+	if(f.type == Tflush){
+		flushtag(f.oldtag);
+		f.type = Rflush;
+		wr(c, &f);
+		return;
+	}
+	ops = c->server->ops;
+	file = nil;
+	fp = findfid(c, f.fid);
+	if(f.type != Tversion && f.type != Tauth && f.type != Tattach){
+		if(fp == nil){
+			f.type = Rerror;
+			f.ename = Enofid;
+			wr(c, &f);
+			return;
+		}
+		else{
+			file = styxfindfile(c->server, fp->qid.path);
+			if(c->server->needfile && file == nil){
+				f.type = Rerror;
+				f.ename = Enonexist;
+				wr(c, &f);
+				return;
+			}
+		}
+	}
+	/* if(fp == nil) fprint(2, "fid not found for %d\n", f.fid); */
+	switch(f.type){
+	case	Twalk:
+		if(Debug){
+			fprint(2, "Twalk %d %d", f.fid, f.newfid);
+			for(i = 0; i < f.nwname; i++)
+				fprint(2, " %s", f.wname[i]);
+			fprint(2, "\n");
+		}
+		nfp = findfid(c, f.newfid);
+		f.type = Rerror;
+		if(nfp){
+			deletefid(c, nfp);
+			nfp = nil;
+		}
+		if(nfp){
+			f.ename = "fid in use";
+			if(Debug) fprint(2, "walk: %s\n", f.ename);
+			wr(c, &f);
+			break;
+		}else if(fp->open){
+			f.ename = "can't clone";
+			wr(c, &f);
+			break;
+		}
+		if(f.newfid != f.fid)
+			nfp = fidclone(fp, f.newfid);
+		else
+			nfp = fp;
+		if((wq = devwalk(c, file, fp, nfp, f.wname, f.nwname, &f.ename)) == nil){
+			if(nfp != fp)
+				deletefid(c, nfp);
+			f.type = Rerror;
+		}else{
+			if(nfp != fp){
+				if(wq->nqid != f.nwname)
+					deletefid(c, nfp);
+			}
+			f.type = Rwalk;
+			f.nwqid = wq->nqid;
+			for(i = 0; i < wq->nqid; i++)
+				f.wqid[i] = wq->qid[i];
+			styxfree(wq);
+		}
+		wr(c, &f);
+		break;
+	case	Topen:
+		if(Debug)
+			fprint(2, "Topen %d\n", f.fid);
+		f.ename = nil;
+		if(fp->open)
+			f.ename = Eopen;
+		else if((fp->qid.type&QTDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+			f.ename = Eperm;
+		else if(file != nil && !styxperm(file, c->uname, f.mode))
+			f.ename = Eperm;
+		else if((f.mode&ORCLOSE) && file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE))
+			f.ename = Eperm;
+		if(f.ename != nil){
+			f.type = Rerror;
+			wr(c, &f);
+			break;
+		}
+		f.ename = Enonexist;
+		decreff(fp);
+		if(ops->open == nil || (f.ename = ops->open(&fp->qid, f.mode)) == nil){
+			f.type = Ropen;
+			f.qid = fp->qid;
+			fp->mode = f.mode;
+			fp->open = 1;
+			fp->offset = 0;
+			incopen(fp);
+		}
+		else
+			f.type = Rerror;
+		increff(fp);
+		wr(c, &f);
+		break;
+	case	Tcreate:
+		if(Debug)
+			fprint(2, "Tcreate %d %s\n", f.fid, f.name);
+		f.ename = nil;
+		if(fp->open)
+			f.ename = Eopen;
+		else if(!(fp->qid.type&QTDIR))
+			f.ename = Enotdir;
+		else if((f.perm&DMDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+			f.ename = Eperm;
+		else if(file != nil && !styxperm(file, c->uname, OWRITE))
+			f.ename = Eperm;
+		if(f.ename != nil){
+			f.type = Rerror;
+			wr(c, &f);
+			break;
+		}
+		f.ename = Eperm;
+		decreff(fp);
+		if(file != nil){
+			if(f.perm&DMDIR)
+				f.perm = (f.perm&~0777) | (file->d.mode&f.perm&0777) | DMDIR;
+			else
+				f.perm = (f.perm&(~0777|0111)) | (file->d.mode&f.perm&0666);
+		}
+		if(ops->create && (f.ename = ops->create(&fp->qid, f.name, f.perm, f.mode)) == nil){
+			f.type = Rcreate;
+			f.qid = fp->qid;
+			fp->mode = f.mode;
+			fp->open = 1;
+			fp->offset = 0;
+			incopen(fp);
+		}
+		else
+			f.type = Rerror;
+		increff(fp);
+		wr(c, &f);
+		break;
+	case	Tread:
+		if(Debug)
+			fprint(2, "Tread %d\n", f.fid);
+		if(!fp->open){
+			f.type = Rerror;
+			f.ename = Ebadfid;
+			wr(c, &f);
+			break;
+		}
+		if(fp->qid.type&QTDIR || (file != nil && file->d.qid.type&QTDIR)){
+			f.type = Rread;
+			if(file == nil){
+				f.ename = Eperm;
+				if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), fp->dri)) == nil){
+					f.data = c->data;
+				}
+				else
+					f.type = Rerror;
+			}
+			else{
+				f.count = devdirread(fp, file, c->data, f.count);
+				f.data = c->data;
+			}		
+		}else{
+			f.ename = Eperm;
+			f.type = Rerror;
+			if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), f.offset)) == nil){
+				f.type = Rread;
+				f.data = c->data;			
+			}
+		}
+		wr(c, &f);
+		break;
+	case	Twrite:
+		if(Debug)
+			fprint(2, "Twrite %d\n", f.fid);
+		if(!fp->open){
+			f.type = Rerror;
+			f.ename = Ebadfid;
+			wr(c, &f);
+			break;
+		}
+		f.ename = Eperm;
+		f.type = Rerror;
+		if(ops->write && (f.ename = ops->write(fp->qid, f.data, (ulong*)(&f.count), f.offset)) == nil){
+			f.type = Rwrite;
+		}
+		wr(c, &f);
+		break;
+	case	Tclunk:
+		if(Debug)
+			fprint(2, "Tclunk %d\n", f.fid);
+		open = fp->open;
+		mode = fp->mode;
+		qid = fp->qid;
+		deletefid(c, fp);
+		f.type = Rclunk;
+		if(open && ops->close && (f.ename = ops->close(qid, mode)) != nil)
+			f.type = Rerror;
+		wr(c, &f);
+		break;
+	case	Tremove:
+		if(Debug)
+			fprint(2, "Tremove %d\n", f.fid);
+		if(file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE)){
+			f.type = Rerror;
+			f.ename = Eperm;
+			deletefid(c, fp);
+			wr(c, &f);
+			break;
+		}
+		f.ename = Eperm;
+		if(ops->remove && (f.ename = ops->remove(fp->qid)) == nil) 
+			f.type = Rremove;
+		else
+			f.type = Rerror;
+		deletefid(c, fp);
+		wr(c, &f);
+		break;
+	case	Tstat:
+		if(Debug)
+			fprint(2, "Tstat %d qid=%llx\n", f.fid, fp->qid.path);
+		f.stat = styxmalloc(MAXSTAT);
+		f.ename = "stat error";
+		if(ops->stat == nil && file != nil){
+			f.type = Rstat;
+			f.nstat = convD2M(&file->d, f.stat, MAXSTAT);
+		}
+		else if(ops->stat && (f.ename = ops->stat(fp->qid, &dir)) == nil){
+			f.type = Rstat;
+			f.nstat = convD2M(&dir, f.stat, MAXSTAT);
+		}
+		else
+			f.type = Rerror;
+		wr(c, &f);
+		styxfree(f.stat);
+		break;
+	case	Twstat:
+		if(Debug)
+			fprint(2, "Twstat %d\n", f.fid);
+		f.ename = Eperm;
+		convM2D(f.stat, f.nstat, &dir, strs);
+		dir.name = nilconv(dir.name);
+		dir.uid = nilconv(dir.uid);
+		dir.gid = nilconv(dir.gid);
+		dir.muid = nilconv(dir.muid);
+		if(ops->wstat && (f.ename = ops->wstat(fp->qid, &dir)) == nil)
+			f.type = Rwstat;
+		else
+			f.type = Rerror;
+		wr(c, &f);
+		break;
+	case	Tversion:
+		if(Debug)
+			fprint(2, "Tversion\n");
+		f.type = Rversion;
+		f.tag = NOTAG;
+		wr(c, &f);
+		break;
+	case Tauth:
+		if(Debug)
+			fprint(2, "Tauth\n");
+		f.type = Rauth;
+		wr(c, &f);
+		break;
+	case	Tattach:
+		if(Debug)
+			fprint(2, "Tattach %d %s %s\n", f.fid, f.uname[0] ? f.uname : c->uname, f.aname[0]? f.aname: c->aname);
+		if(fp){
+			f.type = Rerror;
+			f.ename = "fid in use";
+		}else{
+			Qid q;
+
+			if(f.uname[0]){
+				free(c->uname);
+				c->uname = strdup(f.uname);
+			}
+			if(f.aname[0]){
+				free(c->aname);
+				c->aname = strdup(f.aname);
+			}
+			q.path = Qroot;
+			q.type = QTDIR;
+			q.vers = 0;
+			fp = newfid(c, f.fid, q);
+			f.type = Rattach;
+			f.fid = fp->fid;
+			f.qid = q;
+			if(ops->attach && (f.ename = ops->attach(c->uname, c->aname)) != nil)
+				f.type = Rerror;
+		}
+		wr(c, &f);
+		break;
+	}
+}
+
+char *
+styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile)
+{
+	int i;
+
+	if(Debug)
+		fprint(2, "Initialising Styx server on port %s\n", port);
+	if(perm == -1)
+		perm = 0555;
+	server->ops = ops;
+	server->clients = nil;
+	server->root = nil;
+	server->ftab = (Styxfile**)malloc(TABSZ*sizeof(Styxfile*));
+	for(i = 0; i < TABSZ; i++)
+		server->ftab[i] = nil;
+	server->qidgen = Qroot+1;
+	if(styxinitsocket() < 0)
+		return "styxinitsocket failed";
+	server->connfd = styxannounce(server, port);
+	if(server->connfd < 0)
+		return "can't announce on network port";
+	styxinitwait(server);
+	server->root = newfile(server, nil, 1, Qroot, "/", perm|DMDIR, eve);
+	server->needfile = needfile;
+	return nil;
+}
+
+char*
+styxend(Styxserver *server)
+{
+	USED(server);
+	styxendsocket();
+	return nil;
+}
+
+char *
+styxwait(Styxserver *server)
+{
+	return styxwaitmsg(server);
+}
+
+char *
+styxprocess(Styxserver *server)
+{
+	Client *c;
+	int s;
+
+	if(styxnewcall(server)){
+		s = styxaccept(server);
+		if(s >= 0){
+			newclient(server, s);
+			styxnewclient(server, s);
+		}
+	}
+	for(c = server->clients; c != nil; ){
+		Client *next = c->next;
+
+		server->curc = c;
+		if(c->fd >= 0 && styxnewmsg(server, c->fd))
+			c->state |= CRECV;
+		if(c->state&(CNREAD|CRECV)){
+			if(c->state&CDISC){
+				styxfreeclient(server, c->fd);
+				freeclient(c);
+			}else
+				do
+					run(c);
+				while(c->state&CNREAD);
+		}
+		c = next;
+	}
+	
+	return nil;
+}
+
+Client*
+styxclient(Styxserver *server)
+{
+	return server->curc;
+}
+
+Styxfile*
+styxaddfile(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+	Styxfile *f, *parent;
+
+	parent = styxfindfile(server, pqid);
+	if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+		return nil;
+	f = newfile(server, parent, 0, qid, name, mode, owner);
+	return f;
+}
+
+Styxfile*
+styxadddir(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+	Styxfile *f, *parent;
+
+	parent = styxfindfile(server, pqid);
+	if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+		return nil;
+	f = newfile(server, parent, 1, qid, name, mode|DMDIR, owner);
+	return f;
+}
+
+long
+styxreadstr(ulong off, char *buf, ulong n, char *str)
+{
+	int size;
+
+	size = strlen(str);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, str+off, n);
+	return n;
+}
+
+Qid
+styxqid(int path, int isdir)
+{
+	Qid q;
+
+	q.path = path;
+	q.vers = 0;
+	if(isdir)
+		q.type = QTDIR;
+	else
+		q.type = 0;
+	return q;
+}
+
+void
+styxsetowner(char *name)
+{
+	eve = name;
+}
--- /dev/null
+++ b/tools/mkfile
@@ -1,0 +1,7 @@
+<../mkconfig
+
+DIRS=\
+	libstyx\
+	styxtest\
+
+<$ROOT/mkfiles/mksubdirs
--- /dev/null
+++ b/tools/odbc/mkfile
@@ -1,0 +1,19 @@
+<../../mkconfig
+
+TARG=odbc
+
+OFILES=\
+	odbc.$O\
+
+HFILES=\
+	../libstyx/styxserver.h\
+
+LIBS=styx 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<mkfile-$SYSTARG
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../libstyx
--- /dev/null
+++ b/tools/odbc/mkfile-Linux
@@ -1,0 +1,1 @@
+SYSLIBS=-lodbc
--- /dev/null
+++ b/tools/odbc/mkfile-MacOSX
@@ -1,0 +1,1 @@
+SYSLIBS= -liodbc
--- /dev/null
+++ b/tools/odbc/mkfile-Nt
@@ -1,0 +1,2 @@
+SYSLIBS= odbc32.lib wsock32.lib $SYSLIBS
+CFLAGS= $CFLAGS -DWINDOWSNT
--- /dev/null
+++ b/tools/odbc/mkfile-Solaris
@@ -1,0 +1,1 @@
+SYSLIBS=$EMULIBS
--- /dev/null
+++ b/tools/odbc/odbc.c
@@ -1,0 +1,1147 @@
+#ifdef WINDOWSNT
+#include <windows.h>
+#endif
+#include <lib9.h>
+#include <styx.h>
+#include "styxserver.h"
+/* #include <winsock.h> */
+
+#define DEFCOLSIZE			10000
+
+static int ODebug;
+
+char Eodbcalloc[] =	"no free ODBC handles";
+char Enoconnect[] =	"no ODBC connection";
+
+static char	*netport = "6700";
+static char	*inferno = "inferno";
+
+Styxserver *iserver;
+
+/* ----- */
+#include <sql.h>
+#include <sqlext.h>
+
+int nclients = 0;
+
+typedef struct Env Env;
+struct Env
+{
+	SQLHENV h;			/* ODBC environment handle */
+};
+
+typedef struct Conn Conn;
+struct Conn
+{
+	SQLHDBC	h;			/* ODBC connection handle */
+	int		connected;
+};
+
+typedef struct Coltype Coltype;
+struct Coltype
+{
+	char		name[255];
+	ushort	type;
+	SQLUINTEGER		size;
+	ushort	digits;
+	ushort	nulls;
+};
+
+typedef struct Column Column;
+struct Column
+{
+	char 	*data;
+	SQLINTEGER	len;
+};
+
+typedef struct Stmt Stmt;
+struct Stmt
+{
+	SQLHSTMT h;			/* ODBC statement handle */
+	ushort	ncols;		/* number of columns in result */
+	ulong	nrows;		/* number of rows affected by update, insert, delete */
+	Coltype	*cols;		/* column descriptions */
+	Column	*rec;			/* data record */
+	char		*headstr;		/* column headings if requested */
+};
+
+/* ----- */
+enum
+{
+	Qtopdir		= 0,	/* top level directory */
+	Qnclients,
+	Qprotodir,
+	Qclonus,
+	Qconvdir,
+	Qdata,
+	Qcmd,
+	Qctl,
+	Qstatus,
+	Qformat,
+	Qsources,
+	Qerror,
+
+	MAXPROTO	= 1
+};
+#define TYPE(x) 	((x).path & 0xf)
+#define CONV(x) 	(((x).path >> 4)&0xfff)
+#define PROTO(x) 	(((x).path >> 16)&0xff)
+#define QID(p, c, y) 	(((p)<<16) | ((c)<<4) | (y))
+
+typedef struct Proto	Proto;
+typedef struct Conv	Conv;
+typedef struct Output Output;
+
+struct Output {
+	enum {Fixed, Float} style;			/* output style */
+	uchar	fs;					/* float: field separator */
+	uchar	rs;					/* float: record separator */
+};
+Output defoutput = {Float, '|', '\n'};
+
+struct Conv
+{
+	int	x;
+	int	ref;
+	int	perm;
+	char	*owner;
+	char*	state;
+	Proto*	p;
+
+	/*-----*/
+	Conn	c;
+	Stmt		s;
+	Output	out;
+	int		headings;
+	char		errmsg[400];	/* odbc error messages can be big */
+};
+
+struct Proto
+{
+	int	x;
+	char	*name;
+	uint	nc;
+	int	maxconv;
+	Conv**	conv;
+	Qid	qid;
+};
+
+typedef struct Dirtab	Dirtab;
+struct Dirtab
+{
+	char	name[255];
+	Qid	qid;
+	long	length;
+	long	perm;
+};
+
+static	int		np;
+static	Proto	proto[MAXPROTO];
+static	Conv*	protoclone(Proto*, char*);
+
+typedef int    Devgen(Fid*, char *, Dirtab*, int, int, Dir*);
+
+struct xClient {
+	/* ---- */
+	Env		e;
+};
+
+#define H(c)	((Env*)(c->u))->h
+
+void
+fatal(char *fmt, ...)
+{
+	char buf[1024], *out;
+	va_list arg;
+	out = vseprint(buf, buf+sizeof(buf), "Fatal error: ", 0);
+	va_start(arg, fmt);
+	out = vseprint(out, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	write(2, buf, out-buf);
+	exit(1);
+}
+
+#define ASSERT(A,B) xassert((int)A,B)
+
+void
+xassert(int true, char *reason)
+{
+	if(!true)
+		fatal("assertion failed: %s\n", reason);
+}
+
+void *
+xmalloc(int bytes)
+{
+	char *m = malloc(bytes);
+	if(m)
+		memset(m, 0, bytes);
+//	print("xmalloc: %lux (%d)\n", m, bytes);
+	return m;
+}
+
+void
+xfree(void *p, char *from)
+{
+//	print("xfree: %lux [%s]\n", p, from);
+	free(p);
+}
+
+char *
+odbcerror(Conv *cv, int lasterr)
+{
+	char sqlstate[6];
+	long native;
+	char *mp;
+	short msglen;
+
+	if(cv == 0)
+		return "";
+	if(lasterr)
+		return cv->errmsg;
+	if(cv->c.connected)
+		SQLGetDiagRec(SQL_HANDLE_STMT, cv->s.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
+	else
+		SQLGetDiagRec(SQL_HANDLE_DBC, cv->c.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
+	cv->errmsg[msglen]=0;
+	/* fprint(2, "c: sqlstate: %s, msg %s\n", sqlstate, cv->errmsg); */
+	if((mp=strrchr(cv->errmsg, ']')) != 0)
+		return mp+1;
+	return cv->errmsg;
+}
+
+char*
+odbcsources(Client *c)
+{
+	int ss, i;
+	char server[SQL_MAX_DSN_LENGTH+1];
+	char source[1024];
+	char buff[1024+SQL_MAX_DSN_LENGTH+1];
+	short serverlen, sourcelen;
+	char *all = nil, *p;
+
+	for (i=0;; i++) {
+		ss = SQLDataSources(H(c), (i==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT),
+				server, sizeof(server), &serverlen,
+				source, sizeof(source), &sourcelen);
+		if (ss != SQL_SUCCESS)
+			break;
+		snprint(buff, sizeof(buff), "%s:%s\n", server, source);
+		if (i == 0)
+			all = strdup(buff);
+		else {
+			p = all;
+			all = malloc(strlen(all)+strlen(buff)+1);
+			strcpy(all, p);
+			strcat(all, buff);
+			free(p);
+		}
+	}
+	return all;
+}
+
+
+int
+sqlerr(Conv *c, int sqlstatus, char *errp, char *func, char *sqlcall)
+{
+	char *e;
+
+	errp[0] = 0;
+	e = "failed";
+	if (sqlstatus == SQL_ERROR || sqlstatus == SQL_SUCCESS_WITH_INFO)
+		strecpy(errp, errp+ERRMAX, odbcerror(c, 0));
+	if (sqlstatus == SQL_SUCCESS_WITH_INFO)
+		e = "info";
+	if (sqlstatus != SQL_SUCCESS)
+		fprint(2, "%s: %s %s - %s\n", func, sqlcall, e, errp);
+	if (sqlstatus != SQL_SUCCESS && sqlstatus != SQL_SUCCESS_WITH_INFO)
+		return 1;
+	return 0;
+}
+
+char*
+odbcnewclient(Client *c)
+{
+	int ss;
+
+	/* ---- */
+	c->u = styxmalloc(sizeof(Env));
+	ss = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H(c));
+	if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+		fprint(2, "newclient: SQLAllocHandle failed\n");
+		return "SQLAllocHandle failed";
+	}
+	ss = SQLSetEnvAttr(H(c), SQL_ATTR_ODBC_VERSION, (char*)SQL_OV_ODBC2, 0);
+	if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+		fprint(2, "newclient: SQLSetEnvAttr failed\n");
+		return "SQLSetEnvAttr failed";
+	}
+	nclients++;
+	return nil;
+}
+
+char*
+odbcfreeclient(Client *c)
+{
+	int ss;
+
+	ss = SQLFreeHandle(SQL_HANDLE_ENV, H(c));
+	if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+		fprint(2, "freeclient: SQLFreeHandle failed\n");
+	styxfree(c->u);
+	nclients--;
+	return nil;
+}
+
+int
+parsefields(char *lp, char **fields, int n, char *sep)
+{
+	int i;
+
+	for(i=0; lp && *lp && i<n; i++){
+		while(*lp && strchr(sep, *lp) != 0)
+			*lp++=0;
+		if(*lp == 0)
+			break;
+		fields[i]=lp;
+		while(*lp && strchr(sep, *lp) == 0)
+			lp++;
+	}
+	return i;
+}
+
+void
+odbcdisconnect(Conv *c)
+{
+	int ss;
+
+	if(c->c.connected){
+		ss = SQLFreeHandle(SQL_HANDLE_STMT, c->s.h);
+		if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+			fprint(2, "odbcdisconnect: SQLFreeHandle failed\n");
+		ss = SQLDisconnect(c->c.h);
+		if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+			fprint(2, "odbcdisconnect: SQLDisconnect failed\n");
+		c->c.connected = 0;
+	}
+}
+
+int
+odbcconnect(Conv *c, char *server, char *user, char *auth, char *ename)
+{
+	int ss;
+
+	odbcdisconnect(c);
+	ss = SQLConnect(c->c.h, server, SQL_NTS, user, strlen(user), auth, strlen(auth));
+	if (sqlerr(c, ss, ename, "odbcconnect", "SQLConnect"))
+		return -1;
+	c->c.connected = 1;
+	ss = SQLAllocHandle(SQL_HANDLE_STMT, c->c.h, &c->s.h);
+	if (sqlerr(c, ss, ename, "odbcconnect", "SQLAllocHandle"))
+		return -1;
+	return 0;
+}
+
+int
+odbcnewconv(Client *c, Conv *cv)
+{
+	int ss;
+
+	ss = SQLAllocHandle(SQL_HANDLE_DBC, H(c), &cv->c.h);
+	if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
+		fprint(2, "odbcnewconv: SQLAllocHandle failed\n");
+		return -1;
+	}
+	return 0;
+}
+
+void
+odbcfreeconv(Conv *c)
+{
+	int ss;
+
+	odbcdisconnect(c);
+	ss = SQLFreeHandle(SQL_HANDLE_DBC, c->c.h);
+	if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
+		fprint(2, "odbcfreeconv: SQLFreeHandle failed\n");
+}
+
+/* Free up all memory used in the last query */
+void
+freestat(Stmt *s)
+{
+	int i;
+	if (s->ncols == 0)
+		return;
+	for(i=0; i<s->ncols; i++){
+		Column *d = &s->rec[i];
+		xfree(d->data, "freestat - data");
+	}
+	xfree(s->cols, "freestat - cols");
+	s->cols = 0;
+	xfree(s->rec, "freestat - rec");
+	s->rec = 0;
+	xfree(s->headstr, "freestat - headstr");
+	s->headstr = 0;
+	s->ncols = 0;
+}
+
+
+/* build an array describing the columns */
+int
+mkcols(Conv *c, char *ename)
+{
+	Stmt *s;
+	int rv, i, err, hsize;
+	ushort ignore;
+	char *p;
+
+	s = &c->s;
+	s->ncols = 0;
+	rv = SQLNumResultCols(s->h, &s->ncols);
+	if (sqlerr(c, rv, ename, "mkcols", "SQLNumResultCols"))
+		return -1;
+	s->cols = xmalloc(s->ncols*sizeof(Coltype));
+	err = 0;
+	hsize = 0;
+	for(i=0; i<s->ncols; i++){
+		Coltype *t = &s->cols[i];
+		rv = SQLDescribeCol(s->h, i+1, t->name, sizeof(t->name), &ignore, &t->type, &t->size, &t->digits, &t->nulls);
+		if (sqlerr(c, rv, ename, "mkcols", "SQLDescribeCol"))
+			err++;
+		if(t->size == 0 || t->size > MSGMAX) /* odbc should return 0 if size not available, not -1 */
+			t->size = DEFCOLSIZE;
+		hsize += strlen(t->name) + 1;
+	}
+	if (c->headings) {
+		hsize += 2;
+		s->headstr = xmalloc(hsize);
+		p = s->headstr;
+		for(i=0; i<s->ncols; i++) {
+			Coltype *t = &s->cols[i];
+			p += sprint(p, "%s%c", t->name, c->out.fs);
+		}
+		p[-1] = c->out.rs;
+	} else 
+		s->headstr = 0;
+	return (err ? -1 : 0);
+}
+
+/* build a record to hold `fetched' results */
+int
+mkrec(Conv *c, char *ename)
+{
+	Stmt *s;
+	int rv, i;
+
+	s = &c->s;
+	s->rec = xmalloc(s->ncols*sizeof(Column));
+	for(i=0; i<s->ncols; i++){
+		Coltype *t = &s->cols[i];
+		Column *d = &s->rec[i];
+		if (ODebug)
+			print("Column %d size=%ud type=%hd\n", i, t->size, t->type);
+		d->data = xmalloc(t->size+1);		/* expects to zero terminate */
+		rv = SQLBindCol(s->h, i+1, SQL_C_CHAR, d->data, t->size+1, &d->len);
+		if (sqlerr(c, rv, ename, "mkrec", "SQLBindCol"))
+			return -1;
+	}
+	return 0;
+}
+
+int
+rowcount(Conv *c, char *ename)
+{
+	Stmt *s;
+	int rv;
+
+	s = &c->s;
+	s->nrows = 0;
+	rv = SQLRowCount(s->h, &s->nrows);
+	if (sqlerr(c, rv, ename, "rowcount", "SQLRowCount"))
+		return -1;
+	return 0;
+}
+
+int
+odbcfetch(Conv *c, char *ename)
+{
+	Stmt *s = &c->s;
+	int rv;
+
+	rv = SQLFetch(s->h);
+	if(rv == SQL_NO_DATA) {
+		freestat(s);
+		return 0;
+	}
+	if (sqlerr(c, rv, ename, "odbcfetch", "SQLFetch")) {
+		freestat(s);
+		return -1;
+	}
+	return 1;
+}
+
+int
+odbcresults(Conv *c, char *ename)
+{
+	if(mkcols(c, ename))
+		return -1;
+	if(mkrec(c, ename))
+		return -1;
+	if(rowcount(c, ename))
+		return -1;
+	return 0;
+}
+
+void
+struncate(char *s, long *len)
+{
+	long i;
+	for (i=0; i<*len; i++)
+		if (s[i] == 0) {
+			*len = i;
+			return;
+		}
+}
+
+void
+fix_delim(char *p, int len, char delim)
+{
+	int i;
+	for (i=0; i<len; i++)
+		if (p[i] == delim)
+			p[i] = '\\';
+}
+
+long
+odbcdataread(Conv *c, void *a, long n, ulong offset, char *ename)
+{
+	Stmt *s = &c->s;
+	int i, r;
+	long left;
+	char *p, *lastp;
+
+	if(c->c.connected == 0){
+		strcpy(ename, Enoconnect);
+		return -1;
+	}
+	if (s->cols == 0 || s->rec == 0)
+		return 0;
+	p = a;
+	left = n;
+	if (c->headings) {
+		r = strlen(s->headstr);
+		if (r && offset < r) {
+			memcpy(p, s->headstr+offset, r-offset);
+			p +=  r-offset;
+			left -=  r-offset;
+			return n-left;
+		}
+	}
+	if((r=odbcfetch(c, ename)) < 0)
+		return -1;	
+	if(r == 0)
+		return 0;
+	for(i=0; i<s->ncols; i++){
+		Coltype *t = &s->cols[i];
+		Column *d = &s->rec[i];
+		if (ODebug)
+			fprint(2, "Col %d Returned data len=%d\n", i, d->len);
+		if(d->len <= 0)			/* SQL_NULL_DATA or strange error! */
+			d->len = 0;
+		if (d->len > t->size+1)
+			d->len = t->size+1;
+		if(left <= d->len+1)		/* whole fields */
+			break;
+		struncate(d->data, &d->len);	/* assume string data and stop on an embedded null */
+		memcpy(p, d->data, d->len);
+		lastp = p;
+		left -= d->len;
+		p += d->len;
+		fix_delim(lastp, d->len, '\n');
+		switch(c->out.style){
+		case Float:
+			fix_delim(lastp, d->len, c->out.fs);
+			*p++ = (i==s->ncols-1)? c->out.rs: c->out.fs;
+			left--;
+			break;
+		case Fixed:
+			r = t->size - d->len;
+			if(r < 0)
+				r = 0;
+			if(left < r)
+				r = left;
+			memset(p, ' ', r);
+			left -= r;
+			p += r;
+			break;
+		}
+	}
+	if (left < 0)
+		fprint(2, "*** left<0 n=%d left=%d\n", n, left);
+	return n-left;
+}
+
+/*
+ * Returns a description of the format of a fixed width output
+ * record. `start' is the offset of the first character in the field.
+ * `end' is one greater than the offset of the last character of the field.
+ * `name' is the column name (which may contain spaces).
+ * `start' and `end' are terminated with a space, `name' with a newline.
+ * return 1 record containing one line for each field in the output:
+ *    start1 end1 name1\n
+ *    start2 end2 name2\n
+ *    ....
+ */
+long
+odbcfmtread(Conv *c, void *a, long n, ulong offset, char *ename)
+{
+	Stmt *s = &c->s;
+	int i, len;
+	long left, off;
+	char *p;
+	char buf[100];
+
+	if(offset > 0)
+		return 0;
+	p = a;
+	left = n;
+	off = 0;
+	for(i=0; i<s->ncols; i++){
+		Coltype *t = &s->cols[i];
+
+		len = snprint(buf, sizeof(buf), "%ld %ld %s\n", off, off+t->size, t->name);
+		off += t->size;
+		if(left < len)
+			break;
+		memcpy(p, buf, len);
+		left -= len;
+		p += len;
+
+	}
+	return n-left;
+}
+
+int
+odbctables(Conv *c, char *ename)
+{
+	int rv;
+
+	if(c->c.connected == 0){
+		strcpy(ename, Enoconnect);
+		return -1;
+	}
+	rv = SQLCloseCursor(c->s.h);
+	rv = SQLTables(c->s.h, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (sqlerr(c, rv, ename, "odbctables", "SQLTables"))
+		return -1;
+	if(odbcresults(c, ename))
+		return -1;
+	return 0;
+}
+
+int
+odbccolumns(Conv *c, char *table, char *ename)
+{
+	int rv;
+
+	if(c->c.connected == 0){
+		strcpy(ename, Enoconnect);
+		return -1;
+	}
+	rv = SQLCloseCursor(c->s.h);
+	rv = SQLColumns(c->s.h, 0, 0, 0, 0, table, strlen(table), 0, 0);
+	if (sqlerr(c, rv, ename, "odbccolumns", "SQLColumns"))
+		return -1;
+	if(odbcresults(c, ename))
+		return -1;
+	return 0;
+}
+
+int
+odbcexec(Conv *c, char *cmd, int cmdlen, char *ename)
+{
+	int rv;
+
+	if(c->c.connected == 0){
+		strcpy(ename, Enoconnect);
+		return -1;
+	}
+	SQLCloseCursor(c->s.h);
+	rv = SQLExecDirect(c->s.h, cmd, cmdlen);
+	if (sqlerr(c, rv, ename, "odbcexec", "SQLExecDirect"))
+		return -1;
+	if(odbcresults(c, ename))
+		return -1;
+	return 0;
+}
+
+int
+odbctrans(Conv *c, char *cmd, char *ename)
+{
+	int rv;
+
+	if(strcmp(cmd, "auto") == 0){
+		rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_ON, 0);
+	} else if(strcmp(cmd, "begin") == 0){
+		rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_OFF, 0);
+	} else if(strcmp(cmd, "commit") == 0){
+		rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_COMMIT);
+	} else if(strcmp(cmd, "rollback") == 0){
+		rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_ROLLBACK);
+	} else {
+		strcpy(ename, Ebadarg);
+		return -1;
+	}
+	if (sqlerr(c, rv, ename, "odbctrans", "SQLSetConnectAttr/SQLEndTran"))
+		return -1;
+	return 0;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+	int size;
+
+	size = strlen(str);
+	if(off >= size)
+		return 0;
+	if(off+n > size)
+		n = size-off;
+	memmove(buf, str+off, n);
+	return n;
+}
+
+static void
+newproto(char *name, int maxconv)
+{
+	int l;
+	Proto *p;
+
+	if(np >= MAXPROTO) {
+		print("no %s: increase MAXPROTO", name);
+		return;
+	}
+
+	p = &proto[np];
+	p->name = strdup(name);
+	p->qid.path = QID(np, 0, Qprotodir);
+	p->qid.type = QTDIR;
+	p->x = np++;
+	p->maxconv = maxconv;
+	l = sizeof(Conv*)*(p->maxconv+1);
+	p->conv = xmalloc(l);
+	if(p->conv == 0)
+		fatal("no memory");
+	memset(p->conv, 0, l);
+}
+
+char*
+openmode(int *o)
+{
+	if(*o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)){
+		return Ebadarg;
+	}
+	*o &= ~(OTRUNC|OCEXEC|ORCLOSE);
+	if(*o > OEXEC){
+		return Ebadarg;
+	}
+	if(*o == OEXEC)
+		*o = OREAD;
+	return nil;
+}
+
+static Conv*
+protoclone(Proto *p, char *user)
+{
+	Conv *c, **pp, **ep;
+	uvlong nr;
+	char buf[16];
+
+	c = 0;
+	ep = &p->conv[p->maxconv];
+	for(pp = p->conv; pp < ep; pp++) {
+		c = *pp;
+		if(c == 0) {
+			c = xmalloc(sizeof(Conv));
+			if(c == 0)
+				return 0;
+			c->ref = 1;
+			c->p = p;
+			c->x = pp - p->conv;
+			p->nc++;
+			*pp = c;
+			break;
+		}
+		if(c->ref == 0) {
+			c->ref++;
+			break;
+		}
+	}
+	if(pp >= ep)
+		return 0;
+
+	c->owner = strdup(user);
+	c->perm = 0660;
+	c->state = "Open";
+	c->out = defoutput;
+	c->headings = 0;
+	c->errmsg[0] = 0;
+
+	nr = QID(0, c->x, Qconvdir);
+	snprint(buf, sizeof(buf), "%d", c->x);
+	styxadddir(iserver, Qprotodir, nr, buf, 0555, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qcmd), "cmd", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qctl), "ctl", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qdata), "data", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qerror), "error", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qformat), "format", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qsources), "sources", c->perm, c->owner);
+	styxaddfile(iserver, nr, QID(0, c->x, Qstatus), "status", 0444, c->owner);
+
+	return c;
+}
+
+char*
+dbopen(Qid *qid, int omode)
+{
+	Proto *p;
+	int perm;
+	Conv *cv;
+	char *user;
+	Qid q;
+	Client *c;
+
+	q = *qid;
+	c = styxclient(iserver);
+
+	perm = 0;
+	omode &= 3;
+	switch(omode) {
+	case OREAD:
+		perm = 4;
+		break;
+	case OWRITE:
+		perm = 2;
+		break;
+	case ORDWR:
+		perm = 6;
+		break;
+	}
+
+	switch(TYPE(q)) {
+	default:
+		break;
+	case Qtopdir:
+	case Qprotodir:
+	case Qconvdir:
+	case Qstatus:
+	case Qformat:
+	case Qsources:
+	case Qerror:
+		if(omode != OREAD){
+			return Eperm;
+		}
+		break;
+	case Qclonus:
+		p = &proto[PROTO(q)];
+		cv = protoclone(p, c->uname);
+		if(cv == 0){
+			return Enodev;
+		}
+		qid->path = QID(p->x, cv->x, Qctl);
+		qid->type = 0;
+		qid->vers = 0;
+		if(odbcnewconv(c, cv) != 0){
+			return Eodbcalloc;
+		}
+		break;
+	case Qdata:
+	case Qcmd:
+	case Qctl:
+		p = &proto[PROTO(q)];
+		cv = p->conv[CONV(q)];
+		user = c->uname;
+		if((perm & (cv->perm>>6)) != perm) {
+			if(strcmp(user, cv->owner) != 0 ||
+		 	  (perm & cv->perm) != perm) {
+				return Eperm;
+			}
+		}
+		cv->ref++;
+		if(cv->ref == 1) {
+			cv->state = "Open";
+			cv->owner = strdup(user);
+			cv->perm = 0660;
+			if(odbcnewconv(c, cv) != 0){
+				return Eodbcalloc;
+			}
+		}
+		break;
+	}
+	return openmode(&omode);
+}
+
+char*
+dbclose(Qid qid, int mode)
+{
+	Conv *cc;
+
+	USED(mode);
+	switch(TYPE(qid)) {
+	case Qctl:
+	case Qcmd:
+	case Qdata:
+		cc = proto[PROTO(qid)].conv[CONV(qid)];
+		if(--cc->ref != 0)
+			break;
+		cc->owner = inferno;
+		cc->perm = 0666;
+		cc->state = "Closed";
+		odbcfreeconv(cc);
+		styxrmfile(iserver, QID(0, cc->x, Qconvdir));
+		break;
+	}
+	return nil;
+}
+
+static char ebuf[ERRMAX];
+
+char*
+dbread(Qid qid, char *ba, ulong *n, vlong offset)
+{
+	uchar *a = ba;
+	Conv *c;
+	Proto *x;
+	char buf[128], *p, *s;
+	long r;
+	ulong m;
+
+	m = *n;
+	ebuf[0] = 0;
+	p = a;
+	switch(TYPE(qid)) {
+	default:
+		return Eperm;
+	case Qnclients:
+		snprint(buf, sizeof(buf), "%d\n", nclients);
+		*n = readstr(offset, p, m, buf);
+		return nil;
+	case Qprotodir:
+	case Qtopdir:
+	case Qconvdir:
+		return "bad read of directory";
+	case Qctl:
+		snprint(buf, sizeof(buf), "%ld", CONV(qid));
+		*n = readstr(offset, p, m, buf);
+		return nil;
+	case Qstatus:
+		x = &proto[PROTO(qid)];
+		c = x->conv[CONV(qid)];
+		snprint(buf, sizeof(buf), "%s/%d %ld %s %s\n",
+			c->p->name, c->x, c->ref, c->state, "");
+		*n = readstr(offset, p, m, buf);
+		return nil;
+	case Qdata:
+		c = proto[PROTO(qid)].conv[CONV(qid)];
+		*n = odbcdataread(c, a, m, offset, ebuf);
+		if(ebuf[0] != 0)
+			return ebuf;
+		return nil;
+	case Qformat:
+		c = proto[PROTO(qid)].conv[CONV(qid)];
+		*n = odbcfmtread(c, a, m, offset, ebuf);
+		if(ebuf[0] != 0)
+			return ebuf;
+		return nil;
+	case Qerror:
+		c = proto[PROTO(qid)].conv[CONV(qid)];
+		*n = readstr(offset, p, m, odbcerror(c, 1));
+		return nil;
+	case Qsources:
+		c = proto[PROTO(qid)].conv[CONV(qid)];
+		s = odbcsources(styxclient(iserver));
+		r = readstr(offset, p, m, s);
+		free(s);
+		*n = r;
+		return nil;
+	}
+	return nil;
+}
+
+char*
+dbwrite(Qid qid, char *ba, ulong *n, vlong offset)
+{
+	uchar *a = ba;
+	int nf;
+	Conv *c;
+	Proto *x;
+	char *fields[10], buf[512], safebuf[512];
+	ulong m;
+
+	m = *n;
+	ebuf[0] = 0;
+	switch(TYPE(qid)) {
+	default:
+		return Eperm;
+	case Qctl:
+		x = &proto[PROTO(qid)];
+		c = x->conv[CONV(qid)];
+		// 
+		if(m > sizeof(buf)-1)
+			m = sizeof(buf)-1;
+		memmove(buf, a, m);
+		buf[m] = '\0';
+		if (ODebug)
+			fprint(2, "write Qctl: <%s>\n", buf);
+		fields[0] = 0;
+		nf = parsefields(buf, fields, sizeof(fields)/sizeof(*fields), " \n\t");
+		if (nf == 0) {
+			return Ebadarg;
+		}
+		if(strcmp(fields[0], "connect") == 0){ 	/* connect database [user!auth] */
+			char *afields[2];
+			char *user = "";
+			char *auth = "";
+			switch(nf){
+			default:
+				return Ebadarg;
+			case 2:
+				break;
+			case 3:
+				nf = parsefields(fields[2], afields, 2, "!");
+				switch(nf){
+				case 2:
+					user = afields[0];
+					auth = afields[1];
+					break;
+				case 1:
+					if(fields[2][0] == 0)
+						auth = afields[0];
+					else
+						user = afields[0];
+					break;
+				default:
+					break;
+				}
+				break;
+			}
+			if(odbcconnect(c, fields[1], user, auth, ebuf) < 0)
+				return ebuf;
+			c->state = "Connected";
+		} else if(strcmp(fields[0], "disconnect") == 0){
+			odbcdisconnect(c);
+			c->state = "Disconnected";
+		} else if(strcmp(fields[0], "fixed") == 0){
+			c->out = defoutput;
+			c->out.style = Fixed;
+		} else if(strcmp(fields[0], "float") == 0){
+			c->out = defoutput;
+			c->out.style = Float;
+			if(nf > 1)
+				c->out.fs = fields[1][0];
+			if(nf > 2)
+				c->out.rs = fields[2][0];
+		} else if(strcmp(fields[0], "headings") == 0){
+			c->headings = 1;
+		} else if(strcmp(fields[0], "noheadings") == 0){
+			c->headings = 0;
+		} else if(strcmp(fields[0], "trans") == 0){ /* begin, auto, commit, rollback */
+			if(nf < 2){
+				return Ebadarg;
+			}
+			if(odbctrans(c, fields[1], ebuf) < 0)
+				return ebuf;
+		} else {
+			return Ebadcmd;
+		}
+		*n = m;
+		return nil;
+	case Qcmd:
+		x = &proto[PROTO(qid)];
+		c = x->conv[CONV(qid)];
+		if(m > sizeof(buf)-1)
+			m = sizeof(buf)-1;
+		memmove(buf, a, m);
+		buf[m] = '\0';
+		if (ODebug)
+			fprint(2, "write Qcmd: <%s>\n", buf);
+		memmove(safebuf, a, m);
+		safebuf[m] = '\0';
+		fields[0] = 0;
+		nf = parsefields(buf, fields, 3, " \n\t");
+		if (nf == 0) {
+			return Ebadarg;
+		}
+		if(strcmp(fields[0], "tables") == 0){
+			if(odbctables(c, ebuf))
+				return ebuf;
+		}else if(strcmp(fields[0], "columns") == 0){
+			if(nf < 2){
+				return Ebadarg;
+			}
+			if(odbccolumns(c, &safebuf[strlen(fields[0])+1], ebuf))	/* allow for spaces in table name */
+				return ebuf;
+		} else
+			if (odbcexec(c, a, m, ebuf))
+				return ebuf;
+		*n = m;
+		return nil;
+	case Qdata:
+		return Eperm;
+	}
+	return nil;
+}
+
+void
+badusage(void)
+{
+	fprint(2, "Usage: odbc [-d] [-p port]\n");
+	exit(1);
+}
+
+Styxops ops = {
+	odbcnewclient,			/* newclient */
+	odbcfreeclient,			/* freeclient */
+
+	nil,			/* attach */
+	nil,			/* walk */
+	dbopen,		/* open */
+	nil,			/* create */
+	dbread,		/* read */
+	dbwrite,		/* write */
+	dbclose,		/* close */
+	nil,			/* remove */
+	nil,			/* stat */
+	nil,			/* wstat */
+};
+
+void
+main(int argc, char *argv[])
+{
+	Styxserver s;
+
+	ARGBEGIN {
+	default:
+		badusage();
+	case 'd':		/* Debug */
+		ODebug = 1;
+		styxdebug();
+		break;
+	case 'p':		/* Debug */
+		netport = EARGF(badusage());
+		break;
+	} ARGEND
+
+	iserver = &s;
+	styxinit(&s, &ops, netport, -1, 1);
+	styxaddfile(&s, Qroot, Qnclients, "nclients", 0444, inferno);
+	styxadddir(&s, Qroot, Qprotodir, "db", 0555, inferno);
+	styxaddfile(&s, Qprotodir, Qclonus, "new", 0666, inferno);
+	newproto("db", 100);
+	for (;;) {
+		styxwait(&s);
+		styxprocess(&s);
+	}
+	styxend(&s);
+}
--- /dev/null
+++ b/tools/styxtest/mkfile
@@ -1,0 +1,23 @@
+<../../mkconfig
+
+TARG=styxtest
+
+OFILES=\
+	styxtest.$O\
+
+HFILES=\
+	../libstyx/styxserver.h\
+
+LIBS=styx 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<mkfile-$SYSTARG
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../libstyx
+
+
+
+
--- /dev/null
+++ b/tools/styxtest/mkfile-Nt
@@ -1,0 +1,1 @@
+SYSLIBS= wsock32.lib $SYSLIBS
--- /dev/null
+++ b/tools/styxtest/mkfile-Solaris
@@ -1,0 +1,1 @@
+SYSLIBS=$EMULIBS
--- /dev/null
+++ b/tools/styxtest/styxtest.c
@@ -1,0 +1,198 @@
+#include <lib9.h>
+#include "styxserver.h"
+
+/*
+ * An in-memory file server
+ * allowing truncation, removal on closure, wstat and
+ * all other file operations
+ */
+
+char *fsremove(Qid);
+
+Styxserver *server;
+
+char*
+fsopen(Qid *qid, int mode)
+{
+	Styxfile *f;
+
+	f = styxfindfile(server, qid->path);
+	if(mode&OTRUNC){	/* truncate on open */
+		styxfree(f->u);
+		f->u = nil;
+		f->d.length = 0;
+	}
+	return nil;
+}
+
+char*
+fsclose(Qid qid, int mode)
+{
+	if(mode&ORCLOSE)	/* remove on close */
+		return fsremove(qid);
+	return nil;
+}
+
+char *
+fscreate(Qid *qid, char *name, int perm, int mode)
+{
+	int isdir;
+	Styxfile *f;
+
+	USED(mode);
+	isdir = perm&DMDIR;
+	if(isdir)
+		f = styxadddir(server, qid->path, -1, name, perm, "inferno");
+	else
+		f = styxaddfile(server, qid->path, -1, name, perm, "inferno");
+	if(f == nil)
+		return Eexist;
+	f->u = nil;
+	f->d.length = 0;
+	*qid = f->d.qid;
+	return nil;
+}
+
+char *
+fsremove(Qid qid)
+{
+	Styxfile *f;
+
+	f = styxfindfile(server, qid.path);
+	if((f->d.qid.type&QTDIR) && f->child != nil)
+		return "directory not empty";
+	styxfree(f->u);
+	styxrmfile(server, qid.path);	
+	return nil;
+}
+
+char *
+fsread(Qid qid, char *buf, ulong *n, vlong off)
+{
+	int m;
+	Styxfile *f;
+
+	f = styxfindfile(server, qid.path);
+	m = f->d.length;
+	if(off >= m)
+		*n = 0;
+	else{
+		if(off + *n > m)
+			*n = m-off;
+		memmove(buf, (char*)f->u+off, *n);
+	}
+	return nil;
+}
+
+char*
+fswrite(Qid qid, char *buf, ulong *n, vlong off)
+{
+	Styxfile *f;
+	vlong m, p;
+	char *u;
+
+	f = styxfindfile(server, qid.path);
+	m = f->d.length;
+	p = off + *n;
+	if(p > m){	/* just grab a larger piece of memory */
+		u = styxmalloc(p);
+		if(u == nil)
+			return "out of memory";
+		memset(u, 0, p);
+		memmove(u, f->u, m);
+		styxfree(f->u);
+		f->u = u;
+		f->d.length = p;
+	}
+	memmove((char*)f->u+off, buf, *n);
+	return nil;
+}
+
+char*
+fswstat(Qid qid, Dir *d)
+{
+	Styxfile *f, *tf;
+	Client *c;
+	int owner;
+
+	/* the most complicated operation when fully allowed */
+
+	c = styxclient(server);
+	f = styxfindfile(server, qid.path);
+	owner = strcmp(c->uname, f->d.uid) == 0;
+	if(d->name != nil && strcmp(d->name, f->d.name) != 0){
+		/* need write permission in parent directory */
+		if(!styxperm(f->parent, c->uname, OWRITE))
+			return Eperm;
+		if((tf = styxaddfile(server, f->parent->d.qid.path, -1, d->name, 0, "")) == nil){
+			/* file with same name exists */
+			return Eexist;
+		}
+		else{
+			/* undo above addfile */
+			styxrmfile(server, tf->d.qid.path);
+		}
+		/* ok to change name now */
+		styxfree(f->d.name);
+		f->d.name = strdup(d->name);	
+	}
+	if(d->uid != nil && strcmp(d->uid, f->d.uid) != 0){
+		if(!owner)
+			return Eperm;
+		styxfree(f->d.uid);
+		f->d.uid = strdup(d->uid);
+	}
+	if(d->gid != nil && strcmp(d->gid, f->d.gid) != 0){
+		if(!owner)
+			return Eperm;
+		styxfree(f->d.gid);
+		f->d.gid = strdup(d->gid);
+	}
+	if(d->mode != ~0 && d->mode != f->d.mode){
+		if(!owner)
+			return Eperm;
+		if((d->mode&DMDIR) != (f->d.mode&DMDIR))
+			return Eperm;	/* cannot change file->directory or vice-verse */
+		f->d.mode = d->mode;
+	}
+	if(d->mtime != ~0 && d->mtime != f->d.mtime){
+		if(!owner)
+			return Eperm;
+		f->d.mtime = d->mtime;
+	}
+	/* all other file attributes cannot be changed by wstat */
+	return nil;
+}
+
+Styxops ops = {
+	nil,			/* newclient */
+	nil,			/* freeclient */
+
+	nil,			/* attach */
+	nil,			/* walk */
+	fsopen,		/* open */
+	fscreate,		/* create */
+	fsread,		/* read */
+	fswrite,		/* write */
+	fsclose,		/* close */
+	fsremove,	/* remove */
+	nil,			/* stat */
+	fswstat,		/* wstat */
+};
+
+void
+main(int argc, char **argv)
+{
+	Styxserver s;
+
+	USED(argc);
+	USED(argv);
+	server = &s;
+	styxdebug();
+	styxinit(&s, &ops, "6701", 0777, 1);
+	for(;;){
+		styxwait(&s);
+		styxprocess(&s);
+	}
+	exits(nil);
+}
--- /dev/null
+++ b/tools/styxtest/styxtest0.c
@@ -1,0 +1,95 @@
+#include <lib9.h>
+#include "styxserver.h"
+
+int nq;
+Styxserver *server;
+
+void
+myinit(Styxserver *s)
+{
+	styxaddfile(s, Qroot, 1, "fred", 0664, "inferno");
+	styxaddfile(s, Qroot, 2, "joe", 0664, "inferno");
+	styxadddir(s, Qroot, 3, "adir", 0775, "inferno");
+	styxaddfile(s, 3, 4, "bill", 0664, "inferno");
+	styxadddir(s, Qroot, 5, "new", 0775, "inferno");
+	styxadddir(s, 5, 6, "cdir", 0775, "inferno");
+	styxaddfile(s, 6, 7, "cfile", 0664, "inferno");
+	nq = 8;
+}
+
+char *
+mycreate(Qid *qid, char *name, int perm, int mode)
+{
+	int isdir;
+	Styxfile *f;
+
+	USED(mode);
+	isdir = perm&DMDIR;
+	if(isdir)
+		f = styxadddir(server, qid->path, nq++, name , perm, "inferno");
+	else
+		f = styxaddfile(server, qid->path, nq++, name, perm, "inferno");
+	if(f == nil)
+		return Eexist;
+	*qid = f->d.qid;
+	return nil;
+}
+
+char *
+myremove(Qid qid)
+{
+	Styxfile *f;
+
+	f = styxfindfile(server, qid.path);
+	if(f != nil && (f->d.qid.type&QTDIR) && f->child != nil)
+		return "directory not empty";
+
+	if(styxrmfile(server, qid.path) < 0)
+		return Enonexist;	
+	return nil;
+}
+
+char *
+myread(Qid qid, char *d, ulong *n, vlong offset)
+{
+	if(qid.path != 1){
+		*n = 0;
+		return nil;
+	}
+	*n = styxreadstr(offset, d, *n, "abcdefghijklmn");
+	return nil;
+}
+
+Styxops ops = {
+	nil,			/* newclient */
+	nil,			/* freeclient */
+
+	nil,			/* attach */
+	nil,			/* walk */
+	nil,			/* open */
+	mycreate,		/* create */
+	myread,		/* read */
+	nil,			/* write */
+	nil,			/* close */
+	myremove,	/* remove */
+	nil,			/* stat */
+	nil,			/* wstat */
+};
+
+main(int argc, char **argv)
+{
+	Styxserver s;
+
+	USED(argc);
+	USED(argv);
+	server = &s;
+	styxdebug();
+	styxinit(&s, &ops, "6701", 0555, 0);
+	myinit(&s);
+	for(;;){
+		styxwait(&s);
+		styxprocess(&s);
+	}
+	return 0;
+}
+
--- /dev/null
+++ b/utils/0a/a.h
@@ -1,0 +1,183 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../vc/v.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+
+#define	MAXALIGN	7
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	FPCHIP		1
+#define	NSYMB		8192
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym*	sym;
+	long	offset;
+	short	type;
+	short	reg;
+	short	name;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	nosched;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+int	assemble(char*);
+void*	alloc(long);
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+int	isreg(Gen*);
+void	outcode(int, Gen*, int, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	maclin(void);
+void	macprag(void);
+void	macif(int);
+void	macend(void);
+void	outhist(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+
+/*
+ *	Posix.c/Inferno.c/Nt.c
+ */
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/0a/a.y
@@ -1,0 +1,588 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	long	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token	<lval>	LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token	<lval>	LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token	<lval>	LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token	<lval>	LCONST LSP LSB LFP LPC LHI LLO LMREG 
+%token	<lval>	LTYPEX LREG LFREG LFCREG LR LM LF
+%token	<lval>	LFCR LSCHED
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg
+%type	<gen>	gen vgen lgen vlgen rel reg freg mreg fcreg
+%type	<gen>	imm ximm ireg name oreg imr nireg fgen
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LSCHED ';'
+	{
+		nosched = $1;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * Immed-type
+ */
+	LTYPE1 imr ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE1 imr ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * NOR
+ */
+|	LTYPE2 imr ',' sreg ',' imr
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE2 imr ',' imr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * LOAD/STORE, but not MOVW
+ */
+|	LTYPE3 lgen ',' gen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * SPECIAL
+ */
+|	LTYPE4 comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * MOVW
+ */
+|	LTYPE5 vlgen ',' vgen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * MUL/DIV
+ */
+|	LTYPE6 reg ',' sreg comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTYPE6 reg ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * JMP/JAL
+ */
+|	LTYPE7 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE7 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 sreg ',' nireg
+	{
+		outcode($1, &nullgen, $2, &$4);
+	}
+/*
+ * BEQ/BNE
+ */
+|	LTYPE9 gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPE9 gen ',' sreg ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * B-other
+ */
+|	LTYPEA gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTYPEB name ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEB name ',' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * DATA
+ */
+|	LTYPEC name '/' con ',' ximm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * floating-type
+ */
+|	LTYPED freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' LFREG ',' freg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPEF freg ',' LFREG comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+/*
+ * coprocessor branch
+ */
+|	LTYPEG comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * word
+ */
+|	LTYPEH comma ximm
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * NOP
+ */
+|	LTYPEI comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEI ',' vgen
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPEI vgen comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * BREAK -- overloaded with CACHE opcode
+ */
+|	LTYPEJ comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEJ vgen ',' vgen
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+
+comma:
+|	','
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+vlgen:
+	lgen
+|	fgen
+|	mreg
+|	fcreg
+|	LHI
+	{
+		$$ = nullgen;
+		$$.type = D_HI;
+	}
+|	LLO
+	{
+		$$ = nullgen;
+		$$.type = D_LO;
+	}
+
+vgen:
+	gen
+|	fgen
+|	mreg
+|	fcreg
+|	LHI
+	{
+		$$ = nullgen;
+		$$.type = D_HI;
+	}
+|	LLO
+	{
+		$$ = nullgen;
+		$$.type = D_LO;
+	}
+
+lgen:
+	gen
+|	ximm
+
+fgen:
+	freg
+
+mreg:
+	LMREG
+	{
+		$$ = nullgen;
+		$$.type = D_MREG;
+		$$.reg = $1;
+	}
+|	LM '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_MREG;
+		$$.reg = $3;
+	}
+
+fcreg:
+	LFCREG
+	{
+		$$ = nullgen;
+		$$.type = D_FCREG;
+		$$.reg = $1;
+	}
+|	LFCR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCREG;
+		$$.reg = $3;
+	}
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+ximm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' oreg
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' '*' '$' oreg
+	{
+		$$ = $4;
+		$$.type = D_OCONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+nireg:
+	ireg
+|	con ireg
+	{
+		if($1 != 0)
+			yyerror("offset must be zero");
+		$$ = $2;
+	}
+|	name
+	{
+		$$ = $1;
+		if($1.name != D_EXTERN && $1.name != D_STATIC) {
+		}
+	}
+
+ireg:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+
+gen:
+	reg
+|	con
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.offset = $1;
+	}
+|	oreg
+
+oreg:
+	name
+|	name '(' sreg ')'
+	{
+		$$ = $1;
+		$$.type = D_OREG;
+		$$.reg = $3;
+	}
+|	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+imr:
+	reg
+|	imm
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+reg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+sreg:
+	LREG
+|	LR '(' expr ')'
+	{
+		if($$ < 0 || $$ >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/0a/l.s
@@ -1,0 +1,703 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+
+#define	MAXMACH		4			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	MS2HZ		50			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/20)		/* ticks to seconds */
+#define	TK2MS(t)	((t)*MS2HZ)		/* ticks to milliseconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * CP0 registers
+ */
+
+#define INDEX		0
+#define RANDOM		1
+#define TLBPHYS		2
+#define CONTEXT		4
+#define BADVADDR	8
+#define TLBVIRT		10
+#define STATUS		12
+#define CAUSE		13
+#define EPC		14
+#define	PRID		15
+
+/*
+ * M(STATUS) bits
+ */
+#define IEC		0x00000001
+#define KUC		0x00000002
+#define IEP		0x00000004
+#define KUP		0x00000008
+#define INTMASK		0x0000ff00
+#define SW0		0x00000100
+#define SW1		0x00000200
+#define INTR0		0x00000400
+#define INTR1		0x00000800
+#define INTR2		0x00001000
+#define INTR3		0x00002000
+#define INTR4		0x00004000
+#define INTR5		0x00008000
+#define ISC		0x00010000
+#define SWC		0x00020000
+#define CU1		0x20000000
+
+/*
+ * Traps
+ */
+
+#define	UTLBMISS	(KSEG0+0x00)
+#define	EXCEPTION	(KSEG0+0x80)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		25		/* R25 is m-> */
+#define	USER		24		/* R24 is u-> */
+#define	MPID		0xBF000000	/* long; low 3 bits identify mp bus slot */
+#define WBFLUSH		0xBC000000	/* D-CACHE data; used for write buffer flush */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	MACHADDR	0x80014000
+#define	USERADDR	0xC0000000
+#define	UREGADDR	(USERADDR+BY2PG-4-0xA0)
+/*
+ * MMU
+ */
+
+#define	KUSEG	0x00000000
+#define KSEG0	0x80000000
+#define KSEG1	0xA0000000
+#define	KSEG2	0xC0000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	PTEGLOBL	(1<<8)
+#define	PTEVALID	(1<<9)
+#define	PTEWRITE	(1<<10)
+#define	PTEPID(n)	((n)<<6)
+
+#define	NTLBPID	64	/* number of pids */
+#define	NTLB	64	/* number of entries */
+#define	TLBROFF	8	/* offset of first randomly indexed entry */
+
+/*
+ * Address spaces
+ */
+
+#define	UZERO	KUSEG			/* base of user address space */
+#define	UTZERO	(UZERO+BY2PG)		/* first address in user text */
+#define	USTKTOP	KZERO			/* byte just beyond user stack */
+#define	TSTKTOP	(USERADDR+100*BY2PG)	/* top of temporary stack */
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KSEG0+0x20000)		/* first address in kernel text */
+#define	USTACKSIZE	(4*1024*1024)	/* size of user stack */
+/*
+ * Exception codes
+ */
+#define	CINT	 0		/* external interrupt */
+#define	CTLBM	 1		/* TLB modification */
+#define	CTLBL	 2		/* TLB miss (load or fetch) */
+#define	CTLBS	 3		/* TLB miss (store) */
+#define	CADREL	 4		/* address error (load or fetch) */
+#define	CADRES	 5		/* address error (store) */
+#define	CBUSI	 6		/* bus error (fetch) */
+#define	CBUSD	 7		/* bus error (data load or store) */
+#define	CSYS	 8		/* system call */
+#define	CBRK	 9		/* breakpoint */
+#define	CRES	10		/* reserved instruction */
+#define	CCPU	11		/* coprocessor unusable */
+#define	COVF	12		/* arithmetic overflow */
+#define	CUNK13	13		/* undefined 13 */
+#define	CUNK14	14		/* undefined 14 */
+#define	CUNK15	15		/* undefined 15 */
+
+#define	NSEG	5
+
+#define SP		R29
+
+#define PROM		(KSEG1+0x1FC00000)
+#define	NOOP		NOR R0,R0
+#define	WAIT		NOOP; NOOP
+
+/*
+ * Boot first processor
+ *   - why is the processor number loaded from R0 ?????
+ */
+TEXT	start(SB), $-4
+
+	MOVW	$setR30(SB), R30
+	MOVW	$(CU1|INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+	MOVW	R1, M(STATUS)
+	WAIT
+
+	MOVW	$(0x1C<<7), R1
+	MOVW	R1, FCR31	/* permit only inexact and underflow */
+	NOOP
+	MOVD	$0.5, F26
+	SUBD	F26, F26, F24
+	ADDD	F26, F26, F28
+	ADDD	F28, F28, F30
+
+	MOVD	F24, F0
+	MOVD	F24, F2
+	MOVD	F24, F4
+	MOVD	F24, F6
+	MOVD	F24, F8
+	MOVD	F24, F10
+	MOVD	F24, F12
+	MOVD	F24, F14
+	MOVD	F24, F16
+	MOVD	F24, F18
+	MOVD	F24, F20
+	MOVD	F24, F22
+
+	MOVW	$MACHADDR, R(MACH)
+	ADDU	$(BY2PG-4), R(MACH), SP
+	MOVW	$0, R(USER)
+	MOVW	R0, 0(R(MACH))
+
+	MOVW	$edata(SB), R1
+	MOVW	$end(SB), R2
+
+clrbss:
+	MOVB	$0, (R1)
+	ADDU	$1, R1
+	BNE	R1, R2, clrbss
+
+	MOVW	R4, _argc(SB)
+	MOVW	R5, _argv(SB)
+	MOVW	R6, _env(SB)
+	JAL	main(SB)
+	JMP	(R0)
+
+/*
+ * Take first processor into user mode
+ * 	- argument is stack pointer to user
+ */
+
+TEXT	touser(SB), $-4
+
+	MOVW	M(STATUS), R1
+	OR	$(KUP|IEP), R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	0(FP), SP
+	MOVW	$(UTZERO+32), R26	/* header appears in text */
+	RFE	(R26)
+
+/*
+ * Bring subsequent processors on line
+ */
+TEXT	newstart(SB), $0
+
+	MOVW	$setR30(SB), R30
+	MOVW	$(INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	$MACHADDR, R(MACH)
+	MOVB	(MPID+3), R1
+	AND	$7, R1
+	SLL	$PGSHIFT, R1, R2
+	ADDU	R2, R(MACH)
+	ADDU	$(BY2PG-4), R(MACH), SP
+	MOVW	$0, R(USER)
+	MOVW	R1, 0(R(MACH))
+	JAL	online(SB)
+	JMP	(R0)
+
+TEXT	firmware(SB), $0
+
+	MOVW	$(PROM+0x18), R1 /**/
+/*	MOVW	$(PROM+0x00), R1 /**/
+	JMP	(R1)
+
+TEXT	splhi(SB), $0
+
+	MOVW	M(STATUS), R1
+	AND	$~IEC, R1, R2
+	MOVW	R2, M(STATUS)
+	NOOP
+	RET
+
+TEXT	spllo(SB), $0
+
+	MOVW	M(STATUS), R1
+	OR	$IEC, R1, R2
+	MOVW	R2, M(STATUS)
+	NOOP
+	RET
+
+TEXT	splx(SB), $0
+
+	MOVW	0(FP), R1
+	MOVW	M(STATUS), R2
+	AND	$IEC, R1
+	AND	$~IEC, R2
+	OR	R2, R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	RET
+
+TEXT	wbflush(SB), $-4
+
+	MOVW	$WBFLUSH, R1
+	MOVW	0(R1), R1
+	RET
+
+TEXT	setlabel(SB), $0
+
+	MOVW	0(FP), R2
+	MOVW	$0, R1
+	MOVW	R31, 0(R2)
+	MOVW	R29, 4(R2)
+	RET
+
+TEXT	gotolabel(SB), $0
+
+	MOVW	0(FP), R2
+	MOVW	$1, R1
+	MOVW	0(R2), R31
+	MOVW	4(R2), R29
+	RET
+
+TEXT	gotopc(SB), $8
+
+	MOVW	0(FP), R7		/* save arguments for later */
+	MOVW	_argc(SB), R4
+	MOVW	_argv(SB), R5
+	MOVW	_env(SB), R6
+	MOVW	R0, 4(SP)
+	MOVW	$(64*1024), R1
+	MOVW	R1, 8(SP)
+	JAL	icflush(SB)
+	JMP	(R7)
+
+TEXT	puttlb(SB), $4
+
+	JAL	splhi(SB)
+	MOVW	0(FP), R2
+	MOVW	4(FP), R3
+	MOVW	R1, 4(SP)
+	MOVW	R2, M(TLBVIRT)
+	MOVW	R3, M(TLBPHYS)
+	NOOP
+	TLBP
+	NOOP
+	MOVW	M(INDEX), R4
+	BGEZ	R4, index
+	TLBWR
+	NOOP
+	JAL	splx(SB)
+	RET
+index:
+	TLBWI
+	NOOP
+	JAL	splx(SB)
+	RET
+
+TEXT	puttlbx(SB), $0
+
+	MOVW	0(FP), R4
+	MOVW	4(FP), R2
+	MOVW	8(FP), R3
+	SLL	$8, R4
+	MOVW	R2, M(TLBVIRT)
+	MOVW	R3, M(TLBPHYS)
+	MOVW	R4, M(INDEX)
+	NOOP
+	TLBWI
+	NOOP
+	RET
+
+TEXT	tlbp(SB), $0
+	TLBP
+	NOOP
+	MOVW	M(INDEX), R1
+	RET
+	
+TEXT	tlbvirt(SB), $0
+	TLBP
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	RET
+	
+
+TEXT	gettlb(SB), $0
+
+	MOVW	0(FP), R3
+	MOVW	4(FP), R4
+	SLL	$8, R3
+	MOVW	R3, M(INDEX)
+	NOOP
+	TLBR
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	MOVW	M(TLBPHYS), R2
+	NOOP
+	MOVW	R1, 0(R4)
+	MOVW	R2, 4(R4)
+	RET
+
+TEXT	gettlbvirt(SB), $0
+
+	MOVW	0(FP), R3
+	SLL	$8, R3
+	MOVW	R3, M(INDEX)
+	NOOP
+	TLBR
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	NOOP
+	RET
+
+TEXT	vector80(SB), $-4
+
+	MOVW	$exception(SB), R26
+	JMP	(R26)
+
+TEXT	exception(SB), $-4
+
+	MOVW	M(STATUS), R26
+	AND	$KUP, R26
+	BEQ	R26, waskernel
+
+wasuser:
+	MOVW	SP, R26
+		/*
+		 * set kernel sp: ureg - ureg* - pc
+		 * done in 2 steps because R30 is not set
+		 * and the loader will make a literal
+		 */
+	MOVW	$((UREGADDR-2*BY2WD) & 0xffff0000), SP
+	OR	$((UREGADDR-2*BY2WD) & 0xffff), SP
+	MOVW	R26, 0x10(SP)			/* user SP */
+	MOVW	R31, 0x28(SP)
+	MOVW	R30, 0x2C(SP)
+	MOVW	M(CAUSE), R26
+	MOVW	R(MACH), 0x3C(SP)
+	MOVW	R(USER), 0x40(SP)
+	AND	$(0xF<<2), R26
+	SUB	$(CSYS<<2), R26
+
+	JAL	saveregs(SB)
+
+	MOVW	$setR30(SB), R30
+	SUBU	$(UREGADDR-2*BY2WD-USERADDR), SP, R(USER)
+	MOVW	$MPID, R1
+	MOVB	3(R1), R1
+	MOVW	$MACHADDR, R(MACH)		/* locn of mach 0 */
+	AND	$7, R1
+	SLL	$PGSHIFT, R1
+	ADDU	R1, R(MACH)			/* add offset for mach # */
+
+	BNE	R26, notsys
+
+	JAL	syscall(SB)
+
+	MOVW	0x28(SP), R31
+	MOVW	0x08(SP), R26
+	MOVW	0x2C(SP), R30
+	MOVW	R26, M(STATUS)
+	NOOP
+	MOVW	0x0C(SP), R26		/* old pc */
+	MOVW	0x10(SP), SP
+	RFE	(R26)
+
+notsys:
+	JAL	trap(SB)
+
+restore:
+	JAL	restregs(SB)
+	MOVW	0x28(SP), R31
+	MOVW	0x2C(SP), R30
+	MOVW	0x3C(SP), R(MACH)
+	MOVW	0x40(SP), R(USER)
+	MOVW	0x10(SP), SP
+	RFE	(R26)
+
+waskernel:
+	MOVW	$1, R26			/* not sys call */
+	MOVW	SP, -0x90(SP)		/* drop this if possible */
+	SUB	$0xA0, SP
+	MOVW	R31, 0x28(SP)
+	JAL	saveregs(SB)
+	JAL	trap(SB)
+	JAL	restregs(SB)
+	MOVW	0x28(SP), R31
+	ADD	$0xA0, SP
+	RFE	(R26)
+
+TEXT	saveregs(SB), $-4
+	MOVW	R1, 0x9C(SP)
+	MOVW	R2, 0x98(SP)
+	ADDU	$8, SP, R1
+	MOVW	R1, 0x04(SP)		/* arg to base of regs */
+	MOVW	M(STATUS), R1
+	MOVW	M(EPC), R2
+	MOVW	R1, 0x08(SP)
+	MOVW	R2, 0x0C(SP)
+
+	BEQ	R26, return		/* sys call, don't save */
+
+	MOVW	M(CAUSE), R1
+	MOVW	M(BADVADDR), R2
+	MOVW	R1, 0x14(SP)
+	MOVW	M(TLBVIRT), R1
+	MOVW	R2, 0x18(SP)
+	MOVW	R1, 0x1C(SP)
+	MOVW	HI, R1
+	MOVW	LO, R2
+	MOVW	R1, 0x20(SP)
+	MOVW	R2, 0x24(SP)
+					/* LINK,SB,SP missing */
+	MOVW	R28, 0x30(SP)
+					/* R27, R26 not saved */
+					/* R25, R24 missing */
+	MOVW	R23, 0x44(SP)
+	MOVW	R22, 0x48(SP)
+	MOVW	R21, 0x4C(SP)
+	MOVW	R20, 0x50(SP)
+	MOVW	R19, 0x54(SP)
+	MOVW	R18, 0x58(SP)
+	MOVW	R17, 0x5C(SP)
+	MOVW	R16, 0x60(SP)
+	MOVW	R15, 0x64(SP)
+	MOVW	R14, 0x68(SP)
+	MOVW	R13, 0x6C(SP)
+	MOVW	R12, 0x70(SP)
+	MOVW	R11, 0x74(SP)
+	MOVW	R10, 0x78(SP)
+	MOVW	R9, 0x7C(SP)
+	MOVW	R8, 0x80(SP)
+	MOVW	R7, 0x84(SP)
+	MOVW	R6, 0x88(SP)
+	MOVW	R5, 0x8C(SP)
+	MOVW	R4, 0x90(SP)
+	MOVW	R3, 0x94(SP)
+return:
+	RET
+
+TEXT	restregs(SB), $-4
+					/* LINK,SB,SP missing */
+	MOVW	0x30(SP), R28
+					/* R27, R26 not saved */
+					/* R25, R24 missing */
+	MOVW	0x44(SP), R23
+	MOVW	0x48(SP), R22
+	MOVW	0x4C(SP), R21
+	MOVW	0x50(SP), R20
+	MOVW	0x54(SP), R19
+	MOVW	0x58(SP), R18
+	MOVW	0x5C(SP), R17
+	MOVW	0x60(SP), R16
+	MOVW	0x64(SP), R15
+	MOVW	0x68(SP), R14
+	MOVW	0x6C(SP), R13
+	MOVW	0x70(SP), R12
+	MOVW	0x74(SP), R11
+	MOVW	0x78(SP), R10
+	MOVW	0x7C(SP), R9
+	MOVW	0x80(SP), R8
+	MOVW	0x84(SP), R7
+	MOVW	0x88(SP), R6
+	MOVW	0x8C(SP), R5
+	MOVW	0x90(SP), R4
+	MOVW	0x94(SP), R3
+	MOVW	0x24(SP), R2
+	MOVW	0x20(SP), R1
+	MOVW	R2, LO
+	MOVW	R1, HI
+	MOVW	0x08(SP), R1
+	MOVW	0x98(SP), R2
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	0x9C(SP), R1
+	MOVW	0x0C(SP), R26		/* old pc */
+	RET
+
+TEXT	rfnote(SB), $0
+	MOVW	0(FP), R26		/* 1st arg is &uregpointer */
+	SUBU	$(BY2WD), R26, SP	/* pc hole */
+	BNE	R26, restore
+	
+
+TEXT	clrfpintr(SB), $0
+	MOVW	FCR31, R1
+	MOVW	R1, R2
+	AND	$~(0x3F<<12), R2
+	MOVW	R2, FCR31
+	RET
+
+TEXT	savefpregs(SB), $0
+	MOVW	M(STATUS), R3
+	MOVW	0(FP), R1
+	MOVW	FCR31, R2
+
+	MOVD	F0, 0x00(R1)
+	MOVD	F2, 0x08(R1)
+	MOVD	F4, 0x10(R1)
+	MOVD	F6, 0x18(R1)
+	MOVD	F8, 0x20(R1)
+	MOVD	F10, 0x28(R1)
+	MOVD	F12, 0x30(R1)
+	MOVD	F14, 0x38(R1)
+	MOVD	F16, 0x40(R1)
+	MOVD	F18, 0x48(R1)
+	MOVD	F20, 0x50(R1)
+	MOVD	F22, 0x58(R1)
+	MOVD	F24, 0x60(R1)
+	MOVD	F26, 0x68(R1)
+	MOVD	F28, 0x70(R1)
+	MOVD	F30, 0x78(R1)
+
+	MOVW	R2, 0x80(R1)
+	AND	$~CU1, R3
+	MOVW	R3, M(STATUS)
+	RET
+
+TEXT	restfpregs(SB), $0
+
+	MOVW	M(STATUS), R3
+	MOVW	0(FP), R1
+	OR	$CU1, R3
+	MOVW	R3, M(STATUS)
+	MOVW	0x80(R1), R2
+
+	MOVD	0x00(R1), F0
+	MOVD	0x08(R1), F2
+	MOVD	0x10(R1), F4
+	MOVD	0x18(R1), F6
+	MOVD	0x20(R1), F8
+	MOVD	0x28(R1), F10
+	MOVD	0x30(R1), F12
+	MOVD	0x38(R1), F14
+	MOVD	0x40(R1), F16
+	MOVD	0x48(R1), F18
+	MOVD	0x50(R1), F20
+	MOVD	0x58(R1), F22
+	MOVD	0x60(R1), F24
+	MOVD	0x68(R1), F26
+	MOVD	0x70(R1), F28
+	MOVD	0x78(R1), F30
+
+	MOVW	R2, FCR31
+	AND	$~CU1, R3
+	MOVW	R3, M(STATUS)
+	RET
+
+/*
+ *  we avoid using R4, R5, R6, and R7 so gotopc can call us without saving them
+ */
+TEXT icflush(SB), $-4			/* icflush(physaddr, nbytes) */
+
+	MOVW	M(STATUS), R10
+	MOVW	0(FP), R8
+	MOVW	4(FP), R9
+	MOVW	$KSEG0, R3
+	OR	R3, R8
+	MOVW	$0, M(STATUS)
+	MOVW	$WBFLUSH, R1		/* wbflush */
+	MOVW	0(R1), R1
+	NOOP
+	MOVW	$KSEG1, R3
+	MOVW	$icflush0(SB), R2	/* make sure PC is in uncached address space */
+	MOVW	$(SWC|ISC), R1
+	OR	R3, R2
+	JMP	(R2)
+
+TEXT icflush0(SB), $-4
+
+	MOVW	R1, M(STATUS)		/* swap and isolate cache, splhi */
+	MOVW	$icflush1(SB), R2
+	JMP	(R2)
+
+TEXT icflush1(SB), $-4
+
+_icflush1:
+	MOVBU	R0, 0x00(R8)
+	MOVBU	R0, 0x04(R8)
+	MOVBU	R0, 0x08(R8)
+	MOVBU	R0, 0x0C(R8)
+	MOVBU	R0, 0x10(R8)
+	MOVBU	R0, 0x14(R8)
+	MOVBU	R0, 0x18(R8)
+	MOVBU	R0, 0x1C(R8)
+	MOVBU	R0, 0x20(R8)
+	MOVBU	R0, 0x24(R8)
+	MOVBU	R0, 0x28(R8)
+	MOVBU	R0, 0x2C(R8)
+	MOVBU	R0, 0x30(R8)
+	MOVBU	R0, 0x34(R8)
+	MOVBU	R0, 0x38(R8)
+	MOVBU	R0, 0x3C(R8)
+	SUB	$0x40, R9
+	ADD	$0x40, R8
+	BGTZ	R9, _icflush1
+	MOVW	$icflush2(SB), R2	/* make sure PC is in uncached address space */
+	OR	R3, R2
+	JMP	(R2)
+
+TEXT icflush2(SB), $-4
+
+	MOVW	$0, M(STATUS)		/* swap back caches, de-isolate them, and stay splhi */
+	NOOP				/* +++ */
+	MOVW	R10, M(STATUS)
+	RET
+
+TEXT dcflush(SB), $-4			/* dcflush(physaddr, nbytes) */
+
+	MOVW	M(STATUS), R6
+	MOVW	0(FP), R4
+	MOVW	4(FP), R5
+	MOVW	$KSEG0, R3
+	OR	R3, R4
+	MOVW	$0, M(STATUS)
+	MOVW	$WBFLUSH, R1
+	MOVW	0(R1), R1
+	NOOP
+	MOVW	$ISC, R1
+	MOVW	R1, M(STATUS)
+_dcflush0:
+	MOVBU	R0, 0x00(R4)
+	MOVBU	R0, 0x04(R4)
+	MOVBU	R0, 0x08(R4)
+	MOVBU	R0, 0x0C(R4)
+	MOVBU	R0, 0x10(R4)
+	MOVBU	R0, 0x14(R4)
+	MOVBU	R0, 0x18(R4)
+	MOVBU	R0, 0x1C(R4)
+	MOVBU	R0, 0x20(R4)
+	MOVBU	R0, 0x24(R4)
+	MOVBU	R0, 0x28(R4)
+	MOVBU	R0, 0x2C(R4)
+	MOVBU	R0, 0x30(R4)
+	MOVBU	R0, 0x34(R4)
+	MOVBU	R0, 0x38(R4)
+	MOVBU	R0, 0x3C(R4)
+	SUB	$0x40, R5
+	ADD	$0x40, R4
+	BGTZ	R5, _dcflush0
+	MOVW	$0, M(STATUS)
+	NOOP				/* +++ */
+	MOVW	R6, M(STATUS)
+	RET
--- /dev/null
+++ b/utils/0a/lex.c
@@ -1,0 +1,697 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = '0';
+	thestring = "spim";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)) {
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, '/');
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+	"HI",		LHI,	D_HI,
+	"LO",		LLO,	D_LO,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+	"R16",		LREG,	16,
+	"R17",		LREG,	17,
+	"R18",		LREG,	18,
+	"R19",		LREG,	19,
+	"R20",		LREG,	20,
+	"R21",		LREG,	21,
+	"R22",		LREG,	22,
+	"R23",		LREG,	23,
+	"R24",		LREG,	24,
+	"R25",		LREG,	25,
+	"R26",		LREG,	26,
+	"R27",		LREG,	27,
+	"R28",		LREG,	28,
+	"R29",		LREG,	29,
+	"R30",		LREG,	30,
+	"R31",		LREG,	31,
+
+	"M",		LM,	0,
+	"M0",		LMREG,	0,
+	"M1",		LMREG,	1,
+	"M2",		LMREG,	2,
+	"M3",		LMREG,	3,
+	"M4",		LMREG,	4,
+	"M5",		LMREG,	5,
+	"M6",		LMREG,	6,
+	"M7",		LMREG,	7,
+	"M8",		LMREG,	8,
+	"M9",		LMREG,	9,
+	"M10",		LMREG,	10,
+	"M11",		LMREG,	11,
+	"M12",		LMREG,	12,
+	"M13",		LMREG,	13,
+	"M14",		LMREG,	14,
+	"M15",		LMREG,	15,
+	"M16",		LMREG,	16,
+	"M17",		LMREG,	17,
+	"M18",		LMREG,	18,
+	"M19",		LMREG,	19,
+	"M20",		LMREG,	20,
+	"M21",		LMREG,	21,
+	"M22",		LMREG,	22,
+	"M23",		LMREG,	23,
+	"M24",		LMREG,	24,
+	"M25",		LMREG,	25,
+	"M26",		LMREG,	26,
+	"M27",		LMREG,	27,
+	"M28",		LMREG,	28,
+	"M29",		LMREG,	29,
+	"M30",		LMREG,	30,
+	"M31",		LMREG,	31,
+
+	"F",		LF,	0,
+
+	"F0",		LFREG,	0,
+	"F1",		LFREG,	1,
+	"F2",		LFREG,	2,
+	"F3",		LFREG,	3,
+	"F4",		LFREG,	4,
+	"F5",		LFREG,	5,
+	"F6",		LFREG,	6,
+	"F7",		LFREG,	7,
+	"F8",		LFREG,	8,
+	"F9",		LFREG,	9,
+	"F10",		LFREG,	10,
+	"F11",		LFREG,	11,
+	"F12",		LFREG,	12,
+	"F13",		LFREG,	13,
+	"F14",		LFREG,	14,
+	"F15",		LFREG,	15,
+	"F16",		LFREG,	16,
+	"F17",		LFREG,	17,
+	"F18",		LFREG,	18,
+	"F19",		LFREG,	19,
+	"F20",		LFREG,	20,
+	"F21",		LFREG,	21,
+	"F22",		LFREG,	22,
+	"F23",		LFREG,	23,
+	"F24",		LFREG,	24,
+	"F25",		LFREG,	25,
+	"F26",		LFREG,	26,
+	"F27",		LFREG,	27,
+	"F28",		LFREG,	28,
+	"F29",		LFREG,	29,
+	"F30",		LFREG,	30,
+	"F31",		LFREG,	31,
+
+	"FCR",		LFCR,	0,
+	"FCR0",		LFCREG,	0,
+	"FCR1",		LFCREG,	1,
+	"FCR2",		LFCREG,	2,
+	"FCR3",		LFCREG,	3,
+	"FCR4",		LFCREG,	4,
+	"FCR5",		LFCREG,	5,
+	"FCR6",		LFCREG,	6,
+	"FCR7",		LFCREG,	7,
+	"FCR8",		LFCREG,	8,
+	"FCR9",		LFCREG,	9,
+	"FCR10",	LFCREG,	10,
+	"FCR11",	LFCREG,	11,
+	"FCR12",	LFCREG,	12,
+	"FCR13",	LFCREG,	13,
+	"FCR14",	LFCREG,	14,
+	"FCR15",	LFCREG,	15,
+	"FCR16",	LFCREG,	16,
+	"FCR17",	LFCREG,	17,
+	"FCR18",	LFCREG,	18,
+	"FCR19",	LFCREG,	19,
+	"FCR20",	LFCREG,	20,
+	"FCR21",	LFCREG,	21,
+	"FCR22",	LFCREG,	22,
+	"FCR23",	LFCREG,	23,
+	"FCR24",	LFCREG,	24,
+	"FCR25",	LFCREG,	25,
+	"FCR26",	LFCREG,	26,
+	"FCR27",	LFCREG,	27,
+	"FCR28",	LFCREG,	28,
+	"FCR29",	LFCREG,	29,
+	"FCR30",	LFCREG,	30,
+	"FCR31",	LFCREG,	31,
+
+	"ADD",		LTYPE1, AADD,
+	"ADDU",		LTYPE1, AADDU,
+	"SUB",		LTYPE1, ASUB,	/* converted to ADD(-) in loader */
+	"SUBU",		LTYPE1, ASUBU,
+	"SGT",		LTYPE1, ASGT,
+	"SGTU",		LTYPE1, ASGTU,
+	"AND",		LTYPE1, AAND,
+	"OR",		LTYPE1, AOR,
+	"XOR",		LTYPE1, AXOR,
+	"SLL",		LTYPE1, ASLL,
+	"SRL",		LTYPE1, ASRL,
+	"SRA",		LTYPE1, ASRA,
+
+	"ADDV",		LTYPE1, AADDV,
+	"ADDVU",	LTYPE1, AADDVU,
+	"SUBV",		LTYPE1, ASUBV,	/* converted to ADD(-) in loader */
+	"SUBVU",	LTYPE1, ASUBVU,
+	"SLLV",		LTYPE1, ASLLV,
+	"SRLV",		LTYPE1, ASRLV,
+	"SRAV",		LTYPE1, ASRAV,
+
+	"NOR",		LTYPE2, ANOR,
+
+	"MOVB",		LTYPE3, AMOVB,
+	"MOVBU",	LTYPE3, AMOVBU,
+	"MOVH",		LTYPE3, AMOVH,
+	"MOVHU",	LTYPE3, AMOVHU,
+	"MOVWL",	LTYPE3, AMOVWL,
+	"MOVWR",	LTYPE3, AMOVWR,
+	"MOVVL",	LTYPE3, AMOVVL,
+	"MOVVR",	LTYPE3, AMOVVR,
+
+	"BREAK",	LTYPEJ, ABREAK,		/* overloaded CACHE opcode */
+	"END",		LTYPE4, AEND,
+	"REM",		LTYPE6, AREM,
+	"REMU",		LTYPE6, AREMU,
+	"RET",		LTYPE4, ARET,
+	"SYSCALL",	LTYPE4, ASYSCALL,
+	"TLBP",		LTYPE4, ATLBP,
+	"TLBR",		LTYPE4, ATLBR,
+	"TLBWI",	LTYPE4, ATLBWI,
+	"TLBWR",	LTYPE4, ATLBWR,
+
+	"MOVW",		LTYPE5, AMOVW,
+	"MOVV",		LTYPE5, AMOVV,
+	"MOVD",		LTYPE5, AMOVD,
+	"MOVF",		LTYPE5, AMOVF,
+
+	"DIV",		LTYPE6, ADIV,
+	"DIVU",		LTYPE6, ADIVU,
+	"MUL",		LTYPE6, AMUL,
+	"MULU",		LTYPE6, AMULU,
+	"DIVV",		LTYPE6, ADIVV,
+	"DIVVU",		LTYPE6, ADIVVU,
+	"MULV",		LTYPE6, AMULV,
+	"MULVU",		LTYPE6, AMULVU,
+
+	"RFE",		LTYPE7, ARFE,
+	"JMP",		LTYPE7, AJMP,
+
+	"JAL",		LTYPE8, AJAL,
+
+	"BEQ",		LTYPE9, ABEQ,
+	"BNE",		LTYPE9, ABNE,
+
+	"BGEZ",		LTYPEA, ABGEZ,
+	"BGEZAL",	LTYPEA, ABGEZAL,
+	"BGTZ",		LTYPEA, ABGTZ,
+	"BLEZ",		LTYPEA, ABLEZ,
+	"BLTZ",		LTYPEA, ABLTZ,
+	"BLTZAL",	LTYPEA, ABLTZAL,
+
+	"TEXT",		LTYPEB, ATEXT,
+	"GLOBL",	LTYPEB, AGLOBL,
+
+	"DATA",		LTYPEC, ADATA,
+
+	"MOVDF",	LTYPE5, AMOVDF,
+	"MOVDW",	LTYPE5, AMOVDW,
+	"MOVFD",	LTYPE5, AMOVFD,
+	"MOVFW",	LTYPE5, AMOVFW,
+	"MOVWD",	LTYPE5, AMOVWD,
+	"MOVWF",	LTYPE5, AMOVWF,
+
+	"ABSD",		LTYPED, AABSD,
+	"ABSF",		LTYPED, AABSF,
+	"ABSW",		LTYPED, AABSW,
+	"NEGD",		LTYPED, ANEGD,
+	"NEGF",		LTYPED, ANEGF,
+	"NEGW",		LTYPED, ANEGW,
+
+	"CMPEQD",	LTYPEF, ACMPEQD,
+	"CMPEQF",	LTYPEF, ACMPEQF,
+	"CMPGED",	LTYPEF, ACMPGED,
+	"CMPGEF",	LTYPEF, ACMPGEF,
+	"CMPGTD",	LTYPEF, ACMPGTD,
+	"CMPGTF",	LTYPEF, ACMPGTF,
+
+	"ADDD",		LTYPEE, AADDD,
+	"ADDF",		LTYPEE, AADDF,
+	"ADDW",		LTYPEE, AADDW,
+	"DIVD",		LTYPEE, ADIVD,
+	"DIVF",		LTYPEE, ADIVF,
+	"DIVW",		LTYPEE, ADIVW,
+	"MULD",		LTYPEE, AMULD,
+	"MULF",		LTYPEE, AMULF,
+	"MULW",		LTYPEE, AMULW,
+	"SUBD",		LTYPEE, ASUBD,
+	"SUBF",		LTYPEE, ASUBF,
+	"SUBW",		LTYPEE, ASUBW,
+
+	"BFPT",		LTYPEG, ABFPT,
+	"BFPF",		LTYPEG, ABFPF,
+
+	"WORD",		LTYPEH, AWORD,
+	"NOP",		LTYPEI, ANOP,
+	"SCHED",	LSCHED, 0,
+	"NOSCHED",	LSCHED, 0x80,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = alloc(100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+	USED(g);
+	return 1;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+	case D_BRANCH:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+jackpot:
+	sf = 0;
+	s = g1->sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g1->name;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->name;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, reg|nosched);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/0a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=0a
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../vc/v.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=	a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/0c/cgen.c
@@ -1,0 +1,1148 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesu[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	if(n->addable > INDEXED) {
+		if(nn != Z)
+			gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	o = n->op;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+			if(o != OAND)
+				break;
+			if(nn != Z)
+				gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case OMUL:
+	case ODIV:
+	case OMOD:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			gopcode(o, &nod, &nod1, &nod);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+
+			regalloc(&nod, n, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+		} else {
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+
+		regalloc(&nod, n, nn);
+		gmove(&nod2, &nod);
+		gopcode(o, &nod1, Z, &nod);
+		gmove(&nod, &nod2);
+		if(nn != Z)
+			gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(o, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type)) {
+			if(nocast(n->type, nn->type)) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+	return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		if(nn == Z || typefd[n->type->etype]) {
+			o = ONE;
+			if(true)
+				o = comrel[relindex(o)];
+			if(typefd[n->type->etype]) {
+				nodreg(&nod1, n, NREG+FREGZERO);
+				gopcode(o, &nod, &nod1, Z);
+			} else
+				gopcode(o, &nod, Z, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(true)
+			gopcode(OCOND, &nod, nodconst(0), &nod);
+		else
+			gopcode(OCOND, nodconst(1), &nod, &nod);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(nn != Z && !typefd[l->type->etype]) {
+			if(l->complex >= r->complex) {
+				regalloc(&nod1, l, nn);
+				cgen(l, &nod1);
+				regalloc(&nod, r, Z);
+				cgen(r, &nod);
+			} else {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+			}
+			switch(o) {
+			case OEQ:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, &nod, nodconst(0), &nod);
+				break;
+			case ONE:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, nodconst(1), &nod, &nod);
+				break;
+			case OLE:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				break;
+			case OGT:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLT:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OGE:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				break;
+			case OLS:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				break;
+			case OHI:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLO:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OHS:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				break;
+			}
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+			regfree(&nod1);
+			break;
+		}
+		if(sconst(l)) {
+			switch(o) {
+			default:
+				if(l->vconst != 0)
+					break;
+
+			case OGT:
+			case OHI:
+			case OLE:
+			case OLS:
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gopcode(o, l, &nod, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(sconst(r)) {
+			switch(o) {
+			default:
+				if(r->vconst != 0)
+					break;
+
+			case OGE:
+			case OHS:
+			case OLT:
+			case OLO:
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				gopcode(o, &nod, r, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod1, &nod, Z);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && nn->complex >= FNX) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TVLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TVLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TVLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TVLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_VLONG;
+	if(w <= 5) {
+		layout(&nod1, &nod2, w, 0, Z);
+		goto out;
+	}
+
+	/*
+	 * minimize space for unrolling loop
+	 * 3,4,5 times. (6 or more is never minimum)
+	 * if small structure, try 2 also.
+	 */
+	c = 0; /* set */
+	m = 100;
+	i = 3;
+	if(w <= 15)
+		i = 2;
+	for(; i<=5; i++)
+		if(i + w%i <= m) {
+			c = i;
+			m = c + w%c;
+		}
+
+	regalloc(&nod3, &regnode, Z);
+	layout(&nod1, &nod2, w%c, w/c, &nod3);
+
+	pc1 = pc;
+	layout(&nod1, &nod2, c, 0, Z);
+
+	gopcode(OSUB, nodconst(1), Z, &nod3);
+	nod1.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod1);
+	nod2.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod2);
+
+	gopcode(OEQ, &nod3, Z, Z);
+	p->as = ABGTZ;
+	patch(p, pc1);
+
+	regfree(&nod3);
+out:
+	regfree(&nod1);
+	regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &regnode, Z);
+	regalloc(&t2, &regnode, Z);
+	t1.type = types[TVLONG];
+	t2.type = types[TVLONG];
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_VLONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_VLONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_VLONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
--- /dev/null
+++ b/utils/0c/enam.c
@@ -1,0 +1,118 @@
+char*	anames[] =
+{
+	"XXX",
+	"ABSD",
+	"ABSF",
+	"ABSW",
+	"ADD",
+	"ADDD",
+	"ADDF",
+	"ADDU",
+	"ADDW",
+	"AND",
+	"BEQ",
+	"BFPF",
+	"BFPT",
+	"BGEZ",
+	"BGEZAL",
+	"BGTZ",
+	"BLEZ",
+	"BLTZ",
+	"BLTZAL",
+	"BNE",
+	"BREAK",
+	"CMPEQD",
+	"CMPEQF",
+	"CMPGED",
+	"CMPGEF",
+	"CMPGTD",
+	"CMPGTF",
+	"DATA",
+	"DIV",
+	"DIVD",
+	"DIVF",
+	"DIVU",
+	"DIVW",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"JAL",
+	"JMP",
+	"MOVB",
+	"MOVBU",
+	"MOVD",
+	"MOVDF",
+	"MOVDW",
+	"MOVF",
+	"MOVFD",
+	"MOVFW",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVWD",
+	"MOVWF",
+	"MOVWL",
+	"MOVWR",
+	"MUL",
+	"MULD",
+	"MULF",
+	"MULU",
+	"MULW",
+	"NAME",
+	"NEGD",
+	"NEGF",
+	"NEGW",
+	"NOP",
+	"NOR",
+	"OR",
+	"REM",
+	"REMU",
+	"RET",
+	"RFE",
+	"SGT",
+	"SGTU",
+	"SLL",
+	"SRA",
+	"SRL",
+	"SUB",
+	"SUBD",
+	"SUBF",
+	"SUBU",
+	"SUBW",
+	"SYSCALL",
+	"TEXT",
+	"TLBP",
+	"TLBR",
+	"TLBWI",
+	"TLBWR",
+	"WORD",
+	"XOR",
+	"END",
+	"MOVV",
+	"MOVVL",
+	"MOVVR",
+	"SLLV",
+	"SRAV",
+	"SRLV",
+	"DIVV",
+	"DIVVU",
+	"REMV",
+	"REMVU",
+	"MULV",
+	"MULVU",
+	"ADDV",
+	"ADDVU",
+	"SUBV",
+	"SUBVU",
+	"DYNT",
+	"INIT",
+	"BCASE",
+	"CASE",
+	"TRUNCFV",
+	"TRUNCDV",
+	"TRUNCFW",
+	"TRUNCDW",
+	"MOVWU",
+	"SIGNAME",
+	"LAST",
+};
--- /dev/null
+++ b/utils/0c/gc.h
@@ -1,0 +1,332 @@
+#include	"../cc/cc.h"
+#include	"../vc/v.out.h"
+
+/*
+ * 0c/spim
+ * Mips 4000 little endian
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	vlong	vval;
+	char	sval[NSNAME];
+	Ieee	ieee;
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	char	as;
+	char	reg;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	long	val;
+	long	label;
+	char	def;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	long	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Multab	multab[20];
+EXTERN	int	retok;
+EXTERN	int	hintabsize;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	int	reg[NREG+NREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	4
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	xcom(Node*);
+void	bcomplex(Node*);
+void	usedset(Node*, int);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	llconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(long);
+void	nullwarn(Node*, Node*);
+void	sextern(Sym*, Node*, long, long);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/0c/list.c
@@ -1,0 +1,244 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('D', Dconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		sprint(str, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCREG:
+		sprint(str, "FCR%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_LO:
+		sprint(str, "LO");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(LO)(REG)", a);
+		break;
+
+	case D_HI:
+		sprint(str, "HI");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(HI)(REG)", a);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/0c/mkenam
@@ -1,0 +1,15 @@
+ed - ../vc/v.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/0c/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=0c
+
+OFILES=	cgen.$O\
+	enam.$O\
+	list.$O\
+	peep.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+	mul.$O\
+
+HFILES=	gc.h\
+	v.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/0c/mul.c
@@ -1,0 +1,608 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("multiply table failure %ld\n", v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=6; g++) {
+		if(g >= 6 && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("multiply table failure %ld\n", v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/0c/peep.c
@@ -1,0 +1,695 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+		if(regtyp(&p->to)) {
+			if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			if(regzer(&p->from))
+			if(p->to.type == D_REG) {
+				p->from.type = D_REG;
+				p->from.reg = 0;
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regzer(Adr *a)
+{
+
+	if(a->type == D_CONST)
+		if(a->sym == S)
+			if(a->offset == 0)
+				return 1;
+	if(a->type == D_REG)
+		if(a->reg == 0)
+			return 1;
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG) {
+		if(a->reg != 0)
+			return 1;
+		return 0;
+	}
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case AJAL:
+			return 0;
+
+		case ASGT:
+		case ASGTU:
+
+		case AADD:
+		case AADDU:
+		case ASUB:
+		case ASUBU:
+		case ASLL:
+		case ASRL:
+		case ASRA:
+		case AOR:
+		case AAND:
+		case AXOR:
+		case AMUL:
+		case AMULU:
+		case ADIV:
+		case ADIVU:
+
+		case AADDD:
+		case AADDF:
+		case ASUBD:
+		case ASUBF:
+		case AMULD:
+		case AMULF:
+		case ADIVD:
+		case ADIVF:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AMOVF:
+		case AMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVF:
+	case AMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVDW:
+	case AMOVWD:
+	case AMOVFD:
+	case AMOVDF:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ASGT:	/* read, read, write */
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case ANOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:	/* read, read */
+	case ABNE:
+	case ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+	case ABFPF:
+	case ABFPT:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub1(p, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		return 0;
+
+	case AJMP:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case AJAL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(REGARG && v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG)
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+	/* not reached */
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+	case ABEQ:
+	case ABNE:
+	case ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ASGT:
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+		return D_REG;
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v))
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG)
+		if(a->type == D_OREG)
+			if(v->reg == a->reg)
+				return 1;
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v))
+		if(p->from.type == v->type || p->to.type == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v))
+		a->reg = s->reg;
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
--- /dev/null
+++ b/utils/0c/reg.c
@@ -1,0 +1,1152 @@
+#include "gc.h"
+
+void	addsplits(void);
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AJMP:
+		case ARFE:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVV:
+		case AMOVF:
+		case AMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case AJAL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+	addsplits();
+
+	if(debug['R'] && debug['v']) {
+		print("\nprop structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->set.b[z] |
+					r->refahead.b[z] | r->calahead.b[z] |
+					r->refbehind.b[z] | r->calbehind.b[z] |
+					r->use1.b[z] | r->use2.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+				if(bany(&r->refahead))
+					print(" ra=%B", r->refahead);
+				if(bany(&r->calahead))
+					print(" ca=%B", r->calahead);
+				if(bany(&r->refbehind))
+					print(" rb=%B", r->refbehind);
+				if(bany(&r->calbehind))
+					print(" cb=%B", r->calbehind);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L $%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L $%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L $%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+void
+addsplits(void)
+{
+	Reg *r, *r1;
+	int z, i;
+	Bits bit;
+
+	for(r = firstr; r != R; r = r->link) {
+		if(r->loop > 1)
+			continue;
+		if(r->prog->as == AJAL)
+			continue;
+		for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+			if(r1->loop <= 1)
+				continue;
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r1->calbehind.b[z] &
+					(r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+					~(r->calahead.b[z] & addrs.b[z]);
+			while(bany(&bit)) {
+				i = bnum(bit);
+				bit.b[i/32] &= ~(1L << (i%32));
+			}
+		}
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TVLONG || v->etype == TUVLONG)
+		p1->as = AMOVV;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !(typechlpfd[et]/* || typev[et]*/))	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case AJAL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R3
+ *	1	R4
+ *	...	...
+ *	19	R22
+ *	20	R23
+ */
+long
+RtoB(int r)
+{
+
+	if(r < 3 || r > 23)
+		return 0;
+	return 1L << (r-3);
+}
+
+int
+BtoR(long b)
+{
+
+	b &= 0x001fffffL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + 3;
+}
+
+/*
+ *	bit	reg
+ *	22	F4
+ *	23	F6
+ *	...	...
+ *	31	F22
+ */
+long
+FtoB(int f)
+{
+
+	if(f < 4 || f > 22 || (f&1))
+		return 0;
+	return 1L << (f/2 + 20);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xffc00000L;
+	if(b == 0)
+		return 0;
+	return bitno(b)*2 - 40;
+}
--- /dev/null
+++ b/utils/0c/sgen.c
@@ -1,0 +1,569 @@
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+	Prog *sp;
+	Node *n1, nod, nod1;
+
+	cursafe = 0;
+	curarg = 0;
+	maxargsafe = 0;
+
+	/*
+	 * isolate name
+	 */
+	for(n1 = nn;; n1 = n1->left) {
+		if(n1 == Z) {
+			diag(nn, "cant find function name");
+			return;
+		}
+		if(n1->op == ONAME)
+			break;
+	}
+	nearln = nn->lineno;
+	gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+	sp = p;
+
+	/*
+	 * isolate first argument
+	 */
+	if(REGARG) {
+		if(typesu[thisfn->link->etype]) {
+			nod1 = *nodret->left;
+			nodreg(&nod, &nod1, REGARG);
+			gopcode(OAS, &nod, Z, &nod1);
+		} else
+		if(firstarg && typechlp[firstargtype->etype]) {
+			nod1 = *nodret->left;
+			nod1.sym = firstarg;
+			nod1.type = firstargtype;
+			nod1.xoffset = align(0, firstargtype, Aarg1);
+			nod1.etype = firstargtype->etype;
+			nodreg(&nod, &nod1, REGARG);
+			gopcode(OAS, &nod, Z, &nod1);
+		}
+	}
+
+	retok = 0;
+	gen(n);
+	if(!retok)
+		if(thisfn->link->etype != TVOID)
+			warn(Z, "no return at end of function: %s", n1->sym->name);
+	noretval(3);
+	gbranch(ORETURN);
+
+	if(!debug['N'] || debug['R'] || debug['P'])
+		regopt(sp);
+
+	sp->to.offset += maxargsafe;
+}
+
+void
+gen(Node *n)
+{
+	Node *l, nod;
+	Prog *sp, *spc, *spb;
+	Case *cn;
+	long sbc, scc;
+	int o;
+
+loop:
+	if(n == Z)
+		return;
+	nearln = n->lineno;
+	o = n->op;
+	if(debug['G'])
+		if(o != OLIST)
+			print("%L %O\n", nearln, o);
+
+	retok = 0;
+	switch(o) {
+
+	default:
+		complex(n);
+		cgen(n, Z);
+		break;
+
+	case OLIST:
+		gen(n->left);
+
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		retok = 1;
+		complex(n);
+		if(n->type == T)
+			break;
+		l = n->left;
+		if(l == Z) {
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(typesu[n->type->etype]) {
+			sugen(l, nodret, n->type->width);
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		regret(&nod, n);
+		cgen(l, &nod);
+		regfree(&nod);
+		if(typefd[n->type->etype])
+			noretval(1);
+		else
+			noretval(2);
+		gbranch(ORETURN);
+		break;
+
+	case OLABEL:
+		l = n->left;
+		if(l) {
+			l->pc = pc;
+			if(l->label)
+				patch(l->label, pc);
+		}
+		gbranch(OGOTO);	/* prevent self reference in reg */
+		patch(p, pc);
+		goto rloop;
+
+	case OGOTO:
+		retok = 1;
+		n = n->left;
+		if(n == Z)
+			return;
+		if(n->complex == 0) {
+			diag(Z, "label undefined: %s", n->sym->name);
+			return;
+		}
+		gbranch(OGOTO);
+		if(n->pc) {
+			patch(p, n->pc);
+			return;
+		}
+		if(n->label)
+			patch(n->label, pc-1);
+		n->label = p;
+		return;
+
+	case OCASE:
+		l = n->left;
+		if(cases == C)
+			diag(n, "case/default outside a switch");
+		if(l == Z) {
+			casf();
+			cases->val = 0;
+			cases->def = 1;
+			cases->label = pc;
+			goto rloop;
+		}
+		complex(l);
+		if(l->type == T)
+			goto rloop;
+		if(l->op == OCONST)
+		if(typechl[l->type->etype]) {
+			casf();
+			cases->val = l->vconst;
+			cases->def = 0;
+			cases->label = pc;
+			goto rloop;
+		}
+		diag(n, "case expression must be integer constant");
+		goto rloop;
+
+	case OSWITCH:
+		l = n->left;
+		complex(l);
+		if(l->type == T)
+			break;
+		if(!typechl[l->type->etype]) {
+			diag(n, "switch expression must be integer");
+			break;
+		}
+
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		cn = cases;
+		cases = C;
+		casf();
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		gen(n->right);
+		gbranch(OGOTO);
+		patch(p, breakpc);
+
+		patch(sp, pc);
+		regalloc(&nod, l, Z);
+		nod.type = types[TLONG];
+		cgen(l, &nod);
+		doswit(&nod);
+		regfree(&nod);
+		patch(spb, pc);
+
+		cases = cn;
+		breakpc = sbc;
+		break;
+
+	case OWHILE:
+	case ODWHILE:
+		l = n->left;
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		if(n->op == OWHILE)
+			patch(sp, pc);
+		bcomplex(l);		/* test */
+		patch(p, breakpc);
+
+		if(n->op == ODWHILE)
+			patch(sp, pc);
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		break;
+
+	case OFOR:
+		l = n->left;
+		gen(l->right->left);	/* init */
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		gen(l->right->right);	/* inc */
+		patch(sp, pc);	
+		if(l->left != Z) {	/* test */
+			bcomplex(l->left);
+			patch(p, breakpc);
+		}
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		break;
+
+	case OCONTINUE:
+		if(continpc < 0) {
+			diag(n, "continue not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, continpc);
+		break;
+
+	case OBREAK:
+		if(breakpc < 0) {
+			diag(n, "break not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, breakpc);
+		break;
+
+	case OIF:
+		l = n->left;
+		bcomplex(l);
+		sp = p;
+		if(n->right->left != Z)
+			gen(n->right->left);
+		if(n->right->right != Z) {
+			gbranch(OGOTO);
+			patch(sp, pc);
+			sp = p;
+			gen(n->right->right);
+		}
+		patch(sp, pc);
+		break;
+
+	case OSET:
+	case OUSED:
+		usedset(n->left, o);
+		break;
+	}
+}
+
+void
+usedset(Node *n, int o)
+{
+	if(n->op == OLIST) {
+		usedset(n->left, o);
+		usedset(n->right, o);
+		return;
+	}
+	complex(n);
+	switch(n->op) {
+	case OADDR:	/* volatile */
+		gins(ANOP, n, Z);
+		break;
+	case ONAME:
+		if(o == OSET)
+			gins(ANOP, Z, n);
+		else
+			gins(ANOP, n, Z);
+		break;
+	}
+}
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASLMUL:
+	case OASMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		t = vlog(l);
+		if(t >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+	case OEQ:
+	case ONE:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
+
+void
+bcomplex(Node *n)
+{
+
+	complex(n);
+	if(n->type != T)
+	if(tcompat(n, T, n->type, tnot))
+		n->type = T;
+	if(n->type != T)
+		boolgen(n, 1, Z);
+	else
+		gbranch(OGOTO);
+}
--- /dev/null
+++ b/utils/0c/swt.c
@@ -1,0 +1,711 @@
+#include "gc.h"
+
+int
+swcmp(void *a1, void *a2)
+{
+	C1 *p1, *p2;
+
+	p1 = (C1*)a1;
+	p2 = (C1*)a2;
+	if(p1->val < p2->val)
+		return -1;
+	return  p1->val > p2->val;
+}
+
+void
+doswit(Node *n)
+{
+	Case *c;
+	C1 *q, *iq;
+	long def, nc, i;
+	Node tn;
+
+	def = 0;
+	nc = 0;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def) {
+			if(def)
+				diag(n, "more than one default in switch");
+			def = c->label;
+			continue;
+		}
+		nc++;
+	}
+
+	iq = alloc(nc*sizeof(C1));
+	q = iq;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def)
+			continue;
+		q->label = c->label;
+		q->val = c->val;
+		q++;
+	}
+	qsort(iq, nc, sizeof(C1), swcmp);
+	if(debug['W'])
+		for(i=0; i<nc; i++)
+			print("case %2ld: = %.8lux\n", i, iq[i].val);
+	if(def == 0)
+		def = breakpc;
+	for(i=0; i<nc-1; i++)
+		if(iq[i].val == iq[i+1].val)
+			diag(n, "duplicate cases in switch %ld", iq[i].val);
+	regalloc(&tn, &regnode, Z);
+	swit1(iq, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit1(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['W'])
+				print("case = %.8lux\n", q->val);
+			gmove(nodconst(q->val), tn);
+			gopcode(OEQ, n, tn, Z);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(debug['W'])
+		print("case > %.8lux\n", r->val);
+	gmove(nodconst(r->val), tn);
+	gopcode(OLT, tn, n, Z);
+	sp = p;
+	gopcode(OEQ, n, tn, Z);
+	patch(p, r->label);
+	swit1(q, i, def, n, tn);
+
+	if(debug['W'])
+		print("case < %.8lux\n", r->val);
+	patch(sp, pc);
+	swit1(r+1, nc-i-1, def, n, tn);
+}
+
+void
+casf(void)
+{
+	Case *c;
+
+	c = alloc(sizeof(*c));
+	c->link = cases;
+	cases = c;
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+long
+outlstring(TRune *s, long n)
+{
+	char buf[2];
+	int c;
+	long r;
+
+	while(nstring & 1)
+		outstring("", 1);
+	r = nstring;
+	while(n > 0) {
+		c = *s++;
+		if(align(0, types[TCHAR], Aarg1)) {
+			buf[0] = c>>8;
+			buf[1] = c;
+		} else {
+			buf[0] = c;
+			buf[1] = c>>8;
+		}
+		outstring(buf, 2);
+		n -= sizeof(ushort);
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	if(debug['M'] && debug['v'])
+		print("%L multiply: %ld\n", n->lineno, v);
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	if(v < 0)
+		gopcode(OSUB, &nod1, nodconst(0), &nod1);
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+nullwarn(Node *l, Node *r)
+{
+	warn(Z, "result of operation not used");
+	if(l != Z)
+		cgen(l, Z);
+	if(r != Z)
+		cgen(r, Z);
+}
+
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+	if(a->op == OCONST && typev[a->type->etype]) {
+		gpseudo(ADATA, s, nodconst(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		gpseudo(ADATA, s, nodconst(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+
+	bf[0] = p->as;
+	bf[1] = p->reg;
+	bf[2] = p->lineno;
+	bf[3] = p->lineno>>8;
+	bf[4] = p->lineno>>16;
+	bf[5] = p->lineno>>24;
+	bp = zaddr(bf+6, &p->from, sf);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[7];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = sig;
+		bf[2] = sig>>8;
+		bf[3] = sig>>16;
+		bf[4] = sig>>24;
+		bf[5] = t;
+		bf[6] = s->sym;
+		Bwrite(b, bf, 7);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = t;	/* type */
+		bf[2] = s->sym;	/* sym */
+		Bwrite(b, bf, 3);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	vlong v;
+	long l;
+	Ieee e;
+
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_CONST:
+	case D_OREG:
+	case D_BRANCH:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	case D_VCONST:
+		v = a->vval;
+		bp[0] = v;
+		bp[1] = v>>8;
+		bp[2] = v>>16;
+		bp[3] = v>>24;
+		bp[4] = v>>32;
+		bp[5] = v>>40;
+		bp[6] = v>>48;
+		bp[7] = v>>56;
+		bp += 8;
+		break;
+	}
+	return bp;
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_VLONG)
+			w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesu[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_VLONG) {
+			w = SZ_VLONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v += SZ_VLONG-1;
+	if(v > max)
+		max = round(v, SZ_VLONG);
+	return max;
+}
--- /dev/null
+++ b/utils/0c/txt.c
@@ -1,0 +1,1483 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+	int i;
+	Type *t;
+
+	thechar = '0';
+	thestring = "spim";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMP;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	for(i=0; i<nelem(reg); i++) {
+		reg[i] = 0;
+		if(i == REGZERO ||
+		  (i >= NREG && ((i-NREG)&1)))
+			reg[i] = 1;
+	}
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(i != REGZERO)
+			if(reg[i])
+				diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NREG; i+=2)
+		if(reg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesu[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gopcode(OAS, n, Z, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+	int i;
+
+	for(i=REGRET+1; i<NREG; i++)
+		if(reg[i] == 0)
+			return i;
+	diag(Z, "out of fixed registers");
+	return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TUVLONG:
+	case TVLONG:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i > 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NREG)
+				goto out;
+		}
+		j = 0*2 + NREG;
+		for(i=NREG; i<NREG+NREG; i+=2) {
+			if(j >= NREG+NREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j += 2;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	i = 0;
+out:
+	if(i)
+		reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_VLONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = 0;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else
+		if(llconst(n)) {
+			a->type = D_VCONST;
+			a->vval = n->vconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod;
+	Prog *p1;
+	double d;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0.0) {
+			a = FREGZERO;
+			goto ffreg;
+		}
+		if(d == 0.5) {
+			a = FREGHALF;
+			goto ffreg;
+		}
+		if(d == 1.0) {
+			a = FREGONE;
+			goto ffreg;
+		}
+		if(d == 2.0) {
+			a = FREGTWO;
+			goto ffreg;
+		}
+		if(d == -.5) {
+			fop(OSUB, FREGHALF, FREGZERO, t);
+			return;
+		}
+		if(d == -1.0) {
+			fop(OSUB, FREGONE, FREGZERO, t);
+			return;
+		}
+		if(d == -2.0) {
+			fop(OSUB, FREGTWO, FREGZERO, t);
+			return;
+		}
+		if(d == 1.5) {
+			fop(OADD, FREGONE, FREGHALF, t);
+			return;
+		}
+		if(d == 2.5) {
+			fop(OADD, FREGTWO, FREGHALF, t);
+			return;
+		}
+		if(d == 3.0) {
+			fop(OADD, FREGTWO, FREGONE, t);
+			return;
+		}
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0) {
+			a = FREGZERO;
+		ffreg:
+			nodreg(&nod, f, NREG+a);
+			gmove(&nod, t);
+			return;
+		}
+	}
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			if(typefd[tt]) {
+				/* special case can load mem to Freg */
+				regalloc(&nod, t, t);
+				gins(AMOVW, f, &nod);
+				a = AMOVWD;
+				if(tt == TFLOAT)
+					a = AMOVWF;
+				gins(a, &nod, &nod);
+				gmove(&nod, t);
+				regfree(&nod);
+				return;
+			}
+			a = AMOVW;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TUVLONG:
+		case TVLONG:
+			a = AMOVV;
+			break;
+		}
+		if(typechlp[ft] && typeilp[tt])
+			regalloc(&nod, t, t);
+		else
+			regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TUVLONG:
+		case TVLONG:
+			a = AMOVV;
+			break;
+		}
+		if(!typefd[ft] && vconst(f) == 0) {
+			gins(a, f, t);
+			return;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TUVLONG:
+	case TVLONG:
+		switch(tt) {
+		case TUVLONG:
+		case TVLONG:
+			a = AMOVV;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		case TDOUBLE:
+			gins(AMOVW, f, t);
+			gins(AMOVWD, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGED, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVD, nodfconst(4294967296.), &nod);
+				gins(AADDD, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TFLOAT:
+			gins(AMOVW, f, t);
+			gins(AMOVWF, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGEF, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVF, nodfconst(4294967296.), &nod);
+				gins(AADDF, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		}
+		break;
+	case TDOUBLE:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+			a = AMOVD;
+			if(ft == TFLOAT)
+				a = AMOVFD;
+			break;
+		case TFLOAT:
+			a = AMOVDF;
+			if(ft == TFLOAT)
+				a = AMOVF;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			regalloc(&nod, f, Z);
+			gins(ATRUNCDW, f, &nod);
+			if(ft == TFLOAT)
+				p->as = ATRUNCFW;
+			gins(AMOVW, &nod, t);
+			regfree(&nod);
+			return;
+		case TUVLONG:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(ATRUNCDV, f, &nod);
+			if(ft == TFLOAT)
+				p->as = ATRUNCFV;
+			gins(AMOVV, &nod, t);
+			regfree(&nod);
+			return;
+		}
+		break;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+			gins(AMOVW, f, t);
+			gins(AMOVWD, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGED, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVD, nodfconst(4294967296.), &nod);
+				gins(AADDD, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TFLOAT:
+			gins(AMOVW, f, t);
+			gins(AMOVWF, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGEF, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVF, nodfconst(4294967296.), &nod);
+				gins(AADDF, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TUVLONG:
+		case TVLONG:
+			if(ft == TULONG || ft == TUINT) {
+				a = AMOVWU;
+				break;
+			}
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AMOVF || a == AMOVD || a == AMOVV)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et, ett;
+	Adr ta;
+	Node nod;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		et = f1->type->etype;
+	ett = TLONG;
+	if(t != Z && t->type != T)
+		ett = t->type->etype;
+	if(llconst(f1) && o != OAS) {
+		regalloc(&nod, f1, Z);
+		gmove(f1, &nod);
+		gopcode(o, &nod, f2, t);
+		regfree(&nod);
+		return;
+	}
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADDU;
+		if(et == TVLONG || et == TUVLONG)
+			a = AADDVU;
+		else
+		if(et == TFLOAT)
+			a = AADDF;
+		else
+		if(et == TDOUBLE)
+			a = AADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBU;
+		if(et == TVLONG || et == TUVLONG)
+			a = ASUBVU;
+		else
+		if(et == TFLOAT)
+			a = ASUBF;
+		else
+		if(et == TDOUBLE)
+			a = ASUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		if(ett == TVLONG || ett == TUVLONG)
+			a = ASRLV;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		if(ett == TVLONG || ett == TUVLONG)
+			a = ASRAV;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		if(ett == TVLONG || ett == TUVLONG)
+			a = ASLLV;
+		break;
+
+	case OFUNC:
+		a = AJAL;
+		break;
+
+	case OCOND:
+		a = ASGTU;
+		break;
+
+	case OCOMMA:
+		a = ASGT;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT) {
+			a = AMULF;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = AMULD;
+			break;
+		}
+		a = AMUL;
+		if(et == TVLONG || et == TUVLONG)
+			a = AMULV;
+		goto muldiv;
+
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = ADIVF;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = ADIVD;
+			break;
+		}
+		a = ADIV;
+		if(et == TVLONG || et == TUVLONG)
+			a = ADIVV;
+		goto muldiv;
+
+	case OASMOD:
+	case OMOD:
+		a = ADIV;
+		o = OMOD;
+		if(et == TVLONG || et == TUVLONG)
+			a = ADIVV;
+		goto muldiv;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULU;
+		if(et == TVLONG || et == TUVLONG)
+			a = AMULVU;
+		goto muldiv;
+
+	case OASLMOD:
+	case OLMOD:
+		o = OMOD;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVU;
+		if(et == TVLONG || et == TUVLONG)
+			a = ADIVVU;
+		goto muldiv;
+
+	muldiv:
+		nextpc();
+		naddr(f1, &p->from);
+		if(f2 == Z)
+			raddr(t, p);
+		else
+			raddr(f2, p);
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+		nextpc();
+		p->as = AMOVW;
+		if(et == TVLONG || et == TUVLONG)
+			p->as = AMOVV;
+		a = D_LO;
+		if(o == OMOD)
+			a = D_HI;
+		p->from.type = a;
+		naddr(t, &p->to);
+		if(debug['g'])
+			print("%P\n", p);
+		return;
+
+	case OEQ:
+		if(!typefd[et]) {
+			a = ABEQ;
+			break;
+		}
+
+	case ONE:
+		if(!typefd[et]) {
+			a = ABNE;
+			break;
+		}
+
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+		if(typefd[et]) {
+			nextpc();
+			if(et == TFLOAT) {
+				a = ACMPGTF;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQF;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGEF;
+			} else {
+				a = ACMPGTD;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQD;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGED;
+			}
+			p->as = a;
+			naddr(f1, &p->from);
+			raddr(f2, p);
+			if(debug['g'])
+				print("%P\n", p);
+			nextpc();
+			a = ABFPF;
+			if(o == OEQ || o == OGE || o == OGT)
+				a = ABFPT;
+			p->as = a;
+			if(debug['g'])
+				print("%P\n", p);
+			return;
+		}
+		if(vconst(f1) == 0 || vconst(f2) == 0) {
+			if(vconst(f1) == 0) {
+				o = invrel[relindex(o)];
+				f1 = f2;
+			}
+			switch(o) {
+			case OLT:
+				a = ABLTZ;
+				break;
+			case OLE:
+				a = ABLEZ;
+				break;
+			case OGE:
+				a = ABGEZ;
+				break;
+			case OGT:
+				a = ABGTZ;
+				break;
+			}
+			f2 = Z;
+			break;
+		}
+
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		nextpc();
+		if(o == OLE || o == OGT || o == OLS || o == OHI) {
+			naddr(f1, &p->from);
+			raddr(f2, p);
+		} else {
+			naddr(f2, &p->from);
+			raddr(f1, p);
+		}
+		naddr(&regnode, &p->to);
+		p->to.reg = tmpreg();
+		a = ASGT;
+		if(o == OLO || o == OLS || o == OHS || o == OHI)
+			a = ASGTU;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+
+		nextpc();
+		naddr(&regnode, &p->from);
+		p->from.reg = tmpreg();
+		a = ABEQ;
+		if(o == OLT || o == OGT || o == OLO || o == OHI)
+			a = ABNE;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+		return;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+		if(ta.type == D_CONST && ta.offset == 0)
+			p->reg = REGZERO;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	p->from.name = D_EXTERN;
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= (vlong)-32766 && vv < (vlong)32766)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+llconst(Node *n)
+{
+	vlong vv;
+
+	if(n != Z && n->op == OCONST) {
+		if(typev[n->type->etype]) {
+			vv = n->vconst >> 32;
+			if(vv != 0 && vv != -1)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+sval(long v)
+{
+	if(v >= -32766L && v < 32766L)
+		return 1;
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= 16)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= 16)
+			return 0;
+		o = exfregoffset + NREG;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/*[TXXX]*/	
+	SZ_CHAR,	/*[TCHAR]*/	
+	SZ_CHAR,	/*[TUCHAR]*/
+	SZ_SHORT,	/*[TSHORT]*/
+	SZ_SHORT,	/*[TUSHORT]*/
+	SZ_INT,		/*[TINT]*/
+	SZ_INT,		/*[TUINT]*/
+	SZ_LONG,	/*[TLONG]*/
+	SZ_LONG,	/*[TULONG]*/
+	SZ_VLONG,	/*[TVLONG]*/
+	SZ_VLONG,	/*[TUVLONG]*/
+	SZ_FLOAT,	/*[TFLOAT]*/
+	SZ_DOUBLE,	/*[TDOUBLE]*/
+	SZ_IND,		/*[TIND]*/
+	0,		/*[TFUNC]*/
+	-1,		/*[TARRAY]*/
+	0,		/*[TVOID]*/
+	-1,		/*[TSTRUCT]*/
+	-1,		/*[TUNION]*/
+	SZ_INT,		/*[TENUM]*/
+};
+long	ncast[NTYPE] =
+{
+	0,				/*[TXXX]*/
+	BCHAR|BUCHAR,			/*[TCHAR]*/
+	BCHAR|BUCHAR,			/*[TUCHAR]*/	
+	BSHORT|BUSHORT,			/*[TSHORT]*/
+	BSHORT|BUSHORT,			/*[TUSHORT]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TINT]*/		
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TUINT]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TLONG]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TULONG]*/
+	BVLONG|BUVLONG,			/*[TVLONG]*/
+	BVLONG|BUVLONG,			/*[TUVLONG]*/
+	BFLOAT,				/*[TFLOAT]*/
+	BDOUBLE,			/*[TDOUBLE]*/
+	BLONG|BULONG|BIND,		/*[TIND]*/
+	0,				/*[TFUNC]*/
+	0,				/*[TARRAY]*/
+	0,				/*[TVOID]*/
+	BSTRUCT,			/*[TSTRUCT]*/
+	BUNION,				/*[TUNION]*/
+	0,				/*[TENUM]*/
+};
--- /dev/null
+++ b/utils/0c/v.out.h
@@ -1,0 +1,201 @@
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+#define	REGZERO		0
+#define	REGRET		1
+#define	REGARG		1
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R3-R23 */
+#define	REGEXT		25
+/* compiler allocates external registers R25 down */
+/* dont use R26 R27 */
+#define	REGTMP		28
+#define	REGSP		29
+#define	REGSB		30
+#define	REGLINK		31
+
+#define	FREGRET		0
+/* compiler allocates register variables F4-F22 */
+/* compiler allocates external registers F22 down */
+#define	FREGEXT		22
+#define	FREGZERO	24	/* both float and double */
+#define	FREGHALF	26	/* double */
+#define	FREGONE		28	/* double */
+#define	FREGTWO		30	/* double */
+
+enum	as
+{
+	AXXX,
+
+	AABSD,
+	AABSF,
+	AABSW,
+	AADD,
+	AADDD,
+	AADDF,
+	AADDU,
+	AADDW,
+	AAND,
+	ABEQ,
+	ABFPF,
+	ABFPT,
+	ABGEZ,
+	ABGEZAL,
+	ABGTZ,
+	ABLEZ,
+	ABLTZ,
+	ABLTZAL,
+	ABNE,
+	ABREAK,
+	ACMPEQD,
+	ACMPEQF,
+	ACMPGED,
+	ACMPGEF,
+	ACMPGTD,
+	ACMPGTF,
+	ADATA,
+	ADIV,
+	ADIVD,
+	ADIVF,
+	ADIVU,
+	ADIVW,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AJAL,
+	AJMP,
+	AMOVB,
+	AMOVBU,
+	AMOVD,
+	AMOVDF,
+	AMOVDW,
+	AMOVF,
+	AMOVFD,
+	AMOVFW,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVWD,
+	AMOVWF,
+	AMOVWL,
+	AMOVWR,
+	AMUL,
+	AMULD,
+	AMULF,
+	AMULU,
+	AMULW,
+	ANAME,
+	ANEGD,
+	ANEGF,
+	ANEGW,
+	ANOP,
+	ANOR,
+	AOR,
+	AREM,
+	AREMU,
+	ARET,
+	ARFE,
+	ASGT,
+	ASGTU,
+	ASLL,
+	ASRA,
+	ASRL,
+	ASUB,
+	ASUBD,
+	ASUBF,
+	ASUBU,
+	ASUBW,
+	ASYSCALL,
+	ATEXT,
+	ATLBP,
+	ATLBR,
+	ATLBWI,
+	ATLBWR,
+	AWORD,
+	AXOR,
+
+	AEND,
+
+	AMOVV,
+	AMOVVL,
+	AMOVVR,
+	ASLLV,
+	ASRAV,
+	ASRLV,
+	ADIVV,
+	ADIVVU,
+	AREMV,
+	AREMVU,
+	AMULV,
+	AMULVU,
+	AADDV,
+	AADDVU,
+	ASUBV,
+	ASUBVU,
+
+	ADYNT,
+	AINIT,
+
+	ABCASE,
+	ACASE,
+
+	ATRUNCFV,
+	ATRUNCDV,
+	ATRUNCFW,
+	ATRUNCDW,
+	AMOVWU,
+	AMOVFV,
+	AMOVDV,
+	AMOVVF,
+	AMOVVD,
+
+	ASIGNAME,
+
+	ALAST,
+};
+
+/* type/name */
+#define	D_GOK	0
+#define	D_NONE	1
+
+/* type */
+#define	D_BRANCH (D_NONE+1)
+#define	D_OREG	(D_NONE+2)
+#define	D_EXTERN (D_NONE+3)	/* name */
+#define	D_STATIC (D_NONE+4)	/* name */
+#define	D_AUTO	(D_NONE+5)	/* name */
+#define	D_PARAM	(D_NONE+6)	/* name */
+#define	D_CONST	(D_NONE+7)
+#define	D_FCONST (D_NONE+8)
+#define	D_SCONST (D_NONE+9)
+#define	D_HI	(D_NONE+10)
+#define	D_LO	(D_NONE+11)
+#define	D_REG	(D_NONE+12)
+#define	D_FREG	(D_NONE+13)
+#define	D_FCREG	(D_NONE+14)
+#define	D_MREG	(D_NONE+15)
+#define	D_FILE	(D_NONE+16)
+#define	D_OCONST (D_NONE+17)
+#define	D_FILE1	(D_NONE+18)
+#define	D_VCONST (D_NONE+19)
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/0l/asm.c
@@ -1,0 +1,1433 @@
+#include	"l.h"
+
+long	OFFSET;
+/*
+long	BADOFFSET	=	-1;
+
+		if(OFFSET <= BADOFFSET && OFFSET+4 > BADOFFSET)\
+			abort();\
+		OFFSET += 4;\
+
+		if(OFFSET == BADOFFSET)\
+			abort();\
+		OFFSET++;\
+*/
+
+void
+cput(int c)
+{
+	cbp[0] = c;
+	cbp++;
+	cbc--;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+bput(long l)
+{
+	cbp[0] = l>>24;
+	cbp[1] = l>>16;
+	cbp[2] = l>>8;
+	cbp[3] = l;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)
+{
+
+	cbp[0] = l;
+	cbp[1] = l>>8;
+	cbp[2] = l>>16;
+	cbp[3] = l>>24;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	if(s->type != STEXT && s->type != SLEAF)
+		diag("entry not text: %s", s->name);
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	OFFSET = HEADR;
+	seek(cout, OFFSET, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 8;
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux\n",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(asmout(p, o, 0)) {
+			p = p->link;
+			pc += 4;
+		}
+		pc += o->size;
+	}
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 4:
+		OFFSET = rnd(HEADR+textsize, 4096);
+		seek(cout, OFFSET, 0);
+		break;
+	case 1:
+	case 2:
+	case 3:
+	case 5:
+	case 6:
+		OFFSET = HEADR+textsize;
+		seek(cout, OFFSET, 0);
+		break;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100);
+		else
+			datblk(t, datsize-t);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 4:
+			OFFSET = rnd(HEADR+textsize, 4096)+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		case 3:
+		case 2:
+		case 1:
+		case 5:
+		case 6:
+			OFFSET = HEADR+textsize+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		cflush();
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f header\n", cputime());
+	Bflush(&bso);
+	OFFSET = 0;
+	seek(cout, OFFSET, 0);
+	switch(HEADTYPE) {
+	case 0:
+		bput(0x160L<<16);		/* magic and sections */
+		bput(0L);			/* time and date */
+		bput(rnd(HEADR+textsize, 4096)+datsize);
+		bput(symsize);			/* nsyms */
+		bput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+		bput((0413<<16)|0437L);		/* magic and version */
+		bput(rnd(HEADR+textsize, 4096));	/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(entryvalue());		/* va of entry */
+		bput(INITTEXT-HEADR);		/* va of base of text */
+		bput(INITDAT);			/* va of base of data */
+		bput(INITDAT+datsize);		/* va of base of bss */
+		bput(~0L);			/* gp reg mask */
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(~0L);			/* gp value ?? */
+		break;
+	case 1:
+		bput(0x160L<<16);		/* magic and sections */
+		bput(0L);			/* time and date */
+		bput(HEADR+textsize+datsize);
+		bput(symsize);			/* nsyms */
+		bput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		bput((0407<<16)|0437L);		/* magic and version */
+		bput(textsize);			/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(entryvalue());		/* va of entry */
+		bput(INITTEXT);			/* va of base of text */
+		bput(INITDAT);			/* va of base of data */
+		bput(INITDAT+datsize);		/* va of base of bss */
+		bput(~0L);			/* gp reg mask */
+		bput(lcsize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(~0L);			/* gp value ?? */
+		bput(0L);			/* complete mystery */
+		break;
+	case 2:
+		t = 22;
+		bput(((((4*t)+0)*t)+7));	/* magic */
+		bput(textsize);			/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(symsize);			/* nsyms */
+		bput(entryvalue());		/* va of entry */
+		bput(0L);
+		bput(lcsize);
+		break;
+	case 3:
+		bput((0x160L<<16)|3L);		/* magic and sections */
+		bput(time(0));			/* time and date */
+		bput(HEADR+textsize+datsize);
+		bput(symsize);			/* nsyms */
+		bput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		bput((0407<<16)|0437L);		/* magic and version */
+		bput(textsize);			/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(entryvalue());		/* va of entry */
+		bput(INITTEXT);			/* va of base of text */
+		bput(INITDAT);			/* va of base of data */
+		bput(INITDAT+datsize);		/* va of base of bss */
+		bput(~0L);			/* gp reg mask */
+		bput(lcsize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(~0L);			/* gp value ?? */
+
+		strnput(".text", 8);		/* text segment */
+		bput(INITTEXT);			/* address */
+		bput(INITTEXT);
+		bput(textsize);
+		bput(HEADR);
+		bput(0L);
+		bput(HEADR+textsize+datsize+symsize);
+		bput(lcsize);			/* line number size */
+		bput(0x20L);			/* flags */
+
+		strnput(".data", 8);		/* data segment */
+		bput(INITDAT);			/* address */
+		bput(INITDAT);
+		bput(datsize);
+		bput(HEADR+textsize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0x40L);			/* flags */
+
+		strnput(".bss", 8);		/* bss segment */
+		bput(INITDAT+datsize);		/* address */
+		bput(INITDAT+datsize);
+		bput(bsssize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0x80L);			/* flags */
+		break;
+	case 4:
+
+		bput((0x160L<<16)|3L);		/* magic and sections */
+		bput(time(0));			/* time and date */
+		bput(rnd(HEADR+textsize, 4096)+datsize);
+		bput(symsize);			/* nsyms */
+		bput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		bput((0413<<16)|01012L);	/* magic and version */
+		bput(textsize);			/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(entryvalue());		/* va of entry */
+		bput(INITTEXT);			/* va of base of text */
+		bput(INITDAT);			/* va of base of data */
+		bput(INITDAT+datsize);		/* va of base of bss */
+		bput(~0L);			/* gp reg mask */
+		bput(lcsize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(~0L);			/* gp value ?? */
+
+		strnput(".text", 8);		/* text segment */
+		bput(INITTEXT);			/* address */
+		bput(INITTEXT);
+		bput(textsize);
+		bput(HEADR);
+		bput(0L);
+		bput(HEADR+textsize+datsize+symsize);
+		bput(lcsize);			/* line number size */
+		bput(0x20L);			/* flags */
+
+		strnput(".data", 8);		/* data segment */
+		bput(INITDAT);			/* address */
+		bput(INITDAT);
+		bput(datsize);
+		bput(rnd(HEADR+textsize, 4096));	/* sizes */
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0x40L);			/* flags */
+
+		strnput(".bss", 8);		/* bss segment */
+		bput(INITDAT+datsize);		/* address */
+		bput(INITDAT+datsize);
+		bput(bsssize);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0L);
+		bput(0x80L);			/* flags */
+		break;
+	case 5:
+		strnput("\177ELF", 4);		/* e_ident */
+		cput(1);			/* class = 32 bit */
+		cput(2);			/* data = MSB */
+		cput(1);			/* version = CURRENT */
+		strnput("", 9);
+		bput((2L<<16)|8L);		/* type = EXEC; machine = MIPS */
+		bput(1L);			/* version = CURRENT */
+		bput(entryvalue());		/* entry vaddr */
+		bput(52L);			/* offset to first phdr */
+		bput(0L);			/* offset to first shdr */
+		bput(0L);			/* flags = MIPS */
+		bput((52L<<16)|32L);		/* Ehdr & Phdr sizes*/
+		bput((3L<<16)|0L);		/* # Phdrs & Shdr size */
+		bput((0L<<16)|0L);		/* # Shdrs & shdr string size */
+
+		bput(1L);			/* text - type = PT_LOAD */
+		bput(0L);			/* file offset */
+		bput(INITTEXT-HEADR);		/* vaddr */
+		bput(INITTEXT-HEADR);		/* paddr */
+		bput(HEADR+textsize);		/* file size */
+		bput(HEADR+textsize);		/* memory size */
+		bput(0x05L);			/* protections = RX */
+		bput(0x10000L);			/* alignment code?? */
+
+		bput(1L);			/* data - type = PT_LOAD */
+		bput(HEADR+textsize);		/* file offset */
+		bput(INITDAT);			/* vaddr */
+		bput(INITDAT);			/* paddr */
+		bput(datsize);			/* file size */
+		bput(datsize+bsssize);		/* memory size */
+		bput(0x06L);			/* protections = RW */
+		bput(0x10000L);			/* alignment code?? */
+
+		bput(0L);			/* data - type = PT_NULL */
+		bput(HEADR+textsize+datsize);	/* file offset */
+		bput(0L);
+		bput(0L);
+		bput(symsize);			/* symbol table size */
+		bput(lcsize);			/* line number size */
+		bput(0x04L);			/* protections = R */
+		bput(0x04L);			/* alignment code?? */
+		break;
+	case 6:
+		t = 22;
+		bput(((((4*t)+0)*t)+7));	/* magic */
+		bput(textsize);			/* sizes */
+		bput(datsize);
+		bput(bsssize);
+		bput(symsize);			/* nsyms */
+		bput(entryvalue());		/* va of entry */
+		bput(0L);
+		bput(lcsize);
+		break;
+	}
+	cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		cput(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		cput(0);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = (uchar*)buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+	if(c->outof)
+	Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+		c->outof - c->count, c->outof,
+		(double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+8, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	bput(v);
+	if(ver)
+		t += 'a' - 'A';
+	cput(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		cput(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			cput(s[i]);
+			cput(s[i+1]);
+		}
+		cput(0);
+		cput(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			cput(s[i]);
+		cput(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['L'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['L'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			cput(s+128);	/* 129-255 +pc */
+			if(debug['L'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			cput(0);	/* 0 vv +lc */
+			cput(s>>24);
+			cput(s>>16);
+			cput(s>>8);
+			cput(s);
+			if(debug['L']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			cput(0+s);	/* 1-64 +lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			cput(64-s);	/* 65-128 -lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		cput(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['L'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization\n");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization\n%P\n", p);
+			break;
+
+		case D_VCONST:
+			cast = (char*)p->to.ieee;
+			for(; i<c; i++) {
+				buf.dbuf[l] = cast[fnuxi8[i]];
+				l++;
+			}
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			if(p->to.sym) {
+				if(p->to.sym->type == STEXT ||
+				   p->to.sym->type == SLEAF)
+					d += p->to.sym->value;
+				if(p->to.sym->type == SDATA)
+					d += p->to.sym->value + INITDAT;
+				if(p->to.sym->type == SBSS)
+					d += p->to.sym->value + INITDAT;
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P\n", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+#define	OP_RRR(op,r1,r2,r3)\
+	(op|(((r1)&31L)<<16)|(((r2)&31L)<<21)|(((r3)&31L)<<11))
+#define	OP_IRR(op,i,r2,r3)\
+	(op|((i)&0xffffL)|(((r2)&31L)<<21)|(((r3)&31L)<<16))
+#define	OP_SRR(op,s,r2,r3)\
+	(op|(((s)&31L)<<6)|(((r2)&31L)<<16)|(((r3)&31L)<<11))
+#define	OP_FRRR(op,r1,r2,r3)\
+	(op|(((r1)&31L)<<16)|(((r2)&31L)<<11)|(((r3)&31L)<<6))
+#define	OP_JMP(op,i)\
+		((op)|((i)&0x3ffffffL))
+
+#define	OP(x,y)\
+	(((x)<<3)|((y)<<0))
+#define	SP(x,y)\
+	(((x)<<29)|((y)<<26))
+#define	BCOND(x,y)\
+	(((x)<<19)|((y)<<16))
+#define	MMU(x,y)\
+	(SP(2,0)|(16<<21)|((x)<<3)|((y)<<0))
+#define	FPF(x,y)\
+	(SP(2,1)|(16<<21)|((x)<<3)|((y)<<0))
+#define	FPD(x,y)\
+	(SP(2,1)|(17<<21)|((x)<<3)|((y)<<0))
+#define	FPW(x,y)\
+	(SP(2,1)|(20<<21)|((x)<<3)|((y)<<0))
+#define	FPV(x,y)\
+	(SP(2,1)|(21<<21)|((x)<<3)|((y)<<0))
+
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+	long o1, o2, o3, o4, o5, o6, o7, v;
+	Prog *ct;
+	int r, a;
+
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	o6 = 0;
+	o7 = 0;
+	switch(o->type) {
+	default:
+		diag("unknown type %d\n", o->type);
+		if(!debug['a'])
+			prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		if(aflag) {
+			if(p->link) {
+				if(p->as == ATEXT) {
+					ct = curtext;
+					o2 = autosize;
+					curtext = p;
+					autosize = p->to.offset + 8;
+					o1 = asmout(p->link, oplook(p->link), aflag);
+					curtext = ct;
+					autosize = o2;
+				} else
+					o1 = asmout(p->link, oplook(p->link), aflag);
+			}
+			return o1;
+		}
+		break;
+
+	case 1:		/* mov[v] r1,r2 ==> OR r1,r0,r2 */
+		o1 = OP_RRR(oprrr(AOR), p->from.reg, REGZERO, p->to.reg);
+		break;
+
+	case 2:		/* add/sub r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 3:		/* mov $soreg, r ==> or/add $i,o,r */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		a = AADDU;
+		if(o->a1 == C_ANDCON)
+			a = AOR;
+		o1 = OP_IRR(opirr(a), v, r, p->to.reg);
+		break;
+
+	case 4:		/* add $scon,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(opirr(p->as), v, r, p->to.reg);
+		break;
+
+	case 5:		/* syscall */
+		if(aflag)
+			return 0;
+		o1 = oprrr(p->as);
+		break;
+
+	case 6:		/* beq r1,[r2],sbra */
+		if(aflag)
+			return 0;
+		if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
+			nop.branch.count--;
+			nop.branch.outof--;
+			nop.jump.outof++;
+			o2 = asmout(p->cond, oplook(p->cond), 1);
+			if(o2) {
+				if(p->cond == P)
+					v = -4 >> 2;
+				else
+					v = (p->cond->pc+4 - pc-4) >> 2;
+				if(((v << 16) >> 16) != v)
+					diag("short branch too far: %d\n%P\n", v, p);
+				o1 = OP_IRR(opirr(p->as+ALAST), v, p->from.reg, p->reg);
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
+						p->pc, o1, o2, p);
+				lput(o1);
+				lput(o2);
+				return 1;
+			}
+		}
+		if(p->cond == P)
+			v = -4 >> 2;
+		else
+			v = (p->cond->pc - pc-4) >> 2;
+		if(((v << 16) >> 16) != v)
+			diag("short branch too far: %d\n%P\n", v, p);
+		o1 = OP_IRR(opirr(p->as), v, p->from.reg, p->reg);
+		break;
+
+	case 7:		/* mov r, soreg ==> sw o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = OP_IRR(opirr(p->as), v, r, p->from.reg);
+		break;
+
+	case 8:		/* mov soreg, r ==> lw o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(p->as+ALAST), v, r, p->to.reg);
+		break;
+
+	case 9:		/* asl r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
+		break;
+
+	case 10:	/* add $con,[r1],r2 ==> mov $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		r = AOR;
+		if(v < 0)
+			r = AADDU;
+		o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 11:	/* jmp lbra */
+		if(aflag)
+			return 0;
+		if(p->cond == P)
+			v = p->pc >> 2;
+		else
+			v = p->cond->pc >> 2;
+		o1 = OP_JMP(opirr(p->as), v);
+		if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
+			nop.branch.count--;
+			nop.branch.outof--;
+			nop.jump.outof++;
+			o2 = asmout(p->cond, oplook(p->cond), 1);
+			if(o2) {
+				o1 += 1;
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
+						p->pc, o1, o2, p);
+				lput(o1);
+				lput(o2);
+				return 1;
+			}
+		}
+		break;
+
+	case 12:	/* movbs r,r */
+		v = 16;
+		if(p->as == AMOVB)
+			v = 24;
+		o1 = OP_SRR(opirr(ASLL), v, p->from.reg, p->to.reg);
+		o2 = OP_SRR(opirr(ASRA), v, p->to.reg, p->to.reg);
+		break;
+
+	case 13:	/* movbu r,r */
+		if(p->as == AMOVBU)
+			o1 = OP_IRR(opirr(AAND), 0xffL, p->from.reg, p->to.reg);
+		else
+			o1 = OP_IRR(opirr(AAND), 0xffffL, p->from.reg, p->to.reg);
+		break;
+
+	case 14:	/* movwu r,r */
+		v = 32-32;
+		o1 = OP_SRR(opirr(ASLLV+ALAST), v, p->from.reg, p->to.reg);
+		o2 = OP_SRR(opirr(ASRLV+ALAST), v, p->to.reg, p->to.reg);
+		break;
+
+	case 16:	/* sll $c,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		if(v >= 32)
+			o1 = OP_SRR(opirr(p->as+ALAST), v-32, r, p->to.reg);
+		else
+			o1 = OP_SRR(opirr(p->as), v, r, p->to.reg);
+		break;
+
+	case 18:	/* jmp [r1],0(r2) */
+		if(aflag)
+			return 0;
+		r = p->reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = OP_RRR(oprrr(p->as), 0, p->to.reg, r);
+		break;
+
+	case 19:	/* mov $lcon,r ==> lu+or */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+		o2 = OP_IRR(opirr(AOR), v, p->to.reg, p->to.reg);
+		break;
+
+	case 20:	/* mov lohi,r */
+		r = OP(2,0);		/* mfhi */
+		if(p->from.type == D_LO)
+			r = OP(2,2);	/* mflo */
+		o1 = OP_RRR(r, REGZERO, REGZERO, p->to.reg);
+		break;
+
+	case 21:	/* mov r,lohi */
+		r = OP(2,1);		/* mthi */
+		if(p->to.type == D_LO)
+			r = OP(2,3);	/* mtlo */
+		o1 = OP_RRR(r, REGZERO, p->from.reg, REGZERO);
+		break;
+
+	case 22:	/* mul r1,r2 */
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, p->reg, REGZERO);
+		break;
+
+	case 23:	/* add $lcon,r1,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP || p->reg == REGTMP)
+			diag("cant synthesize large constant\n%P\n", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o3 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 24:	/* mov $ucon,,r ==> lu r */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+		break;
+
+	case 25:	/* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 26:	/* mov $lsext/auto/oreg,,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP)
+			diag("cant synthesize large constant\n%P\n", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o3 = OP_RRR(oprrr(AADDU), REGTMP, r, p->to.reg);
+		break;
+
+	case 27:		/* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		if(p->as == AMOVD)
+			o4 = opirr(AMOVD+ALAST);
+		else
+			o4 = opirr(AMOVF+ALAST);
+		switch(o->size) {
+		case 16:
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(o4, 0, REGTMP, p->to.reg);
+			break;
+		case 4:
+			o1 = OP_IRR(o4, v, r, p->to.reg);
+			break;
+		}
+		break;
+
+	case 28:		/* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		if(p->as == AMOVD)
+			o4 = opirr(AMOVD);
+		else
+			o4 = opirr(AMOVF);
+		switch(o->size) {
+		case 16:
+			if(r == REGTMP)
+				diag("cant synthesize large constant\n%P\n", p);
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(o4, 0, REGTMP, p->from.reg);
+			break;
+		case 4:
+			o1 = OP_IRR(o4, v, r, p->from.reg);
+			break;
+		}
+		break;
+
+	case 30:	/* movw r,fr */
+		r = SP(2,1)|(4<<21);		/* mtc1 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 31:	/* movw fr,r */
+		r = SP(2,1)|(0<<21);		/* mfc1 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+
+	case 32:	/* fadd fr1,[fr2],fr3 */
+		r = p->reg;
+		if(r == NREG)
+			o1 = OP_FRRR(oprrr(p->as), p->from.reg, p->to.reg, p->to.reg);
+		else
+			o1 = OP_FRRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 33:	/* fabs fr1,fr3 */
+		o1 = OP_FRRR(oprrr(p->as), 0, p->from.reg, p->to.reg);
+		break;
+
+	case 34:	/* mov $con,fr ==> or/add $i,r,r2 */
+		v = regoff(&p->from);
+		r = AADDU;
+		if(o->a1 == C_ANDCON)
+			r = AOR;
+		o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+		o2 = OP_RRR(SP(2,1)|(4<<21), REGTMP, 0, p->to.reg);	/* mtc1 */
+		break;
+
+	case 35:	/* mov r,lext/luto/oreg ==> sw o(r) */
+		/*
+		 * the lowbits of the constant cannot
+		 * be moved into the offset of the load
+		 * because the mips 4000 in 64-bit mode
+		 * does a 64-bit add and it will screw up.
+		 */
+		v = regoff(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P\n", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+		o4 = OP_IRR(opirr(p->as), 0, REGTMP, p->from.reg);
+		break;
+
+	case 36:	/* mov lext/lauto/lreg,r ==> lw o(r30) */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P\n", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+		o4 = OP_IRR(opirr(p->as+ALAST), 0, REGTMP, p->to.reg);
+		break;
+
+	case 37:	/* movw r,mr */
+		r = SP(2,0)|(4<<21);		/* mtc0 */
+		if(p->as == AMOVV)
+			r = SP(2,0)|(5<<21);	/* dmtc0 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 38:	/* movw mr,r */
+		r = SP(2,0)|(0<<21);		/* mfc0 */
+		if(p->as == AMOVV)
+			r = SP(2,0)|(1<<21);	/* dmfc0 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+
+	case 39:	/* rfe ==> jmp+rfe */
+		if(aflag)
+			return 0;
+		o1 = OP_RRR(oprrr(AJMP), 0, p->to.reg, REGZERO);
+		o2 = oprrr(p->as);
+		break;
+
+	case 40:	/* word */
+		if(aflag)
+			return 0;
+		o1 = regoff(&p->to);
+		break;
+
+	case 41:	/* movw r,fcr */
+		o1 = OP_RRR(SP(2,1)|(2<<21), REGZERO, 0, p->to.reg); 	/* mfcc1 */
+		o2 = OP_RRR(SP(2,1)|(6<<21), p->from.reg, 0, p->to.reg);/* mtcc1 */
+		break;
+
+	case 42:	/* movw fcr,r */
+		o1 = OP_RRR(SP(2,1)|(2<<21), p->to.reg, 0, p->from.reg);/* mfcc1 */
+		break;
+
+	case 47:	/* movv r,fr */
+		r = SP(2,1)|(5<<21);		/* dmtc1 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 48:	/* movv fr,r */
+		r = SP(2,1)|(1<<21);		/* dmfc1 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+	}
+	if(aflag)
+		return o1;
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		lput(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		lput(o1);
+		lput(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		lput(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		lput(o4);
+		lput(o5);
+		break;
+
+	case 28:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, o6, o7, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		lput(o4);
+		lput(o5);
+		lput(o6);
+		lput(o7);
+		break;
+	}
+	return 0;
+}
+
+int
+isnop(Prog *p)
+{
+	if(p->as != ANOR)
+		return 0;
+	if(p->reg != REGZERO && p->reg != NREG)
+		return 0;
+	if(p->from.type != D_REG || p->from.reg != REGZERO)
+		return 0;
+	if(p->to.type != D_REG || p->to.reg != REGZERO)
+		return 0;
+	return 1;
+}
+
+long
+oprrr(int a)
+{
+	switch(a) {
+	case AADD:	return OP(4,0);
+	case AADDU:	return OP(4,1);
+	case ASGT:	return OP(5,2);
+	case ASGTU:	return OP(5,3);
+	case AAND:	return OP(4,4);
+	case AOR:	return OP(4,5);
+	case AXOR:	return OP(4,6);
+	case ASUB:	return OP(4,2);
+	case ASUBU:	return OP(4,3);
+	case ANOR:	return OP(4,7);
+	case ASLL:	return OP(0,4);
+	case ASRL:	return OP(0,6);
+	case ASRA:	return OP(0,7);
+
+	case ASLLV:	return OP(2,4);
+	case ASRLV:	return OP(2,6);
+	case ASRAV:	return OP(2,7);
+
+	case AADDV:	return OP(5,4);
+	case AADDVU:	return OP(5,5);
+	case ASUBV:	return OP(5,6);
+	case ASUBVU:	return OP(5,7);
+	case AREM:
+	case ADIV:	return OP(3,2);
+	case AREMU:
+	case ADIVU:	return OP(3,3);
+	case AMUL:	return OP(3,0);
+	case AMULU:	return OP(3,1);
+
+	case AREMV:
+	case ADIVV:	return OP(3,6);
+	case AREMVU:
+	case ADIVVU:	return OP(3,7);
+	case AMULV:	return OP(3,4);
+	case AMULVU:	return OP(3,5);
+
+	case AJMP:	return OP(1,0);
+	case AJAL:	return OP(1,1);
+
+	case ABREAK:	return OP(1,5);
+	case ASYSCALL:	return OP(1,4);
+	case ATLBP:	return MMU(1,0);
+	case ATLBR:	return MMU(0,1);
+	case ATLBWI:	return MMU(0,2);
+	case ATLBWR:	return MMU(0,6);
+	case ARFE:	return MMU(2,0);
+
+	case ADIVF:	return FPF(0,3);
+	case ADIVD:	return FPD(0,3);
+	case AMULF:	return FPF(0,2);
+	case AMULD:	return FPD(0,2);
+	case ASUBF:	return FPF(0,1);
+	case ASUBD:	return FPD(0,1);
+	case AADDF:	return FPF(0,0);
+	case AADDD:	return FPD(0,0);
+
+	case ATRUNCFV:	return FPF(1,1);
+	case ATRUNCDV:	return FPD(1,1);
+	case ATRUNCFW:	return FPF(1,5);
+	case ATRUNCDW:	return FPD(1,5);
+	case AMOVFV:	return FPF(4,5);
+	case AMOVDV:	return FPD(4,5);
+	case AMOVVF:	return FPV(4,0);
+	case AMOVVD:	return FPV(4,1);
+
+	case AMOVFW:	return FPF(4,4);
+	case AMOVDW:	return FPD(4,4);
+	case AMOVWF:	return FPW(4,0);
+	case AMOVDF:	return FPD(4,0);
+	case AMOVWD:	return FPW(4,1);
+	case AMOVFD:	return FPF(4,1);
+	case AABSF:	return FPF(0,5);
+	case AABSD:	return FPD(0,5);
+	case AMOVF:	return FPF(0,6);
+	case AMOVD:	return FPD(0,6);
+	case ANEGF:	return FPF(0,7);
+	case ANEGD:	return FPD(0,7);
+
+	case ACMPEQF:	return FPF(6,2);
+	case ACMPEQD:	return FPD(6,2);
+	case ACMPGTF:	return FPF(7,4);
+	case ACMPGTD:	return FPD(7,4);
+	case ACMPGEF:	return FPF(7,6);
+	case ACMPGED:	return FPD(7,6);
+	}
+	if(a >= ALAST)
+		diag("bad rrr %A+ALAST", a-ALAST);
+	else
+		diag("bad rrr %A", a);
+	return 0;
+}
+
+long
+opirr(int a)
+{
+	switch(a) {
+	case AADD:		return SP(1,0);
+	case AADDU:		return SP(1,1);
+	case ASGT:		return SP(1,2);
+	case ASGTU:		return SP(1,3);
+	case AAND:		return SP(1,4);
+	case AOR:		return SP(1,5);
+	case AXOR:		return SP(1,6);
+	case ALAST:		return SP(1,7);			/* lui */
+	case ASLL:		return OP(0,0);
+	case ASRL:		return OP(0,2);
+	case ASRA:		return OP(0,3);
+
+	case AADDV:		return SP(3,0);
+	case AADDVU:		return SP(3,1);
+
+	case AJMP:		return SP(0,2);
+	case AJAL:		return SP(0,3);
+	case ABEQ:		return SP(0,4);
+	case ABEQ+ALAST:	return SP(2,4);			/* likely */
+	case ABNE:		return SP(0,5);
+	case ABNE+ALAST:	return SP(2,5);			/* likely */
+
+	case ABGEZ:		return SP(0,1)|BCOND(0,1);
+	case ABGEZ+ALAST:	return SP(0,1)|BCOND(0,3);	/* likely */
+	case ABGEZAL:		return SP(0,1)|BCOND(2,1);
+	case ABGEZAL+ALAST:	return SP(0,1)|BCOND(2,3);	/* likely */
+	case ABGTZ:		return SP(0,7);
+	case ABGTZ+ALAST:	return SP(2,7);			/* likely */
+	case ABLEZ:		return SP(0,6);
+	case ABLEZ+ALAST:	return SP(2,6);			/* likely */
+	case ABLTZ:		return SP(0,1)|BCOND(0,0);
+	case ABLTZ+ALAST:	return SP(0,1)|BCOND(0,2);	/* likely */
+	case ABLTZAL:		return SP(0,1)|BCOND(2,0);
+	case ABLTZAL+ALAST:	return SP(0,1)|BCOND(2,2);	/* likely */
+
+	case ABFPT:		return SP(2,1)|(257<<16);
+	case ABFPT+ALAST:	return SP(2,1)|(259<<16);	/* likely */
+	case ABFPF:		return SP(2,1)|(256<<16);
+	case ABFPF+ALAST:	return SP(2,1)|(258<<16);	/* likely */
+
+	case AMOVB:
+	case AMOVBU:		return SP(5,0);
+	case AMOVH:
+	case AMOVHU:		return SP(5,1);
+	case AMOVW:		return SP(5,3);
+	case AMOVV:		return SP(7,7);
+	case AMOVF:		return SP(7,1);
+	case AMOVD:		return SP(7,5);
+	case AMOVWL:		return SP(5,2);
+	case AMOVWR:		return SP(5,6);
+	case AMOVVL:		return SP(5,4);
+	case AMOVVR:		return SP(5,5);
+
+	case ABREAK:		return SP(5,7);
+
+	case AMOVWL+ALAST:	return SP(4,2);
+	case AMOVWR+ALAST:	return SP(4,6);
+	case AMOVVL+ALAST:	return SP(3,2);
+	case AMOVVR+ALAST:	return SP(3,3);
+	case AMOVB+ALAST:	return SP(4,0);
+	case AMOVBU+ALAST:	return SP(4,4);
+	case AMOVH+ALAST:	return SP(4,1);
+	case AMOVHU+ALAST:	return SP(4,5);
+	case AMOVW+ALAST:	return SP(4,3);
+	case AMOVV+ALAST:	return SP(6,7);
+	case AMOVF+ALAST:	return SP(6,1);
+	case AMOVD+ALAST:	return SP(6,5);
+
+	case ASLLV:		return OP(7,0);
+	case ASRLV:		return OP(7,2);
+	case ASRAV:		return OP(7,3);
+	case ASLLV+ALAST:	return OP(7,4);
+	case ASRLV+ALAST:	return OP(7,6);
+	case ASRAV+ALAST:	return OP(7,7);
+	}
+	if(a >= ALAST)
+		diag("bad irr %A+ALAST", a-ALAST);
+	else
+		diag("bad irr %A", a);
+	return 0;
+}
--- /dev/null
+++ b/utils/0l/enam.c
@@ -1,0 +1,122 @@
+char*	anames[] =
+{
+	"XXX",
+	"ABSD",
+	"ABSF",
+	"ABSW",
+	"ADD",
+	"ADDD",
+	"ADDF",
+	"ADDU",
+	"ADDW",
+	"AND",
+	"BEQ",
+	"BFPF",
+	"BFPT",
+	"BGEZ",
+	"BGEZAL",
+	"BGTZ",
+	"BLEZ",
+	"BLTZ",
+	"BLTZAL",
+	"BNE",
+	"BREAK",
+	"CMPEQD",
+	"CMPEQF",
+	"CMPGED",
+	"CMPGEF",
+	"CMPGTD",
+	"CMPGTF",
+	"DATA",
+	"DIV",
+	"DIVD",
+	"DIVF",
+	"DIVU",
+	"DIVW",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"JAL",
+	"JMP",
+	"MOVB",
+	"MOVBU",
+	"MOVD",
+	"MOVDF",
+	"MOVDW",
+	"MOVF",
+	"MOVFD",
+	"MOVFW",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVWD",
+	"MOVWF",
+	"MOVWL",
+	"MOVWR",
+	"MUL",
+	"MULD",
+	"MULF",
+	"MULU",
+	"MULW",
+	"NAME",
+	"NEGD",
+	"NEGF",
+	"NEGW",
+	"NOP",
+	"NOR",
+	"OR",
+	"REM",
+	"REMU",
+	"RET",
+	"RFE",
+	"SGT",
+	"SGTU",
+	"SLL",
+	"SRA",
+	"SRL",
+	"SUB",
+	"SUBD",
+	"SUBF",
+	"SUBU",
+	"SUBW",
+	"SYSCALL",
+	"TEXT",
+	"TLBP",
+	"TLBR",
+	"TLBWI",
+	"TLBWR",
+	"WORD",
+	"XOR",
+	"END",
+	"MOVV",
+	"MOVVL",
+	"MOVVR",
+	"SLLV",
+	"SRAV",
+	"SRLV",
+	"DIVV",
+	"DIVVU",
+	"REMV",
+	"REMVU",
+	"MULV",
+	"MULVU",
+	"ADDV",
+	"ADDVU",
+	"SUBV",
+	"SUBVU",
+	"DYNT",
+	"INIT",
+	"BCASE",
+	"CASE",
+	"TRUNCFV",
+	"TRUNCDV",
+	"TRUNCFW",
+	"TRUNCDW",
+	"MOVWU",
+	"MOVFV",
+	"MOVDV",
+	"MOVVF",
+	"MOVVD",
+	"SIGNAME",
+	"LAST",
+};
--- /dev/null
+++ b/utils/0l/l.h
@@ -1,0 +1,329 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../vc/v.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+typedef	struct	Oprang	Oprang;
+typedef	uchar	Opcross[32][2][32];
+typedef	struct	Count	Count;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char*	u0sval;
+		Ieee*	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	union
+	{
+		long	u0regused;
+		Prog*	u0forwd;
+	} u0;
+	Prog*	cond;
+	Prog*	link;
+	long	pc;
+	long	line;
+	uchar	mark;
+	uchar	optab;
+	char	as;
+	char	reg;
+};
+
+#define	regused	u0.u0regused
+#define	forwd	u0.u0forwd
+
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	long	value;
+	Sym*	link;
+};
+struct	Autom
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	char	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+};
+struct	Oprang
+{
+	Optab*	start;
+	Optab*	stop;
+};
+struct	Count
+{
+	long	count;
+	long	outof;
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+
+	C_NONE		= 0,
+	C_REG,
+	C_FREG,
+	C_FCREG,
+	C_MREG,
+	C_HI,
+	C_LO,
+	C_ZCON,
+	C_SCON,
+	C_ADD0CON,
+	C_AND0CON,
+	C_ADDCON,
+	C_ANDCON,
+	C_UCON,
+	C_LCON,
+	C_SACON,
+	C_SECON,
+	C_LACON,
+	C_LECON,
+	C_SBRA,
+	C_LBRA,
+	C_SAUTO,
+	C_SEXT,
+	C_LAUTO,
+	C_LEXT,
+	C_ZOREG,
+	C_SOREG,
+	C_LOREG,
+	C_GOK,
+
+	NSCHED		= 20,
+
+/* mark flags */
+	FOLL		= 1<<0,
+	LABEL		= 1<<1,
+	LEAF		= 1<<2,
+	SYNC		= 1<<3,
+	BRANCH		= 1<<4,
+	LOAD		= 1<<5,
+	FCMP		= 1<<6,
+	NOSCHED		= 1<<7,
+
+	BIG		= 32766,
+	STRINGSZ	= 200,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+};
+
+EXTERN union
+{
+	struct
+	{
+		char	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi4[4];
+EXTERN	char	fnuxi8[8];
+EXTERN	char*	noname;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	instoffset;
+EXTERN	Opcross	opcross[10];
+EXTERN	Oprang	oprange[ALAST];
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	uchar	repop[ALAST];
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	char	xcmp[32][32];
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+
+EXTERN	struct
+{
+	Count	branch;
+	Count	fcmp;
+	Count	load;
+	Count	mfrom;
+	Count	page;
+	Count	jump;
+} nop;
+
+extern	char*	anames[];
+extern	Optab	optab[];
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	addnop(Prog*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmlc(void);
+int	asmout(Prog*, Optab*, int);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	buildrep(int, int);
+void	cflush(void);
+int	cmp(int, int);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long);
+void	diag(char*, ...);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+int	find1(long, int);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+vlong	ieeedtov(Ieee*);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	lput(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	noops(void);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opirr(int);
+Optab*	oplook(Prog*);
+long	oprrr(int);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	sched(Prog*, Prog*);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+void	xfol(Prog*);
+void	nopstat(char*, Count*);
--- /dev/null
+++ b/utils/0l/list.c
@@ -1,0 +1,277 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	if(a == ADATA || a == ADYNT || a == AINIT)
+		sprint(str, "(%ld)	%A	%D/%d,%D",
+			p->line, a, &p->from, p->reg, &p->to);
+	else{
+		s = str;
+		s += sprint(s, "(%ld)", p->line);
+		if(p->mark & NOSCHED)
+			s += sprint(s, "*");
+		if(p->reg == NREG)
+			sprint(s, "	%A	%D,%D",
+				a, &p->from, &p->to);
+		else
+		if(p->from.type != D_FREG)
+			sprint(s, "	%A	%D,R%d,%D",
+				a, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%A	%D,F%d,%D",
+				a, &p->from, p->reg, &p->to);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		sprint(str, "$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OCONST:
+		sprint(str, "$*$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_MREG:
+		sprint(str, "M%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCREG:
+		sprint(str, "FC%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_LO:
+		sprint(str, "LO");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(LO)(REG)", a);
+		break;
+
+	case D_HI:
+		sprint(str, "HI");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(HI)(REG)", a);
+		break;
+
+	case D_BRANCH:	/* botch */
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(v >= INITTEXT)
+				v -= INITTEXT-HEADR;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%e", ieeedtod(a->ieee));
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		if(s == S)
+			sprint(str, "%ld(SB)", a->offset);
+		else
+			sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		if(s == S)
+			sprint(str, "<>+%ld(SB)", a->offset);
+		else
+			sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		if(s == S)
+			sprint(str, "%ld(SP)", a->offset);
+		else
+			sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		if(s == S)
+			sprint(str, "%ld(FP)", a->offset);
+		else
+			sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/0l/mkfile
@@ -1,0 +1,33 @@
+<../../mkconfig
+
+TARG=0l
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	sched.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+
+HFILES=\
+	l.h\
+	../vc/v.out.h\
+	../include/ar.h\
+
+LIBS=bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I ../include -I.
+
+enam.$O:	../0c/enam.c
+	$CC $CFLAGS ../0c/enam.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/0l/noop.c
@@ -1,0 +1,420 @@
+#include	"l.h"
+
+void
+noops(void)
+{
+	Prog *p, *p1, *q, *q1;
+	int o, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			p->mark |= LABEL|LEAF|SYNC;
+			if(p->link)
+				p->link->mark |= LABEL;
+			curtext = p;
+			break;
+
+		/* too hard, just leave alone */
+		case AMOVW:
+		case AMOVV:
+			if(p->to.type == D_FCREG ||
+			   p->to.type == D_MREG) {
+				p->mark |= LABEL|SYNC;
+				break;
+			}
+			if(p->from.type == D_FCREG ||
+			   p->from.type == D_MREG) {
+				p->mark |= LABEL|SYNC;
+				addnop(p);
+				addnop(p);
+				nop.mfrom.count += 2;
+				nop.mfrom.outof += 2;
+				break;
+			}
+			break;
+
+		/* too hard, just leave alone */
+		case ACASE:
+		case ASYSCALL:
+		case AWORD:
+		case ATLBWR:
+		case ATLBWI:
+		case ATLBP:
+		case ATLBR:
+			p->mark |= LABEL|SYNC;
+			break;
+
+		case ANOR:
+			if(p->to.type == D_REG && p->to.reg == REGZERO)
+				p->mark |= LABEL|SYNC;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+
+			if(p->link != P)
+				p->link->mark |= LABEL;
+			break;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		case ABCASE:
+			p->mark |= LABEL|SYNC;
+			goto dstlab;
+
+		case ABGEZAL:
+		case ABLTZAL:
+		case AJAL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case AJMP:
+		case ABEQ:
+		case ABGEZ:
+		case ABGTZ:
+		case ABLEZ:
+		case ABLTZ:
+		case ABNE:
+		case ABFPT:
+		case ABFPF:
+			p->mark |= BRANCH;
+
+		dstlab:
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+				if(!(q1->mark & LEAF))
+					q1->mark |= LABEL;
+			} else
+				p->mark |= LABEL;
+			q1 = p->link;
+			if(q1 != P)
+				q1->mark |= LABEL;
+			break;
+		}
+		q = p;
+	}
+
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case AJAL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 8;
+			if(autosize <= 8)
+			if(curtext->mark & LEAF) {
+				p->to.offset = -8;
+				autosize = 0;
+			}
+
+			q = p;
+			if(autosize) {
+				if(autosize & 7)
+					Bprint(&bso, "odd stack in: %s\n",
+						curtext->from.sym->name);
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = -autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			} else
+			if(!(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				Bflush(&bso);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+				break;
+			}
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->from.offset = 0;
+			q1->to.reg = REGSP;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		case ARET:
+			nocache(p);
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = AJMP;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 0;
+					p->to.reg = REGLINK;
+					p->mark |= BRANCH;
+					break;
+				}
+
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = AJMP;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 0;
+				q->to.reg = REGLINK;
+				q->mark |= BRANCH;
+
+				q->link = p->link;
+				p->link = q;
+				break;
+			}
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = 2;
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			}
+
+			q1 = prg();
+			q1->as = AJMP;
+			q1->line = p->line;
+			q1->to.type = D_OREG;
+			q1->to.offset = 0;
+			q1->to.reg = 2;
+			q1->mark |= BRANCH;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				q = prg();
+				q->line = p->line;
+				q->as = AJMP;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				q->mark |= BRANCH;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+			}
+			q = prg();
+			q->line = p->line;
+			q->as = AJMP;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			q->mark |= BRANCH;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AADD;
+			q->from.type = D_CONST;
+			q->from.offset = autosize;
+			q->to.type = D_REG;
+			q->to.reg = REGSP;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVW;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+		}
+	}
+
+	curtext = P;
+	q = P;		/* p - 1 */
+	q1 = firstp;	/* top of block */
+	o = 0;		/* count of instructions */
+	for(p = firstp; p != P; p = p1) {
+		p1 = p->link;
+		o++;
+		if(p->mark & NOSCHED){
+			if(q1 != p){
+				sched(q1, q);
+			}
+			for(; p != P; p = p->link){
+				if(!(p->mark & NOSCHED))
+					break;
+				q = p;
+			}
+			p1 = p;
+			q1 = p;
+			o = 0;
+			continue;
+		}
+		if(p->mark & (LABEL|SYNC)) {
+			if(q1 != p)
+				sched(q1, q);
+			q1 = p;
+			o = 1;
+		}
+		if(p->mark & (BRANCH|SYNC)) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		if(o >= NSCHED) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		q = p;
+	}
+}
+
+void
+addnop(Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = ANOR;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGZERO;
+	q->to.type = D_REG;
+	q->to.reg = REGZERO;
+
+	q->link = p->link;
+	p->link = q;
+}
+
+void
+nocache(Prog *p)
+{
+	p->optab = 0;
+	p->from.class = 0;
+	p->to.class = 0;
+}
--- /dev/null
+++ b/utils/0l/obj.c
@@ -1,0 +1,1384 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= '0';
+char	*thestring 	= "spim";
+
+/*
+ *	-H0 -T0x40004C -D0x10000000	is abbrev unix
+ *	-H1 -T0x80020000 -R4		is bootp() format for 3k
+ *	-H2 -T4128 -R4096		is plan9 spim format
+ *	-H3 -T0x80020000 -R8		is bootp() format for 4k
+ *	-H4 -T0x400000 -R4		is sgi unix coff executable
+ *	-H5 -T0x4000A0 -R4		is sgi unix elf executable
+ *	-H6 -T0x4000A0 -R4		is plan9 spim format
+ */
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		/* do something about setting INITTEXT */
+		break;
+	} ARGEND
+
+	USED(argc);
+
+	if(*argv == 0) {
+		diag("usage: vl [-options] objects\n");
+		errorexit();
+	}
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+
+	case 0:	/* unix simple */
+		HEADR = 20L+56L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x40004CL;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000L;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 1:	/* boot for 3k */
+		HEADR = 20L+60L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80020000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 spim */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* boot for 4k */
+		HEADR = 20L+56L+3*40L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80020000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 8;
+		break;
+	case 4:	/* sgi unix coff executable */
+		HEADR = 20L+56L+3*40L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 5:	/* sgi unix elf executable */
+		HEADR = rnd(52L+3*32L, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 6:	/* plan 9 spim */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	}
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%x -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0)
+		outfile = "0.out";
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("%s: cannot create\n", outfile);
+		errorexit();
+	}
+	nuxiinit();
+
+	version = 0;
+	cbp = (uchar*)buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+	errorexit();
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	Bflush(&bso);
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[100], pname[150];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(file[0] == '-' && file[1] == 'l') {
+		if(debug['9'])
+			sprint(name, "/%s/lib/lib", thestring);
+		else
+			sprint(name, "/usr/%clib/lib", thechar);
+		strcat(name, file+2);
+		strcat(name, ".a");
+		file = name;
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open file: %s\n", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header\n", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header\n", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			l = read(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s\n", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive\n", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	long l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = ALAST+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		a->sval = malloc(NSNAME);
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_VCONST:
+	case D_FCONST:
+		a->ieee = malloc(sizeof(Ieee));
+		a->ieee->l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee->h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		return c;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char name[1024], comp[256], *p;
+	int i;
+
+	if(histfrogp <= 0)
+		return;
+
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else
+	if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		if(debug['9'])
+			sprint(name, "/%s/lib", thestring);
+		else
+			sprint(name, "/usr/%clib", thechar);
+		i = 0;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) {
+			diag("library component too long");
+			return;
+		}
+		strcat(name, "/");
+		strcat(name, comp);
+	}
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	long ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= AXXX || o >= ALAST) {
+		diag("%s: line %ld: opcode out of range %d\n", pn, pc-ipc, o);
+		print("	probably not a .v file\n");
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		if(o == ASIGNAME) {
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->reg = bloc[1] & 0x7f;
+	if(bloc[1] & 0x80)
+		p->mark = NOSCHED;
+	p->line = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg < 0 || p->reg > NREG)
+		diag("register out of range %d\n", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P\n", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P\n", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset && s->value && p->to.offset != s->value)
+			print("warning: resize of a global: %s\n%P\n", s->name, p);
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P\n", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P\n", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P\n", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P\n", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+	
+	case ADATA:
+		if(p->from.sym == S) {
+			diag("DATA without a sym\n%P\n", p);
+			break;
+		}
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P\n", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P\n", p);
+			errorexit();
+		}
+		autosize = p->to.offset;
+		if(autosize & 7) {
+			diag("stack frame not 8 multiple: %s\n%P\n", s->name, p);
+			autosize = autosize + 7 & ~7;
+			p->to.offset = autosize;
+		}
+		autosize += 8;
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P\n", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->cond = p;
+		etextp = p;
+		break;
+
+	case ASUB:
+	case ASUBU:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE) {
+			p->from.offset = -p->from.offset;
+			if(p->as == ASUB)
+				p->as = AADD;
+			else
+				p->as = AADDU;
+		}
+		goto casedef;
+
+	case AMOVF:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVD:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->h, p->from.ieee->l);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVV:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_VCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->h, p->from.ieee->l);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s\n", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory\n");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADDU;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+	s2 = lookup("_profin", 0);
+	s4 = lookup("_profout", 0);
+	if(s2->type != STEXT || s4->type != STEXT) {
+		diag("_profin/_profout not defined\n");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				ps2 = p;
+				p->reg = 1;
+			}
+			if(p->from.sym == s4) {
+				ps4 = p;
+				p->reg = 1;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->reg & NOPROF) {
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JAL	profin, R2
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AJAL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JAL	profout
+			 */
+			p->as = AJAL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		fnuxi4[i] = c;
+		fnuxi8[i] = c;
+		fnuxi8[i+4] = c+4;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+vlong
+ieeedtov(Ieee *ieeep)
+{
+	vlong v;
+
+	v = (vlong)ieeep->l & (vlong)0xffffffff;
+	v |= (vlong)ieeep->h << 32;
+	return v;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow\n");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
--- /dev/null
+++ b/utils/0l/optab.c
@@ -1,0 +1,221 @@
+#include	"l.h"
+
+#define	X	99
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		12, 8, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_REG,		13, 4, 0 },
+	{ AMOVWU,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+
+	{ ASUB,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ ASUB,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+
+	{ ASLL,		C_REG,	C_NONE,	C_REG,		 9, 4, 0 },
+	{ ASLL,		C_REG,	C_REG,	C_REG,		 9, 4, 0 },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		32, 4, 0 },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		32, 4, 0 },
+	{ ACMPEQF,	C_FREG,	C_REG,	C_NONE,		32, 4, 0 },
+	{ AABSF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVD,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVV,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVBU,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVWL,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVV,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVBU,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVWL,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVV,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVBU,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVWL,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVV,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVBU,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVV,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVBU,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVV,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVBU,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+
+	{ AMOVW,	C_SECON,C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVW,	C_SACON,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		26, 12, REGSB },
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		26, 12, REGSP },
+	{ AMOVW,	C_ADDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVV,	C_ADDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVW,	C_ANDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVV,	C_ANDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+
+	{ AMOVW,	C_UCON, C_NONE, C_REG,		24, 4, 0 },
+	{ AMOVV,	C_UCON, C_NONE, C_REG,		24, 4, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		19, 8, 0 },
+	{ AMOVV,	C_LCON,	C_NONE,	C_REG,		19, 8, 0 },
+
+	{ AMOVW,	C_HI,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVV,	C_HI,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVW,	C_LO,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVV,	C_LO,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_HI,		21, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_HI,		21, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_LO,		21, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_LO,		21, 4, 0 },
+
+	{ AMUL,		C_REG,	C_REG,	C_NONE,		22, 4, 0 },
+
+	{ AADD,		C_ADD0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ADD0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ANDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AADD,		C_ANDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AAND,		C_AND0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AAND,		C_AND0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AAND,		C_ADDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AAND,		C_ADDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AADD,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AADD,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+
+	{ ASLL,		C_SCON,	C_REG,	C_REG,		16, 4, 0 },
+	{ ASLL,		C_SCON,	C_NONE,	C_REG,		16, 4, 0 },
+
+	{ ASYSCALL,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ ABEQ,		C_REG,	C_REG,	C_SBRA,		 6, 4, 0 },
+	{ ABEQ,		C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABLEZ,	C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABFPT,	C_NONE,	C_NONE,	C_SBRA,		 6, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+	{ AJAL,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGZERO },
+	{ AJAL,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGLINK },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVF,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVD,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVF,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVD,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVF,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVD,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVF,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVD,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVF,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVD,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVF,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVD,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FREG,		30, 4, 0 },
+	{ AMOVW,	C_FREG,	C_NONE,	C_REG,		31, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FREG,		47, 4, 0 },
+	{ AMOVV,	C_FREG,	C_NONE,	C_REG,		48, 4, 0 },
+
+	{ AMOVW,	C_ADDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_ANDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_UCON, C_NONE, C_FREG,		35, 8, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_FREG,		36, 12, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_MREG,		37, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_MREG,		37, 4, 0 },
+	{ AMOVW,	C_MREG,	C_NONE,	C_REG,		38, 4, 0 },
+	{ AMOVV,	C_MREG,	C_NONE,	C_REG,		38, 4, 0 },
+
+	{ ARFE,		C_NONE,	C_NONE,	C_ZOREG,	39, 8, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_LCON,		40, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCREG,	41, 8, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FCREG,	41, 8, 0 },
+	{ AMOVW,	C_FCREG,C_NONE,	C_REG,		42, 4, 0 },
+	{ AMOVV,	C_FCREG,C_NONE,	C_REG,		42, 4, 0 },
+
+	{ ABREAK,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },	/* really CACHE instruction */
+	{ ABREAK,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ ABREAK,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ ABREAK,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/0l/pass.c
@@ -1,0 +1,498 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p, *p1;
+	long orig, orig1, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P\n",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P\n",
+				s->value, s->name, p);
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on R30)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size\n", s->name);
+			v = 1;
+		}
+		while(v & 7)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+	orig1 = orig;
+
+	/*
+	 * pass 2
+	 *	assign 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		while(v & 7)
+			v++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		while(v & 7)
+			v++;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	/*
+	 * pass 4
+	 *	add literals to all large values.
+	 *	at this time:
+	 *		small data is allocated DATA
+	 *		large data is allocated DATA1
+	 *		large bss is allocated BSS
+	 *	the new literals are loaded between
+	 *	small data and large data.
+	 */
+	orig = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as != AMOVW)
+			continue;
+		if(p->from.type != D_CONST)
+			continue;
+		if(s = p->from.sym) {
+			t = s->type;
+			if(t != SDATA && t != SDATA1 && t != SBSS)
+				continue;
+			t = p->from.name;
+			if(t != D_EXTERN && t != D_STATIC)
+				continue;
+			v = s->value + p->from.offset;
+			if(v >= 0 && v <= 0xffff)
+				continue;
+			if(!strcmp(s->name, "setR30"))
+				continue;
+			/* size should be 19 max */
+			if(strlen(s->name) >= 10)	/* has loader address */ 
+				sprint(literal, "$%lux.%lux", (long)s, p->from.offset);
+			else
+				sprint(literal, "$%s.%d.%lux", s->name, s->version, p->from.offset);
+		} else {
+			if(p->from.name != D_NONE)
+				continue;
+			if(p->from.reg != NREG)
+				continue;
+			v = p->from.offset;
+			if(v >= -0x7fff && v <= 0xffff)
+				continue;
+			if(!(v & 0xffff))
+				continue;
+			/* size should be 9 max */
+			sprint(literal, "$%lux", v);
+		}
+		s = lookup(literal, 0);
+		if(s->type == 0) {
+			s->type = SDATA;
+			s->value = orig1+orig;
+			orig += 8;
+			p1 = prg();
+			p1->line = p->line;
+			p1->as = ADATA;
+			p1->from.type = D_OREG;
+			p1->from.sym = s;
+			p1->from.name = D_EXTERN;
+			p1->reg = 4;
+			p1->to = p->from;
+			p1->link = datap;
+			datap = p1;
+		}
+		if(s->type != SDATA)
+			diag("literal not data: %s", s->name);
+		p->from.type = D_OREG;
+		p->from.sym = s;
+		p->from.name = D_EXTERN;
+		p->from.offset = 0;
+		nocache(p);
+		continue;
+	}
+	while(orig & 7)
+		orig++;
+	/*
+	 * pass 5
+	 *	re-adjust offsets
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t == SBSS) {
+			s->value += orig;
+			continue;
+		}
+		if(t == SDATA1) {
+			s->type = SDATA;
+			s->value += orig;
+			continue;
+		}
+	}
+	datsize += orig;
+	xdefine("setR30", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined\n", s->name);
+}
+
+void
+follow(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AJMP) {
+		q = p->cond;
+		if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
+			p->mark |= FOLL;
+			lastp->link = p;
+			lastp = p;
+			p = p->link;
+			xfol(p);
+			p = q;
+			if(p && !(p->mark & FOLL))
+				goto loop;
+			return;
+		}
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp || (q->mark&NOSCHED))
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AJMP || a == ARET || a == ARFE)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			if(a != ABEQ && a != ABNE)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AJMP || a == ARET || a == ARFE)
+					return;
+				r->as = ABNE;
+				if(a == ABNE)
+					r->as = ABEQ;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+		a = AJMP;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AJMP || a == ARET || a == ARFE){
+		if(p->mark & NOSCHED){
+			p = p->link;
+			goto loop;
+		}
+		return;
+	}
+	if(p->cond != P)
+	if(a != AJAL && p->link != P) {
+		xfol(p->link);
+		p = p->cond;
+		if(p == P || (p->mark&FOLL))
+			return;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == AJAL || a == AJMP || a == ARET) &&
+		   p->to.type != D_BRANCH && p->to.sym != S) {
+			s = p->to.sym;
+			if(s->type != STEXT) {
+				diag("undefined: %s\n%P\n", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+			}
+			p->to.offset = s->value;
+			p->to.type = D_BRANCH;
+		}
+		if(p->to.type != D_BRANCH)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P\n", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->cond != P) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AJMP || (p->mark&NOSCHED))
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/utils/0l/sched.c
@@ -1,0 +1,709 @@
+#include	"l.h"
+
+enum
+{
+	E_HILO	= 1<<0,
+	E_FCR	= 1<<1,
+	E_MCR	= 1<<2,
+	E_MEM	= 1<<3,
+	E_MEMSP	= 1<<4,	/* uses offset and size */
+	E_MEMSB	= 1<<5,	/* uses offset and size */
+	ANYMEM	= E_MEM|E_MEMSP|E_MEMSB,
+	DELAY	= BRANCH|LOAD|FCMP,
+};
+
+typedef	struct	Sch	Sch;
+typedef	struct	Dep	Dep;
+
+struct	Dep
+{
+	ulong	ireg;
+	ulong	freg;
+	ulong	cc;
+};
+struct	Sch
+{
+	Prog	p;
+	Dep	set;
+	Dep	used;
+	long	soffset;
+	char	size;
+	char	nop;
+	char	comp;
+};
+
+void	markregused(Sch*, Prog*);
+int	depend(Sch*, Sch*);
+int	conflict(Sch*, Sch*);
+int	offoverlap(Sch*, Sch*);
+void	dumpbits(Sch*, Dep*);
+
+void
+sched(Prog *p0, Prog *pe)
+{
+	Prog *p, *q;
+	Sch sch[NSCHED], *s, *t, *u, *se, stmp;
+
+	/*
+	 * build side structure
+	 */
+	s = sch;
+	for(p=p0;; p=p->link) {
+		memset(s, 0, sizeof(*s));
+		s->p = *p;
+		markregused(s, p);
+		if(debug['X']) {
+			Bprint(&bso, "%P\t\tset", &s->p);
+			dumpbits(s, &s->set);
+			Bprint(&bso, "; used");
+			dumpbits(s, &s->used);
+			if(s->comp)
+				Bprint(&bso, "; compound");
+			if(s->p.mark & LOAD)
+				Bprint(&bso, "; load");
+			if(s->p.mark & BRANCH)
+				Bprint(&bso, "; branch");
+			if(s->p.mark & FCMP)
+				Bprint(&bso, "; fcmp");
+			Bprint(&bso, "\n");
+		}
+		if(p == pe)
+			break;
+		s++;
+	}
+	se = s;
+
+	/*
+	 * prepass to move things around
+	 * does nothing, but tries to make
+	 * the actual scheduler work better
+	 */
+	for(s=sch; s<=se; s++) {
+		if(!(s->p.mark & LOAD))
+			continue;
+		/* always good to put nonconflict loads together */
+		for(t=s+1; t<=se; t++) {
+			if(!(t->p.mark & LOAD))
+				continue;
+			if(t->p.mark & BRANCH)
+				break;
+			if(conflict(s, t))
+				break;
+			for(u=t-1; u>s; u--)
+				if(depend(u, t))
+					goto no11;
+			u = s+1;
+			stmp = *t;
+			memmove(s+2, u, (uchar*)t - (uchar*)u);
+			*u = stmp;
+			break;
+		}
+	no11:
+
+		/* put schedule fodder above load */
+		for(t=s+1; t<=se; t++) {
+			if(t->p.mark & BRANCH)
+				break;
+			if(s > sch && conflict(s-1, t))
+				continue;
+			for(u=t-1; u>=s; u--)
+				if(depend(t, u))
+					goto no1;
+			stmp = *t;
+			memmove(s+1, s, (uchar*)t - (uchar*)s);
+			*s = stmp;
+			if(!(s->p.mark & LOAD))
+				break;
+		no1:;
+		}
+	}
+
+	for(s=se; s>=sch; s--) {
+		if(!(s->p.mark & DELAY))
+			continue;
+		if(s < se)
+			if(!conflict(s, s+1))
+				goto out3;
+		/*
+		 * s is load, s+1 is immediate use of result or end of block
+		 * t is the trial instruction to insert between s and s+1
+		 */
+		if(!debug['Y'])
+		for(t=s-1; t>=sch; t--) {
+			if(t->comp)
+				if(s->p.mark & BRANCH)
+					goto no2;
+			if(t->p.mark & DELAY)
+				if(s >= se || conflict(t, s+1))
+					goto no2;
+			for(u=t+1; u<=s; u++)
+				if(depend(u, t))
+					goto no2;
+			goto out2;
+		no2:;
+		}
+		if(debug['X'])
+			Bprint(&bso, "?l%P\n", &s->p);
+		if(s->p.mark & BRANCH)
+			s->nop = 1;
+		if(debug['v']) {
+			if(s->p.mark & LOAD) {
+				nop.load.count++;
+				nop.load.outof++;
+			}
+			if(s->p.mark & BRANCH) {
+				nop.branch.count++;
+				nop.branch.outof++;
+			}
+			if(s->p.mark & FCMP) {
+				nop.fcmp.count++;
+				nop.fcmp.outof++;
+			}
+		}
+		continue;
+
+	out2:
+		if(debug['X']) {
+			Bprint(&bso, "!l%P\n", &t->p);
+			Bprint(&bso, "%P\n", &s->p);
+		}
+		stmp = *t;
+		memmove(t, t+1, (uchar*)s - (uchar*)t);
+		*s = stmp;
+		s--;
+
+	out3:
+		if(debug['v']) {
+			if(s->p.mark & LOAD)
+				nop.load.outof++;
+			if(s->p.mark & BRANCH)
+				nop.branch.outof++;
+			if(s->p.mark & FCMP)
+				nop.fcmp.outof++;
+		}
+	}
+
+	/* Avoid HI/LO use->set */
+	t = sch+1;
+	for(s=sch; s<se-1; s++, t++) {
+		if((s->used.cc & E_HILO) == 0)
+			continue;
+		if(t->set.cc & E_HILO)
+			s->nop = 2;
+	}
+
+	/*
+	 * put it all back
+	 */
+	for(s=sch, p=p0; s<=se; s++, p=q) {
+		q = p->link;
+		if(q != s->p.link) {
+			*p = s->p;
+			p->link = q;
+		}
+		while(s->nop--)
+			addnop(p);
+	}
+	if(debug['X']) {
+		Bprint(&bso, "\n");
+		Bflush(&bso);
+	}
+}
+
+void
+markregused(Sch *s, Prog *realp)
+{
+	int c, ar, ad, ld, sz;
+	ulong m;
+	Prog *p;
+
+	p = &s->p;
+	s->comp = compound(p);
+	s->nop = 0;
+	if(s->comp) {
+		s->set.ireg |= 1<<REGTMP;
+		s->used.ireg |= 1<<REGTMP;
+	}
+
+	ar = 0;		/* dest is really reference */
+	ad = 0;		/* source/dest is really address */
+	ld = 0;		/* opcode is load instruction */
+	sz = 20;	/* size of load/store for overlap computation */
+
+/*
+ * flags based on opcode
+ */
+	switch(p->as) {
+	case ATEXT:
+		curtext = realp;
+		autosize = p->to.offset + 8;
+		ad = 1;
+		break;
+	case AJAL:
+		c = p->reg;
+		if(c == NREG)
+			c = REGLINK;
+		s->set.ireg |= 1<<c;
+		ar = 1;
+		ad = 1;
+		break;
+	case ABGEZAL:
+	case ABLTZAL:
+		s->set.ireg |= 1<<REGLINK;
+	case ABEQ:
+	case ABGEZ:
+	case ABGTZ:
+	case ABLEZ:
+	case ABLTZ:
+	case ABNE:
+		ar = 1;
+		ad = 1;
+		break;
+	case ABFPT:
+	case ABFPF:
+		ad = 1;
+		s->used.cc |= E_FCR;
+		break;
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+		ar = 1;
+		s->set.cc |= E_FCR;
+		p->mark |= FCMP;
+		break;
+	case AJMP:
+		ar = 1;
+		ad = 1;
+		break;
+	case AMOVB:
+	case AMOVBU:
+		sz = 1;
+		ld = 1;
+		break;
+	case AMOVH:
+	case AMOVHU:
+		sz = 2;
+		ld = 1;
+		break;
+	case AMOVF:
+	case AMOVW:
+	case AMOVWL:
+	case AMOVWR:
+		sz = 4;
+		ld = 1;
+		break;
+	case AMOVD:
+	case AMOVV:
+	case AMOVVL:
+	case AMOVVR:
+		sz = 8;
+		ld = 1;
+		break;
+	case ADIV:
+	case ADIVU:
+	case AMUL:
+	case AMULU:
+	case AREM:
+	case AREMU:
+	case ADIVV:
+	case ADIVVU:
+	case AMULV:
+	case AMULVU:
+	case AREMV:
+	case AREMVU:
+		s->set.cc = E_HILO;
+	case AADD:
+	case AADDU:
+	case AADDV:
+	case AADDVU:
+	case AAND:
+	case ANOR:
+	case AOR:
+	case ASGT:
+	case ASGTU:
+	case ASLL:
+	case ASRA:
+	case ASRL:
+	case ASLLV:
+	case ASRAV:
+	case ASRLV:
+	case ASUB:
+	case ASUBU:
+	case ASUBV:
+	case ASUBVU:
+	case AXOR:
+
+	case AADDD:
+	case AADDF:
+	case AADDW:
+	case ASUBD:
+	case ASUBF:
+	case ASUBW:
+	case AMULF:
+	case AMULD:
+	case AMULW:
+	case ADIVF:
+	case ADIVD:
+	case ADIVW:
+		if(p->reg == NREG) {
+			if(p->to.type == D_REG || p->to.type == D_FREG)
+				p->reg = p->to.reg;
+			if(p->reg == NREG)
+				print("botch %P\n", p);
+		}
+		break;
+	}
+
+/*
+ * flags based on 'to' field
+ */
+	c = p->to.class;
+	if(c == 0) {
+		c = aclass(&p->to) + 1;
+		p->to.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->to);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_ADD0CON:
+	case C_AND0CON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+
+	case C_HI:
+	case C_LO:
+		s->set.cc |= E_HILO;
+		break;
+	case C_FCREG:
+		s->set.cc |= E_FCR;
+		break;
+	case C_MREG:
+		s->set.cc |= E_MCR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->to.reg;
+		s->used.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		if(ar)
+			s->used.cc |= m;
+		else
+			s->set.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		if(ar)
+			s->used.ireg |= 1<<p->to.reg;
+		else
+			s->set.ireg |= 1<<p->to.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		if(ar) {
+			s->used.freg |= 1<<p->to.reg;
+			s->used.freg |= 1<<(p->to.reg|1);
+		} else {
+			s->set.freg |= 1<<p->to.reg;
+			s->set.freg |= 1<<(p->to.reg|1);
+		}
+		if(ld && p->from.type == D_REG)
+			p->mark |= LOAD;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSP;
+		else
+			s->set.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSB;
+		else
+			s->set.cc |= E_MEMSB;
+		break;
+	}
+
+/*
+ * flags based on 'from' field
+ */
+	c = p->from.class;
+	if(c == 0) {
+		c = aclass(&p->from) + 1;
+		p->from.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->from);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_ADD0CON:
+	case C_AND0CON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+	case C_HI:
+	case C_LO:
+		s->used.cc |= E_HILO;
+		break;
+	case C_FCREG:
+		s->used.cc |= E_FCR;
+		break;
+	case C_MREG:
+		s->used.cc |= E_MCR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->from.reg;
+		s->used.ireg |= 1<<c;
+		if(ld)
+			p->mark |= LOAD;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		s->used.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		s->used.ireg |= 1<<p->from.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		s->used.freg |= 1<<p->from.reg;
+		s->used.freg |= 1<<(p->from.reg|1);
+		if(ld && p->to.type == D_REG)
+			p->mark |= LOAD;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSB;
+		break;
+	}
+
+	c = p->reg;
+	if(c != NREG) {
+		if(p->from.type == D_FREG || p->to.type == D_FREG) {
+			s->used.freg |= 1<<c;
+			s->used.freg |= 1<<(c|1);
+		} else
+			s->used.ireg |= 1<<c;
+	}
+	s->set.ireg &= ~(1<<REGZERO);		/* R0 cant be set */
+}
+
+/*
+ * test to see if 2 instrictions can be
+ * interchanged without changing semantics
+ */
+int
+depend(Sch *sa, Sch *sb)
+{
+	ulong x;
+
+	if(sa->set.ireg & (sb->set.ireg|sb->used.ireg))
+		return 1;
+	if(sb->set.ireg & sa->used.ireg)
+		return 1;
+
+	if(sa->set.freg & (sb->set.freg|sb->used.freg))
+		return 1;
+	if(sb->set.freg & sa->used.freg)
+		return 1;
+
+	/*
+	 * special case.
+	 * loads from same address cannot pass.
+	 * this is for hardware fifo's and the like
+	 */
+	if(sa->used.cc & sb->used.cc & E_MEM)
+		if(sa->p.reg == sb->p.reg)
+		if(regoff(&sa->p.from) == regoff(&sb->p.from))
+			return 1;
+
+	x = (sa->set.cc & (sb->set.cc|sb->used.cc)) |
+		(sb->set.cc & sa->used.cc);
+	if(x) {
+		/*
+		 * allow SB and SP to pass each other.
+		 * allow SB to pass SB iff doffsets are ok
+		 * anything else conflicts
+		 */
+		if(x != E_MEMSP && x != E_MEMSB)
+			return 1;
+		x = sa->set.cc | sb->set.cc |
+			sa->used.cc | sb->used.cc;
+		if(x & E_MEM)
+			return 1;
+		if(offoverlap(sa, sb))
+			return 1;
+	}
+
+	return 0; 
+}
+
+int
+offoverlap(Sch *sa, Sch *sb)
+{
+
+	if(sa->soffset < sb->soffset) {
+		if(sa->soffset+sa->size > sb->soffset)
+			return 1;
+		return 0;
+	}
+	if(sb->soffset+sb->size > sa->soffset)
+		return 1;
+	return 0;
+}
+
+/*
+ * test 2 adjacent instructions
+ * and find out if inserted instructions
+ * are desired to prevent stalls.
+ */
+int
+conflict(Sch *sa, Sch *sb)
+{
+
+	if(sa->set.ireg & sb->used.ireg)
+		return 1;
+	if(sa->set.freg & sb->used.freg)
+		return 1;
+	if(sa->set.cc & sb->used.cc)
+		return 1;
+
+	return 0;
+}
+
+int
+compound(Prog *p)
+{
+	Optab *o;
+
+	o = oplook(p);
+	if(o->size != 4)
+		return 1;
+	if(p->to.type == D_REG && p->to.reg == REGSB)
+		return 1;
+	return 0;
+}
+
+void
+dumpbits(Sch *s, Dep *d)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(d->ireg & (1<<i))
+			Bprint(&bso, " R%d", i);
+	for(i=0; i<32; i++)
+		if(d->freg & (1<<i))
+			Bprint(&bso, " F%d", i);
+	for(i=0; i<32; i++)
+		switch(d->cc & (1<<i)) {
+		default:
+			break;
+		case E_HILO:
+			Bprint(&bso, " HILO");
+			break;
+		case E_FCR:
+			Bprint(&bso, " FCR");
+			break;
+		case E_MCR:
+			Bprint(&bso, " MCR");
+			break;
+		case E_MEM:
+			Bprint(&bso, " MEM%d", s->size);
+			break;
+		case E_MEMSB:
+			Bprint(&bso, " SB%d", s->size);
+			break;
+		case E_MEMSP:
+			Bprint(&bso, " SP%d", s->size);
+			break;
+		}
+}
--- /dev/null
+++ b/utils/0l/span.c
@@ -1,0 +1,628 @@
+#include	"l.h"
+
+void
+span(void)
+{
+	Prog *p, *q;
+	Sym *setext;
+	Optab *o;
+	int m, bflag;
+	long c, otxt;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	otxt = c;
+	for(p = firstp; p != P; p = p->link) {
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 8;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches */
+				if(c-otxt >= 1L<<17)
+					bflag = 1;
+				otxt = c;
+				continue;
+			}
+			diag("zero-width instruction\n%P\n", p);
+			continue;
+		}
+		c += m;
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			p->pc = c;
+			o = oplook(p);
+			if(o->type == 6 && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < 0)
+					otxt = -otxt;
+				if(otxt >= (1L<<17) - 10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					addnop(p->link);
+					addnop(p);
+					bflag = 1;
+				}
+			}
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 8;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				diag("zero-width instruction\n%P\n", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+		
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_FCREG:
+		return C_FCREG;
+
+	case D_MREG:
+		return C_MREG;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s\n",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			instoffset = a->sym->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SEXT;
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 8L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			if(instoffset == 0)
+				return C_ZOREG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SOREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_HI:
+		return C_LO;
+	case D_LO:
+		return C_HI;
+
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s\n",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF)
+				instoffset = s->value + a->offset;
+			return C_LCON;
+		}
+		return C_GOK;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+		consize:
+			if(instoffset > 0) {
+				if(instoffset <= 0x7fff)
+					return C_SCON;
+				if(instoffset <= 0xffff)
+					return C_ANDCON;
+				if((instoffset & 0xffff) == 0)
+					return C_UCON;
+				return C_LCON;
+			}
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x8000)
+				return C_ADDCON;
+			if((instoffset & 0xffff) == 0)
+				return C_UCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s\n",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SCONST:
+				instoffset = s->value + a->offset;
+				goto consize;
+			case STEXT:
+			case SLEAF:
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			instoffset = s->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG && instoffset != 0L)
+				return C_SECON;
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 8L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r, t;
+	char *c1, *c3;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0) {
+		t = opcross[repop[r]][a1][a2][a3];
+		if(t) {
+			p->optab = t+1;
+			return optab+t;
+		}
+		o = oprange[r].stop; /* just generate an error */
+	}
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, a1, a2, a3);
+	if(!debug['a'])
+		prasm(p);
+	o = optab;
+	p->optab = (o-optab)+1;
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_ZCON || b == C_SCON || b == C_UCON ||
+		   b == C_ADDCON || b == C_ANDCON)
+			return 1;
+		break;
+	case C_ADD0CON:
+		if(b == C_ADDCON)
+			return 1;
+	case C_ADDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_AND0CON:
+		if(b == C_ANDCON)
+			return 1;
+	case C_ANDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_UCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_SCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_SACON)
+			return 1;
+		break;
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_LEXT:
+		if(b == C_SEXT)
+			return 1;
+		break;
+	case C_LAUTO:
+		if(b == C_SAUTO)
+			return 1;
+		break;
+	case C_REG:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LOREG:
+		if(b == C_ZOREG || b == C_SOREG)
+			return 1;
+		break;
+	case C_SOREG:
+		if(b == C_ZOREG)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	for(i=0; i<32; i++)
+		for(n=0; n<32; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+		
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A\n", r);
+			errorexit();
+		case AABSF:
+			oprange[AMOVFD] = oprange[r];
+			oprange[AMOVDF] = oprange[r];
+			oprange[AMOVWF] = oprange[r];
+			oprange[AMOVFW] = oprange[r];
+			oprange[AMOVWD] = oprange[r];
+			oprange[AMOVDW] = oprange[r];
+			oprange[ANEGF] = oprange[r];
+			oprange[ANEGD] = oprange[r];
+			oprange[AABSD] = oprange[r];
+			oprange[ATRUNCDW] = oprange[r];
+			oprange[ATRUNCFW] = oprange[r];
+			oprange[ATRUNCDV] = oprange[r];
+			oprange[ATRUNCFV] = oprange[r];
+			oprange[AMOVDV] = oprange[r];
+			oprange[AMOVFV] = oprange[r];
+			oprange[AMOVVD] = oprange[r];
+			oprange[AMOVVF] = oprange[r];
+			break;
+		case AADD:
+			buildrep(1, AADD);
+			oprange[ASGT] = oprange[r];
+			repop[ASGT] = 1;
+			oprange[ASGTU] = oprange[r];
+			repop[ASGTU] = 1;
+			oprange[AADDU] = oprange[r];
+			repop[AADDU] = 1;
+			oprange[AADDVU] = oprange[r];
+			repop[AADDVU] = 1;
+			oprange[AADDV] = oprange[r];
+			repop[AADDV] = 1;
+			break;
+		case AADDF:
+			oprange[ADIVF] = oprange[r];
+			oprange[ADIVD] = oprange[r];
+			oprange[AMULF] = oprange[r];
+			oprange[AMULD] = oprange[r];
+			oprange[ASUBF] = oprange[r];
+			oprange[ASUBD] = oprange[r];
+			oprange[AADDD] = oprange[r];
+			break;
+		case AAND:
+			buildrep(2, AAND);
+			oprange[AXOR] = oprange[r];
+			repop[AXOR] = 2;
+			oprange[AOR] = oprange[r];
+			repop[AOR] = 2;
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			break;
+		case ABLEZ:
+			oprange[ABGEZ] = oprange[r];
+			oprange[ABGEZAL] = oprange[r];
+			oprange[ABLTZ] = oprange[r];
+			oprange[ABLTZAL] = oprange[r];
+			oprange[ABGTZ] = oprange[r];
+			break;
+		case AMOVB:
+			buildrep(3, AMOVB);
+			oprange[AMOVH] = oprange[r];
+			repop[AMOVH] = 3;
+			break;
+		case AMOVBU:
+			buildrep(4, AMOVBU);
+			oprange[AMOVHU] = oprange[r];
+			repop[AMOVHU] = 4;
+			break;
+		case AMUL:
+			oprange[AREM] = oprange[r];
+			oprange[AREMU] = oprange[r];
+			oprange[ADIVU] = oprange[r];
+			oprange[AMULU] = oprange[r];
+			oprange[ADIV] = oprange[r];
+			oprange[ADIVV] = oprange[r];
+			oprange[ADIVVU] = oprange[r];
+			oprange[AMULV] = oprange[r];
+			oprange[AMULVU] = oprange[r];
+			oprange[AREMV] = oprange[r];
+			oprange[AREMVU] = oprange[r];
+			break;
+		case ASLL:
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			oprange[ASLLV] = oprange[r];
+			oprange[ASRAV] = oprange[r];
+			oprange[ASRLV] = oprange[r];
+			break;
+		case ASUB:
+			oprange[ASUBU] = oprange[r];
+			oprange[ASUBV] = oprange[r];
+			oprange[ASUBVU] = oprange[r];
+			oprange[ANOR] = oprange[r];
+			break;
+		case ASYSCALL:
+			oprange[ATLBP] = oprange[r];
+			oprange[ATLBR] = oprange[r];
+			oprange[ATLBWI] = oprange[r];
+			oprange[ATLBWR] = oprange[r];
+			break;
+		case ACMPEQF:
+			oprange[ACMPGTF] = oprange[r];
+			oprange[ACMPGTD] = oprange[r];
+			oprange[ACMPGEF] = oprange[r];
+			oprange[ACMPGED] = oprange[r];
+			oprange[ACMPEQD] = oprange[r];
+			break;
+		case ABFPT:
+			oprange[ABFPF] = oprange[r];
+			break;
+		case AMOVWL:
+			oprange[AMOVWR] = oprange[r];
+			oprange[AMOVVR] = oprange[r];
+			oprange[AMOVVL] = oprange[r];
+			break;
+		case AMOVW:
+			buildrep(5, AMOVW);
+			break;
+		case AMOVD:
+			buildrep(6, AMOVD);
+			break;
+		case AMOVF:
+			buildrep(7, AMOVF);
+			break;
+		case AMOVV:
+			buildrep(8, AMOVV);
+			break;
+		case ABREAK:
+		case AWORD:
+		case ARFE:
+		case AJAL:
+		case AJMP:
+		case ATEXT:
+		case ACASE:
+		case ABCASE:
+		case AMOVWU:
+			break;
+		}
+	}
+}
+
+void
+buildrep(int x, int as)
+{
+	Opcross *p;
+	Optab *e, *s, *o;
+	int a1, a2, a3, n;
+
+	if(C_NONE != 0 ||
+	   C_REG != 1 ||
+	   C_GOK >= 32 ||
+	   x >= nelem(opcross)) {
+		diag("assumptions fail in buildrep");
+		errorexit();
+	}
+	repop[as] = x;
+	p = (opcross + x);
+	s = oprange[as].start;
+	e = oprange[as].stop;
+	for(o=e-1; o>=s; o--) {
+		n = o-optab;
+		for(a2=0; a2<2; a2++) {
+			if(a2) {
+				if(o->a2 == C_NONE)
+					continue;
+			} else
+				if(o->a2 != C_NONE)
+					continue;
+			for(a1=0; a1<32; a1++) {
+				if(!xcmp[a1][o->a1])
+					continue;
+				for(a3=0; a3<32; a3++)
+					if(xcmp[a3][o->a3])
+						(*p)[a1][a2][a3] = n;
+			}
+		}
+	}
+	oprange[as].start = 0;
+}
--- /dev/null
+++ b/utils/5a/a.h
@@ -1,0 +1,182 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../5c/5.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		8192
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+EXTERN	struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN	struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym*	sym;
+	long	offset;
+	short	type;
+	short	reg;
+	short	name;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+
+	Always	= 14,
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	alloc(long);
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+int	isreg(Gen*);
+void	outcode(int, int, Gen*, int, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	maclin(void);
+void	macprag(void);
+void	macif(int);
+void	macend(void);
+void	outhist(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+int	myfork(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/5a/a.y
@@ -1,0 +1,673 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	long	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token	<lval>	LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token	<lval>	LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token	<lval>	LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token	<lval>	LTYPEL LTYPEM LTYPEN LTYPEBX
+%token	<lval>	LCONST LSP LSB LFP LPC
+%token	<lval>	LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR
+%token	<lval>	LCOND LS LAT
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr oexpr pointer offset sreg spreg creg
+%type	<lval>	rcon cond reglist
+%type	<gen>	gen rel reg regreg freg shift fcon frcon
+%type	<gen>	imm ximm name oreg ireg nireg ioreg imsr
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * ADD
+ */
+	LTYPE1 cond imsr ',' spreg ',' reg
+	{
+		outcode($1, $2, &$3, $5, &$7);
+	}
+|	LTYPE1 cond imsr ',' spreg ','
+	{
+		outcode($1, $2, &$3, $5, &nullgen);
+	}
+|	LTYPE1 cond imsr ',' reg
+	{
+		outcode($1, $2, &$3, NREG, &$5);
+	}
+/*
+ * MVN
+ */
+|	LTYPE2 cond imsr ',' reg
+	{
+		outcode($1, $2, &$3, NREG, &$5);
+	}
+/*
+ * MOVW
+ */
+|	LTYPE3 cond gen ',' gen
+	{
+		outcode($1, $2, &$3, NREG, &$5);
+	}
+/*
+ * B/BL
+ */
+|	LTYPE4 cond comma rel
+	{
+		outcode($1, $2, &nullgen, NREG, &$4);
+	}
+|	LTYPE4 cond comma nireg
+	{
+		outcode($1, $2, &nullgen, NREG, &$4);
+	}
+/*
+ * BX
+ */
+|	LTYPEBX comma ireg
+	{
+		outcode($1, Always, &nullgen, NREG, &$3);
+	}
+/*
+ * BEQ
+ */
+|	LTYPE5 comma rel
+	{
+		outcode($1, Always, &nullgen, NREG, &$3);
+	}
+/*
+ * SWI
+ */
+|	LTYPE6 cond comma gen
+	{
+		outcode($1, $2, &nullgen, NREG, &$4);
+	}
+/*
+ * CMP
+ */
+|	LTYPE7 cond imsr ',' spreg comma
+	{
+		outcode($1, $2, &$3, $5, &nullgen);
+	}
+/*
+ * MOVM
+ */
+|	LTYPE8 cond ioreg ',' '[' reglist ']'
+	{
+		Gen g;
+
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $6;
+		outcode($1, $2, &$3, NREG, &g);
+	}
+|	LTYPE8 cond '[' reglist ']' ',' ioreg
+	{
+		Gen g;
+
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $4;
+		outcode($1, $2, &g, NREG, &$7);
+	}
+/*
+ * SWAP
+ */
+|	LTYPE9 cond reg ',' ireg ',' reg
+	{
+		outcode($1, $2, &$5, $3.reg, &$7);
+	}
+|	LTYPE9 cond reg ',' ireg comma
+	{
+		outcode($1, $2, &$5, $3.reg, &$3);
+	}
+|	LTYPE9 cond comma ireg ',' reg
+	{
+		outcode($1, $2, &$4, $6.reg, &$6);
+	}
+/*
+ * RET
+ */
+|	LTYPEA cond comma
+	{
+		outcode($1, $2, &nullgen, NREG, &nullgen);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTYPEB name ',' imm
+	{
+		outcode($1, Always, &$2, NREG, &$4);
+	}
+|	LTYPEB name ',' con ',' imm
+	{
+		outcode($1, Always, &$2, $4, &$6);
+	}
+/*
+ * DATA
+ */
+|	LTYPEC name '/' con ',' ximm
+	{
+		outcode($1, Always, &$2, $4, &$6);
+	}
+/*
+ * CASE
+ */
+|	LTYPED cond reg comma
+	{
+		outcode($1, $2, &$3, NREG, &nullgen);
+	}
+/*
+ * word
+ */
+|	LTYPEH comma ximm
+	{
+		outcode($1, Always, &nullgen, NREG, &$3);
+	}
+/*
+ * floating-point coprocessor
+ */
+|	LTYPEI cond freg ',' freg
+	{
+		outcode($1, $2, &$3, NREG, &$5);
+	}
+|	LTYPEK cond frcon ',' freg
+	{
+		outcode($1, $2, &$3, NREG, &$5);
+	}
+|	LTYPEK cond frcon ',' LFREG ',' freg
+	{
+		outcode($1, $2, &$3, $5, &$7);
+	}
+|	LTYPEL cond freg ',' freg comma
+	{
+		outcode($1, $2, &$3, $5.reg, &nullgen);
+	}
+/*
+ * MCR MRC
+ */
+|	LTYPEJ cond con ',' expr ',' spreg ',' creg ',' creg oexpr
+	{
+		Gen g;
+
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset =
+			(0xe << 24) |		/* opcode */
+			($1 << 20) |		/* MCR/MRC */
+			($2 << 28) |		/* scond */
+			(($3 & 15) << 8) |	/* coprocessor number */
+			(($5 & 7) << 21) |	/* coprocessor operation */
+			(($7 & 15) << 12) |	/* arm register */
+			(($9 & 15) << 16) |	/* Crn */
+			(($11 & 15) << 0) |	/* Crm */
+			(($12 & 7) << 5) |	/* coprocessor information */
+			(1<<4);			/* must be set */
+		outcode(AWORD, Always, &nullgen, NREG, &g);
+	}
+/*
+ * MULL hi,lo,r1,r2
+ */
+|	LTYPEM cond reg ',' reg ',' regreg
+	{
+		outcode($1, $2, &$3, $5.reg, &$7);
+	}
+/*
+ * MULA hi,lo,r1,r2
+ */
+|	LTYPEN cond reg ',' reg ',' reg ',' spreg 
+	{
+		$7.type = D_REGREG;
+		$7.offset = $9;
+		outcode($1, $2, &$3, $5.reg, &$7);
+	}
+/*
+ * END
+ */
+|	LTYPEE comma
+	{
+		outcode($1, Always, &nullgen, NREG, &nullgen);
+	}
+
+cond:
+	{
+		$$ = Always;
+	}
+|	cond LCOND
+	{
+		$$ = ($1 & ~C_SCOND) | $2;
+	}
+|	cond LS
+	{
+		$$ = $1 | $2;
+	}
+
+comma:
+|	',' comma
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+ximm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' oreg
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' '*' '$' oreg
+	{
+		$$ = $4;
+		$$.type = D_OCONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	fcon
+
+fcon:
+	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+reglist:
+	spreg
+	{
+		$$ = 1 << $1;
+	}
+|	spreg '-' spreg
+	{
+		int i;
+		$$=0;
+		for(i=$1; i<=$3; i++)
+			$$ |= 1<<i;
+		for(i=$3; i<=$1; i++)
+			$$ |= 1<<i;
+	}
+|	spreg comma reglist
+	{
+		$$ = (1<<$1) | $3;
+	}
+
+gen:
+	reg
+|	ximm
+|	shift
+|	shift '(' spreg ')'
+	{
+		$$ = $1;
+		$$.reg = $3;
+	}
+|	LPSR
+	{
+		$$ = nullgen;
+		$$.type = D_PSR;
+		$$.reg = $1;
+	}
+|	LFCR
+	{
+		$$ = nullgen;
+		$$.type = D_FPCR;
+		$$.reg = $1;
+	}
+|	con
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.offset = $1;
+	}
+|	oreg
+|	freg
+
+nireg:
+	ireg
+|	name
+	{
+		$$ = $1;
+		if($1.name != D_EXTERN && $1.name != D_STATIC) {
+		}
+	}
+
+ireg:
+	'(' spreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+
+ioreg:
+	ireg
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+oreg:
+	name
+|	name '(' sreg ')'
+	{
+		$$ = $1;
+		$$.type = D_OREG;
+		$$.reg = $3;
+	}
+|	ioreg
+
+imsr:
+	reg
+|	imm
+|	shift
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+reg:
+	spreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+regreg:
+	'(' spreg ',' spreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_REGREG;
+		$$.reg = $2;
+		$$.offset = $4;
+	}
+
+shift:
+	spreg '<' '<' rcon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = $1 | $4 | (0 << 5);
+	}
+|	spreg '>' '>' rcon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = $1 | $4 | (1 << 5);
+	}
+|	spreg '-' '>' rcon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = $1 | $4 | (2 << 5);
+	}
+|	spreg LAT '>' rcon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = $1 | $4 | (3 << 5);
+	}
+
+rcon:
+	spreg
+	{
+		if($$ < 0 || $$ >= 16)
+			print("register value out of range\n");
+		$$ = (($1&15) << 8) | (1 << 4);
+	}
+|	con
+	{
+		if($$ < 0 || $$ >= 32)
+			print("shift value out of range\n");
+		$$ = ($1&31) << 7;
+	}
+
+sreg:
+	LREG
+|	LPC
+	{
+		$$ = REGPC;
+	}
+|	LR '(' expr ')'
+	{
+		if($3 < 0 || $3 >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+spreg:
+	sreg
+|	LSP
+	{
+		$$ = REGSP;
+	}
+
+creg:
+	LCREG
+|	LC '(' expr ')'
+	{
+		if($3 < 0 || $3 >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+frcon:
+	freg
+|	fcon
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+oexpr:
+	{
+		$$ = 0;
+	}
+|	',' expr
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/5a/lex.c
@@ -1,0 +1,696 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = '5';
+	thestring = "arm";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	case 't':
+		thechar = 't';
+		thestring = "thumb";
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+
+	"F",		LF,	0,
+
+	"F0",		LFREG,	0,
+	"F1",		LFREG,	1,
+	"F2",		LFREG,	2,
+	"F3",		LFREG,	3,
+	"F4",		LFREG,	4,
+	"F5",		LFREG,	5,
+	"F6",		LFREG,	6,
+	"F7",		LFREG,	7,
+	"F8",		LFREG,	8,
+	"F9",		LFREG,	9,
+	"F10",		LFREG,	10,
+	"F11",		LFREG,	11,
+	"F12",		LFREG,	12,
+	"F13",		LFREG,	13,
+	"F14",		LFREG,	14,
+	"F15",		LFREG,	15,
+
+	"C",		LC,	0,
+
+	"C0",		LCREG,	0,
+	"C1",		LCREG,	1,
+	"C2",		LCREG,	2,
+	"C3",		LCREG,	3,
+	"C4",		LCREG,	4,
+	"C5",		LCREG,	5,
+	"C6",		LCREG,	6,
+	"C7",		LCREG,	7,
+	"C8",		LCREG,	8,
+	"C9",		LCREG,	9,
+	"C10",		LCREG,	10,
+	"C11",		LCREG,	11,
+	"C12",		LCREG,	12,
+	"C13",		LCREG,	13,
+	"C14",		LCREG,	14,
+	"C15",		LCREG,	15,
+
+	"CPSR",		LPSR,	0,
+	"SPSR",		LPSR,	1,
+
+	"FPSR",		LFCR,	0,
+	"FPCR",		LFCR,	1,
+
+	".EQ",		LCOND,	0,
+	".NE",		LCOND,	1,
+	".CS",		LCOND,	2,
+	".HS",		LCOND,	2,
+	".CC",		LCOND,	3,
+	".LO",		LCOND,	3,
+	".MI",		LCOND,	4,
+	".PL",		LCOND,	5,
+	".VS",		LCOND,	6,
+	".VC",		LCOND,	7,
+	".HI",		LCOND,	8,
+	".LS",		LCOND,	9,
+	".GE",		LCOND,	10,
+	".LT",		LCOND,	11,
+	".GT",		LCOND,	12,
+	".LE",		LCOND,	13,
+	".AL",		LCOND,	Always,
+
+	".U",		LS,	C_UBIT,
+	".S",		LS,	C_SBIT,
+	".W",		LS,	C_WBIT,
+	".P",		LS,	C_PBIT,
+	".PW",		LS,	C_WBIT|C_PBIT,
+	".WP",		LS,	C_WBIT|C_PBIT,
+
+	".F",		LS,	C_FBIT,
+
+	".IBW",		LS,	C_WBIT|C_PBIT|C_UBIT,
+	".IAW",		LS,	C_WBIT|C_UBIT,
+	".DBW",		LS,	C_WBIT|C_PBIT,
+	".DAW",		LS,	C_WBIT,
+	".IB",		LS,	C_PBIT|C_UBIT,
+	".IA",		LS,	C_UBIT,
+	".DB",		LS,	C_PBIT,
+	".DA",		LS,	0,
+
+	"@",		LAT,	0,
+
+	"AND",		LTYPE1,	AAND,
+	"EOR",		LTYPE1,	AEOR,
+	"SUB",		LTYPE1,	ASUB,
+	"RSB",		LTYPE1,	ARSB,
+	"ADD",		LTYPE1,	AADD,
+	"ADC",		LTYPE1,	AADC,
+	"SBC",		LTYPE1,	ASBC,
+	"RSC",		LTYPE1,	ARSC,
+	"ORR",		LTYPE1,	AORR,
+	"BIC",		LTYPE1,	ABIC,
+
+	"SLL",		LTYPE1,	ASLL,
+	"SRL",		LTYPE1,	ASRL,
+	"SRA",		LTYPE1,	ASRA,
+
+	"MUL",		LTYPE1, AMUL,
+	"MULA",		LTYPEN, AMULA,
+	"DIV",		LTYPE1,	ADIV,
+	"MOD",		LTYPE1,	AMOD,
+
+	"MULL",		LTYPEM, AMULL,
+	"MULAL",	LTYPEM, AMULAL,
+	"MULLU",	LTYPEM, AMULLU,
+	"MULALU",	LTYPEM, AMULALU,
+
+	"MVN",		LTYPE2, AMVN,	/* op2 ignored */
+
+	"MOVB",		LTYPE3, AMOVB,
+	"MOVBU",	LTYPE3, AMOVBU,
+	"MOVH",		LTYPE3, AMOVH,
+	"MOVHU",	LTYPE3, AMOVHU,
+	"MOVW",		LTYPE3, AMOVW,
+
+	"MOVD",		LTYPE3, AMOVD,
+	"MOVDF",		LTYPE3, AMOVDF,
+	"MOVDW",	LTYPE3, AMOVDW,
+	"MOVF",		LTYPE3, AMOVF,
+	"MOVFD",		LTYPE3, AMOVFD,
+	"MOVFW",		LTYPE3, AMOVFW,
+	"MOVWD",	LTYPE3, AMOVWD,
+	"MOVWF",		LTYPE3, AMOVWF,
+
+	"LDREX",		LTYPE3, ALDREX,
+	"LDREXD",		LTYPE3, ALDREXD,
+	"STREX",		LTYPE9, ASTREX,
+	"STREXD",		LTYPE9, ASTREXD,
+
+/*
+	"ABSF",		LTYPEI, AABSF,
+	"ABSD",		LTYPEI, AABSD,
+	"NEGF",		LTYPEI, ANEGF,
+	"NEGD",		LTYPEI, ANEGD,
+	"SQTF",		LTYPEI,	ASQTF,
+	"SQTD",		LTYPEI,	ASQTD,
+	"RNDF",		LTYPEI,	ARNDF,
+	"RNDD",		LTYPEI,	ARNDD,
+	"URDF",		LTYPEI,	AURDF,
+	"URDD",		LTYPEI,	AURDD,
+	"NRMF",		LTYPEI,	ANRMF,
+	"NRMD",		LTYPEI,	ANRMD,
+*/
+
+	"SQRTF",	LTYPEI, ASQRTF,
+	"SQRTD",	LTYPEI, ASQRTD,
+	"CMPF",		LTYPEL, ACMPF,
+	"CMPD",		LTYPEL, ACMPD,
+	"ADDF",		LTYPEK,	AADDF,
+	"ADDD",		LTYPEK,	AADDD,
+	"SUBF",		LTYPEK,	ASUBF,
+	"SUBD",		LTYPEK,	ASUBD,
+	"MULF",		LTYPEK,	AMULF,
+	"MULD",		LTYPEK,	AMULD,
+	"DIVF",		LTYPEK,	ADIVF,
+	"DIVD",		LTYPEK,	ADIVD,
+
+	"B",		LTYPE4, AB,
+	"BL",		LTYPE4, ABL,
+	"BX",		LTYPEBX,	ABX,
+
+	"BEQ",		LTYPE5,	ABEQ,
+	"BNE",		LTYPE5,	ABNE,
+	"BCS",		LTYPE5,	ABCS,
+	"BHS",		LTYPE5,	ABHS,
+	"BCC",		LTYPE5,	ABCC,
+	"BLO",		LTYPE5,	ABLO,
+	"BMI",		LTYPE5,	ABMI,
+	"BPL",		LTYPE5,	ABPL,
+	"BVS",		LTYPE5,	ABVS,
+	"BVC",		LTYPE5,	ABVC,
+	"BHI",		LTYPE5,	ABHI,
+	"BLS",		LTYPE5,	ABLS,
+	"BGE",		LTYPE5,	ABGE,
+	"BLT",		LTYPE5,	ABLT,
+	"BGT",		LTYPE5,	ABGT,
+	"BLE",		LTYPE5,	ABLE,
+	"BCASE",	LTYPE5,	ABCASE,
+
+	"SWI",		LTYPE6, ASWI,
+
+	"CMP",		LTYPE7,	ACMP,
+	"TST",		LTYPE7,	ATST,
+	"TEQ",		LTYPE7,	ATEQ,
+	"CMN",		LTYPE7,	ACMN,
+
+	"MOVM",		LTYPE8, AMOVM,
+
+	"SWPBU",	LTYPE9, ASWPBU,
+	"SWPW",		LTYPE9, ASWPW,
+
+	"RET",		LTYPEA, ARET,
+	"RFE",		LTYPEA, ARFE,
+
+	"TEXT",		LTYPEB, ATEXT,
+	"GLOBL",	LTYPEB, AGLOBL,
+	"DATA",		LTYPEC, ADATA,
+	"CASE",		LTYPED, ACASE,
+	"END",		LTYPEE, AEND,
+	"WORD",		LTYPEH, AWORD,
+	"NOP",		LTYPEI, ANOP,
+
+	"MCR",		LTYPEJ, 0,
+	"MRC",		LTYPEJ, 1,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(getwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(getwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+	USED(g);
+	return 1;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, Always, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+	case D_FPCR:
+		break;
+
+	case D_REGREG:
+		Bputc(&obuf, a->offset);
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+static int bcode[] =
+{
+	ABEQ,
+	ABNE,
+	ABCS,
+	ABCC,
+	ABMI,
+	ABPL,
+	ABVS,
+	ABVC,
+	ABHI,
+	ABLS,
+	ABGE,
+	ABLT,
+	ABGT,
+	ABLE,
+	AB,
+	ANOP,
+};
+
+void
+outcode(int a, int scond, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	/* hack to make B.NE etc. work: turn it into the corresponding conditional */
+	if(a == AB){
+		a = bcode[scond&0xf];
+		scond = (scond & ~0xf) | Always;
+	}
+
+	if(pass == 1)
+		goto out;
+jackpot:
+	sf = 0;
+	s = g1->sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g1->name;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->name;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, scond);
+	Bputc(&obuf, reg);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, Always);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/5a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=5a
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../5c/5.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/5c/5.out.h
@@ -1,0 +1,213 @@
+#define	NSNAME		8
+#define	NSYM		50
+#define	NREG		16
+
+#define NOPROF		(1<<0)
+#define DUPOK		(1<<1)
+#define	ALLTHUMBS	(1<<2)
+
+#define	REGRET		0
+#define	REGARG		0
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R2 up */
+#define	REGMIN		2
+#define	REGMAX		8
+#define	REGEXT		10
+/* compiler allocates external registers R10 down */
+#define	REGTMP		11
+#define	REGSB		12
+#define	REGSP		13
+#define	REGLINK		14
+#define	REGPC		15
+
+#define	REGTMPT		7	/* used by the loader for thumb code */
+
+#define	NFREG		8
+#define	FREGRET		0
+#define	FREGEXT		7
+#define	FREGTMP		15
+/* compiler allocates register variables F0 up */
+/* compiler allocates external registers F7 down */
+
+enum	as
+{
+	AXXX,
+
+	AAND,
+	AEOR,
+	ASUB,
+	ARSB,
+	AADD,
+	AADC,
+	ASBC,
+	ARSC,
+	ATST,
+	ATEQ,
+	ACMP,
+	ACMN,
+	AORR,
+	ABIC,
+
+	AMVN,
+
+	AB,
+	ABL,
+
+/* 
+ * Do not reorder or fragment the conditional branch 
+ * opcodes, or the predication code will break 
+ */ 
+	ABEQ,
+	ABNE,
+	ABCS,
+	ABHS,
+	ABCC,
+	ABLO,
+	ABMI,
+	ABPL,
+	ABVS,
+	ABVC,
+	ABHI,
+	ABLS,
+	ABGE,
+	ABLT,
+	ABGT,
+	ABLE,
+
+	AMOVWD,
+	AMOVWF,
+	AMOVDW,
+	AMOVFW,
+	AMOVFD,
+	AMOVDF,
+	AMOVF,
+	AMOVD,
+
+	ACMPF,
+	ACMPD,
+	AADDF,
+	AADDD,
+	ASUBF,
+	ASUBD,
+	AMULF,
+	AMULD,
+	ADIVF,
+	ADIVD,
+//	ASQRTF,
+//	ASQRTD,
+
+	ASRL,
+	ASRA,
+	ASLL,
+	AMULU,
+	ADIVU,
+	AMUL,
+	ADIV,
+	AMOD,
+	AMODU,
+
+	AMOVB,
+	AMOVBU,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVM,
+	ASWPBU,
+	ASWPW,
+
+	ANOP,
+	ARFE,
+	ASWI,
+	AMULA,
+
+	ADATA,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	ANAME,
+	ARET,
+	ATEXT,
+	AWORD,
+	ADYNT,
+	AINIT,
+	ABCASE,
+	ACASE,
+
+	AEND,
+
+	AMULL,
+	AMULAL,
+	AMULLU,
+	AMULALU,
+
+	ABX,
+	ABXRET,
+	ADWORD,
+
+	ASIGNAME,
+
+	/* moved here to preserve values of older identifiers */
+	ASQRTF,
+	ASQRTD,
+
+	ALDREX,
+	ASTREX,
+	
+	ALDREXD,
+	ASTREXD,
+
+	ALAST,
+};
+
+/* scond byte */
+#define	C_SCOND	((1<<4)-1)
+#define	C_SBIT	(1<<4)
+#define	C_PBIT	(1<<5)
+#define	C_WBIT	(1<<6)
+#define	C_FBIT	(1<<7)	/* psr flags-only */
+#define	C_UBIT	(1<<7)	/* up bit */
+
+/* type/name */
+#define	D_GOK	0
+#define	D_NONE	1
+
+/* type */
+#define	D_BRANCH	(D_NONE+1)
+#define	D_OREG		(D_NONE+2)
+#define	D_CONST		(D_NONE+7)
+#define	D_FCONST	(D_NONE+8)
+#define	D_SCONST	(D_NONE+9)
+#define	D_PSR		(D_NONE+10)
+#define	D_REG		(D_NONE+12)
+#define	D_FREG		(D_NONE+13)
+#define	D_FILE		(D_NONE+16)
+#define	D_OCONST	(D_NONE+17)
+#define	D_FILE1		(D_NONE+18)
+
+#define	D_SHIFT		(D_NONE+19)
+#define	D_FPCR		(D_NONE+20)
+#define	D_REGREG	(D_NONE+21)
+#define	D_ADDR		(D_NONE+22)
+
+/* name */
+#define	D_EXTERN	(D_NONE+3)
+#define	D_STATIC	(D_NONE+4)
+#define	D_AUTO		(D_NONE+5)
+#define	D_PARAM		(D_NONE+6)
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/5c/cgen.c
@@ -1,0 +1,1168 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+	cgenrel(n, nn, 0);
+}
+
+void
+cgenrel(Node *n, Node *nn, int inrel)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o, t;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesuv[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case ODIV:
+	case OMOD:
+		if(nn != Z)
+		if((t = vlog(r)) >= 0) {
+			/* signed div/mod by constant power of 2 */
+			cgen(l, nn);
+			gopcode(OGE, nodconst(0), nn, Z);
+			p1 = p;
+			if(o == ODIV) {
+				gopcode(OADD, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+				gopcode(OASHR, nodconst(t), Z, nn);
+			} else {
+				gopcode(OSUB, nn, nodconst(0), nn);
+				gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				gopcode(OSUB, nn, nodconst(0), nn);
+				gbranch(OGOTO);
+				patch(p1, pc);
+				p1 = p;
+				gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+			}
+			break;
+		}
+		goto muldiv;
+
+	case OSUB:
+		if(nn != Z)
+		if(l->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(r, nn);
+			gopcode(o, Z, l, nn);
+			break;
+		}
+	case OADD:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+			if(o != OAND)
+				break;
+			if(nn != Z)
+				gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case OMUL:
+	muldiv:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			gopcode(o, &nod, &nod1, &nod);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST)
+		if(!typefd[r->type->etype])
+		if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, r, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+		} else {
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+
+		regalloc(&nod, n, nn);
+		gmove(&nod2, &nod);
+		gopcode(o, &nod1, Z, &nod);
+		gmove(&nod, &nod2);
+		if(nn != Z)
+			gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(o, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		if(REGARG >= 0)
+			o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG >= 0)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+nod.xoffset) > -4096 && v < 4096) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type)) {
+			if(nocast(n->type, nn->type)) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		if(inrel)
+			gmover(&nod, &nod1);
+		else
+			gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		if(nn && l->op == ONAME)	/* in x=++i, emit USED(i) */
+			gins(ANOP, l, Z);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+	return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+t->xoffset) > -4096 && v < 4096) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	} else if(n->op == OINDREG) {
+		if((v = n->xoffset) > -4096 && v < 4096) {
+			n->op = OREGISTER;
+			cgen(n, t);
+			t->xoffset += v;
+			n->op = OINDREG;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+reglpcgen(Node *n, Node *nn, int f)
+{
+	Type *t;
+
+	t = nn->type;
+	nn->type = types[TLONG];
+	if(f)
+		reglcgen(n, nn, Z);
+	else {
+		regialloc(n, nn, Z);
+		lcgen(nn, n);
+		regind(n, nn);
+	}
+	nn->type = t;
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		o = ONE;
+		if(true)
+			o = comrel[relindex(o)];
+		if(typefd[n->type->etype]) {
+			gopcode(o, nodfconst(0), &nod, Z);
+		} else
+			gopcode(o, nodconst(0), &nod, Z);
+		regfree(&nod);
+		goto com;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgenrel(r, &nod, 1);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(sconst(l)) {
+			regalloc(&nod, r, nn);
+			cgenrel(r, &nod, 1);
+			o = invrel[relindex(o)];
+			gopcode(o, l, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(sconst(r)) {
+			regalloc(&nod, l, nn);
+			cgenrel(l, &nod, 1);
+			gopcode(o, r, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgenrel(l, &nod1, 1);
+			regalloc(&nod, r, Z);
+			cgenrel(r, &nod, 1);
+		} else {
+			regalloc(&nod, r, nn);
+			cgenrel(r, &nod, 1);
+			regalloc(&nod1, l, Z);
+			cgenrel(l, &nod1, 1);
+		}
+		gopcode(o, &nod, &nod1, Z);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			nod1.xoffset += SZ_LONG;
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+
+			regfree(&nod1);
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && nn->complex >= FNX) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 2) {
+		if(n->complex > nn->complex) {
+			reglpcgen(&nod1, n, 1);
+			reglpcgen(&nod2, nn, 1);
+		} else {
+			reglpcgen(&nod2, nn, 1);
+			reglpcgen(&nod1, n, 1);
+		}
+		regalloc(&nod3, &regnode, Z);
+		regalloc(&nod4, &regnode, Z);
+		if(nod3.reg > nod4.reg){
+			/* code below assumes nod3 loaded first */
+			Node t = nod3; nod3 = nod4; nod4 = t;
+		}
+		nod0 = *nodconst((1<<nod3.reg)|(1<<nod4.reg));
+		if(w == 2 && nod1.xoffset == 0)
+			gmovm(&nod1, &nod0, 0);
+		else {
+			gmove(&nod1, &nod3);
+			if(w == 2) {
+				nod1.xoffset += SZ_LONG;
+				gmove(&nod1, &nod4);
+			}
+		}
+		if(w == 2 && nod2.xoffset == 0)
+			gmovm(&nod0, &nod2, 0);
+		else {
+			gmove(&nod3, &nod2);
+			if(w == 2) {
+				nod2.xoffset += SZ_LONG;
+				gmove(&nod4, &nod2);
+			}
+		}
+		regfree(&nod1);
+		regfree(&nod2);
+		regfree(&nod3);
+		regfree(&nod4);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		reglpcgen(&nod1, n, 0);
+		reglpcgen(&nod2, nn, 0);
+	} else {
+		reglpcgen(&nod2, nn, 0);
+		reglpcgen(&nod1, n, 0);
+	}
+
+	m = 0;
+	for(c = 0; c < w && c < 4; c++) {
+		i = tmpreg();
+		if (i == 0)
+			break;
+		reg[i]++;
+		m |= 1<<i;
+	}
+	nod4 = *(nodconst(m));
+	if(w < 3*c) {
+		for (; w>c; w-=c) {
+			gmovm(&nod1, &nod4, 1);
+			gmovm(&nod4, &nod2, 1);
+		}
+		goto out;
+	}
+
+	regalloc(&nod3, &regnode, Z);
+	gopcode(OAS, nodconst(w/c), Z, &nod3);
+	w %= c;
+	
+	pc1 = pc;
+	gmovm(&nod1, &nod4, 1);
+	gmovm(&nod4, &nod2, 1);
+
+	gopcode(OSUB, nodconst(1), Z, &nod3);
+	gopcode(OEQ, nodconst(0), &nod3, Z);
+	p->as = ABGT;
+	patch(p, pc1);
+	regfree(&nod3);
+
+out:
+	if (w) {
+		i = 0;
+		while (c>w) {
+			while ((m&(1<<i)) == 0)
+				i++;
+			m &= ~(1<<i);
+			reg[i] = 0;
+			c--;
+			i++;
+		}
+		nod4.vconst = m;
+		gmovm(&nod1, &nod4, 0);
+		gmovm(&nod4, &nod2, 0);
+	}
+	i = 0;
+	do {
+		while ((m&(1<<i)) == 0)
+			i++;
+		reg[i] = 0;
+		c--;
+		i++;
+	} while (c>0);
+	regfree(&nod1);
+	regfree(&nod2);
+}
--- /dev/null
+++ b/utils/5c/enam.c
@@ -1,0 +1,104 @@
+char*	anames[] =
+{
+	"XXX",
+	"AND",
+	"EOR",
+	"SUB",
+	"RSB",
+	"ADD",
+	"ADC",
+	"SBC",
+	"RSC",
+	"TST",
+	"TEQ",
+	"CMP",
+	"CMN",
+	"ORR",
+	"BIC",
+	"MVN",
+	"B",
+	"BL",
+	"BEQ",
+	"BNE",
+	"BCS",
+	"BHS",
+	"BCC",
+	"BLO",
+	"BMI",
+	"BPL",
+	"BVS",
+	"BVC",
+	"BHI",
+	"BLS",
+	"BGE",
+	"BLT",
+	"BGT",
+	"BLE",
+	"MOVWD",
+	"MOVWF",
+	"MOVDW",
+	"MOVFW",
+	"MOVFD",
+	"MOVDF",
+	"MOVF",
+	"MOVD",
+	"CMPF",
+	"CMPD",
+	"ADDF",
+	"ADDD",
+	"SUBF",
+	"SUBD",
+	"MULF",
+	"MULD",
+	"DIVF",
+	"DIVD",
+	"SRL",
+	"SRA",
+	"SLL",
+	"MULU",
+	"DIVU",
+	"MUL",
+	"DIV",
+	"MOD",
+	"MODU",
+	"MOVB",
+	"MOVBU",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVM",
+	"SWPBU",
+	"SWPW",
+	"NOP",
+	"RFE",
+	"SWI",
+	"MULA",
+	"DATA",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"NAME",
+	"RET",
+	"TEXT",
+	"WORD",
+	"DYNT",
+	"INIT",
+	"BCASE",
+	"CASE",
+	"END",
+	"MULL",
+	"MULAL",
+	"MULLU",
+	"MULALU",
+	"BX",
+	"BXRET",
+	"DWORD",
+	"SIGNAME",
+	"SQRTF",
+	"SQRTD",
+	"LDREX",
+	"STREX",
+	"LDREXD",
+	"STREXD",
+	"LAST",
+};
--- /dev/null
+++ b/utils/5c/gc.h
@@ -1,0 +1,351 @@
+#include	"../cc/cc.h"
+#include	"../5c/5.out.h"
+
+/*
+ * 5c/arm
+ * Arm
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+
+#define	R0ISZERO	0
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	char	sval[NSNAME];
+	Ieee	ieee;
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	char	as;
+	char	reg;
+	uchar	scond;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Multab	multab[20];
+EXTERN	int	hintabsize;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	char	reg[NREG+NFREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+EXTERN	int	suppress;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	4
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	usedset(Node*, int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	cgenrel(Node*, Node*, int);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+int	tmpreg(void);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmovm(Node*, Node*, int);
+void	gmove(Node*, Node*);
+void	gmover(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	swit2(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(long);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+int	Rconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	shiftprop(Reg*);
+void	constprop(Adr*, Adr*, Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+void	predicate(void); 
+int	isbranch(Prog *); 
+int	predicable(Prog *p); 
+int	modifiescpsr(Prog *p); 
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"R"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/5c/list.c
@@ -1,0 +1,304 @@
+#define	EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+char *extra [] = {
+	".EQ", ".NE", ".CS", ".CC", 
+	".MI", ".PL", ".VS", ".VC", 
+	".HI", ".LS", ".GE", ".LT", 
+	".GT", ".LE", "", ".NV",
+};
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], sc[20];
+	Prog *p;
+	int a, s;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	s = p->scond; 
+	strcpy(sc, extra[s & C_SCOND]);
+	if(s & C_SBIT)
+		strcat(sc, ".S");
+	if(s & C_PBIT)
+		strcat(sc, ".P");
+	if(s & C_WBIT)
+		strcat(sc, ".W");
+	if(s & C_UBIT)		/* ambiguous with FBIT */
+		strcat(sc, ".U");
+	if(a == AMOVM) {
+		if(p->from.type == D_CONST)
+			sprint(str, "	%A%s	%R,%D", a, sc, &p->from, &p->to);
+		else
+		if(p->to.type == D_CONST)
+			sprint(str, "	%A%s	%D,%R", a, sc, &p->from, &p->to);
+		else
+			sprint(str, "	%A%s	%D,%D", a, sc, &p->from, &p->to);
+	} else
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A%s	%D,%D", a, sc, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A%s	%D,R%d,%D", a, sc, &p->from, p->reg, &p->to);
+	else
+		sprint(str, "	%A%s	%D,F%d,%D", a, sc, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	char *op;
+	int v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_SHIFT:
+		v = a->offset;
+		op = "<<>>->@>" + (((v>>5) & 3) << 1);
+		if(v & (1<<4))
+			sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
+		else
+			sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31);
+		if(a->reg != NREG)
+			sprint(str+strlen(str), "(R%d)", a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_PSR:
+		sprint(str, "PSR");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(PSR)(REG)", a);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	int i, v;
+
+	a = va_arg(fp->args, Adr*);
+	sprint(str, "GOK-reglist");
+	switch(a->type) {
+	case D_CONST:
+		if(a->reg != NREG)
+			break;
+		if(a->sym != S)
+			break;
+		v = a->offset;
+		strcpy(str, "");
+		for(i=0; i<NREG; i++) {
+			if(v & (1<<i)) {
+				if(str[0] == 0)
+					strcat(str, "[R");
+				else
+					strcat(str, ",R");
+				sprint(strchr(str, 0), "%d", i);
+			}
+		}
+		strcat(str, "]");
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/5c/mkenam
@@ -1,0 +1,15 @@
+ed - ../5c/5.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/5c/mkfile
@@ -1,0 +1,38 @@
+<../../mkconfig
+
+TARG=5c
+
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	mul.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+
+HFILES=\
+	gc.h\
+	5.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	5.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/5c/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static  int	maxmulops = 3;	/* max # of ops to replace mul with */
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("multiply table failure %ld\n", v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=maxmulops; g++) {
+		if(g >= maxmulops && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("multiply table failure %ld\n", v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/5c/peep.c
@@ -1,0 +1,1439 @@
+#include "gc.h"
+
+int xtramodes(Reg*, Adr*);
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == ASLL || p->as == ASRL || p->as == ASRA) {
+			/*
+			 * elide shift into D_SHIFT operand of subsequent instruction
+			 */
+			if(shiftprop(r)) {
+				excise(r);
+				t++;
+			}
+		}
+		if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+		if(regtyp(&p->to)) {
+			if(p->from.type == D_CONST)
+				constprop(&p->from, &p->to, r->s1);
+			else if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AEOR:
+			/*
+			 * EOR -1,x,y => MVN x,y
+			 */
+			if(p->from.type == D_CONST && p->from.offset == -1) {
+				p->as = AMVN;
+				p->from.type = D_REG;
+				if(p->reg != NREG)
+					p->from.reg = p->reg;
+				else
+					p->from.reg = p->to.reg;
+				p->reg = NREG;
+			}
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		case AMOVW:
+		case AMOVB:
+		case AMOVBU:
+			if(p->from.type == D_OREG && p->from.offset == 0)
+				xtramodes(r, &p->from);
+			else if(p->to.type == D_OREG && p->to.offset == 0)
+				xtramodes(r, &p->to);
+			else
+				continue;
+			break;
+		case ACMP:
+			/*
+			 * elide CMP $0,x if calculation of x can set condition codes
+			 */
+			if(p->from.type != D_CONST || p->from.offset != 0)
+				continue;
+			r2 = r->s1;
+			if(r2 == R)
+				continue;
+			t = r2->prog->as;
+			switch(t) {
+			default:
+				continue;
+			case ABEQ:
+			case ABNE:
+			case ABMI:
+			case ABPL:
+				break;
+			case ABGE:
+				t = ABPL;
+				break;
+			case ABLT:
+				t = ABMI;
+				break;
+			case ABHI:
+				t = ABNE;
+				break;
+			case ABLS:
+				t = ABEQ;
+				break;
+			}
+			r1 = r;
+			do
+				r1 = uniqp(r1);
+			while (r1 != R && r1->prog->as == ANOP);
+			if(r1 == R)
+				continue;
+			p1 = r1->prog;
+			if(p1->to.type != D_REG)
+				continue;
+			if(p1->to.reg != p->reg)
+			if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg))
+				continue;
+			switch(p1->as) {
+			default:
+				continue;
+			case AMOVW:
+				if(p1->from.type != D_REG)
+					continue;
+			case AAND:
+			case AEOR:
+			case AORR:
+			case ABIC:
+			case AMVN:
+			case ASUB:
+			case ARSB:
+			case AADD:
+			case AADC:
+			case ASBC:
+			case ARSC:
+				break;
+			}
+			p1->scond |= C_SBIT;
+			r2->prog->as = t;
+			excise(r);
+			continue;
+		}
+	}
+
+	predicate();
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->scond = zprog.scond;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG)
+		return 1;
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ABL:
+			return 0;
+
+		case ACMP:
+		case ACMN:
+		case AADD:
+		case ASUB:
+		case ARSB:
+		case ASLL:
+		case ASRL:
+		case ASRA:
+		case AORR:
+		case AAND:
+		case AEOR:
+		case AMUL:
+		case ADIV:
+		case ADIVU:
+
+		case ACMPF:
+		case ACMPD:
+		case AADDD:
+		case AADDF:
+		case ASUBD:
+		case ASUBF:
+		case AMULD:
+		case AMULF:
+		case ADIVD:
+		case ADIVF:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AMOVF:
+		case AMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+
+		case AMOVM:
+			t = 1<<v2->reg;
+			if((p->from.type == D_CONST && (p->from.offset&t)) ||
+			   (p->to.type == D_CONST && (p->to.offset&t)))
+				return 0;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant constants.
+ *	$c1->v1
+ *	($c1->v2 s/$c1/v1)*
+ *	set v1  return
+ * The v1->v2 should be eliminated by copy propagation.
+ */
+void
+constprop(Adr *c1, Adr *v1, Reg *r)
+{
+	Prog *p;
+
+	if(debug['C'])
+		print("constprop %D->%D\n", c1, v1);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['C'])
+			print("%P", p);
+		if(uniqp(r) == R) {
+			if(debug['C'])
+				print("; merge; return\n");
+			return;
+		}
+		if(p->as == AMOVW && copyas(&p->from, c1)) {
+				if(debug['C'])
+					print("; sub%D/%D", &p->from, v1);
+				p->from = *v1;
+		} else if(copyu(p, v1, A) > 1) {
+			if(debug['C'])
+				print("; %Dset; return\n", v1);
+			return;
+		}
+		if(debug['C'])
+			print("\n");
+		if(r->s2)
+			constprop(c1, v1, r->s2);
+	}
+}
+
+/*
+ * ASLL x,y,w
+ * .. (not use w, not set x y w)
+ * AXXX w,a,b (a != w)
+ * .. (not use w)
+ * (set w)
+ * ----------- changed to
+ * ..
+ * AXXX (x<<y),a,b
+ * ..
+ */
+#define FAIL(msg) { if(debug['H']) print("\t%s; FAILURE\n", msg); return 0; }
+int
+shiftprop(Reg *r)
+{
+	Reg *r1;
+	Prog *p, *p1, *p2;
+	int n, o;
+	Adr a;
+
+	p = r->prog;
+	if(p->to.type != D_REG)
+		FAIL("BOTCH: result not reg");
+	n = p->to.reg;
+	a = zprog.from;
+	if(p->reg != NREG && p->reg != p->to.reg) {
+		a.type = D_REG;
+		a.reg = p->reg;
+	}
+	if(debug['H'])
+		print("shiftprop\n%P", p);
+	r1 = r;
+	for(;;) {
+		/* find first use of shift result; abort if shift operands or result are changed */
+		r1 = uniqs(r1);
+		if(r1 == R)
+			FAIL("branch");
+		if(uniqp(r1) == R)
+			FAIL("merge");
+		p1 = r1->prog;
+		if(debug['H'])
+			print("\n%P", p1);
+		switch(copyu(p1, &p->to, A)) {
+		case 0:	/* not used or set */
+			if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) ||
+			   (a.type == D_REG && copyu(p1, &a, A) > 1))
+				FAIL("args modified");
+			continue;
+		case 3:	/* set, not used */
+			FAIL("BOTCH: noref");
+		}
+		break;
+	}
+	/* check whether substitution can be done */
+	switch(p1->as) {
+	default:
+		FAIL("non-dpi");
+	case AAND:
+	case AEOR:
+	case AADD:
+	case AADC:
+	case AORR:
+	case ASUB:
+	case ARSB:
+	case ASBC:
+	case ARSC:
+		if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) {
+			if(p1->from.type != D_REG)
+				FAIL("can't swap");
+			p1->reg = p1->from.reg;
+			p1->from.reg = n;
+			switch(p1->as) {
+			case ASUB:
+				p1->as = ARSB;
+				break;
+			case ARSB:
+				p1->as = ASUB;
+				break;
+			case ASBC:
+				p1->as = ARSC;
+				break;
+			case ARSC:
+				p1->as = ASBC;
+				break;
+			}
+			if(debug['H'])
+				print("\t=>%P", p1);
+		}
+	case ABIC:
+	case ACMP:
+	case ACMN:
+		if(p1->reg == n)
+			FAIL("can't swap");
+		if(p1->reg == NREG && p1->to.reg == n)
+			FAIL("shift result used twice");
+	case AMVN:
+		if(p1->from.type == D_SHIFT)
+			FAIL("shift result used in shift");
+		if(p1->from.type != D_REG || p1->from.reg != n)
+			FAIL("BOTCH: where is it used?");
+		break;
+	}
+	/* check whether shift result is used subsequently */
+	p2 = p1;
+	if(p1->to.reg != n)
+	for (;;) {
+		r1 = uniqs(r1);
+		if(r1 == R)
+			FAIL("inconclusive");
+		p1 = r1->prog;
+		if(debug['H'])
+			print("\n%P", p1);
+		switch(copyu(p1, &p->to, A)) {
+		case 0:	/* not used or set */
+			continue;
+		case 3: /* set, not used */
+			break;
+		default:/* used */
+			FAIL("reused");
+		}
+		break;
+	}
+	/* make the substitution */
+	p2->from.type = D_SHIFT;
+	p2->from.reg = NREG;
+	o = p->reg;
+	if(o == NREG)
+		o = p->to.reg;
+	switch(p->from.type){
+	case D_CONST:
+		o |= (p->from.offset&0x1f)<<7;
+		break;
+	case D_REG:
+		o |= (1<<4) | (p->from.reg<<8);
+		break;
+	}
+	switch(p->as){
+	case ASLL:
+		o |= 0<<5;
+		break;
+	case ASRL:
+		o |= 1<<5;
+		break;
+	case ASRA:
+		o |= 2<<5;
+		break;
+	}
+	p2->from.offset = o;
+	if(debug['H'])
+		print("\t=>%P\tSUCCEED\n", p2);
+	return 1;
+}
+
+Reg*
+findpre(Reg *r, Adr *v)
+{
+	Reg *r1;
+
+	for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
+		if(uniqs(r1) != r)
+			return R;
+		switch(copyu(r1->prog, v, A)) {
+		case 1: /* used */
+		case 2: /* read-alter-rewrite */
+			return R;
+		case 3: /* set */
+		case 4: /* set and used */
+			return r1;
+		}
+	}
+	return R;
+}
+
+Reg*
+findinc(Reg *r, Reg *r2, Adr *v)
+{
+	Reg *r1;
+	Prog *p;
+
+
+	for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
+		if(uniqp(r1) != r)
+			return R;
+		switch(copyu(r1->prog, v, A)) {
+		case 0: /* not touched */
+			continue;
+		case 4: /* set and used */
+			p = r1->prog;
+			if(p->as == AADD)
+			if(p->from.type == D_CONST)
+			if(p->from.offset > -4096 && p->from.offset < 4096)
+				return r1;
+		default:
+			return R;
+		}
+	}
+	return R;
+}
+
+int
+nochange(Reg *r, Reg *r2, Prog *p)
+{
+	Adr a[3];
+	int i, n;
+
+	if(r == r2)
+		return 1;
+	n = 0;
+	if(p->reg != NREG && p->reg != p->to.reg) {
+		a[n].type = D_REG;
+		a[n++].reg = p->reg;
+	}
+	switch(p->from.type) {
+	case D_SHIFT:
+		a[n].type = D_REG;
+		a[n++].reg = p->from.offset&0xf;
+	case D_REG:
+		a[n].type = D_REG;
+		a[n++].reg = p->from.reg;
+	}
+	if(n == 0)
+		return 1;
+	for(; r!=R && r!=r2; r=uniqs(r)) {
+		p = r->prog;
+		for(i=0; i<n; i++)
+			if(copyu(p, &a[i], A) > 1)
+				return 0;
+	}
+	return 1;
+}
+
+int
+findu1(Reg *r, Adr *v)
+{
+	for(; r != R; r = r->s1) {
+		if(r->active)
+			return 0;
+		r->active = 1;
+		switch(copyu(r->prog, v, A)) {
+		case 1: /* used */
+		case 2: /* read-alter-rewrite */
+		case 4: /* set and used */
+			return 1;
+		case 3: /* set */
+			return 0;
+		}
+		if(r->s2)
+			if (findu1(r->s2, v))
+				return 1;
+	}
+	return 0;
+}
+
+int
+finduse(Reg *r, Adr *v)
+{
+	Reg *r1;
+
+	for(r1=firstr; r1!=R; r1=r1->link)
+		r1->active = 0;
+	return findu1(r, v);
+}
+
+int
+xtramodes(Reg *r, Adr *a)
+{
+	Reg *r1, *r2, *r3;
+	Prog *p, *p1;
+	Adr v;
+
+	p = r->prog;
+	if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG)	/* byte load */
+		return 0;
+	v = *a;
+	v.type = D_REG;
+	r1 = findpre(r, &v);
+	if(r1 != R) {
+		p1 = r1->prog;
+		if(p1->to.type == D_REG && p1->to.reg == v.reg)
+		switch(p1->as) {
+		case AADD:
+			if(p1->from.type == D_REG ||
+			   (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
+			    (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+			   (p1->from.type == D_CONST && 
+			    p1->from.offset > -4096 && p1->from.offset < 4096))
+			if(nochange(uniqs(r1), r, p1)) {
+				if(a != &p->from || v.reg != p->to.reg)
+				if (finduse(r->s1, &v)) {
+					if(p1->reg == NREG || p1->reg == v.reg)
+						/* pre-indexing */
+						p->scond |= C_WBIT;
+					else return 0;	
+				}
+				switch (p1->from.type) {
+				case D_REG:
+					/* register offset */
+					a->type = D_SHIFT;
+					a->offset = p1->from.reg;
+					break;
+				case D_SHIFT:
+					/* scaled register offset */
+					a->type = D_SHIFT;
+				case D_CONST:
+					/* immediate offset */
+					a->offset = p1->from.offset;
+					break;
+				}
+				if(p1->reg != NREG)
+					a->reg = p1->reg;
+				excise(r1);
+				return 1;
+			}
+			break;
+		case AMOVW:
+			if(p1->from.type == D_REG)
+			if((r2 = findinc(r1, r, &p1->from)) != R) {
+			for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
+				;
+			if(r3 == r) {
+				/* post-indexing */
+				p1 = r2->prog;
+				a->reg = p1->to.reg;
+				a->offset = p1->from.offset;
+				p->scond |= C_PBIT;
+				if(!finduse(r, &r1->prog->to))
+					excise(r1);
+				excise(r2);
+				return 1;
+			}
+			}
+			break;
+		}
+	}
+	if(a != &p->from || a->reg != p->to.reg)
+	if((r1 = findinc(r, R, &v)) != R) {
+		/* post-indexing */
+		p1 = r1->prog;
+		a->offset = p1->from.offset;
+		p->scond |= C_PBIT;
+		excise(r1);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+	case AMOVM:
+		if(v->type != D_REG)
+			return 0;
+		if(p->from.type == D_CONST) {	/* read reglist, read/rar */
+			if(s != A) {
+				if(p->from.offset&(1<<v->reg))
+					return 1;
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+				return 0;
+			}
+			if(copyau(&p->to, v)) {
+				if(p->scond&C_WBIT)
+					return 2;
+				return 1;
+			}
+			if(p->from.offset&(1<<v->reg))
+				return 1;
+		} else {			/* read/rar, write reglist */
+			if(s != A) {
+				if(p->to.offset&(1<<v->reg))
+					return 1;
+				if(copysub(&p->from, v, s, 1))
+					return 1;
+				return 0;
+			}
+			if(copyau(&p->from, v)) {
+				if(p->scond&C_WBIT)
+					return 2;
+				if(p->to.offset&(1<<v->reg))
+					return 4;
+				return 1;
+			}
+			if(p->to.offset&(1<<v->reg))
+				return 3;
+		}
+		return 0;
+		
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVF:
+	case AMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVDW:
+	case AMOVWD:
+	case AMOVFD:
+	case AMOVDF:
+		if(p->scond&(C_WBIT|C_PBIT))
+		if(v->type == D_REG) {
+			if(p->from.type == D_OREG || p->from.type == D_SHIFT) {
+				if(p->from.reg == v->reg)
+					return 2;
+			} else {
+		  		if(p->to.reg == v->reg)
+				return 2;
+			}
+		}
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+
+	case AADD:	/* read, read, write */
+	case ASUB:
+	case ARSB:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AORR:
+	case AAND:
+	case AEOR:
+	case AMUL:
+	case ADIV:
+	case ADIVU:
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+
+	case ACMPF:
+	case ACMPD:
+	case ACMP:
+	case ACMN:
+	case ACASE:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:	/* read, read */
+	case ABNE:
+	case ABCS:
+	case ABHS:
+	case ABCC:
+	case ABLO:
+	case ABMI:
+	case ABPL:
+	case ABVS:
+	case ABVC:
+	case ABHI:
+	case ABLS:
+	case ABGE:
+	case ABLT:
+	case ABGT:
+	case ABLE:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub1(p, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		return 0;
+
+	case AB:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case ABL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(v->reg == (uchar)REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG)
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == (uchar)REGARG)
+				return 3;
+		return 0;
+	}
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+
+	case ACMP:
+	case ACMN:
+
+	case AADD:
+	case ASUB:
+	case ARSB:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AORR:
+	case AAND:
+	case AEOR:
+	case AMUL:
+	case ADIV:
+	case ADIVU:
+		return D_REG;
+
+	case ACMPF:
+	case ACMPD:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	} else if(v->type == D_CONST) {		/* for constprop */
+		if(a->type == v->type)
+		if(a->name == v->name)
+		if(a->sym == v->sym)
+		if(a->reg == v->reg)
+		if(a->offset == v->offset)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG) {
+		if(a->type == D_OREG) {
+			if(v->reg == a->reg)
+				return 1;
+		} else if(a->type == D_SHIFT) {
+			if((a->offset&0xf) == v->reg)
+				return 1;
+			if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a2type(p) == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v)) {
+		if(a->type == D_SHIFT) {
+			if((a->offset&0xf) == v->reg)
+				a->offset = (a->offset&~0xf)|s->reg;
+			if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+				a->offset = (a->offset&~(0xf<<8))|(s->reg<<8);
+		} else
+			a->reg = s->reg;
+	}
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
+
+struct {
+	int opcode;
+	int notopcode;
+	int scond; 
+	int notscond; 
+} predinfo[]  = { 
+	{ ABEQ,	ABNE,	0x0,	0x1, }, 
+	{ ABNE,	ABEQ,	0x1,	0x0, }, 
+	{ ABCS,	ABCC,	0x2,	0x3, }, 
+	{ ABHS,	ABLO,	0x2,	0x3, }, 
+	{ ABCC,	ABCS,	0x3,	0x2, }, 
+	{ ABLO,	ABHS,	0x3,	0x2, }, 
+	{ ABMI,	ABPL,	0x4,	0x5, }, 
+	{ ABPL,	ABMI,	0x5,	0x4, }, 
+	{ ABVS,	ABVC,	0x6,	0x7, }, 
+	{ ABVC,	ABVS,	0x7,	0x6, }, 
+	{ ABHI,	ABLS,	0x8,	0x9, }, 
+	{ ABLS,	ABHI,	0x9,	0x8, }, 
+	{ ABGE,	ABLT,	0xA,	0xB, }, 
+	{ ABLT,	ABGE,	0xB,	0xA, }, 
+	{ ABGT,	ABLE,	0xC,	0xD, }, 
+	{ ABLE,	ABGT,	0xD,	0xC, }, 
+}; 
+
+typedef struct {
+	Reg *start;
+	Reg *last;
+	Reg *end;
+	int len;
+} Joininfo;
+
+enum {
+	Join,
+	Split,
+	End,
+	Branch,
+	Setcond,
+	Toolong
+};
+	
+enum {
+	Falsecond,
+	Truecond,
+	Delbranch,
+	Keepbranch
+};
+
+int 
+isbranch(Prog *p)
+{
+	return (ABEQ <= p->as) && (p->as <= ABLE); 
+}
+
+int
+predicable(Prog *p)
+{
+	if (isbranch(p)
+		|| p->as == ANOP
+		|| p->as == AXXX
+		|| p->as == ADATA
+		|| p->as == AGLOBL
+		|| p->as == AGOK
+		|| p->as == AHISTORY
+		|| p->as == ANAME
+		|| p->as == ASIGNAME
+		|| p->as == ATEXT
+		|| p->as == AWORD
+		|| p->as == ADYNT
+		|| p->as == AINIT
+		|| p->as == ABCASE
+		|| p->as == ACASE)
+		return 0; 
+	return 1; 
+}
+
+/* 
+ * Depends on an analysis of the encodings performed by 5l. 
+ * These seem to be all of the opcodes that lead to the "S" bit
+ * being set in the instruction encodings. 
+ * 
+ * C_SBIT may also have been set explicitly in p->scond.
+ */ 
+int
+modifiescpsr(Prog *p)
+{
+	return (p->scond&C_SBIT)
+		|| p->as == ATST 
+		|| p->as == ATEQ 
+		|| p->as == ACMN
+		|| p->as == ACMP
+		|| p->as == AMULU
+		|| p->as == ADIVU
+		|| p->as == AMUL
+		|| p->as == ADIV
+		|| p->as == AMOD
+		|| p->as == AMODU
+		|| p->as == ABL;
+} 
+
+/*
+ * Find the maximal chain of instructions starting with r which could
+ * be executed conditionally
+ */
+int
+joinsplit(Reg *r, Joininfo *j)
+{
+	j->start = r;
+	j->last = r;
+	j->len = 0;
+	do {
+		if (r->p2 && (r->p1 || r->p2->p2link)) {
+			j->end = r;
+			return Join;
+		}
+		if (r->s1 && r->s2) {
+			j->end = r;
+			return Split;
+		}
+		j->last = r;
+		if (r->prog->as != ANOP)
+			j->len++;
+		if (!r->s1 && !r->s2) {
+			j->end = r->link;
+			return End;
+		}
+		if (r->s2) {
+			j->end = r->s2;
+			return Branch;
+		}
+		if (modifiescpsr(r->prog)) {
+			j->end = r->s1;
+			return Setcond;
+		}
+		r = r->s1;
+	} while (j->len < 4);
+	j->end = r;
+	return Toolong;
+}
+
+Reg *
+successor(Reg *r)
+{
+	if (r->s1)
+		return r->s1; 
+	else
+		return r->s2; 
+}
+
+void
+applypred(Reg *rstart, Joininfo *j, int cond, int branch)
+{
+	int pred; 
+	Reg *r; 
+
+	if(j->len == 0)
+		return;
+	if (cond == Truecond)
+		pred = predinfo[rstart->prog->as - ABEQ].scond;
+	else
+		pred = predinfo[rstart->prog->as - ABEQ].notscond; 
+	
+	for (r = j->start; ; r = successor(r)) {
+		if (r->prog->as == AB) {
+			if (r != j->last || branch == Delbranch)
+				excise(r);
+			else {
+			  if (cond == Truecond)
+				r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode;
+			  else
+				r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode;
+			}
+		}
+		else if (predicable(r->prog)) 
+			r->prog->scond = (r->prog->scond&~C_SCOND)|pred;
+		if (r->s1 != r->link) {
+			r->s1 = r->link;
+			r->link->p1 = r;
+		}
+		if (r == j->last)
+			break;
+	}
+}
+
+void
+predicate(void)
+{	
+	Reg *r;
+	int t1, t2;
+	Joininfo j1, j2;
+
+	for(r=firstr; r!=R; r=r->link) {
+		if (isbranch(r->prog)) {
+			t1 = joinsplit(r->s1, &j1);
+			t2 = joinsplit(r->s2, &j2);
+			if(j1.last->link != j2.start)
+				continue;
+			if(j1.end == j2.end)
+			if((t1 == Branch && (t2 == Join || t2 == Setcond)) ||
+			   (t2 == Join && (t1 == Join || t1 == Setcond))) {
+				applypred(r, &j1, Falsecond, Delbranch);
+				applypred(r, &j2, Truecond, Delbranch);
+				excise(r);
+				continue;
+			}
+			if(t1 == End || t1 == Branch) {
+				applypred(r, &j1, Falsecond, Keepbranch);
+				excise(r);
+				continue;
+			}
+		} 
+	} 
+}
--- /dev/null
+++ b/utils/5c/reg.c
@@ -1,0 +1,1158 @@
+#include "gc.h"
+
+void	addsplits(void);
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AB:
+		case ARFE:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVF:
+		case AMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case ABL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+
+		if(p->as == AMOVM) {
+			if(p->from.type == D_CONST)
+				z = p->from.offset;
+			else
+				z = p->to.offset;
+			for(i=0; z; i++) {
+				if(z&1)
+					regbits |= RtoB(i);
+				z >>= 1;
+			}
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+	addsplits();
+
+	if(debug['R'] && debug['v']) {
+		print("\nprop structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->set.b[z] |
+					r->refahead.b[z] | r->calahead.b[z] |
+					r->refbehind.b[z] | r->calbehind.b[z] |
+					r->use1.b[z] | r->use2.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+				if(bany(&r->refahead))
+					print(" ra=%B", r->refahead);
+				if(bany(&r->calahead))
+					print(" ca=%B", r->calahead);
+				if(bany(&r->refbehind))
+					print(" rb=%B", r->refbehind);
+				if(bany(&r->calbehind))
+					print(" cb=%B", r->calbehind);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L $%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L $%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L $%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+void
+addsplits(void)
+{
+	Reg *r, *r1;
+	int z, i;
+	Bits bit;
+
+	for(r = firstr; r != R; r = r->link) {
+		if(r->loop > 1)
+			continue;
+		if(r->prog->as == ABL)
+			continue;
+		for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+			if(r1->loop <= 1)
+				continue;
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r1->calbehind.b[z] &
+					(r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+					~(r->calahead.b[z] & addrs.b[z]);
+			while(bany(&bit)) {
+				i = bnum(bit);
+				bit.b[i/32] &= ~(1L << (i%32));
+			}
+		}
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ABL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TVLONG:
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R0
+ *	1	R1
+ *	...	...
+ *	10	R10
+ */
+long
+RtoB(int r)
+{
+
+	if(r >= REGMIN && r <= REGMAX)
+		return 1L << r;
+	return 0;
+}
+
+int
+BtoR(long b)
+{
+	b &= 0x01fcL;	// excluded R9 and R10 for extern registers
+	if(b == 0)
+		return 0;
+	return bitno(b);
+}
+
+/*
+ *	bit	reg
+ *	18	F2
+ *	19	F3
+ *	...	...
+ *	23	F7
+ */
+long
+FtoB(int f)
+{
+
+	if(f < 2 || f > NFREG-1)
+		return 0;
+	return 1L << (f + 16);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xfc0000L;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 16;
+}
--- /dev/null
+++ b/utils/5c/sgen.c
@@ -1,0 +1,219 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASLMUL:
+	case OASMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		t = vlog(l);
+		if(t >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+	case OEQ:
+	case ONE:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
--- /dev/null
+++ b/utils/5c/swt.c
@@ -1,0 +1,619 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	Node tn;
+	
+	regalloc(&tn, &regnode, Z);
+	swit2(q, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	long v;
+	Prog *sp;
+
+	if(nc >= 3) {
+		i = (q+nc-1)->val - (q+0)->val;
+		if(i > 0 && i < nc*2)
+			goto direct;
+	}
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gopcode(OEQ, nodconst(q->val), n, Z);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+
+	i = nc / 2;
+	r = q+i;
+	if(debug['K'])
+		print("case > %.8llux\n", r->val);
+	gopcode(OGT, nodconst(r->val), n, Z);
+	sp = p;
+	gopcode(OEQ, nodconst(r->val), n, Z);	/* just gen the B.EQ */
+	patch(p, r->label);
+	swit2(q, i, def, n, tn);
+
+	if(debug['K'])
+		print("case < %.8llux\n", r->val);
+	patch(sp, pc);
+	swit2(r+1, nc-i-1, def, n, tn);
+	return;
+
+direct:
+	v = q->val;
+	if(v != 0)
+		gopcode(OSUB, nodconst(v), Z, n);
+	gopcode(OCASE, nodconst((q+nc-1)->val - v), n, Z);
+	patch(p, def);
+	for(i=0; i<nc; i++) {
+		if(debug['K'])
+			print("case = %.8llux\n", q->val);
+		while(q->val != v) {
+			nextpc();
+			p->as = ABCASE;
+			patch(p, def);
+			v++;
+		}
+		nextpc();
+		p->as = ABCASE;
+		patch(p, q->label);
+		q++;
+		v++;
+	}
+	gbranch(OGOTO);		/* so that regopt() won't be confused */
+	patch(p, def);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v, vs;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	if(debug['M'] && debug['v'])
+		print("%L multiply: %ld\n", n->lineno, v);
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	vs = v;
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		if(vs < 0) {
+			gopcode(OAS, &nod1, Z, &nod1);
+			gopcode(OSUB, &nod1, nodconst(0), nn);
+		} else 
+			gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+	if(a->op == OCONST && typev[a->type->etype]) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+
+	bf[0] = p->as;
+	bf[1] = p->scond;
+	bf[2] = p->reg;
+	bf[3] = p->lineno;
+	bf[4] = p->lineno>>8;
+	bf[5] = p->lineno>>16;
+	bf[6] = p->lineno>>24;
+	bp = zaddr(bf+7, &p->from, sf);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[7];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = sig;
+		bf[2] = sig>>8;
+		bf[3] = sig>>16;
+		bf[4] = sig>>24;
+		bf[5] = t;
+		bf[6] = s->sym;
+		Bwrite(b, bf, 7);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = t;	/* type */
+		bf[2] = s->sym;	/* sym */
+		Bwrite(b, bf, 3);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	long l;
+	Ieee e;
+
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	}
+	return bp;
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial align of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial align of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total align of automatic */
+		o = align(o, t, Ael2);
+		o = align(o, t, Ael1);
+		w = SZ_LONG;	/* because of a pun in cc/dcl.c:contig() */
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_LONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/5c/txt.c
@@ -1,0 +1,1303 @@
+#include "gc.h"
+
+static	char	resvreg[nelem(reg)];
+
+void
+ginit(void)
+{
+	Type *t;
+
+	thechar = '5';
+	thestring = "arm";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+	zprog.scond = 0xE;  
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMP;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	memset(reg, 0, sizeof(reg));
+	/* don't allocate */
+	reg[REGTMP] = 1;
+	reg[REGSB] = 1;
+	reg[REGSP] = 1;
+	reg[REGLINK] = 1;
+	reg[REGPC] = 1;
+	/* keep two external registers */
+	reg[REGEXT] = 1;
+	reg[REGEXT-1] = 1;
+	memmove(resvreg, reg, sizeof(reg));
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NFREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesuv[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG >= 0 && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+	int i;
+
+	for(i=REGRET+1; i<NREG; i++)
+		if(reg[i] == 0)
+			return i;
+	diag(Z, "out of fixed registers");
+	return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0 && resvreg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+	case TVLONG:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NFREG)
+				goto out;
+		}
+		j = 0*2 + NREG;
+		for(i=NREG; i<NREG+NFREG; i++) {
+			if(j >= NREG+NFREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	nodreg(n, tn, 0);
+	return;
+out:
+	reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_LONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = 0;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmovm(Node *f, Node *t, int w)
+{
+	gins(AMOVM, f, t);
+	p->scond |= C_UBIT;
+	if(w)
+		p->scond |= C_WBIT;
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod, nod1;
+	Prog *p1;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+	}
+
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			a = AMOVW;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		}
+		if(typechlp[ft] && typeilp[tt])
+			regalloc(&nod, t, t);
+		else
+			regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TVLONG:
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TVLONG:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			a = AMOVD;
+			if(ft == TFLOAT)
+				a = AMOVFD;
+			break;
+		case TFLOAT:
+			a = AMOVDF;
+			if(ft == TFLOAT)
+				a = AMOVF;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVDW;
+			if(ft == TFLOAT)
+				a = AMOVFW;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVDW;
+			if(ft == TFLOAT)
+				a = AMOVFW;
+			break;
+		}
+		break;
+	case TUINT:
+	case TULONG:
+		if(tt == TFLOAT || tt == TDOUBLE) {
+			// ugly and probably longer than necessary,
+			// but vfp has a single instruction for this,
+			// so hopefully it won't last long.
+			//
+			//	tmp = f
+			//	tmp1 = tmp & 0x80000000
+			//	tmp ^= tmp1
+			//	t = float(int32(tmp))
+			//	if(tmp1)
+			//		t += 2147483648.
+			//
+			regalloc(&nod, f, Z);
+			regalloc(&nod1, f, Z);
+			gins(AMOVW, f, &nod);
+			gins(AMOVW, &nod, &nod1);
+			gins(AAND, nodconst(0x80000000), &nod1);
+			gins(AEOR, &nod1, &nod);
+			if(tt == TFLOAT)
+				gins(AMOVWF, &nod, t);
+			else
+				gins(AMOVWD, &nod, t);
+			gins(ACMP, nodconst(0), Z);
+			raddr(&nod1, p);
+			gins(ABEQ, Z, Z);
+			regfree(&nod);
+			regfree(&nod1);
+			p1 = p;
+			regalloc(&nod, t, Z);
+			if(tt == TFLOAT) {
+				gins(AMOVF, nodfconst(2147483648.), &nod);
+				gins(AADDF, &nod, t);
+			} else {
+				gins(AMOVD, nodfconst(2147483648.), &nod);
+				gins(AADDD, &nod, t);
+			}
+			regfree(&nod);
+			patch(p1, pc);
+			return;
+		}
+		// fall through
+
+	case TINT:
+	case TLONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+			gins(AMOVWD, f, t);
+			return;
+		case TFLOAT:
+			gins(AMOVWF, f, t);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TUINT:
+		case TINT:
+		case TULONG:
+		case TLONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AMOVF || a == AMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gmover(Node *f, Node *t)
+{
+	int ft, tt, a;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+	a = AGOK;
+	if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){
+		switch(tt){
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		}
+	}
+	if(a == AGOK)
+		gmove(f, t);
+	else
+		gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et;
+	Adr ta;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		et = f1->type->etype;
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADD;
+		if(et == TFLOAT)
+			a = AADDF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		if(f2 && f2->op == OCONST) {
+			Node *t = f1;
+			f1 = f2;
+			f2 = t;
+			a = ARSB;
+		} else
+			a = ASUB;
+		if(et == TFLOAT)
+			a = ASUBF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ASUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AORR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AEOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		break;
+
+	case OFUNC:
+		a = ABL;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		a = AMUL;
+		if(et == TFLOAT)
+			a = AMULF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AMULD;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		a = ADIV;
+		if(et == TFLOAT)
+			a = ADIVF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ADIVD;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = AMOD;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULU;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		a = AMODU;
+		break;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVU;
+		break;
+
+	case OCASE:
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		a = ACMP;
+		if(et == TFLOAT)
+			a = ACMPF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ACMPD;
+		nextpc();
+		p->as = a;
+		naddr(f1, &p->from);
+		if(a == ACMP && f1->op == OCONST && p->from.offset < 0 &&
+		    p->from.offset != 0x80000000) {
+			p->as = ACMN;
+			p->from.offset = -p->from.offset;
+		}
+		raddr(f2, p);
+		switch(o) {
+		case OEQ:
+			a = ABEQ;
+			break;
+		case ONE:
+			a = ABNE;
+			break;
+		case OLT:
+			a = ABLT;
+			break;
+		case OLE:
+			a = ABLE;
+			break;
+		case OGE:
+			a = ABGE;
+			break;
+		case OGT:
+			a = ABGT;
+			break;
+		case OLO:
+			a = ABLO;
+			break;
+		case OLS:
+			a = ABLS;
+			break;
+		case OHS:
+			a = ABHS;
+			break;
+		case OHI:
+			a = ABHI;
+			break;
+		case OCASE:
+			nextpc();
+			p->as = ACASE;
+			p->scond = 0x9;
+			naddr(f2, &p->from);
+			a = ABHI;
+			break;
+		}
+		f1 = Z;
+		f2 = Z;
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AB;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	p->from.name = D_EXTERN;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= (vlong)(-32766) && vv < (vlong)32766)
+				return 1;
+			/*
+			 * should be specialised for constant values which will
+			 * fit in different instructionsl; for now, let 5l
+			 * sort it out
+			 */
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int
+sval(long v)
+{
+	int i;
+
+	for(i=0; i<16; i++) {
+		if((v & ~0xff) == 0)
+			return 1;
+		if((~v & ~0xff) == 0)
+			return 1;
+		v = (v<<2) | ((ulong)v>>30);
+	}
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= REGEXT-2)
+			return 0;
+		o = exregoffset;
+		if(reg[o] && !resvreg[o])
+			return 0;
+		resvreg[o] = reg[o] = 1;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= NFREG-1)
+			return 0;
+		o = exfregoffset + NREG;
+		if(reg[o] && !resvreg[o])
+			return 0;
+		resvreg[o] = reg[o] = 1;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
--- /dev/null
+++ b/utils/5coff/5coff.c
@@ -1,0 +1,316 @@
+#include	"auxi.h"
+
+#define RND(x, y)	((((x)+(y)-1)/(y))*(y))
+
+char *cmd;
+int sflag, dflag;
+
+int ifd, ofd;
+Fhdr ihdr;
+
+long	HEADR, INITTEXT, INITDAT, INITRND, INITENTRY;
+long textsize, datsize, bsssize;
+
+int cout;
+int thumb;
+
+static void	get_file(char *);
+static void	put_file(char *);
+static void	usage(char *);
+static long	strxtol(char *);
+static void	readsyms(void);
+
+char	*fail	= "error";
+
+void
+main(int argc, char	*argv[])
+{
+	char *a, *ifile, *ofile;
+
+	cmd = argv[0];
+
+	INITTEXT = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = -1;
+
+	ARGBEGIN {
+	/*
+	 * Options without args
+	 */
+	case 's':
+		sflag = 1;
+		break;
+	/*
+	 * Options with args
+	 */
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = strxtol(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = strxtol(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = strxtol(a);
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = strxtol(a);
+		break;
+	case 'd':
+		dflag |= strxtol(ARGF());
+		break;
+	default:
+		usage("Invalid option");
+	} ARGEND
+
+	if (argc != 2)
+		usage("Wrong number of arguments");
+
+	ifile = argv[0];
+	ofile = argv[1];
+
+	get_file(ifile);
+	put_file(ofile);
+	exits(0);
+}
+
+char usagemsg[] =
+"usage: %s  options infile outfile\n\t options (for outfile): -H[1234] -s -T<text> -D<data> -R<rnd> -E<entry>\n";
+
+static void
+usage(char *msg)
+{
+	fprint(2, "***Error: %s\n", msg);
+	fprint(2, usagemsg, cmd);
+	exits("usage");
+}
+
+static long
+strxtol(char *s)
+{
+	char *es;
+	int base = 0;
+	long r;
+
+	if (*s == '0')
+		if (*++s == 'x'){
+			base = 16;
+			s++;
+		}
+		else
+			base = 8;
+	r = strtol(s, &es, base);
+	if (*es)
+		usage("bad number");
+	return(r);
+}
+
+static void
+get_file(char *ifile)
+{
+	int h;
+
+	ifd = open(ifile, OREAD);
+	if (ifd < 0) {
+		fprint(2, "5coff: open %s: %r\n", ifile);
+		exits("open");
+	}
+	h = crackhdr(ifd, &ihdr);
+	if (!h || dflag){
+		fprint(2, "Crackhdr: %d, type: %d, name: %s\n", h, ihdr.type, ihdr.name);
+		fprint(2, "txt %llux, ent %llux, txtsz %lux, dataddr %llux\n",
+			ihdr.txtaddr, ihdr.entry, ihdr.txtsz, ihdr.dataddr);
+	}
+	if (!h)
+		usage("File type not recognized");
+	machbytype(ihdr.type);
+	if (dflag)
+		fprint(2, "name: <%s> pgsize:%ux\n", mach->name, mach->pgsize);
+
+	HEADR = 22+28+3*48;
+	if(INITTEXT == -1)
+		INITTEXT = ihdr.txtaddr;
+	else
+		ihdr.txtaddr = INITTEXT;
+	if(INITDAT == -1)
+		INITDAT = ihdr.dataddr;
+	else
+		ihdr.dataddr = INITDAT;
+	if(INITENTRY == -1)
+		INITENTRY = ihdr.entry;
+	else
+		ihdr.entry = INITENTRY;
+	textsize = ihdr.txtsz;
+	datsize = ihdr.datsz;
+	bsssize = ihdr.bsssz;
+	if(INITRND > 0)
+		ihdr.dataddr = INITDAT = RND(INITTEXT+textsize, INITRND);
+	if(0){
+		INITTEXT = INITENTRY;
+		INITDAT = RND(INITTEXT+textsize, 4);
+	}
+	if(0){
+		print("H=%lux T=%lux D=%lux t=%lux d=%lux b=%lux e=%lux\n", HEADR, INITTEXT, INITDAT, textsize, datsize, bsssize, INITENTRY);
+		print("%llux %llux %llux %lux %lux %lux\n", ihdr.txtaddr, ihdr.dataddr, ihdr.entry, ihdr.txtsz, ihdr.datsz, ihdr.bsssz);
+	}
+
+	readsyms();
+}
+
+#define WB	128
+#define WSAFE	(WB-4)
+char Wbuf[WB];
+char *wp = Wbuf;
+
+void
+cflush(void)
+{
+	if(wp > Wbuf)
+		write(ofd, Wbuf, wp-Wbuf);
+	wp = Wbuf;
+}
+
+void
+lput(long l)
+{
+	wp[0] = l>>24;
+	wp[1] = l>>16;
+	wp[2] = l>>8;
+	wp[3] = l;
+	wp += 4;
+	if(wp >= Wbuf+WSAFE)
+		cflush();
+}
+
+void
+cput(int l)
+{
+	wp[0] = l;
+	wp += 1;
+	if(wp >= Wbuf+WSAFE)
+		cflush();
+}
+
+void
+hputl(int l)
+{
+	wp[1] = l>>8;
+	wp[0] = l;
+	wp += 2;
+	if(wp >= Wbuf+WSAFE)
+		cflush();
+}
+
+void
+lputl(long l)
+{
+	wp[3] = l>>24;
+	wp[2] = l>>16;
+	wp[1] = l>>8;
+	wp[0] = l;
+	wp += 4;
+	if(wp >= Wbuf+WSAFE)
+		cflush();
+}
+
+static void
+copyseg(long sz)
+{
+	char	buf[1024];
+
+	cflush();
+	while (sz > 0){
+		long n;
+		long r;
+
+		n = sz;
+		if (n > sizeof buf)
+			n = sizeof buf;
+		sz -= n;
+
+		if ((r = read(ifd, buf, n)) != n){
+			fprint(2, "%ld = read(...%ld) at %ld\n", r, n, (long)seek(ifd, 0, 1));
+			perror("Premature eof");
+			exits(fail);
+		}
+		if ((r = write(ofd, buf, n)) != n){
+			fprint(2, "%ld = write(...%ld)\n", r, n);
+			perror("Write error!");
+			exits(fail);
+		}
+	}
+	cflush();
+}
+
+static void
+put_file(char *ofile)
+{
+	ofd = create(ofile, OWRITE, 0666);
+	if (ofd < 0) {
+		fprint(2, "5coff: create %s: %r\n", ofile);
+		exits("create");
+	}
+	cout = ofd;
+
+	/* TBS lput for Plan9 header before ? */
+
+	seek(ifd, ihdr.txtoff, 0);
+	seek(ofd, HEADR, 0);
+	copyseg(ihdr.txtsz);
+
+	seek(ifd, ihdr.datoff, 0);
+	seek(ofd, HEADR+textsize, 0);
+	copyseg(ihdr.datsz);
+
+	seek(ofd, HEADR+textsize+datsize, 0);
+	coffsym();
+	cflush();
+	cofflc();
+	cflush();
+	
+	seek(ofd, 0, 0);
+	coffhdr();
+	cflush();
+
+	close(ifd);
+	close(ofd);
+}
+
+long
+entryvalue(void)
+{
+	return INITENTRY;
+}
+
+void
+diag(char *s, ...)
+{
+	fprint(2, "%s\n", s);
+	exits("error");
+}
+
+static void
+readsyms(void)
+{
+	int i;
+	long n;
+	Sym *s;
+
+	if(sflag)
+		return;
+	n = syminit(ifd, &ihdr);
+	beginsym();
+	for(i = 0; i < n; i++){
+		s = getsym(i);
+		newsym(i, s->name, s->value, s->type);
+	}
+	endsym();
+}
--- /dev/null
+++ b/utils/5coff/NOTICE
@@ -1,0 +1,27 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 2001-2003 Vita Nuova Holdings Limited.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/5coff/auxi.c
@@ -1,0 +1,251 @@
+#include	"auxi.h"
+
+Prog *firstp, *textp, *curtext, *lastp, *etextp;
+Symx *hash[NHASH];
+Auto *lasta;
+long autosize;
+int version = 0;
+
+static int
+private(char *s)
+{
+	return strcmp(s, "safe") == 0 || strcmp(s, "ret") == 0 || strcmp(s, "string") == 0;
+}
+
+static int
+zlen(char *s)
+{
+	int i;
+
+	for(i=1; s[i] != 0 || s[i+1] != 0; i += 2)
+		;
+	i++;
+	return i+1;
+}
+
+static Symx*
+allocsym(char *symb, int l, int v)
+{
+	Symx *s;
+
+	s = malloc(sizeof(Symx));
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+	s->name[l-1] = '\0';
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->link = nil;
+	return s;
+}
+
+Symx*
+lookupsym(char *symb, int v)
+{
+	Symx *s, **as;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h]; s != nil; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+	s = allocsym(symb, l, v);
+	for(as = &hash[h]; *as != nil; as = &((*as)->link))
+		;
+	*as = s;
+	// s->link = hash[h];
+	// hash[h] = s;
+	return s;
+}
+
+static void
+addauto(Auto **aut, Symx *s, int t, long v)
+{
+	Auto *a, **aa;
+
+	a = (Auto*)malloc(sizeof(Auto));
+	a->asym = s;
+	a->link = nil;
+	a->aoffset = v;
+	a->type = t;
+	for(aa = aut; *aa != nil; aa = &((*aa)->link))
+		;
+	*aa = a;
+}
+
+static Prog*
+newprog(int as, long pc, long ln)
+{
+	Prog *p;
+
+	p = (Prog *)malloc(sizeof(Prog));
+	p->as = as;
+	p->pc = pc;
+	p->line = ln;
+	p->link = p->cond = P;
+	if(firstp == P)
+		firstp = p;
+	else
+		lastp->link = p;
+	lastp = p;
+	if(as == ATEXT){
+		if(textp == P)
+			textp = p;
+		else
+			etextp->cond = p;
+		etextp = p;	
+	}
+	return p;
+}
+
+static int
+line(long pc)
+{
+	char buf[1024], *s;
+
+	// return pc2line(pc);
+	if(fileline(buf, sizeof(buf), pc)){
+		for(s = buf; *s != ':' && *s != '\0'; s++)
+			;
+		if(*s != ':')
+			return -1;
+		return atoi(s+1);
+	}
+	return -1;
+}
+
+static void
+lines(long v)
+{
+	long ll, nl, pc;
+	if(etextp != P){
+		ll = 0;
+		for(pc = etextp->pc; pc < v; pc += 4){
+			nl = line(pc);
+			if(nl != -1 && nl != ll){
+				newprog(ATEXT-1, pc, nl);
+				ll = nl;
+			}
+		}
+		pc -= 4;
+		if(lastp->pc != pc){
+			nl = line(pc);
+			if(nl != -1)
+				newprog(ATEXT-1, pc, nl);
+		}
+	}
+}
+
+void
+beginsym(void)
+{
+}
+
+/* create the same structures as in 5l so we can use same coff.c source file */
+void
+newsym(int i, char *nm, long v, int t)
+{
+	long l, ver;
+	char *os;
+	Symx *s;
+	Prog *p;
+
+	if(i == 0 && (t == 't' || t == 'T') && strcmp(nm, "etext") == 0)
+		return;
+	if(nm[0] == '.' && private(nm+1))
+		return;
+// print("%s %ld %c\n", nm, v, t);
+	ver = 0;
+	if(t == 't' || t == 'l' || t == 'd' || t == 'b'){
+		ver = ++version;
+		if(ver == 0)
+			diag("0 version for static");
+	}
+	if(t == 'a' || t == 'p')
+		s = allocsym(nm, strlen(nm)+1, 0);
+	else if(t == 'z' || t == 'Z')
+		s = allocsym(nm, zlen(nm), 0);
+	else if(t != 'm'){
+		s = lookupsym(nm, ver);
+		if(s->type != 0)
+			diag("seen sym before in newsym");
+		s->value = v;
+	}
+	else
+		s = nil;
+	switch(t){
+	case 'T':
+	case 'L':
+	case 't':
+	case 'l':
+		lines(v);
+		if(t == 'l' || t == 'L')
+			s->type = SLEAF;
+		else
+			s->type = STEXT;
+		p = newprog(ATEXT, v, line(v));
+		p->from.sym = s;
+		p->to.autom = lasta;
+		lasta = nil;
+		break;
+	case 'D':
+	case 'd':
+		s->type = SDATA;
+		s->value -= INITDAT;
+		break;
+	case 'B':
+	case 'b':
+		s->type = SBSS;
+		s->value -= INITDAT;
+		break;
+	case 'f':
+		// version++;
+		s->type = SFILE;
+		os = s->name;
+		l = strlen(os)+1;
+		s->name = malloc(l+1);
+		s->name[0] = '>';
+		memmove(s->name+1, os, l);
+		free(os);
+		break;
+/*
+	case 'f'+'a'-'A':
+		s->type = SFILE;
+		break;
+*/
+	case 'z':
+		addauto(&lasta, s, D_FILE, v);
+		break;
+	case 'Z':
+		addauto(&lasta, s, D_FILE1, v);
+		break;
+	case 'a':
+		addauto(&(etextp->to.autom), s, D_AUTO, -v);
+		break;
+	case 'p':
+		addauto(&(etextp->to.autom), s, D_PARAM, v);
+		break;
+	case 'm':
+		etextp->to.offset = v-4;
+		autosize = v;
+		break;
+	default:
+		diag("bad case in newsym");
+		break;
+	}
+}
+
+void
+endsym(void)
+{
+	lines(INITTEXT+textsize);
+}
--- /dev/null
+++ b/utils/5coff/auxi.h
@@ -1,0 +1,46 @@
+#define	COFFCVT
+#define	Sym	Symx
+#include "../5l/l.h"
+#undef Sym
+#include	<mach.h>
+
+/*
+ * auxi.c
+ */
+extern	Symx *hash[NHASH];
+Symx	*lookupsym(char*, int);
+void	beginsym(void);
+void	endsym(void);
+void	newsym(int, char*, long, int);
+
+extern	long	autosize;
+extern	Prog *firstp, *textp, *curtext, *lastp, *etextp;
+
+/*
+ * coff.c
+ */
+void	coffhdr(void);
+void	coffsym(void);
+void	cofflc(void);
+void	endsym(void);
+
+/*
+ * 5coff.c
+ */
+void	cflush(void);
+void	lput(long);
+void	cput(int);
+void	hputl(int);
+void	lputl(long);
+long	entryvalue(void);
+void	diag(char*, ...);
+extern	long	HEADR;			/* length of header */
+extern	long	INITDAT;		/* data location */
+extern	long	INITRND;		/* data round above text location */
+extern	long	INITTEXT;		/* text location */
+extern	long	INITENTRY;		/* entry point */
+extern	long	textsize;
+extern	long	datsize;
+extern	long	bsssize;
+extern	int	cout;
+extern	int	thumb;
--- /dev/null
+++ b/utils/5coff/coff.c
@@ -1,0 +1,640 @@
+#include	"auxi.h"
+
+/*
+ * in some embedded coff files, edata and end have type 0 not 4,
+ * and file value is pointer to next file sym (as here), but the last one
+ * points to an external symbol, not 0 as here.
+ */
+
+#define C_NULL	0
+#define C_AUTO	1
+#define C_EXT		2
+#define C_STAT	3
+#define C_ARG		9
+#define C_FCN		101
+#define C_FILE		103
+
+#define T_VOID	0
+#define T_CHAR	2
+#define T_SHORT	3
+#define T_INT		4
+#define T_LONG	5
+
+#define DT_NON	0
+#define DT_PTR	1
+#define DT_FCN	2
+#define DT_ARY	3
+
+#define T(a, b)	(((a)<<4)|b)
+
+#define DOTTEXT	".text"
+#define DOTDATA	".data"
+#define DOTBSS	".bss"
+#define DOTBF		".bf"
+#define DOTEF		".ef"
+
+#define SINDEX(s)	(*((long*)(&s->become)))
+#define LINDEX(s)	(*((long*)(&s->used)))
+
+typedef struct Hist Hist;
+
+struct Hist{
+	Auto *a;
+	Hist *n;
+};
+
+static int nsym, nlc, lines;
+
+static void cofflcsz(void);
+
+static Hist *freeh, *curh;
+
+static void
+dohist(Auto *a)
+{
+	Hist *h, **ha;
+
+	if(a->aoffset == 1){	/* new file */
+		for(ha = &curh; *ha != nil; ha = &((*ha)->n))
+			;
+		*ha = freeh;
+		freeh = curh;
+		curh = nil;
+	}
+	if(freeh != nil){
+		h = freeh;
+		freeh = freeh->n;
+	}
+	else
+		h = malloc(sizeof(Hist));
+	h->a = a;
+	h->n = nil;
+	for(ha = &curh; *ha != nil; ha = &((*ha)->n))
+		;
+	*ha = h;
+}
+
+static long
+lineno(long n)
+{
+	long o, d;
+	Hist *h;
+
+	if(1)
+		return n;	/* now using fileline() not pc2line() */
+
+	if(curh == nil)
+		return 0;
+	o = curh->a->aoffset-1;
+	d = 1;
+	for(h = curh->n; d && h != nil; h = h->n){
+		if(h->a->asym->name[1] || h->a->asym->name[2]){
+			if(h->a->type == D_FILE1) {
+				;
+			}
+			else if(d == 1 && n < h->a->aoffset)
+				break;
+			else if(d++ == 1)
+				o -= h->a->aoffset;
+		}
+		else if(--d == 1)
+			o += h->a->aoffset;
+	}
+	return n-o;
+}
+
+static char *
+filelookup(int k)
+{
+	int i;
+	Symx *s;
+
+	for(i = 0; i < NHASH; i++){
+		for(s = hash[i]; s != nil; s = s->link){
+			if(s->type == SFILE && k == s->value)
+				return s->name+1;
+		}
+	}
+	return "";
+}
+
+static char*
+filename(char *s)
+{
+	int j, k, l;
+	static char buf[256];
+
+	buf[0] = '\0';
+	if(s[0] != 0)
+		diag("bad filename");
+	for(j = 1; ; j += 2){
+		k = (s[j]<<8)|s[j+1];
+		if(k == 0)
+			break;
+		l = strlen(buf);
+		if(l != 0 && buf[l-1] != '/')
+			strcat(buf, "/");
+		strcat(buf, filelookup(k));
+	}
+	return buf;
+}
+
+static void
+sput(char *s, int n)
+{
+	int i;
+
+	for(i = 0; i < n && s != nil && *s != '\0'; i++, s++)
+		cput(*s);
+	for( ; i < n; i++)
+		cput(0);
+}
+
+static void
+coffsect(char *s, long a, long sz, long o, long lp, long nl, long f)
+{
+	if(0)
+		print("sect %s pa=%lux va=%lux sz=%lux\n", s, a, a, sz);
+	sput(s, 8);			/* name <= 8 chars in len */
+	lputl(a);			/* pa */
+	lputl(a);			/* va */
+	lputl(sz);			/* size */
+	lputl(o);			/* file offset */
+	lputl(0);			/* reloc */
+	lputl(lp);			/* line nos */
+	lputl(0);			/* no reloc entries */
+	lputl(nl);			/* no line no entries */
+	lputl(f);			/* flags */
+	hputl(0);			/* reserved */
+	hputl(0);			/* mem page no */
+}
+
+void
+coffhdr(void)
+{
+	if(0){
+		print("H=%lux t=%lux d=%lux b=%lux\n", HEADR, textsize, datsize, bsssize);
+		print("e=%lux ts=%lux ds=%lux\n", entryvalue(), INITTEXT, INITDAT);
+	}
+
+	/*
+	 * file header
+	 */
+	hputl(0xc2);			/* version ID */
+	hputl(3);				/* no section hdrs */
+	lputl(0);				/* date stamp */
+	lputl(HEADR+textsize+datsize+6*nlc);	/* sym table */
+	lputl(nsym);			/* no sym table entries */
+	hputl(28);				/* size optional hdr */
+	hputl(0x0103);			/* flags */
+	hputl(0x97);			/* target ID */
+	/*
+	 * optional file header
+	 */
+	hputl(0x108);			/* magic */
+	hputl(0);				/* version stamp */
+	lputl(textsize);			/* text size */
+	lputl(datsize);			/* data size */
+	lputl(bsssize);			/* bss size */
+	lputl(entryvalue());		/* entry pt */
+	lputl(INITTEXT);		/* text start */
+	lputl(INITDAT);			/* data start */
+	/*
+	 * sections
+	 */
+	coffsect(DOTTEXT, INITTEXT, textsize, HEADR, HEADR+textsize+datsize, nlc, 0x20);
+	coffsect(DOTDATA, INITDAT, datsize, HEADR+textsize, 0, 0, 0x40);
+	coffsect(DOTBSS, INITDAT+datsize, bsssize, 0, 0, 0, 0x80);
+}
+
+static int
+private(char *s)
+{
+	return strcmp(s, "safe") == 0 || strcmp(s, "ret") == 0 || strcmp(s, "string") == 0;
+}
+
+static long stoff = 4;
+
+static long
+stput(char *s)
+{
+	long r;
+
+	r = stoff;
+	stoff += strlen(s)+1;
+	return r;
+}
+
+static long
+strput(char *s)
+{
+	int l;
+
+	if((l = strlen(s)) > 8){
+		if(*s == '.' && private(s+1))
+			return 0;
+		while(*s)
+			cput(*s++);
+		cput(*s);
+		return l+1;
+	}
+	return 0;
+}
+
+static void
+stflush(void)
+{
+	int i;
+	long o;
+	Prog *p;
+	Auto *a, *f;
+	Symx *s;
+	char *fn, file[256];
+
+	lputl(stoff);
+	o = 4;
+	for(p = firstp; p != P; p = p->link){
+		if(p->as == ATEXT){
+			f = nil;
+			fn = nil;
+			for(a = p->to.autom; a != nil; a = a->link){
+				if(a->type == D_FILE){
+					f = a;
+					break;
+				}
+			}
+			if(f != nil)
+				fn = filename(f->asym->name);
+			if(fn != nil && *fn != '\0' && strcmp(fn, file) != 0){
+				strcpy(file, fn);
+				o += strput(file);
+			}
+			o += strput(p->from.sym->name);
+			for(a = p->to.autom; a != nil; a = a->link){
+				if(a->type == D_AUTO || a->type == D_PARAM)
+					o += strput(a->asym->name);
+			}
+		}
+	}
+	for(i = 0; i < NHASH; i++){
+		for(s = hash[i]; s != nil; s = s->link){
+			if(s->version > 0 && (s->type == SDATA || s->type == SBSS))
+				o += strput(s->name);
+		}
+	}
+	for(i = 0; i < NHASH; i++){
+		for(s = hash[i]; s != nil; s = s->link){
+			if(s->version == 0 && (s->type == SDATA || s->type == SBSS))
+				o += strput(s->name);
+		}
+	}
+	if(o != stoff)
+		diag("bad stflush offset");
+}
+
+static int
+putsect(Symx *s)
+{
+	int sz, ln;
+
+	sz = ln = 0;
+	// isn't this repetition ?
+	if(strcmp(s->name, DOTTEXT) == 0){
+		sz = textsize;
+		ln = nlc;
+	}
+	else if(strcmp(s->name, DOTDATA) == 0)
+		sz = datsize;
+	else if(strcmp(s->name, DOTBSS) == 0)
+		sz = bsssize;
+	else
+		diag("bad putsect sym");
+	lputl(sz);
+	hputl(0);
+	hputl(ln);
+	sput(nil, 10);
+	return 1;
+}
+
+static int
+putfun(Symx *s)
+{
+	/* lputl(SINDEX(s)+2); */
+	lputl(0);
+	lputl(0);	/* patched later */
+	lputl(HEADR+textsize+datsize+LINDEX(s));
+	lputl(0);	/* patched later */
+	sput(nil, 2);
+	return 1;
+}
+
+static int
+putbf(int lno)
+{
+	lputl(0);
+	hputl(lno);
+	hputl(lines);
+	lputl(autosize);
+	lputl(0);	/* patched later */
+	sput(nil, 2);
+	return 1;
+}
+
+static int
+putef(int lno)
+{
+	sput(nil, 4);
+	hputl(lno);
+	sput(nil, 12);
+	return 1;
+}
+
+static int
+putsym(Symx *s, int sc, int t, int lno)
+{
+	long v;
+
+	if(s == nil || s->name == nil || s->name[0] == '\0' || (s->name[0] == '.' && private(s->name+1)))
+		return 0;
+	if(0)
+		print("putsym %s %d %ld %d %d\n", s->name, s->type, s->value, sc, t);
+	if(strlen(s->name) <= 8)
+		sput(s->name, 8);
+	else{
+		lputl(0);
+		lputl(stput(s->name));
+	}
+	/* value */
+	v = s->value;
+	if(s->type == SDATA || s->type == SDATA1 || s->type == SBSS)
+		lputl(INITDAT+v);
+	else if(sc == C_AUTO)
+		lputl(autosize+v);
+	else if(sc == C_ARG)
+		lputl(autosize+v+4);
+	else
+		lputl(v);
+	switch(s->type){	/* section number */
+	case STEXT:
+	case SLEAF:
+		hputl(1);
+		break;
+	case SDATA:
+	case SDATA1:
+		hputl(2);
+		break;
+	case SBSS:
+		hputl(3);
+		break;
+	case SFILE:
+		hputl(-2);
+		break;
+	default:
+		diag("type %d in putsym", s->type);
+		break;
+	}
+	hputl(t);			/* type */
+	cput(sc);			/* storage class */
+	/* aux entries */
+	if(sc == C_STAT && t == T_VOID && s->name[0] == '.'){	/* section */
+		cput(1);
+		return 1+putsect(s);
+	}
+	else if((t>>4) == DT_FCN){	/* function */
+		cput(1);
+		return 1+putfun(s);
+	}
+	else if(sc == C_FCN && strcmp(s->name, DOTBF) == 0){	/* bf */
+		cput(1);
+		return 1+putbf(lno);
+	}
+	else if(sc == C_FCN && strcmp(s->name, DOTEF) == 0){	/* ef */
+		cput(1);
+		return 1+putef(lno);
+	}
+	cput(0);			/* 0 aux entry */
+	return 1;
+}
+
+static Symx*
+defsym(char *p, int t, long v)
+{
+	Symx *s;
+
+	s = lookupsym(p, 0);
+	if(s->type == SDATA || s->type == SBSS)
+		return nil;		/* already output */
+	if(s->type == 0 || s->type == SXREF){
+		s->type = t;
+		s->value = v;
+	}
+	return s;
+}
+
+static int
+specsym(char *p, int t, long v, int c)
+{
+	return putsym(defsym(p, t, v), c, T_VOID, 0);
+}
+
+static int
+cclass(Symx *s)
+{
+/*
+	if(s->version > 0 && dclass == D_EXTERN)
+		diag("%s: version %d dclass EXTERN", s->name, s->version);
+	if(s->version == 0 && dclass == D_STATIC)
+		diag("%s: version %d dclass STATIC", s->name, s->version);
+*/
+	return s->version > 0 ? C_STAT : C_EXT;
+}
+
+static void
+patchsym(long i, long o, long v)
+{
+	long oo;
+
+	cflush();
+	oo = seek(cout, 0, 1);
+	seek(cout, HEADR+textsize+datsize+6*nlc+18*i+o, 0);
+	lputl(v);
+	cflush();
+	seek(cout, oo, 0);
+}
+
+void
+coffsym(void)
+{
+	int i;
+	long ns, lno, lpc, v, vs, lastf;
+	Prog *p;
+	Auto *a, *f;
+	Symx *s, *bf, *ef, ts;
+	char *fn, file[256];
+
+	file[0] = '\0';
+	cofflcsz();
+	seek(cout, 6*nlc, 1);		/* advance over line table */
+	ns = 0;
+	lpc = -1;
+	lno = -1;
+	lastf = -1;
+	bf = defsym(DOTBF, STEXT, 0);
+	ef = defsym(DOTEF, STEXT, 0);
+	for(p = firstp; p != P; p = p->link){
+		if(p->as != ATEXT){
+			if(p->line != 0)
+				lno = lineno(p->line);
+		}
+		if(p->as == ATEXT){
+			curtext = p;
+			autosize = p->to.offset+4;
+			if(lpc >= 0){
+				ef->value = lpc;
+				ns += putsym(ef, C_FCN, T_VOID, lno);
+			}
+			f = nil;
+			fn = nil;
+			for(a = p->to.autom; a != nil; a = a->link){
+				if(a->type == D_FILE || a->type == D_FILE1)
+					dohist(a);
+				if(f == nil && a->type == D_FILE)
+					f = a;		/* main filename */
+			}
+			if(f != nil)
+				fn = filename(f->asym->name);
+			if(fn != nil && *fn != '\0' && strcmp(fn, file) != 0){
+				strcpy(file, fn);
+				ts.name = file;
+				ts.type = SFILE;
+				ts.value = 0;
+				if(lastf >= 0)
+					patchsym(lastf, 8, ns);
+				lastf = ns;
+				ns += putsym(&ts, C_FILE, T_VOID, 0);
+			}
+			if(p->link != P && p->link->line != 0)
+				lno = lineno(p->link->line);
+			else if(p->line != 0)
+				lno = lineno(p->line);
+			s = p->from.sym;
+			SINDEX(s) = ns;
+			ns += putsym(s, cclass(s), T(DT_FCN, T_INT), 0);
+			if(p->cond != P)
+				lines = LINDEX(p->cond->from.sym)-LINDEX(s)-1;
+			else
+				lines = 0;
+			bf->value = p->pc;
+			ns += putsym(bf, C_FCN, T_VOID, lno);
+			for(a = p->to.autom; a != nil; a = a->link){
+				if(a->type == D_AUTO || a->type == D_PARAM){
+					ts.name = a->asym->name;
+					ts.type = STEXT;
+					ts.value = a->aoffset;
+					ns += putsym(&ts, a->type == D_AUTO ? C_AUTO : C_ARG, T_INT, 0);
+				}
+			}
+		}
+		lpc = p->pc;
+	}
+	if(lpc >= 0){
+		ef->value = lpc;
+		ns += putsym(ef, C_FCN, T_VOID, lno);
+	}
+	/* patch up */
+	for(p = textp; p != P; p = p->cond){
+		s = p->from.sym;
+		if(p->cond != P){
+			v = SINDEX(p->cond->from.sym);
+			vs = p->cond->pc - p->pc;
+		}
+		else{
+			v = 0;
+			vs = INITTEXT+textsize-p->pc;
+		}
+		patchsym(SINDEX(s)+1, 4, 8*vs);
+		patchsym(SINDEX(s)+1, 12, v);
+		patchsym(SINDEX(s)+3, 12, v);
+	}
+	for(i = 0; i < NHASH; i++){
+		for(s = hash[i]; s != nil; s = s->link){
+			if(s->version > 0 && (s->type == SDATA || s->type == SBSS))
+				ns += putsym(s, cclass(s), T_INT, 0);
+		}
+	}
+	for(i = 0; i < NHASH; i++){
+		for(s = hash[i]; s != nil; s = s->link){
+			if(s->version == 0 && (s->type == SDATA || s->type == SBSS))
+				ns += putsym(s, cclass(s), T_INT, 0);
+		}
+	}
+	ns += specsym(DOTTEXT, STEXT, INITTEXT, C_STAT);
+	ns += specsym(DOTDATA, SDATA, 0, C_STAT);
+	ns += specsym(DOTBSS, SBSS, datsize, C_STAT);
+	ns += specsym("etext", STEXT, INITTEXT+textsize, C_EXT);
+	ns += specsym("edata", SDATA, datsize, C_EXT);
+	ns += specsym("end", SBSS, datsize+bsssize, C_EXT);
+	nsym = ns;
+	stflush();
+}
+
+void
+cofflc(void)
+{
+	long olc, nl;
+	Symx *s;
+	Prog *p;
+	Auto *a;
+
+	cflush();
+	seek(cout, HEADR+textsize+datsize, 0);
+	nl = 0;
+	/* opc = INITTEXT; */
+	olc = 0;
+	for(p = firstp; p != P; p = p->link){
+		if(p->as == ATEXT){
+			curtext = p;
+			s = p->from.sym;
+			/* opc = p->pc; */
+			for(a = p->to.autom; a != nil; a = a->link){
+				if(a->type == D_FILE || a->type == D_FILE1)
+					dohist(a);
+			}
+			lputl(SINDEX(s));
+			hputl(0);
+			nl++;
+			continue;
+		}
+		if(p->line == 0 || p->line == olc || p->as == ANOP)
+			continue;
+		lputl(p->pc);
+		hputl(lineno(p->line));
+		nl++;
+		olc = p->line;
+	}
+	if(nl != nlc)
+		diag("bad line count in cofflc()");
+	nlc = nl;
+}
+
+static void
+cofflcsz(void)
+{
+	long olc, nl;
+	Prog *p;
+
+	nl = 0;
+	olc = 0;
+	for(p = firstp; p != P; p = p->link){
+		if(p->as == ATEXT){
+			LINDEX(p->from.sym) = nl;
+			nl++;
+			continue;
+		}
+		if(p->line == 0 || p->line == olc || p->as == ANOP)
+			continue;
+		nl++;
+		olc = p->line;
+	}
+	nlc = nl;
+}
--- /dev/null
+++ b/utils/5coff/mkfile
@@ -1,0 +1,23 @@
+<../../mkconfig
+
+TARG=5coff
+
+OFILES=	5coff.$O\
+		coff.$O\
+		auxi.$O\
+
+HFILES=\
+	a.out.h\
+	bio.h\
+	mach.h\
+
+LIBS=mach bio 9	# order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$O.readcoff:	readcoff.$O
+	$LD $LDFLAGS -o $target readcoff.$O $libs $SYSLIBS
--- /dev/null
+++ b/utils/5coff/readcoff.c
@@ -1,0 +1,298 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	<mach.h>
+
+int fd;
+static void readf(void);
+
+static void
+usage(char *msg)
+{
+	fprint(2, "***Error: %s\n", msg);
+	exits("usage");
+}
+
+static int
+cget(void)
+{
+	uchar b[1];
+
+	if(read(fd, b, 1) != 1){
+		fprint(2, "bad cget\n");
+		exits("cget");
+	}
+	return b[0];
+}
+
+static int
+hget(void)
+{
+	uchar b[2];
+
+	if(read(fd, b, 2) != 2){
+		fprint(2, "bad hget\n");
+		exits("hget");
+	}
+	return b[1]<<8 | b[0];
+}
+
+static int
+lget(void)
+{
+	uchar b[4];
+
+	if(read(fd, b, 4) != 4){
+		fprint(2, "bad lget\n");
+		exits("lget");
+	}
+	return b[3]<<24 | b[2]<<16 | b[1]<<8 | b[0];
+}
+
+static char *
+sget(char *st)
+{
+	int i;
+	static uchar buf[8+1];
+
+	for(i = 0; i < 8+1; i++)
+		buf[i] = 0;
+	if(read(fd, buf, 8) != 8){
+		fprint(2, "bad sget\n");
+		exits("sget");
+	}
+	if(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
+		return st+(buf[7]<<24|buf[6]<<16|buf[5]<<8|buf[4]);
+	return (char*)buf;
+}
+		
+void
+main(int argc, char	*argv[])
+{
+	if (argc != 2)
+		usage("Wrong number of arguments");
+
+	fd = open(argv[1], OREAD);
+	if (fd < 0) {
+		fprint(2, "5coff: open %s: %r\n", argv[1]);
+		exits("open");
+	}
+	readf();
+	exits(0);
+}
+
+static void
+section(int i, char *st, int *linoff, int *linn)
+{
+	int pa, va, sz, off, rel, lin, nrel, nlin, f, res, pno;
+	char *nm;
+
+	nm = sget(st);
+	pa = lget();
+	va = lget();
+	sz = lget();
+	off = lget();
+	rel = lget();
+	lin = lget();
+	nrel = lget();
+	nlin = lget();
+	f = lget();
+	res = hget();
+	pno = hget();
+	print("sect %d %s: pa=0x%x va=0x%x sz=%d off=%d rel=%d lin=%d nrel=%d nlin=%d f=0x%x res=%d pno=%d\n", i, nm, pa, va, sz, off, rel, lin, nrel, nlin, f, res, pno);
+	*linoff = lin;
+	*linn = nlin;
+}
+
+static void
+opthdr(void)
+{
+	int mag, ver, textsz, datasz, bsssz, entry, text, data;
+
+	mag = hget();
+	ver = hget();
+	textsz = lget();
+	datasz = lget();
+	bsssz = lget();
+	entry = lget();
+	text = lget();
+	data = lget();
+	print("opt: mag=0x%x ver=%d txtsz=%d datsz=%d bsssz=%d ent=0x%x txt=0x%x dat=0x%x\n", mag, ver, textsz, datasz, bsssz, entry, text, data);
+}
+
+static void
+readhdr(int *o, int *ns, int *sy, int *nsy)
+{
+	int vid, nsec, date, sym, nsym, opt, f, tid;
+
+	vid = hget();
+	nsec = hget();
+	date = lget();
+	sym = lget();
+	nsym = lget();
+	opt = hget();
+	f = hget();
+	tid = hget();
+	print("hdr: vid=0x%x nsect=%d date=%d sym=%d nsym=%d opt=%d f=0x%x tid=0x%x\n", vid, nsec, date, sym, nsym, opt, f, tid);
+	*o = opt;
+	*ns = nsec;
+	*sy = sym;
+	*nsy = nsym;
+}
+
+static void
+readauxsect(int i)
+{
+	int sz, nrel, ln;
+
+	sz = lget();
+	nrel = hget();
+	ln = hget();
+	lget();
+	hget();
+	lget();
+	print("sym auxsect %d: sz=%d nrel=%d ln=%d\n", i, sz, nrel, ln);
+}
+
+static void
+readauxfun(int i)
+{
+	int ind, sz, fpln, nind;
+
+	ind = lget();
+	sz = lget();
+	fpln = lget();
+	nind = lget();
+	hget();
+	print("sym auxfun %d: ind=%d sz=%d fpln=%d nind=%d\n", i, ind, sz, fpln, nind);
+}
+
+static void
+readauxbf(int i)
+{
+	int rsav, lno, lns, fsz, nind;
+
+	rsav = lget();
+	lno = hget();
+	lns = hget();
+	fsz = lget();
+	nind = lget();
+	hget();
+	print("sym auxbf %d: rsav=%x lno=%d lns=%d fsz=%d nind=%d\n", i, rsav, lno, lns, fsz, nind);
+}
+
+static void
+readauxef(int i)
+{
+	int lno;
+
+	lget();
+	lno = hget();
+	lget();
+	lget();
+	lget();
+	print("sym auxef %d: lno=%d\n", i, lno);
+}
+
+static void
+readauxother(int i)
+{
+	lget();
+	lget();
+	hget();
+	lget();
+	lget();
+	print("sym auxother %d\n", i);
+}
+
+static int
+readsym(int i, char *st)
+{
+	int v, s, t, c, aux;
+	char *nm;
+
+	nm = sget(st);
+	v = lget();
+	s = hget();
+	t = hget();
+	c = cget();
+	aux = cget();
+	print("sym %d %s: val=%d sec=%d type=%d class=%d aux=%d\n", i, nm, v, s, t, c, aux);
+	if(aux){
+		i++;
+		if(strcmp(nm, ".text") == 0 || strcmp(nm, ".data") == 0 || strcmp(nm, ".bss") == 0)
+			readauxsect(i);
+		else if(strcmp(nm, ".bf") == 0)
+			readauxbf(i);
+		else if(strcmp(nm, ".ef") == 0)
+			readauxef(i);
+		else if((t&0x30) == 0x20)	// will do
+			readauxfun(i);
+		else
+			readauxother(i);
+		return 1;
+	}
+	return 0;
+}
+
+static char *
+readstr(int n)
+{
+	char *s = malloc(n);
+
+	if(read(fd, s+4, n-4) != n-4){
+		fprint(2, "bad readstr\n");
+		exits("sget");
+	}
+	return s;
+}
+
+static void
+readln(int i)
+{
+	int a, l;
+
+	a = lget();
+	l = hget();
+	if(l == 0)
+		print("line %d: sym=%d\n", i, a);
+	else
+		print("line %d: addr=0x%x line=%d\n", i, a, l);
+}
+	
+static void
+readf()
+{
+	int i, opt, nsec, sym, nsym, stoff, strsz, linoff, nlin, lino, linn;
+	char *st;
+
+	seek(fd, 0, 0);
+	readhdr(&opt, &nsec, &sym, &nsym);
+	if(opt)
+		opthdr();
+	stoff = sym+18*nsym;
+	seek(fd, stoff, 0);
+	strsz = lget();
+	st = readstr(strsz);
+	linoff = nlin = 0;
+	seek(fd, 22+28, 0);
+	for(i = 0; i < nsec; i++){
+		section(i, st, &lino, &linn);
+		if(linn != 0){
+			if(nlin == 0){
+				nlin = linn;
+				linoff = lino;
+			}
+			else
+				print("multiple line no. tables\n");
+		}
+	}
+	seek(fd, sym, 0);
+	for(i = 0; i < nsym; i++)
+		i += readsym(i, st);
+	print("strsz = %d\n", strsz);
+	if(nlin != 0){
+		seek(fd, linoff, 0);
+		for(i = 0; i < nlin; i++)
+			readln(i);
+	}
+}
--- /dev/null
+++ b/utils/5cv/5cv.c
@@ -1,0 +1,353 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+char *Cmd;
+int Hdrtype;
+int Strip;
+long Txtaddr = -1;
+
+int Ofd;
+int Ifd;
+Fhdr Ihdr;
+
+int Debug;
+
+static void	get_file(char *);
+static void	put_file(char *);
+static void	Usage(char *);
+static long	strxtol(char *);
+
+char	*fail	= "error";
+
+void
+main(int argc, char	*argv[])
+{
+	char *ifile, *ofile;
+
+	Cmd = argv[0];
+	Hdrtype = 2;
+
+	ARGBEGIN {
+	/*
+	 * Options without args
+	 */
+	case 's':
+		Strip = 1;
+		break;
+	/*
+	 * Options with args
+	 */
+	case 'T':
+		Txtaddr = strxtol(ARGF());
+		break;
+	case 'H':
+		Hdrtype = strxtol(ARGF());
+		break;
+	case 'D':
+		Debug |= strxtol(ARGF());
+		break;
+	default:
+		Usage("Invalid option");
+	} ARGEND
+
+	if (argc != 2)
+		Usage("Wrong number of arguments");
+
+	ifile = argv[0];
+	ofile = argv[1];
+
+	get_file(ifile);
+	put_file(ofile);
+	exits(0);
+}
+
+char usagemsg[] =
+"Usage: %s  options infile outfile\n\t options (for outfile): -H[123456] -s -T<text> \n";
+
+static void
+Usage(char *msg)
+{
+	fprint(2, "***Error: %s\n", msg);
+	fprint(2, usagemsg, Cmd);
+	exits("usage");
+}
+
+static long
+strxtol(char *s)
+{
+	char *es;
+	int base = 0;
+	long r;
+
+	if (*s == '0')
+		if (*++s == 'x'){
+			base = 16;
+			s++;
+		}
+		else
+			base = 8;
+	r = strtol(s, &es, base);
+	if (*es)
+		Usage("bad number");
+	return(r);
+}
+
+static void
+get_file(char *ifile)
+{
+	int h;
+	int d;
+
+	Ifd = open(ifile, OREAD);
+	if (Ifd < 0) {
+		fprint(2, "5cv: open %s: %r\n", ifile);
+		exits("open");
+	}
+	h = crackhdr(Ifd, &Ihdr);
+	if (!h || Debug){
+		fprint(2, "Crackhdr: %d, type: %d, name: %s\n", h, Ihdr.type, Ihdr.name);
+		fprint(2, "txt %llux, ent %llux, txtsz %lux, dataddr %llux\n",
+			Ihdr.txtaddr, Ihdr.entry, Ihdr.txtsz, Ihdr.dataddr);
+	}
+	if (!h)
+		Usage("File type not recognized");
+	machbytype(Ihdr.type);
+	if (Debug)
+		fprint(2, "name: <%s> pgsize:%ux\n", mach->name, mach->pgsize);
+
+	if (Txtaddr != -1){
+		d = Txtaddr - Ihdr.txtaddr;
+		Ihdr.txtaddr += d;
+		Ihdr.dataddr = Ihdr.txtaddr + Ihdr.txtsz;
+	}
+}
+
+char Wbuf[128];
+char *wp = Wbuf;
+
+void
+lput(long l)
+{
+	wp[0] = l>>24;
+	wp[1] = l>>16;
+	wp[2] = l>>8;
+	wp[3] = l;
+	wp += 4;
+}
+
+void
+lputl(long l)
+{
+	wp[3] = l>>24;
+	wp[2] = l>>16;
+	wp[1] = l>>8;
+	wp[0] = l;
+	wp += 4;
+}
+
+static void
+copyseg(long sz)
+{
+	char	buf[1024];
+
+	while (sz > 0){
+		long n;
+		long r;
+
+		n = sz;
+		if (n > sizeof buf)
+			n = sizeof buf;
+		sz -= n;
+
+		if ((r = read(Ifd, buf, n)) != n){
+			fprint(2, "%ld = read(...%ld) at %ld\n", r, n, (long)seek(Ifd, 0, 1));
+			perror("Premature eof");
+			exits(fail);
+		}
+		if ((r = write(Ofd, buf, n)) != n){
+			fprint(2, "%ld = write(...%ld)\n", r, n);
+			perror("Write error!");
+			exits(fail);
+		}
+	}
+}
+
+static void
+zero(long sz)
+{
+	char	buf[1024];
+
+	memset(buf, 0, sizeof buf);
+	while (sz > 0){
+		long n;
+		long r;
+
+		n = sz;
+		if (n > sizeof buf)
+			n = sizeof buf;
+		sz -= n;
+
+		if ((r = write(Ofd, buf, n)) != n){
+			fprint(2, "%ld = write(...%ld)\n", r, n);
+			perror("Write error!");
+			exits(fail);
+		}
+	}
+}
+
+static long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
+
+static void
+put_file(char *ofile)
+{
+	int ii;
+	long doff;
+	long dsize;
+	long hlen;
+	long pad;
+
+	Ofd = create(ofile, OWRITE, 0666);
+	if (Ofd < 0) {
+		fprint(2, "5cv: create %s: %r\n", ofile);
+		exits("create");
+	}
+
+	pad = 0;
+
+	switch(Hdrtype) {
+	case 1:	/* aif for risc os */
+		Strip = 1;
+		hlen = 128;
+		lputl(0xe1a00000);		/* NOP - decompress code */
+		lputl(0xe1a00000);		/* NOP - relocation code */
+		lputl(0xeb000000 + 12);		/* BL - zero init code */
+		lputl(0xeb000000 +
+			(Ihdr.entry
+			 - Ihdr.txtaddr
+			 + hlen
+			 - 12
+			 - 8) / 4);		/* BL - entry code */
+
+		lputl(0xef000011);		/* SWI - exit code */
+		doff = Ihdr.txtsz+hlen;
+		lputl(doff);			/* text size */
+		dsize = Ihdr.datsz;
+		lputl(dsize);			/* data size */
+		lputl(0);			/* sym size */
+
+		lputl(Ihdr.bsssz);		/* bss size */
+		lputl(0);			/* sym type */
+		lputl(Ihdr.txtaddr-hlen);	/* text addr */
+		lputl(0);			/* workspace - ignored */
+
+		lputl(32);			/* addr mode / data addr flag */
+		lputl(0);			/* data addr */
+		for(ii=0; ii<2; ii++)
+			lputl(0);		/* reserved */
+
+		for(ii=0; ii<15; ii++)
+			lputl(0xe1a00000);	/* NOP - zero init code */
+		lputl(0xe1a0f00e);		/* B (R14) - zero init return */
+		break;
+
+	case 2:	/* plan 9 */
+		hlen = 32;
+		doff = hlen + Ihdr.txtsz;
+		dsize = Ihdr.datsz;
+		lput(0x647);			/* magic */
+		lput(Ihdr.txtsz);		/* sizes */
+		lput(Ihdr.datsz);
+		lput(Ihdr.bsssz);
+		if (Strip)			/* nsyms */
+			lput(0);
+		else
+			lput(Ihdr.symsz);
+		lput(Ihdr.entry);		/* va of entry */
+		lput(0L);
+		lput(Ihdr.lnpcsz);
+		break;
+
+	case 3:	/* boot for NetBSD */
+		hlen = 32;
+		doff = rnd(hlen+Ihdr.txtsz, 4096);
+		dsize = rnd(Ihdr.datsz, 4096);
+		lput((143<<16)|0413);		/* magic */
+		lputl(doff);
+		lputl(dsize);
+		lputl(Ihdr.bsssz);
+		if (Strip)			/* nsyms */
+			lputl(0);
+		else
+			lputl(Ihdr.symsz);
+		lputl(Ihdr.entry);		/* va of entry */
+		lputl(0L);
+		lputl(0L);
+		break;
+	case 4:	/* no header, stripped, padded to 2K, for serial bootstrap */
+		hlen = 0;
+		Strip = 1;
+		doff = hlen + Ihdr.txtsz;
+		dsize = Ihdr.datsz;
+		pad = 2048;
+		break;
+	case 5:	/* no header, stripped, for all sorts */
+		hlen = 0;
+		Strip = 1;
+		doff = hlen + Ihdr.txtsz;
+		dsize = Ihdr.datsz;
+		break;
+	case 6:	/* fake EPOC IMG format header */
+		hlen = 256;
+		*wp++ = 'E';
+		*wp++ = 'P';
+		Strip = 1;
+		doff = hlen + Ihdr.txtsz;
+		dsize = Ihdr.datsz;
+		break;
+	default:
+		Usage("Bad -Htype");
+		return;
+	}
+	write(Ofd, Wbuf, hlen);
+
+	seek(Ifd, Ihdr.txtoff, 0);
+	copyseg(Ihdr.txtsz);
+
+	seek(Ifd, Ihdr.datoff, 0);
+	seek(Ofd, doff, 0);
+	copyseg(Ihdr.datsz);
+
+	if (!Strip) {
+		/* Write symbols */
+		seek(Ofd, doff + dsize, 0);
+		if (Ihdr.symsz){
+			seek(Ifd, Ihdr.symoff, 0);
+			copyseg(Ihdr.symsz);
+		}
+		if (Hdrtype == 2)
+			copyseg(Ihdr.lnpcsz);
+	}
+
+	if (pad) {
+		if (doff + Ihdr.datsz > pad) {
+			perror("Too big!");
+			exits(fail);
+		}
+		else if (doff + Ihdr.datsz < pad)
+			zero(pad - (doff + Ihdr.datsz));
+	}
+}
--- /dev/null
+++ b/utils/5cv/mkfile
@@ -1,0 +1,18 @@
+<../../mkconfig
+
+TARG=5cv
+
+OFILES=	5cv.$O\
+
+HFILES=\
+	a.out.h\
+	bio.h\
+	mach.h\
+
+LIBS=mach bio 9	# order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/5l/asm.c
@@ -1,0 +1,1806 @@
+#include	"l.h"
+
+long	OFFSET;
+
+static Prog *PP;
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	switch(s->type) {
+	case STEXT:
+	case SLEAF:
+		break;
+	case SDATA:
+		if(dlm)
+			return s->value+INITDAT;
+	default:
+		diag("entry not text: %s", s->name);
+	}
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t, etext;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	OFFSET = HEADR;
+	seek(cout, OFFSET, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 4;
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		asmout(p, o);
+		pc += o->size;
+	}
+
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	/* output strings in text segment */
+	etext = INITTEXT + textsize;
+	for(t = pc; t < etext; t += sizeof(buf)-100) {
+		if(etext-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 1);
+		else
+			datblk(t, etext-t, 1);
+	}
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 1:
+	case 2:
+	case 5:
+	case 7:
+		OFFSET = HEADR+textsize;
+		seek(cout, OFFSET, 0);
+		break;
+	case 3:
+	case 6:	/* no header, padded segments */
+		OFFSET = rnd(HEADR+textsize, 4096);
+		seek(cout, OFFSET, 0);
+		break;
+	}
+	if(dlm){
+		char buf[8];
+
+		write(cout, buf, INITDAT-textsize);
+		textsize = INITDAT;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 0);
+		else
+			datblk(t, datsize-t, 0);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 1:
+		case 4:
+		case 5:
+			debug['s'] = 1;
+			break;
+		case 2:
+			OFFSET = HEADR+textsize+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		case 3:
+		case 6:	/* no header, padded segments */
+			OFFSET += rnd(datsize, 4096);
+			seek(cout, OFFSET, 0);
+			break;
+		case 7:
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		if(dlm)
+			asmdyn();
+		cflush();
+	}
+	else if(dlm){
+		seek(cout, HEADR+textsize+datsize, 0);
+		asmdyn();
+		cflush();
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f header\n", cputime());
+	Bflush(&bso);
+	OFFSET = 0;
+	seek(cout, OFFSET, 0);
+	switch(HEADTYPE) {
+	case 0:	/* no header */
+	case 6:	/* no header, padded segments */
+		break;
+	case 1:	/* aif for risc os */
+		lputl(0xe1a00000);		/* NOP - decompress code */
+		lputl(0xe1a00000);		/* NOP - relocation code */
+		lputl(0xeb000000 + 12);		/* BL - zero init code */
+		lputl(0xeb000000 +
+			(entryvalue()
+			 - INITTEXT
+			 + HEADR
+			 - 12
+			 - 8) / 4);		/* BL - entry code */
+
+		lputl(0xef000011);		/* SWI - exit code */
+		lputl(textsize+HEADR);		/* text size */
+		lputl(datsize);			/* data size */
+		lputl(0);			/* sym size */
+
+		lputl(bsssize);			/* bss size */
+		lputl(0);			/* sym type */
+		lputl(INITTEXT-HEADR);		/* text addr */
+		lputl(0);			/* workspace - ignored */
+
+		lputl(32);			/* addr mode / data addr flag */
+		lputl(0);			/* data addr */
+		for(t=0; t<2; t++)
+			lputl(0);		/* reserved */
+
+		for(t=0; t<15; t++)
+			lputl(0xe1a00000);	/* NOP - zero init code */
+		lputl(0xe1a0f00e);		/* B (R14) - zero init return */
+		break;
+	case 2:	/* plan 9 */
+		if(dlm)
+			lput(0x80000000|0x647);	/* magic */
+		else
+			lput(0x647);			/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 3:	/* boot for NetBSD */
+		lput((143<<16)|0413);		/* magic */
+		lputl(rnd(HEADR+textsize, 4096));
+		lputl(rnd(datsize, 4096));
+		lputl(bsssize);
+		lputl(symsize);			/* nsyms */
+		lputl(entryvalue());		/* va of entry */
+		lputl(0L);
+		lputl(0L);
+		break;
+	case 4: /* boot for IXP1200 */
+		break;
+	case 5: /* boot for ipaq */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		break;
+	case 7:	/* elf */
+		debug['S'] = 1;			/* symbol table */
+		elf32(ARM, ELFDATA2LSB, 0, nil);
+		break;
+	}
+	cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		cput(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		cput(0);
+}
+
+void
+cput(int c)
+{
+	cbp[0] = c;
+	cbp++;
+	cbc--;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+wput(long l)
+{
+
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+wputl(long l)
+{
+
+	cbp[0] = l;
+	cbp[1] = l>>8;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)
+{
+
+	cbp[0] = l>>24;
+	cbp[1] = l>>16;
+	cbp[2] = l>>8;
+	cbp[3] = l;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lputl(long l)
+{
+
+	cbp[3] = l>>24;
+	cbp[2] = l>>16;
+	cbp[1] = l>>8;
+	cbp[0] = l;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+	if(c->outof)
+	Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+		c->outof - c->count, c->outof,
+		(double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SSTRING:
+				putsymb(s->name, 'T', s->value, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	lput(v);
+	if(ver)
+		t += 'a' - 'A';
+	cput(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		cput(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			cput(s[i]);
+			cput(s[i+1]);
+		}
+		cput(0);
+		cput(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			cput(s[i]);
+		cput(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			cput(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			cput(0);	/* 0 vv +lc */
+			cput(s>>24);
+			cput(s>>16);
+			cput(s>>8);
+			cput(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			cput(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			cput(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		cput(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n, int str)
+{
+	Sym *v;
+	Prog *p;
+	char *cast;
+	long a, l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		if(str != (p->from.sym->type == SSTRING))
+			continue;
+		curp = p;
+		a = p->from.sym->value + p->from.offset;
+		l = a - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			v = p->to.sym;
+			if(v) {
+				switch(v->type) {
+				case SUNDEF:
+					ckoff(v, d);
+				case STEXT:
+				case SLEAF:
+				case SSTRING:
+					d += p->to.sym->value;
+					break;
+				case SDATA:
+				case SBSS:
+					d += p->to.sym->value + INITDAT;
+				}
+				if(dlm)
+					dynreloc(v, a+INITDAT, 1);
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+void
+asmout(Prog *p, Optab *o)
+{
+	long o1, o2, o3, o4, o5, o6, v;
+	int r, rf, rt, rt2;
+	Sym *s;
+
+PP = p;
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	o6 = 0;
+	switch(o->type) {
+	default:
+		diag("unknown asm %d", o->type);
+		prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		break;
+
+	case 1:		/* op R,[R],R */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= rf | (r<<16) | (rt<<12);
+		break;
+
+	case 2:		/* movbu $I,[R],R */
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		o1 |= immrot(instoffset);
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 3:		/* add R<<[IR],[R],R */
+	mov:
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		o1 |= p->from.offset;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 4:		/* add $I,[R],R */
+		aclass(&p->from);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(instoffset);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 |= r << 16;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 5:		/* bra s */
+		v = -8;
+		if(p->cond == UP) {
+			s = p->to.sym;
+			if(s->type != SUNDEF)
+				diag("bad branch sym type");
+			v = (ulong)s->value >> (Roffset-2);
+			dynreloc(s, p->pc, 0);
+		}
+		else if(p->cond != P)
+			v = (p->cond->pc - pc) - 8;
+		o1 = opbra(p->as, p->scond);
+		o1 |= (v >> 2) & 0xffffff;
+		break;
+
+	case 6:		/* b ,O(R) -> add $O,R,PC */
+		aclass(&p->to);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(instoffset);
+		o1 |= p->to.reg << 16;
+		o1 |= REGPC << 12;
+		break;
+
+	case 7:		/* bl ,O(R) -> mov PC,link; add $O,R,PC */
+		aclass(&p->to);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(0);
+		o1 |= REGPC << 16;
+		o1 |= REGLINK << 12;
+
+		o2 = oprrr(AADD, p->scond);
+		o2 |= immrot(instoffset);
+		o2 |= p->to.reg << 16;
+		o2 |= REGPC << 12;
+		break;
+
+	case 8:		/* sll $c,[R],R -> mov (R<<$c),R */
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 |= r;
+		o1 |= (instoffset&31) << 7;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 9:		/* sll R,[R],R -> mov (R<<R),R */
+		o1 = oprrr(p->as, p->scond);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 |= r;
+		o1 |= (p->from.reg << 8) | (1<<4);
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 10:	/* swi [$con] */
+		o1 = oprrr(p->as, p->scond);
+		if(p->to.type != D_NONE) {
+			aclass(&p->to);
+			o1 |= instoffset & 0xffffff;
+		}
+		break;
+
+	case 11:	/* word */
+		switch(aclass(&p->to)) {
+		case C_LCON:
+			if(!dlm)
+				break;
+			if(p->to.name != D_EXTERN && p->to.name != D_STATIC)
+				break;
+		case C_ADDR:
+			if(p->to.sym->type == SUNDEF)
+				ckoff(p->to.sym, p->to.offset);
+			dynreloc(p->to.sym, p->pc, 1);
+		}
+		o1 = instoffset;
+		break;
+
+	case 12:	/* movw $lcon, reg */
+		o1 = omvl(p, &p->from, p->to.reg);
+		break;
+
+	case 13:	/* op $lcon, [R], R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = oprrr(p->as, p->scond);
+		o2 |= REGTMP;
+		r = p->reg;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = p->to.reg;
+		o2 |= r << 16;
+		if(p->to.type != D_NONE)
+			o2 |= p->to.reg << 12;
+		break;
+
+	case 14:	/* movb/movbu/movh/movhu R,R */
+		o1 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o2 = oprrr(ASRL, p->scond);
+		else
+			o2 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o1 |= (p->from.reg)|(r<<12);
+		o2 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o1 |= (24<<7);
+			o2 |= (24<<7);
+		} else {
+			o1 |= (16<<7);
+			o2 |= (16<<7);
+		}
+		break;
+
+	case 15:	/* mul r,[r,]r */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(r == NREG)
+			r = rt;
+		if(rt == r) {
+			r = rf;
+			rf = rt;
+		}
+		if(0)
+		if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) {
+			diag("bad registers in MUL");
+			prasm(p);
+		}
+		o1 |= (rf<<8) | r | (rt<<16);
+		break;
+
+
+	case 16:	/* div r,[r,]r */
+		o1 = 0xf << 28;
+		o2 = 0;
+		break;
+
+	case 17:
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		rt2 = p->to.offset;
+		r = p->reg;
+		o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12);
+		break;
+
+	case 20:	/* mov/movb/movbu R,O(R) */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = osr(p->as, p->from.reg, instoffset, r, p->scond);
+		break;
+
+	case 21:	/* mov/movbu O(R),R -> lr */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olr(instoffset, r, p->to.reg, p->scond);
+		if(p->as != AMOVW)
+			o1 |= 1<<22;
+		break;
+
+	case 22:	/* movb/movh/movhu O(R),R -> lr,shl,shr */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olr(instoffset, r, p->to.reg, p->scond);
+
+		o2 = oprrr(ASLL, p->scond);
+		o3 = oprrr(ASRA, p->scond);
+		r = p->to.reg;
+		if(p->as == AMOVB) {
+			o2 |= (24<<7)|(r)|(r<<12);
+			o3 |= (24<<7)|(r)|(r<<12);
+		} else {
+			o2 |= (16<<7)|(r)|(r<<12);
+			if(p->as == AMOVHU)
+				o3 = oprrr(ASRL, p->scond);
+			o3 |= (16<<7)|(r)|(r<<12);
+		}
+		break;
+
+	case 23:	/* movh/movhu R,O(R) -> sb,sb */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = osr(AMOVH, p->from.reg, instoffset, r, p->scond);
+
+		o2 = oprrr(ASRL, p->scond);
+		o2 |= (8<<7)|(p->from.reg)|(REGTMP<<12);
+
+		o3 = osr(AMOVH, REGTMP, instoffset+1, r, p->scond);
+		break;
+
+	case 30:	/* mov/movb/movbu R,L(R) */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = osrr(p->from.reg, REGTMP,r, p->scond);
+		if(p->as != AMOVW)
+			o2 |= 1<<22;
+		break;
+
+	case 31:	/* mov/movbu L(R),R -> lr[b] */
+	case 32:	/* movh/movb L(R),R -> lr[b] */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = olrr(REGTMP,r, p->to.reg, p->scond);
+		if(p->as == AMOVBU || p->as == AMOVB)
+			o2 |= 1<<22;
+		if(o->type == 31)
+			break;
+
+		o3 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o4 = oprrr(ASRL, p->scond);
+		else
+			o4 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o3 |= (r)|(r<<12);
+		o4 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o3 |= (24<<7);
+			o4 |= (24<<7);
+		} else {
+			o3 |= (16<<7);
+			o4 |= (16<<7);
+		}
+		break;
+
+	case 33:	/* movh/movhu R,L(R) -> sb, sb */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = osrr(p->from.reg, REGTMP, r, p->scond);
+		o2 |= (1<<22) ;
+
+		o3 = oprrr(ASRL, p->scond);
+		o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+		o3 |= (1<<6);	/* ROR 8 */
+
+		o4 = oprrr(AADD, p->scond);
+		o4 |= (REGTMP << 12) | (REGTMP << 16);
+		o4 |= immrot(1);
+
+		o5 = osrr(p->from.reg, REGTMP,r,p->scond);
+		o5 |= (1<<22);
+
+		o6 = oprrr(ASRL, p->scond);
+		o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+		o6 |= (1<<6);	/* ROL 8 */
+
+		break;
+		
+	case 34:	/* mov $lacon,R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+
+		o2 = oprrr(AADD, p->scond);
+		o2 |= REGTMP;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 |= r << 16;
+		if(p->to.type != D_NONE)
+			o2 |= p->to.reg << 12;
+		break;
+
+	case 35:	/* mov PSR,R */
+		o1 = (2<<23) | (0xf<<16) | (0<<0);
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= (p->from.reg & 1) << 22;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 36:	/* mov R,PSR */
+		o1 = (2<<23) | (0x29f<<12) | (0<<4);
+		if(p->scond & C_FBIT)
+			o1 ^= 0x010 << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= (p->to.reg & 1) << 22;
+		o1 |= p->from.reg << 0;
+		break;
+
+	case 37:	/* mov $con,PSR */
+		aclass(&p->from);
+		o1 = (2<<23) | (0x29f<<12) | (0<<4);
+		if(p->scond & C_FBIT)
+			o1 ^= 0x010 << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= immrot(instoffset);
+		o1 |= (p->to.reg & 1) << 22;
+		o1 |= p->from.reg << 0;
+		break;
+
+	case 38:	/* movm $con,oreg -> stm */
+		o1 = (0x4 << 25);
+		o1 |= p->from.offset & 0xffff;
+		o1 |= p->to.reg << 16;
+		aclass(&p->to);
+		goto movm;
+
+	case 39:	/* movm oreg,$con -> ldm */
+		o1 = (0x4 << 25) | (1 << 20);
+		o1 |= p->to.offset & 0xffff;
+		o1 |= p->from.reg << 16;
+		aclass(&p->from);
+	movm:
+		if(instoffset != 0)
+			diag("offset must be zero in MOVM");
+		o1 |= (p->scond & C_SCOND) << 28;
+		if(p->scond & C_PBIT)
+			o1 |= 1 << 24;
+		if(p->scond & C_UBIT)
+			o1 |= 1 << 23;
+		if(p->scond & C_SBIT)
+			o1 |= 1 << 22;
+		if(p->scond & C_WBIT)
+			o1 |= 1 << 21;
+		break;
+
+	case 40:	/* swp oreg,reg,reg */
+		aclass(&p->from);
+		if(instoffset != 0)
+			diag("offset must be zero in SWP");
+		o1 = (0x2<<23) | (0x9<<4);
+		if(p->as != ASWPW)
+			o1 |= 1 << 22;
+		o1 |= p->from.reg << 16;
+		o1 |= p->reg << 0;
+		o1 |= p->to.reg << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		break;
+
+	case 41:	/* rfe -> movm.s.w.u 0(r13),[r15] */
+		o1 = 0xe8fd8000;
+		break;
+
+	case 50:	/* floating point store */
+		v = regoff(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = ofsr(p->as, p->from.reg, v, r, p->scond, p);
+		break;
+
+	case 51:	/* floating point load */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = ofsr(p->as, p->to.reg, v, r, p->scond, p) | (1<<20);
+		break;
+
+	case 52:	/* floating point store, long offset UGLY */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+		o3 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+		break;
+
+	case 53:	/* floating point load, long offset UGLY */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+		o3 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+		break;
+
+	case 54:	/* floating point arith */
+		o1 = oprrr(p->as, p->scond);
+		if(p->from.type == D_FCONST) {
+			rf = chipfloat(p->from.ieee);
+			if(rf < 0){
+				diag("invalid floating-point immediate\n%P", p);
+				rf = 0;
+			}
+			rf |= (1<<3);
+		} else
+			rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;	/* CMP[FD] */
+		else if(o1 & (1<<15))
+			r = 0;	/* monadic */
+		else if(r == NREG)
+			r = rt;
+		o1 |= rf | (r<<16) | (rt<<12);
+		break;
+
+	case 55:	/* floating point fix and float */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		if(p->to.type == D_NONE){
+			rt = 0;
+			diag("to.type==D_NONE (asm/fp)");
+		}
+		if(p->from.type == D_REG)
+			o1 |= (rf<<12) | (rt<<16);
+		else
+			o1 |= rf | (rt<<12);
+		break;
+
+	/* old arm 7500 fp using coproc 1 (1<<8) */
+	case 56:	/* move to FP[CS]R */
+		o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+		o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12);
+		break;
+
+	case 57:	/* move from FP[CS]R */
+		o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+		o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20);
+		break;
+	case 58:	/* movbu R,R */
+		o1 = oprrr(AAND, p->scond);
+		o1 |= immrot(0xff);
+		rt = p->to.reg;
+		r = p->from.reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 59:	/* movw/bu R<<I(R),R -> ldr indexed */
+		if(p->from.reg == NREG) {
+			if(p->as != AMOVW)
+				diag("byte MOV from shifter operand");
+			goto mov;
+		}
+		if(p->from.offset&(1<<4))
+			diag("bad shift in LDR");
+		o1 = olrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+		if(p->as == AMOVBU)
+			o1 |= 1<<22;
+		break;
+
+	case 60:	/* movb R(R),R -> ldrsb indexed */
+		if(p->from.reg == NREG) {
+			diag("byte MOV from shifter operand");
+			goto mov;
+		}
+		if(p->from.offset&(~0xf))
+			diag("bad shift in LDRSB");
+		o1 = olhrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+		o1 ^= (1<<5)|(1<<6);
+		break;
+
+	case 61:	/* movw/b/bu R,R<<[IR](R) -> str indexed */
+		if(p->to.reg == NREG)
+			diag("MOV to shifter operand");
+		o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond);
+		if(p->as == AMOVB || p->as == AMOVBU)
+			o1 |= 1<<22;
+		break;
+
+	case 62:	/* case R -> movw	R<<2(PC),PC */
+		o1 = olrr(p->from.reg, REGPC, REGPC, p->scond);
+		o1 |= 2<<7;
+		break;
+
+	case 63:	/* bcase */
+		if(p->cond != P) {
+			o1 = p->cond->pc;
+			if(dlm)
+				dynreloc(S, p->pc, 1);
+		}
+		break;
+
+	/* reloc ops */
+	case 64:	/* mov/movb/movbu R,addr */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+		break;
+
+	case 65:	/* mov/movbu addr,R */
+	case 66:	/* movh/movhu/movb addr,R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = olr(0, REGTMP, p->to.reg, p->scond);
+		if(p->as == AMOVBU || p->as == AMOVB)
+			o2 |= 1<<22;
+		if(o->type == 65)
+			break;
+
+		o3 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o4 = oprrr(ASRL, p->scond);
+		else
+			o4 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o3 |= (r)|(r<<12);
+		o4 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o3 |= (24<<7);
+			o4 |= (24<<7);
+		} else {
+			o3 |= (16<<7);
+			o4 |= (16<<7);
+		}
+		break;
+
+	case 67:	/* movh/movhu R,addr -> sb, sb */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+		o3 = oprrr(ASRL, p->scond);
+		o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+		o3 |= (1<<6);	/* ROR 8 */
+
+		o4 = oprrr(AADD, p->scond);
+		o4 |= (REGTMP << 12) | (REGTMP << 16);
+		o4 |= immrot(1);
+
+		o5 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+		o6 = oprrr(ASRL, p->scond);
+		o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+		o6 |= (1<<6);	/* ROL 8 */
+		break;
+
+	case 68:	/* floating point store -> ADDR */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+		break;
+
+	case 69:	/* floating point load <- ADDR */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+		break;
+
+	/* ArmV4 ops: */
+	case 70:	/* movh/movhu R,O(R) -> strh */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = oshr(p->from.reg, instoffset, r, p->scond);
+		break;	
+	case 71:	/* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olhr(instoffset, r, p->to.reg, p->scond);
+		if(p->as == AMOVB)
+			o1 ^= (1<<5)|(1<<6);
+		else if(p->as == AMOVH)
+			o1 ^= (1<<6);
+		break;
+	case 72:	/* movh/movhu R,L(R) -> strh */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oshrr(p->from.reg, REGTMP,r, p->scond);
+		break;	
+	case 73:	/* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = olhrr(REGTMP, r, p->to.reg, p->scond);
+		if(p->as == AMOVB)
+			o2 ^= (1<<5)|(1<<6);
+		else if(p->as == AMOVH)
+			o2 ^= (1<<6);
+		break;
+
+	/* VFP ops: */
+	case 74:	/* vfp floating point arith */
+		o1 = opvfprrr(p->as, p->scond);
+		rf = p->from.reg;
+		if(p->from.type == D_FCONST) {
+			diag("invalid floating-point immediate\n%P", p);
+			rf = 0;
+		}
+		rt = p->to.reg;
+		r = p->reg;
+		if(r == NREG)
+			r = rt;
+		o1 |= rt<<12;
+		if(((o1>>20)&0xf) == 0xb)
+			o1 |= rf<<0;
+		else
+			o1 |= r<<16 | rf<<0;
+		break;
+	case 75:	/* vfp floating point compare */
+		o1 = opvfprrr(p->as, p->scond);
+		rf = p->from.reg;
+		if(p->from.type == D_FCONST) {
+			if(p->from.ieee->h != 0 || p->from.ieee->l != 0)
+				diag("invalid floating-point immediate\n%P", p);
+			o1 |= 1<<16;
+			rf = 0;
+		}
+		rt = p->reg;
+		o1 |= rt<<12 | rf<<0;
+		o2 = 0x0ef1fa10;	/* MRS APSR_nzcv, FPSCR */
+		o2 |= (p->scond & C_SCOND) << 28;
+		break;
+	case 76:	/* vfp floating point fix and float */
+		o1 = opvfprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		if(p->from.type == D_REG) {
+			o2 = o1 | rt<<12 | rt<<0;
+			o1 = 0x0e000a10;	/* VMOV F,R */
+			o1 |= (p->scond & C_SCOND) << 28 | rt<<16 | rf<<12;
+		} else {
+			o1 |= FREGTMP<<12 | rf<<0;
+			o2 = 0x0e100a10;	/* VMOV R,F */
+			o2 |= (p->scond & C_SCOND) << 28 | FREGTMP<<16 | rt<<12;
+		}
+		break;
+	}
+
+	if(debug['a'] > 1)
+		Bprint(&bso, "%2d ", o->type);
+
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		lputl(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		lputl(o1);
+		lputl(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		lputl(o5);
+		break;
+	case 24:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, o6, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		lputl(o5);
+		lputl(o6);
+		break;
+	}
+}
+
+long
+oprrr(int a, int sc)
+{
+	long o;
+
+	o = (sc & C_SCOND) << 28;
+	if(sc & C_SBIT)
+		o |= 1 << 20;
+	if(sc & (C_PBIT|C_WBIT))
+		diag(".P/.W on dp instruction");
+	switch(a) {
+	case AMULU:
+	case AMUL:	return o | (0x0<<21) | (0x9<<4);
+	case AMULA:	return o | (0x1<<21) | (0x9<<4);
+	case AMULLU:	return o | (0x4<<21) | (0x9<<4);
+	case AMULL:	return o | (0x6<<21) | (0x9<<4);
+	case AMULALU:	return o | (0x5<<21) | (0x9<<4);
+	case AMULAL:	return o | (0x7<<21) | (0x9<<4);
+	case AAND:	return o | (0x0<<21);
+	case AEOR:	return o | (0x1<<21);
+	case ASUB:	return o | (0x2<<21);
+	case ARSB:	return o | (0x3<<21);
+	case AADD:	return o | (0x4<<21);
+	case AADC:	return o | (0x5<<21);
+	case ASBC:	return o | (0x6<<21);
+	case ARSC:	return o | (0x7<<21);
+	case ATST:	return o | (0x8<<21) | (1<<20);
+	case ATEQ:	return o | (0x9<<21) | (1<<20);
+	case ACMP:	return o | (0xa<<21) | (1<<20);
+	case ACMN:	return o | (0xb<<21) | (1<<20);
+	case AORR:	return o | (0xc<<21);
+	case AMOVW:	return o | (0xd<<21);
+	case ABIC:	return o | (0xe<<21);
+	case AMVN:	return o | (0xf<<21);
+	case ASLL:	return o | (0xd<<21) | (0<<5);
+	case ASRL:	return o | (0xd<<21) | (1<<5);
+	case ASRA:	return o | (0xd<<21) | (2<<5);
+	case ASWI:	return o | (0xf<<24);
+
+	/* old arm 7500 fp using coproc 1 (1<<8) */
+	case AADDD:	return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7);
+	case AADDF:	return o | (0xe<<24) | (0x0<<20) | (1<<8);
+	case AMULD:	return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7);
+	case AMULF:	return o | (0xe<<24) | (0x1<<20) | (1<<8);
+	case ASUBD:	return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7);
+	case ASUBF:	return o | (0xe<<24) | (0x2<<20) | (1<<8);
+	case ADIVD:	return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7);
+	case ADIVF:	return o | (0xe<<24) | (0x4<<20) | (1<<8);
+	case ACMPD:
+	case ACMPF:	return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4);	/* arguably, ACMPF should expand to RNDF, CMPD */
+
+	case AMOVF:
+	case AMOVDF:	return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8);
+	case AMOVD:
+	case AMOVFD:	return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7);
+
+	case AMOVWF:	return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4);
+	case AMOVWD:	return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7);
+	case AMOVFW:	return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4);
+	case AMOVDW:	return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7);
+	}
+	diag("bad rrr %d", a);
+	prasm(curp);
+	return 0;
+}
+
+long
+opvfprrr(int a, int sc)
+{
+	long o;
+
+	o = (sc & C_SCOND) << 28;
+	if(sc & (C_SBIT|C_PBIT|C_WBIT))
+		diag(".S/.P/.W on vfp instruction");
+	o |= 0xe<<24;
+	switch(a) {
+	case AMOVWD:	return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x8<<16 | 1<<7;
+	case AMOVWF:	return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x8<<16 | 1<<7;
+	case AMOVDW:	return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0xD<<16 | 1<<7;
+	case AMOVFW:	return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0xD<<16 | 1<<7;
+	case AMOVFD:	return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x7<<16 | 1<<7;
+	case AMOVDF:	return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x7<<16 | 1<<7;
+	case AMOVF:	return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x0<<16 | 0<<7;
+	case AMOVD:	return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x0<<16 | 0<<7;
+	case ACMPF:	return o | 0xa<<8 | 0xb<<20 | 1<<6 | 0x4<<16 | 0<<7;
+	case ACMPD:	return o | 0xb<<8 | 0xb<<20 | 1<<6 | 0x4<<16 | 0<<7;
+	case AADDF:	return o | 0xa<<8 | 0x3<<20;
+	case AADDD:	return o | 0xb<<8 | 0x3<<20;
+	case ASUBF:	return o | 0xa<<8 | 0x3<<20 | 1<<6;
+	case ASUBD:	return o | 0xb<<8 | 0x3<<20 | 1<<6;
+	case AMULF:	return o | 0xa<<8 | 0x2<<20;
+	case AMULD:	return o | 0xb<<8 | 0x2<<20;
+	case ADIVF:	return o | 0xa<<8 | 0x8<<20;
+	case ADIVD:	return o | 0xb<<8 | 0x8<<20;
+	}
+	diag("bad vfp rrr %d", a);
+	prasm(curp);
+	return 0;
+}
+
+long
+opbra(int a, int sc)
+{
+
+	if(sc & (C_SBIT|C_PBIT|C_WBIT))
+		diag(".S/.P/.W on bra instruction");
+	sc &= C_SCOND;
+	if(a == ABL)
+		return (sc<<28)|(0x5<<25)|(0x1<<24);
+	if(sc != 0xe)
+		diag(".COND on bcond instruction");
+	switch(a) {
+	case ABEQ:	return (0x0<<28)|(0x5<<25);
+	case ABNE:	return (0x1<<28)|(0x5<<25);
+	case ABCS:	return (0x2<<28)|(0x5<<25);
+	case ABHS:	return (0x2<<28)|(0x5<<25);
+	case ABCC:	return (0x3<<28)|(0x5<<25);
+	case ABLO:	return (0x3<<28)|(0x5<<25);
+	case ABMI:	return (0x4<<28)|(0x5<<25);
+	case ABPL:	return (0x5<<28)|(0x5<<25);
+	case ABVS:	return (0x6<<28)|(0x5<<25);
+	case ABVC:	return (0x7<<28)|(0x5<<25);
+	case ABHI:	return (0x8<<28)|(0x5<<25);
+	case ABLS:	return (0x9<<28)|(0x5<<25);
+	case ABGE:	return (0xa<<28)|(0x5<<25);
+	case ABLT:	return (0xb<<28)|(0x5<<25);
+	case ABGT:	return (0xc<<28)|(0x5<<25);
+	case ABLE:	return (0xd<<28)|(0x5<<25);
+	case AB:	return (0xe<<28)|(0x5<<25);
+	}
+	diag("bad bra %A", a);
+	prasm(curp);
+	return 0;
+}
+
+long
+olr(long v, int b, int r, int sc)
+{
+	long o;
+
+	if(sc & C_SBIT)
+		diag(".S on LDR/STR instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(!(sc & C_UBIT))
+		o |= 1 << 23;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (0x1<<26) | (1<<20);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v >= (1<<12))
+		diag("literal span too large: %ld (R%d)\n%P", v, b, PP);
+	o |= v;
+	o |= b << 16;
+	o |= r << 12;
+	return o;
+}
+
+long
+olhr(long v, int b, int r, int sc)
+{
+	long o;
+
+	if(sc & C_SBIT)
+		diag(".S on LDRH/STRH instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (1<<23) | (1<<20)|(0xb<<4);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v >= (1<<8))
+		diag("literal span too large: %ld (R%d)\n%P", v, b, PP);
+	o |= (v&0xf)|((v>>4)<<8)|(1<<22);
+	o |= b << 16;
+	o |= r << 12;
+	return o;
+}
+
+long
+osr(int a, int r, long v, int b, int sc)
+{
+	long o;
+
+	o = olr(v, b, r, sc) ^ (1<<20);
+	if(a != AMOVW)
+		o |= 1<<22;
+	return o;
+}
+
+long
+oshr(int r, long v, int b, int sc)
+{
+	long o;
+
+	o = olhr(v, b, r, sc) ^ (1<<20);
+	return o;
+}
+	
+
+long
+osrr(int r, int i, int b, int sc)
+{
+
+	return olr(i, b, r, sc) ^ ((1<<25) | (1<<20));
+}
+
+long
+oshrr(int r, int i, int b, int sc)
+{
+	return olhr(i, b, r, sc) ^ ((1<<22) | (1<<20));
+}
+
+long
+olrr(int i, int b, int r, int sc)
+{
+
+	return olr(i, b, r, sc) ^ (1<<25);
+}
+
+long
+olhrr(int i, int b, int r, int sc)
+{
+	return olhr(i, b, r, sc) ^ (1<<22);
+}
+
+long
+ovfpmem(int a, int r, long v, int b, int sc, Prog *p)
+{
+	long o;
+
+	if(sc & (C_SBIT|C_PBIT|C_WBIT))
+		diag(".S/.P/.W on VLDR/VSTR instruction");
+	o = (sc & C_SCOND) << 28;
+	o |= 0xd<<24 | (1<<23);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v & 3)
+		diag("odd offset for floating point op: %ld\n%P", v, p);
+	else if(v >= (1<<10))
+		diag("literal span too large: %ld\n%P", v, p);
+	o |= (v>>2) & 0xFF;
+	o |= b << 16;
+	o |= r << 12;
+	switch(a) {
+	default:
+		diag("bad fst %A", a);
+	case AMOVD:
+		o |= 0xb<<8;
+		break;
+	case AMOVF:
+		o |= 0xa<<8;
+		break;
+	}
+	return o;
+}
+
+long
+ofsr(int a, int r, long v, int b, int sc, Prog *p)
+{
+	long o;
+
+	if(vfp)
+		return ovfpmem(a, r, v, b, sc, p);
+	if(sc & C_SBIT)
+		diag(".S on FLDR/FSTR instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (6<<25) | (1<<24) | (1<<23);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v & 3)
+		diag("odd offset for floating point op: %ld\n%P", v, p);
+	else if(v >= (1<<10))
+		diag("literal span too large: %ld\n%P", v, p);
+	o |= (v>>2) & 0xFF;
+	o |= b << 16;
+	o |= r << 12;
+	o |= 1 << 8;
+
+	switch(a) {
+	default:
+		diag("bad fst %A", a);
+	case AMOVD:
+		o |= 1<<15;
+	case AMOVF:
+		break;
+	}
+	return o;
+}
+
+long
+omvl(Prog *p, Adr *a, int dr)
+{	
+	long v, o1;
+	if(!p->cond) {
+		aclass(a);
+		v = immrot(~instoffset);
+		if(v == 0) {
+			diag("missing literal");
+			prasm(p);
+			return 0;
+		}
+		o1 = oprrr(AMVN, p->scond&C_SCOND);
+		o1 |= v;
+		o1 |= dr << 12;
+	} else {
+		v = p->cond->pc - p->pc - 8;
+		o1 = olr(v, REGPC, dr, p->scond&C_SCOND);
+	}
+	return o1;
+}
+
+static Ieee chipfloats[] = {
+	{0x00000000, 0x00000000}, /* 0 */
+	{0x00000000, 0x3ff00000}, /* 1 */
+	{0x00000000, 0x40000000}, /* 2 */
+	{0x00000000, 0x40080000}, /* 3 */
+	{0x00000000, 0x40100000}, /* 4 */
+	{0x00000000, 0x40140000}, /* 5 */
+	{0x00000000, 0x3fe00000}, /* .5 */
+	{0x00000000, 0x40240000}, /* 10 */
+};
+
+int
+chipfloat(Ieee *e)
+{
+	Ieee *p;
+	int n;
+
+	if(vfp)
+		return -1;
+	for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){
+		p = &chipfloats[n];
+		if(p->l == e->l && p->h == e->h)
+			return n;
+	}
+	return -1;
+}
--- /dev/null
+++ b/utils/5l/l.h
@@ -1,0 +1,415 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../5c/5.out.h"
+#include	"../ld/elf.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	LIBNAMELEN	300
+
+void	addlibpath(char*);
+int	fileexists(char*);
+char*	findlib(char*);
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+typedef	struct	Oprang	Oprang;
+typedef	uchar	Opcross[32][2][32];
+typedef	struct	Count	Count;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char*	u0sval;
+		Ieee*	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	union
+	{
+		long	u0regused;
+		Prog*	u0forwd;
+	} u0;
+	Prog*	cond;
+	Prog*	link;
+	long	pc;
+	long	line;
+	uchar	mark;
+	uchar	optab;
+	uchar	as;
+	uchar	scond;
+	uchar	reg;
+};
+#define	regused	u0.u0regused
+#define	forwd	u0.u0forwd
+
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	uchar	subtype;
+	uchar	used;
+	ushort	file;
+	long	value;
+	long	sig;
+	Sym*	link;
+};
+
+#define SIGNINTERN	(1729*325*1729)
+
+struct	Autom
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	char	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+	char	flag;
+};
+struct	Oprang
+{
+	Optab*	start;
+	Optab*	stop;
+};
+struct	Count
+{
+	long	count;
+	long	outof;
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+	SSTRING,
+	SUNDEF,
+
+	SIMPORT,
+	SEXPORT,
+
+	LFROM		= 1<<0,
+	LTO		= 1<<1,
+	LPOOL		= 1<<2,
+	V4		= 1<<3,	/* arm v4 arch */
+	VFP		= 1<<4,	/* arm vfpv3 floating point */
+
+	C_NONE		= 0,
+	C_REG,
+	C_REGREG,
+	C_SHIFT,
+	C_FREG,
+	C_PSR,
+	C_FCR,
+
+	C_RCON,		/* 0xff rotated */
+	C_NCON,		/* ~RCON */
+	C_SCON,		/* 0xffff */
+	C_LCON,
+	C_FCON,
+
+	C_RACON,
+	C_LACON,
+
+	C_RECON,
+	C_LECON,
+
+	C_SBRA,
+	C_LBRA,
+
+	C_HAUTO,	/* halfword insn offset (-0xff to 0xff) */
+	C_FAUTO,	/* float insn offset (0 to 0x3fc, word aligned) */
+	C_HFAUTO,	/* both H and F */
+	C_SAUTO,	/* -0xfff to 0xfff */
+	C_LAUTO,
+
+	C_HEXT,
+	C_FEXT,
+	C_HFEXT,
+	C_SEXT,
+	C_LEXT,
+
+	C_HOREG,
+	C_FOREG,
+	C_HFOREG,
+	C_SOREG,
+	C_ROREG,
+	C_SROREG,	/* both S and R */
+	C_LOREG,
+
+	C_ADDR,		/* relocatable address */
+
+	C_GOK,
+
+/* mark flags */
+	FOLL		= 1<<0,
+	LABEL		= 1<<1,
+	LEAF		= 1<<2,
+
+	BIG		= (1<<12)-4,
+	STRINGSZ	= 200,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	MAXIO		= 8192,
+	MAXHIST		= 20,	/* limit of path elements for history symbols */
+
+	Roffset	= 22,		/* no. bits for offset in relocation address */
+	Rindex	= 10,		/* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+#ifndef COFFCVT
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	long	INITTEXTP;		/* text location (physical) */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi4[4];
+EXTERN	char	fnuxi8[8];
+EXTERN	char*	noname;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	instoffset;
+EXTERN	Opcross	opcross[8];
+EXTERN	Oprang	oprange[ALAST];
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	uchar	repop[ALAST];
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	char	xcmp[C_GOK+1][C_GOK+1];
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+EXTERN	int	armv4;
+EXTERN	int vfp;
+
+EXTERN	int	doexp, dlm;
+EXTERN	int	imports, nimports;
+EXTERN	int	exports, nexports;
+EXTERN	char*	EXPTAB;
+EXTERN	Prog	undefp;
+
+#define	UP	(&undefp)
+
+extern	char*	anames[];
+extern	Optab	optab[];
+
+void	addpool(Prog*, Adr*);
+EXTERN	Prog*	blitrl;
+EXTERN	Prog*	elitrl;
+
+void	initdiv(void);
+EXTERN	Prog*	prog_div;
+EXTERN	Prog*	prog_divu;
+EXTERN	Prog*	prog_mod;
+EXTERN	Prog*	prog_modu;
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"C"	int
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+int	Aconv(Fmt*);
+int	Cconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	addlibpath(char*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmdyn(void);
+void	asmlc(void);
+void	asmout(Prog*, Optab*);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	buildrep(int, int);
+void	cflush(void);
+void	ckoff(Sym*, long);
+int	chipfloat(Ieee*);
+int	cmp(int, int);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long, int);
+void	diag(char*, ...);
+void	divsig(void);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+void	dynreloc(Sym*, long, int);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+void	export(void);
+int	fileexists(char*);
+int	find1(long, int);
+char*	findlib(char*);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+void	import(void);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	cput(int);
+void	llput(vlong);
+void	llputl(vlong);
+void	lput(long);
+void	lputl(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opirr(int);
+Optab*	oplook(Prog*);
+long	oprrr(int, int);
+long	opvfprrr(int, int);
+long	olr(long, int, int, int);
+long	olhr(long, int, int, int);
+long	olrr(int, int, int, int);
+long	olhrr(int, int, int, int);
+long	osr(int, int, long, int, int);
+long	oshr(int, long, int, int);
+long	ofsr(int, int, long, int, int, Prog*);
+long	osrr(int, int, int, int);
+long	oshrr(int, int, int, int);
+long	omvl(Prog*, Adr*, int);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+void	readundefs(char*, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	undefsym(Sym*);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+void	zerosig(char*);
+void	noops(void);
+long	immrot(ulong);
+long	immaddr(long);
+long	opbra(int, int);
+
+#endif
--- /dev/null
+++ b/utils/5l/list.c
@@ -1,0 +1,352 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('C', Cconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	switch(a) {
+	default:
+		s = str;
+		s += sprint(s, "(%ld)", p->line);
+		if(p->reg == NREG)
+			sprint(s, "	%A%C	%D,%D",
+				a, p->scond, &p->from, &p->to);
+		else
+		if(p->from.type != D_FREG)
+			sprint(s, "	%A%C	%D,R%d,%D",
+				a, p->scond, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%A%C	%D,F%d,%D",
+				a, p->scond, &p->from, p->reg, &p->to);
+		break;
+
+	case ASWPW:
+	case ASWPBU:
+		sprint(str, "(%ld)	%A%C	R%d,%D,%D",
+			p->line, a, p->scond, p->reg, &p->from, &p->to);
+		break;
+
+	case ADATA:
+	case AINIT:
+	case ADYNT:
+		sprint(str, "(%ld)	%A%C	%D/%d,%D",
+			p->line, a, p->scond, &p->from, p->reg, &p->to);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+char*	strcond[16] =
+{
+	".EQ",
+	".NE",
+	".HS",
+	".LO",
+	".MI",
+	".PL",
+	".VS",
+	".VC",
+	".HI",
+	".LS",
+	".GE",
+	".LT",
+	".GT",
+	".LE",
+	"",
+	".NV"
+};
+
+int
+Cconv(Fmt *fp)
+{
+	char s[20];
+	int c;
+
+	c = va_arg(fp->args, int);
+	strcpy(s, strcond[c & C_SCOND]);
+	if(c & C_SBIT)
+		strcat(s, ".S");
+	if(c & C_PBIT)
+		strcat(s, ".P");
+	if(c & C_WBIT)
+		strcat(s, ".W");
+	if(c & C_UBIT)		/* ambiguous with FBIT */
+		strcat(s, ".U");
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	char *op;
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg == NREG)
+			sprint(str, "$%N", a);
+		else
+			sprint(str, "$%N(R%d)", a, a->reg);
+		break;
+
+	case D_SHIFT:
+		v = a->offset;
+		op = "<<>>->@>" + (((v>>5) & 3) << 1);
+		if(v & (1<<4))
+			sprint(str, "R%ld%c%cR%ld", v&15, op[0], op[1], (v>>8)&15);
+		else
+			sprint(str, "R%ld%c%c%ld", v&15, op[0], op[1], (v>>7)&31);
+		if(a->reg != NREG)
+			sprint(str+strlen(str), "(R%d)", a->reg);
+		break;
+
+	case D_OCONST:
+		sprint(str, "$*$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_REGREG:
+		sprint(str, "(R%d,R%d)", a->reg, (int)a->offset);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_PSR:
+		switch(a->reg) {
+		case 0:
+			sprint(str, "CPSR");
+			break;
+		case 1:
+			sprint(str, "SPSR");
+			break;
+		default:
+			sprint(str, "PSR%d", a->reg);
+			break;
+		}
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(PSR%d)(REG)", a, a->reg);
+		break;
+
+	case D_FPCR:
+		switch(a->reg){
+		case 0:
+			sprint(str, "FPSR");
+			break;
+		case 1:
+			sprint(str, "FPCR");
+			break;
+		default:
+			sprint(str, "FCR%d", a->reg);
+			break;
+		}
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(FCR%d)(REG)", a, a->reg);
+
+		break;
+
+	case D_BRANCH:	/* botch */
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%e", ieeedtod(a->ieee));
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		if(s == S)
+			sprint(str, "%ld(SB)", a->offset);
+		else
+			sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		if(s == S)
+			sprint(str, "<>+%ld(SB)", a->offset);
+		else
+			sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		if(s == S)
+			sprint(str, "%ld(SP)", a->offset);
+		else
+			sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		if(s == S)
+			sprint(str, "%ld(FP)", a->offset);
+		else
+			sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/5l/mkfile
@@ -1,0 +1,37 @@
+<../../mkconfig
+
+TARG=5l
+
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+	elf.$O\
+
+HFILES=\
+	l.h\
+	../5c/5.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+CFLAGS=$CFLAGS -I../include -I.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+enam.$O:	../5c/enam.c
+	$CC $CFLAGS ../5c/enam.c
+
+elf.$O:	../ld/elf.c
+	$CC $CFLAGS ../ld/elf.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/5l/noop.c
@@ -1,0 +1,531 @@
+#include	"l.h"
+
+static	Sym*	sym_div;
+static	Sym*	sym_divu;
+static	Sym*	sym_mod;
+static	Sym*	sym_modu;
+
+void
+noops(void)
+{
+	Prog *p, *q, *q1;
+	int o, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			p->mark |= LEAF;
+			curtext = p;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+			break;
+
+		case ADIV:
+		case ADIVU:
+		case AMOD:
+		case AMODU:
+			q = p;
+			if(prog_div == P)
+				initdiv();
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+			continue;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		case ABL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case ABCASE:
+		case AB:
+
+		case ABEQ:
+		case ABNE:
+		case ABCS:
+		case ABHS:
+		case ABCC:
+		case ABLO:
+		case ABMI:
+		case ABPL:
+		case ABVS:
+		case ABVC:
+		case ABHI:
+		case ABLS:
+		case ABGE:
+		case ABLT:
+		case ABGT:
+		case ABLE:
+
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+			}
+			break;
+		}
+		q = p;
+	}
+
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case ABL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if(autosize <= 4)
+			if(curtext->mark & LEAF) {
+				p->to.offset = -4;
+				autosize = 0;
+			}
+
+			if(!autosize && !(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				Bflush(&bso);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+#ifdef optimise_time
+				if(autosize) {
+					q = prg();
+					q->as = ASUB;
+					q->line = p->line;
+					q->from.type = D_CONST;
+					q->from.offset = autosize;
+					q->to.type = D_REG;
+					q->to.reg = REGSP;
+
+					q->link = p->link;
+					p->link = q;
+				}
+				break;
+#else
+				if(!autosize)
+					break;
+#endif
+			}
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->scond |= C_WBIT;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->to.offset = -autosize;
+			q1->to.reg = REGSP;
+
+			q1->link = p->link;
+			p->link = q1;
+			break;
+
+		case ARET:
+			nocache(p);
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = AB;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 0;
+					p->to.reg = REGLINK;
+					break;
+				}
+
+#ifdef optimise_time
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = AB;
+				q->scond = p->scond;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 0;
+				q->to.reg = REGLINK;
+
+				q->link = p->link;
+				p->link = q;
+
+				break;
+#endif
+			}
+			p->as = AMOVW;
+			p->scond |= C_PBIT;
+			p->from.type = D_OREG;
+			p->from.offset = autosize;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = REGPC;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				if(!autosize) {
+					p->as = AB;
+					p->from = zprg.from;
+					break;
+				}
+
+#ifdef optimise_time
+				q = prg();
+				q->scond = p->scond;
+				q->line = p->line;
+				q->as = AB;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+#endif
+			}
+			q = prg();
+			q->scond = p->scond;
+			q->line = p->line;
+			q->as = AB;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVW;
+			p->scond |= C_PBIT;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = autosize;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+
+		/*
+		 * 5c code generation for unsigned -> double made the
+		 * unfortunate assumption that single and double floating
+		 * point registers are aliased - true for emulated 7500
+		 * but not for vfp.  Now corrected, but this test is
+		 * insurance against old 5c compiled code in libraries.
+		 */
+		case AMOVWD:
+			if((q = p->link) != P && q->as == ACMP)
+			if((q = q->link) != P && q->as == AMOVF)
+			if((q1 = q->link) != P && q1->as == AADDF)
+			if(q1->to.type == D_FREG && q1->to.reg == p->to.reg) {
+				q1->as = AADDD;
+				q1 = prg();
+				q1->scond = q->scond;
+				q1->line = q->line;
+				q1->as = AMOVFD;
+				q1->from = q->to;
+				q1->to = q1->from;
+				q1->link = q->link;
+				q->link = q1;
+			}
+			break;
+
+		case ADIV:
+		case ADIVU:
+		case AMOD:
+		case AMODU:
+			if(debug['M'])
+				break;
+			if(p->from.type != D_REG)
+				break;
+			if(p->to.type != D_REG)
+				break;
+			q1 = p;
+
+			/* MOV a,4(SP) */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->from.reg;
+			p->to.type = D_OREG;
+			p->to.reg = REGSP;
+			p->to.offset = 4;
+
+			/* MOV b,REGTMP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->reg;
+			if(q1->reg == NREG)
+				p->from.reg = q1->to.reg;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+			p->to.offset = 0;
+
+			/* CALL appropriate */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = ABL;
+			p->line = q1->line;
+			p->to.type = D_BRANCH;
+			p->cond = p;
+			switch(o) {
+			case ADIV:
+				p->cond = prog_div;
+				p->to.sym = sym_div;
+				break;
+			case ADIVU:
+				p->cond = prog_divu;
+				p->to.sym = sym_divu;
+				break;
+			case AMOD:
+				p->cond = prog_mod;
+				p->to.sym = sym_mod;
+				break;
+			case AMODU:
+				p->cond = prog_modu;
+				p->to.sym = sym_modu;
+				break;
+			}
+
+			/* MOV REGTMP, b */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->from.offset = 0;
+			p->to.type = D_REG;
+			p->to.reg = q1->to.reg;
+
+			/* ADD $8,SP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.reg = NREG;
+			p->from.offset = 8;
+			p->reg = NREG;
+			p->to.type = D_REG;
+			p->to.reg = REGSP;
+
+			/* SUB $8,SP */
+			q1->as = ASUB;
+			q1->from.type = D_CONST;
+			q1->from.offset = 8;
+			q1->from.reg = NREG;
+			q1->reg = NREG;
+			q1->to.type = D_REG;
+			q1->to.reg = REGSP;
+			break;
+		}
+	}
+}
+
+static void
+sigdiv(char *n)
+{
+	Sym *s;
+
+	s = lookup(n, 0);
+	if(s->type == STEXT){
+		if(s->sig == 0)
+			s->sig = SIGNINTERN;
+	}
+	else if(s->type == 0 || s->type == SXREF)
+		s->type = SUNDEF;
+}
+
+void
+divsig(void)
+{
+	sigdiv("_div");
+	sigdiv("_divu");
+	sigdiv("_mod");
+	sigdiv("_modu");
+}
+
+static void
+sdiv(Sym *s)
+{
+	if(s->type == 0 || s->type == SXREF){
+		/* undefsym(s); */
+		s->type = SXREF;
+		if(s->sig == 0)
+			s->sig = SIGNINTERN;
+		s->subtype = SIMPORT;
+	}
+	else if(s->type != STEXT)
+		diag("undefined: %s", s->name);
+}
+
+void
+initdiv(void)
+{
+	Sym *s2, *s3, *s4, *s5;
+	Prog *p;
+
+	if(prog_div != P)
+		return;
+	sym_div = s2 = lookup("_div", 0);
+	sym_divu = s3 = lookup("_divu", 0);
+	sym_mod = s4 = lookup("_mod", 0);
+	sym_modu = s5 = lookup("_modu", 0);
+	if(dlm) {
+		sdiv(s2); if(s2->type == SXREF) prog_div = UP;
+		sdiv(s3); if(s3->type == SXREF) prog_divu = UP;
+		sdiv(s4); if(s4->type == SXREF) prog_mod = UP;
+		sdiv(s5); if(s5->type == SXREF) prog_modu = UP;
+	}
+	for(p = firstp; p != P; p = p->link)
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2)
+				prog_div = p;
+			if(p->from.sym == s3)
+				prog_divu = p;
+			if(p->from.sym == s4)
+				prog_mod = p;
+			if(p->from.sym == s5)
+				prog_modu = p;
+		}
+	if(prog_div == P) {
+		diag("undefined: %s", s2->name);
+		prog_div = curtext;
+	}
+	if(prog_divu == P) {
+		diag("undefined: %s", s3->name);
+		prog_divu = curtext;
+	}
+	if(prog_mod == P) {
+		diag("undefined: %s", s4->name);
+		prog_mod = curtext;
+	}
+	if(prog_modu == P) {
+		diag("undefined: %s", s5->name);
+		prog_modu = curtext;
+	}
+}
+
+void
+nocache(Prog *p)
+{
+	p->optab = 0;
+	p->from.class = 0;
+	p->to.class = 0;
+}
--- /dev/null
+++ b/utils/5l/obj.c
@@ -1,0 +1,1641 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= '5';
+char	*thestring 	= "arm";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H0				no header
+ *	-H1 -T0x10005000 -R4		is aif for risc os
+ *	-H2 -T4128 -R4096		is plan9 format
+ *	-H3 -T0xF0000020 -R4		is NetBSD format
+ *	-H4				is IXP1200 (raw)
+ *	-H5 -T0xC0008010 -R1024		is ipaq
+ *	-H6 -R4096			no header with segments padded to pages
+ *	-H7				is elf
+ */
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+static int
+isobjfile(char *f)
+{
+	int n, v;
+	Biobuf *b;
+	char buf1[5], buf2[SARMAG];
+
+	b = Bopen(f, OREAD);
+	if(b == nil)
+		return 0;
+	n = Bread(b, buf1, 5);
+	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+		v = 1;	/* good enough for our purposes */
+	else{
+		Bseek(b, 0, 0);
+		n = Bread(b, buf2, SARMAG);
+		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+	}
+	Bterm(b);
+	return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+	char name[LIBNAMELEN];
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITTEXTP = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		/* do something about setting INITTEXT */
+		break;
+	case 'x':	/* produce export table */
+		doexp = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SEXPORT);
+		break;
+	case 'u':	/* produce dynamically loadable module */
+		dlm = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SIMPORT);
+		break;
+	} ARGEND
+
+	USED(argc);
+
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	a = getenv("ccroot");
+	if(a != nil && *a != '\0') {
+		if(!fileexists(a)) {
+			diag("nonexistent $ccroot: %s", a);
+			errorexit();
+		}
+	}else
+		a = "";
+	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+	addlibpath(name);
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+	case 0:	/* no header */
+	case 6:	/* no header, padded segments */
+		HEADR = 0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 1:	/* aif for risc os */
+		HEADR = 128L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x10005000 + HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* boot for NetBSD */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xF0000020L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 4: /* boot for IXP1200 */
+		HEADR = 0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x0;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 5: /* boot for ipaq */
+		HEADR = 16L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xC0008010;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 1024;
+		break;
+	case 7:	/* elf executable */
+		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 4096+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.scond = 14;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0)
+		outfile = "5.out";
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	nuxiinit();
+
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	if(doexp || dlm){
+		EXPTAB = "_exporttab";
+		zerosig(EXPTAB);
+		zerosig("etext");
+		zerosig("edata");
+		zerosig("end");
+		if(dlm){
+			initdiv();
+			import();
+			HEADTYPE = 2;
+			INITTEXT = INITDAT = 0;
+			INITRND = 8;
+			INITENTRY = EXPTAB;
+		}
+		else
+			divsig();
+		export();
+	}
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+	int i;
+	char name[LIBNAMELEN];
+
+	for(i = 0; i < nlibdir; i++) {
+		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+		if(fileexists(name))
+			return libdir[i];
+	}
+	return nil;
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s\n", cputime(), library[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[LIBNAMELEN], pname[LIBNAMELEN];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	if(file[0] == '-' && file[1] == 'l') {
+		snprint(pname, sizeof(pname), "lib%s.a", file+2);
+		e = findlib(pname);
+		if(e == nil) {
+			diag("cannot find library: %s", file);
+			errorexit();
+		}
+		snprint(name, sizeof(name), "%s/%s", e, pname);
+		file = name;
+	}
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open %s: %r", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			/* need readn to read the dumps (at least) */
+			l = readn(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	int l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = ALAST+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+	case D_FPCR:
+		break;
+
+	case D_REGREG:
+		a->offset = p[4];
+		c++;
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+	case D_SHIFT:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		while(nhunk < NSNAME)
+			gethunk();
+		a->sval = (char*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		while(nhunk < sizeof(Ieee))
+			gethunk();
+		a->ieee = (Ieee*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		a->ieee->l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee->h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		return c;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+
+	cleanname(name);
+
+	if(search){
+		p = findlib(name);
+		if(p != nil){
+			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+			name = fn2;
+		}
+	}
+
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	long ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+	ulong sig;
+	static int files;
+	static char **filen;
+	char **nfilen;
+
+	if((files&15) == 0){
+		nfilen = malloc((files+16)*sizeof(char*));
+		memmove(nfilen, filen, files*sizeof(char*));
+		free(filen);
+		filen = nfilen;
+	}
+	filen[files++] = strdup(pn);
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= AXXX || o >= ALAST) {
+		diag("%s: line %ld: opcode out of range %d", pn, pc-ipc, o);
+		print("	probably not a .5 file\n");
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		sig = 0;
+		if(o == ASIGNAME){
+			sig = bloc[1] | (bloc[2]<<8) | (bloc[3]<<16) | (bloc[4]<<24);
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(sig != 0){
+			if(s->sig != 0 && s->sig != sig)
+				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+			s->sig = sig;
+			s->file = files-1;
+		}
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->scond = bloc[1];
+	p->reg = bloc[2];
+	p->line = bloc[3] | (bloc[4]<<8) | (bloc[5]<<16) | (bloc[6]<<24);
+
+	r = zaddr(bloc+7, &p->from, h) + 7;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+	
+	case ADATA:
+		if(p->from.sym == S) {
+			diag("DATA without a sym\n%P", p);
+			break;
+		}
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		autosize = (p->to.offset+3L) & ~3L;
+		p->to.offset = autosize;
+		autosize += 4;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->cond = p;
+		etextp = p;
+		break;
+
+	case ASUB:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE)
+		if(p->from.offset < 0) {
+			p->from.offset = -p->from.offset;
+			p->as = AADD;
+		}
+		goto casedef;
+
+	case AADD:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE)
+		if(p->from.offset < 0) {
+			p->from.offset = -p->from.offset;
+			p->as = ASUB;
+		}
+		goto casedef;
+
+	case AMOVF:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVD:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->l, p->from.ieee->h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	h &= 0xffffff;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->sig = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+static int brcond[] = {ABEQ, ABNE, ABCS, ABCC, ABMI, ABPL, ABVS, ABVC, ABHI, ABLS, ABGE, ABLT, ABGT, ABLE};
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				ps2 = p;
+				p->reg = 1;
+			}
+			if(p->from.sym == s4) {
+				ps4 = p;
+				p->reg = 1;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->reg & NOPROF) {
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * BL	profin
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = AB;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->cond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = ABL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			q->reg = p->reg;
+			p->link = q;
+
+			if(p->scond != 14) {
+				q = prg();
+				q->as = ABL;
+				q->from = zprg.from;
+				q->to = zprg.to;
+				q->to.type = D_BRANCH;
+				q->cond = ps4;
+				q->to.sym = s4;
+				q->link = p->link;
+				p->link = q;
+
+				p->as = brcond[p->scond^1];	/* complement */
+				p->scond = 14;
+				p->from = zprg.from;
+				p->to = zprg.to;
+				p->to.type = D_BRANCH;
+				p->cond = q->link->link;	/* successor of RET */
+				p->to.offset = q->link->link->pc;
+
+				p = q->link->link;
+			} else {
+
+				/*
+				 * BL	profout
+				 */
+				p->as = ABL;
+				p->from = zprg.from;
+				p->to = zprg.to;
+				p->to.type = D_BRANCH;
+				p->cond = ps4;
+				p->to.sym = s4;
+				p->scond = 14;
+
+				p = q;
+			}
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		fnuxi4[i] = c;
+		if(debug['d'] == 0){
+			fnuxi8[i] = c;
+			fnuxi8[i+4] = c+4;
+		}
+		else{
+			fnuxi8[i] = c+4;		/* ms word first, then ls, even in little endian mode */
+			fnuxi8[i+4] = c;
+		}
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
+
+void
+undefsym(Sym *s)
+{
+	int n;
+
+	n = imports;
+	if(s->value != 0)
+		diag("value != 0 on SXREF");
+	if(n >= 1<<Rindex)
+		diag("import index %d out of range", n);
+	s->value = n<<Roffset;
+	s->type = SUNDEF;
+	imports++;
+}
+
+void
+zerosig(char *sp)
+{
+	Sym *s;
+
+	s = lookup(sp, 0);
+	s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+	int i, n;
+	Sym *s;
+	Biobuf *b;
+	char *l, buf[256], *fields[64];
+
+	if(f == nil)
+		return;
+	b = Bopen(f, OREAD);
+	if(b == nil){
+		diag("could not open %s: %r", f);
+		errorexit();
+	}
+	while((l = Brdline(b, '\n')) != nil){
+		n = Blinelen(b);
+		if(n >= sizeof(buf)){
+			diag("%s: line too long", f);
+			errorexit();
+		}
+		memmove(buf, l, n);
+		buf[n-1] = '\0';
+		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+		if(n == nelem(fields)){
+			diag("%s: bad format", f);
+			errorexit();
+		}
+		for(i = 0; i < n; i++) {
+			s = lookup(fields[i], 0);
+			s->type = SXREF;
+			s->subtype = t;
+			if(t == SIMPORT)
+				nimports++;
+			else
+				nexports++;
+		}
+	}
+	Bterm(b);
+}
--- /dev/null
+++ b/utils/5l/optab.c
@@ -1,0 +1,257 @@
+#include	"l.h"
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_REG,	C_LCON, 	 0, 0, 0 },
+
+	{ AADD,		C_REG,	C_REG,	C_REG,		 1, 4, 0 },
+	{ AADD,		C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMVN,		C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ ACMP,		C_REG,	C_REG,	C_NONE,		 1, 4, 0 },
+
+	{ AADD,		C_RCON,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMOVW,	C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMVN,		C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ ACMP,		C_RCON,	C_REG,	C_NONE,		 2, 4, 0 },
+
+	{ AADD,		C_SHIFT,C_REG,	C_REG,		 3, 4, 0 },
+	{ AADD,		C_SHIFT,C_NONE,	C_REG,		 3, 4, 0 },
+	{ AMVN,		C_SHIFT,C_NONE,	C_REG,		 3, 4, 0 },
+	{ ACMP,		C_SHIFT,C_REG,	C_NONE,		 3, 4, 0 },
+
+	{ AMOVW,	C_RECON,C_NONE,	C_REG,		 4, 4, REGSB },
+	{ AMOVW,	C_RACON,C_NONE,	C_REG,		 4, 4, REGSP },
+
+	{ AB,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0,	LPOOL },
+	{ ABL,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0 },
+	{ ABEQ,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0 },
+
+	{ AB,		C_NONE,	C_NONE,	C_ROREG,	 6, 4, 0,	LPOOL },
+	{ ABL,		C_NONE,	C_NONE,	C_ROREG,	 7, 8, 0 },
+
+	{ ASLL,		C_RCON,	C_REG,	C_REG,		 8, 4, 0 },
+	{ ASLL,		C_RCON,	C_NONE,	C_REG,		 8, 4, 0 },
+
+	{ ASLL,		C_REG,	C_NONE,	C_REG,		 9, 4, 0 },
+	{ ASLL,		C_REG,	C_REG,	C_REG,		 9, 4, 0 },
+
+	{ ASWI,		C_NONE,	C_NONE,	C_NONE,		10, 4, 0 },
+	{ ASWI,		C_NONE,	C_NONE,	C_LOREG,	10, 4, 0 },
+
+	{ AWORD,	C_NONE,	C_NONE,	C_LCON,		11, 4, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_LEXT,		11, 4, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_ADDR,		11, 4, 0 },
+
+	{ AMOVW,	C_NCON,	C_NONE,	C_REG,		12, 4, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		12, 4, 0,	LFROM },
+
+	{ AADD,		C_NCON,	C_REG,	C_REG,		13, 8, 0 },
+	{ AADD,		C_NCON,	C_NONE,	C_REG,		13, 8, 0 },
+	{ AMVN,		C_NCON,	C_NONE,	C_REG,		13, 8, 0 },
+	{ ACMP,		C_NCON,	C_REG,	C_NONE,		13, 8, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		13, 8, 0,	LFROM },
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		13, 8, 0,	LFROM },
+	{ AMVN,		C_LCON,	C_NONE,	C_REG,		13, 8, 0,	LFROM },
+	{ ACMP,		C_LCON,	C_REG,	C_NONE,		13, 8, 0,	LFROM },
+
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_REG,		58, 4, 0 },
+	{ AMOVH,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+
+	{ AMUL,		C_REG,	C_REG,	C_REG,		15, 4, 0 },
+	{ AMUL,		C_REG,	C_NONE,	C_REG,		15, 4, 0 },
+
+	{ ADIV,		C_REG,	C_REG,	C_REG,		16, 4, 0 },
+	{ ADIV,		C_REG,	C_NONE,	C_REG,		16, 4, 0 },
+
+	{ AMULL,	C_REG,	C_REG,	C_REGREG,	17, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		21, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		21, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		21, 4, 0 },
+	{ AMOVBU,	C_SEXT,	C_NONE,	C_REG,		21, 4, REGSB },
+	{ AMOVBU,	C_SAUTO,C_NONE,	C_REG,		21, 4, REGSP },
+	{ AMOVBU,	C_SOREG,C_NONE,	C_REG,		21, 4, 0 },
+
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+	{ AMOVH,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVH,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVH,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+	{ AMOVHU,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVHU,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVHU,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_SEXT,		23, 12, REGSB },
+	{ AMOVH,	C_REG,	C_NONE,	C_SAUTO,	23, 12, REGSP },
+	{ AMOVH,	C_REG,	C_NONE,	C_SOREG,	23, 12, 0 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SEXT,		23, 12, REGSB },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SAUTO,	23, 12, REGSP },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SOREG,	23, 12, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		31, 8, REGSB,	LFROM },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		31, 8, REGSP,	LFROM },
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		31, 8, 0,	LFROM },
+	{ AMOVW,	C_ADDR,	C_NONE,	C_REG,		65, 8, 0,	LFROM },
+	{ AMOVBU,	C_LEXT,	C_NONE,	C_REG,		31, 8, REGSB,	LFROM },
+	{ AMOVBU,	C_LAUTO,C_NONE,	C_REG,		31, 8, REGSP,	LFROM },
+	{ AMOVBU,	C_LOREG,C_NONE,	C_REG,		31, 8, 0,	LFROM },
+	{ AMOVBU,	C_ADDR,	C_NONE,	C_REG,		65, 8, 0,	LFROM },
+
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVB,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+	{ AMOVH,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVH,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVH,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVH,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+	{ AMOVHU,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVHU,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVHU,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVHU,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_LEXT,		33, 24, REGSB,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_LAUTO,	33, 24, REGSP,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_LOREG,	33, 24, 0,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_ADDR,		67, 24, 0,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LEXT,		33, 24, REGSB,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LAUTO,	33, 24, REGSP,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LOREG,	33, 24, 0,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_ADDR,		67, 24, 0,	LTO },
+
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		34, 8, REGSB,	LFROM },
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		34, 8, REGSP,	LFROM },
+
+	{ AMOVW,	C_PSR,	C_NONE,	C_REG,		35, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_PSR,		36, 4, 0 },
+	{ AMOVW,	C_RCON,	C_NONE,	C_PSR,		37, 4, 0 },
+
+	{ AMOVM,	C_LCON,	C_NONE,	C_SOREG,	38, 4, 0 },
+	{ AMOVM,	C_SOREG,C_NONE,	C_LCON,		39, 4, 0 },
+
+	{ ASWPW,	C_SOREG,C_REG,	C_REG,		40, 4, 0 },
+
+	{ ARFE,		C_NONE,	C_NONE,	C_NONE,		41, 4, 0 },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_FEXT,		50, 4, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FAUTO,	50, 4, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FOREG,	50, 4, 0 },
+
+	{ AMOVF,	C_FEXT,	C_NONE,	C_FREG,		51, 4, REGSB },
+	{ AMOVF,	C_FAUTO,C_NONE,	C_FREG,		51, 4, REGSP },
+	{ AMOVF,	C_FOREG,C_NONE,	C_FREG,		51, 4, 0 },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_LEXT,		52, 12, REGSB,	LTO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LAUTO,	52, 12, REGSP,	LTO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LOREG,	52, 12, 0,	LTO },
+
+	{ AMOVF,	C_LEXT,	C_NONE,	C_FREG,		53, 12, REGSB,	LFROM },
+	{ AMOVF,	C_LAUTO,C_NONE,	C_FREG,		53, 12, REGSP,	LFROM },
+	{ AMOVF,	C_LOREG,C_NONE,	C_FREG,		53, 12, 0,	LFROM },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_ADDR,		68, 8, 0,	LTO },
+	{ AMOVF,	C_ADDR,	C_NONE,	C_FREG,		69, 8, 0,	LFROM },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FCON,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FCON,	C_REG,	C_FREG,		54, 4, 0 },
+	{ AMOVF,	C_FCON,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AMOVF,	C_FREG, C_NONE, C_FREG,		54, 4, 0 },
+
+	{ ACMPF,	C_FREG,	C_REG,	C_NONE,		54, 4, 0 },
+	{ ACMPF,	C_FCON,	C_REG,	C_NONE,		54, 4, 0 },
+
+	{ AMOVFW,	C_FREG,	C_NONE,	C_REG,		55, 4, 0 },
+	{ AMOVFW,	C_REG,	C_NONE,	C_FREG,		55, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCR,		56, 4, 0 },
+	{ AMOVW,	C_FCR,	C_NONE,	C_REG,		57, 4, 0 },
+
+	{ AMOVW,	C_SHIFT,C_NONE,	C_REG,		59, 4, 0 },
+	{ AMOVBU,	C_SHIFT,C_NONE,	C_REG,		59, 4, 0 },
+
+	{ AMOVB,	C_SHIFT,C_NONE,	C_REG,		60, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+
+	{ ACASE,	C_REG,	C_NONE,	C_NONE,		62, 4, 0 },
+	{ ABCASE,	C_NONE, C_NONE, C_SBRA,		63, 4, 0 },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		74, 4, 0, VFP },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		74, 4, 0, VFP },
+	{ AMOVF,	C_FREG, C_NONE, C_FREG,		74, 4, 0, VFP },
+	{ ACMPF,	C_FREG,	C_REG,	C_NONE,		75, 8, 0, VFP },
+	{ ACMPF,	C_FCON,	C_REG,	C_NONE,		75, 8, 0, VFP },
+	{ AMOVFW,	C_FREG,	C_NONE,	C_REG,		76, 8, 0, VFP },
+	{ AMOVFW,	C_REG,	C_NONE,	C_FREG,		76, 8, 0, VFP },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_HEXT,		70, 4, REGSB,	V4 },
+	{ AMOVH,	C_REG,	C_NONE, C_HAUTO,	70, 4, REGSP,	V4 },
+	{ AMOVH,	C_REG,	C_NONE,	C_HOREG,	70, 4, 0,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_HEXT,		70, 4, REGSB,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE, C_HAUTO,	70, 4, REGSP,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_HOREG,	70, 4, 0,	V4 },
+
+	{ AMOVB,	C_HEXT,	C_NONE, C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVB,	C_HAUTO,C_NONE,	C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVB,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+	{ AMOVH,	C_HEXT,	C_NONE,	C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVH,	C_HAUTO,C_NONE, C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVH,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+	{ AMOVHU,	C_HEXT,	C_NONE,	C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVHU,	C_HAUTO,C_NONE, C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVHU,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_LEXT,		72, 8, REGSB,	LTO|V4 },
+	{ AMOVH,	C_REG,	C_NONE, C_LAUTO,	72, 8, REGSP,	LTO|V4 },
+	{ AMOVH,	C_REG,	C_NONE,	C_LOREG,	72, 8, 0,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LEXT,		72, 8, REGSB,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE, C_LAUTO,	72, 8, REGSP,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LOREG,	72, 8, 0,	LTO|V4 },
+
+	{ AMOVB,	C_LEXT,	C_NONE, C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+	{ AMOVH,	C_LEXT,	C_NONE,	C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVH,	C_LAUTO,C_NONE, C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVH,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+	{ AMOVHU,	C_LEXT,	C_NONE,	C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVHU,	C_LAUTO,C_NONE, C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVHU,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/5l/pass.c
@@ -1,0 +1,595 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p;
+	long orig, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+	}
+
+	if(debug['t']) {
+		/*
+		 * pull out string constants
+		 */
+		for(p = datap; p != P; p = p->link) {
+			s = p->from.sym;
+			if(p->to.type == D_SCONST)
+				s->type = SSTRING;
+		}
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on R12)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 3)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	/*
+	 * pass 2
+	 *	assign large 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		s->value = orig;
+		orig += v;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	xdefine("setR12", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+Prog*
+brchain(Prog *p)
+{
+	int i;
+
+	for(i=0; i<20; i++) {
+		if(p == P || p->as != AB)
+			return p;
+		p = p->cond;
+	}
+	return P;
+}
+
+int
+relinv(int a)
+{
+	switch(a) {
+	case ABEQ:	return ABNE;
+	case ABNE:	return ABEQ;
+	case ABCS:	return ABCC;
+	case ABHS:	return ABLO;
+	case ABCC:	return ABCS;
+	case ABLO:	return ABHS;
+	case ABMI:	return ABPL;
+	case ABPL:	return ABMI;
+	case ABVS:	return ABVC;
+	case ABVC:	return ABVS;
+	case ABHI:	return ABLS;
+	case ABLS:	return ABHI;
+	case ABGE:	return ABLT;
+	case ABLT:	return ABGE;
+	case ABGT:	return ABLE;
+	case ABLE:	return ABGT;
+	}
+	diag("unknown relation: %s", anames[a]);
+	return a;
+}
+
+void
+follow(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AB) {
+		q = p->cond;
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp)
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			if(a != ABEQ && a != ABNE)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+					return;
+				r->as = ABNE;
+				if(a == ABNE)
+					r->as = ABEQ;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+		a = AB;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){
+		return;
+	}
+	if(p->cond != P)
+	if(a != ABL && p->link != P) {
+		q = brchain(p->link);
+		if(a != ATEXT && a != ABCASE)
+		if(q != P && (q->mark&FOLL)) {
+			p->as = relinv(a);
+			p->link = p->cond;
+			p->cond = q;
+		}
+		xfol(p->link);
+		q = brchain(p->cond);
+		if(q == P)
+			q = p->cond;
+		if(q->mark&FOLL) {
+			p->cond = q;
+			return;
+		}
+		p = q;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == ABL || a == AB || a == ARET) &&
+		   p->to.type != D_BRANCH && p->to.sym != S) {
+			s = p->to.sym;
+			switch(s->type) {
+			default:
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+				break;
+			case STEXT:
+				p->to.offset = s->value;
+				p->to.type = D_BRANCH;
+				break;
+			case SUNDEF:
+				if(p->as != ABL)
+					diag("help: SUNDEF in AB || ARET");
+				p->to.offset = 0;
+				p->to.type = D_BRANCH;
+				p->cond = UP;
+				break;
+			}
+		}
+		if(p->to.type != D_BRANCH || p->cond == UP)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->cond != P && p->cond != UP) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AB)
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
+
+void
+import(void)
+{
+	int i;
+	Sym *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+				undefsym(s);
+				Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value);
+			}
+}
+
+void
+ckoff(Sym *s, long v)
+{
+	if(v < 0 || v >= 1<<Roffset)
+		diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+	Prog *p;
+
+	p = prg();
+	p->link = datap;
+	datap = p;
+	p->as = ADATA;
+	p->reg = w;
+	p->from.type = D_OREG;
+	p->from.name = t;
+	p->from.sym = s;
+	p->from.offset = o;
+	p->to.type = D_CONST;
+	p->to.name = D_NONE;
+	return p;
+}
+
+void
+export(void)
+{
+	int i, j, n, off, nb, sv, ne;
+	Sym *s, *et, *str, **esyms;
+	Prog *p;
+	char buf[NSNAME], *t;
+
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				n++;
+	esyms = malloc(n*sizeof(Sym*));
+	ne = n;
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				esyms[n++] = s;
+	for(i = 0; i < ne-1; i++)
+		for(j = i+1; j < ne; j++)
+			if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+				s = esyms[i];
+				esyms[i] = esyms[j];
+				esyms[j] = s;
+			}
+
+	nb = 0;
+	off = 0;
+	et = lookup(EXPTAB, 0);
+	if(et->type != 0 && et->type != SXREF)
+		diag("%s already defined", EXPTAB);
+	et->type = SDATA;
+	str = lookup(".string", 0);
+	if(str->type == 0)
+		str->type = SDATA;
+	sv = str->value;
+	for(i = 0; i < ne; i++){
+		s = esyms[i];
+		Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type);
+
+		/* signature */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.offset = s->sig;
+
+		/* address */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_EXTERN;
+		p->to.sym = s;
+
+		/* string */
+		t = s->name;
+		n = strlen(t)+1;
+		for(;;){
+			buf[nb++] = *t;
+			sv++;
+			if(nb >= NSNAME){
+				p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+				p->to.type = D_SCONST;
+				p->to.sval = malloc(NSNAME);
+				memmove(p->to.sval, buf, NSNAME);
+				nb = 0;
+			}
+			if(*t++ == 0)
+				break;
+		}
+
+		/* name */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_STATIC;
+		p->to.sym = str;
+		p->to.offset = sv-n;
+	}
+
+	if(nb > 0){
+		p = newdata(str, sv-nb, nb, D_STATIC);
+		p->to.type = D_SCONST;
+		p->to.sval = malloc(NSNAME);
+		memmove(p->to.sval, buf, nb);
+	}
+
+	for(i = 0; i < 3; i++){
+		newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+	}
+	et->value = off;
+	if(sv == 0)
+		sv = 1;
+	str->value = sv;
+	exports = ne;
+	free(esyms);
+}
--- /dev/null
+++ b/utils/5l/span.c
@@ -1,0 +1,987 @@
+#include	"l.h"
+
+static struct {
+	ulong	start;
+	ulong	size;
+} pool;
+
+void	checkpool(Prog*);
+void 	flushpool(Prog*, int);
+
+void
+span(void)
+{
+	Prog *p;
+	Sym *setext, *s;
+	Optab *o;
+	int m, bflag, i;
+	long c, otxt, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	otxt = c;
+	for(p = firstp; p != P; p = p->link) {
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 4;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches */
+				if(c-otxt >= 1L<<17)
+					bflag = 1;
+				otxt = c;
+				continue;
+			}
+			diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		switch(o->flag & (LFROM|LTO|LPOOL)) {
+		case LFROM:
+			addpool(p, &p->from);
+			break;
+		case LTO:
+			addpool(p, &p->to);
+			break;
+		case LPOOL:
+			if ((p->scond&C_SCOND) == 14)
+				flushpool(p, 0);
+			break;
+		}
+		if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14)
+			flushpool(p, 0);
+		c += m;
+		if(blitrl)
+			checkpool(p);
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			p->pc = c;
+			o = oplook(p);
+/* very larg branches
+			if(o->type == 6 && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < 0)
+					otxt = -otxt;
+				if(otxt >= (1L<<17) - 10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AB;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AB;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					bflag = 1;
+				}
+			}
+ */
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 4;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				diag("zero-width instruction\n%P", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+
+	if(debug['t']) {
+		/* 
+		 * add strings to text segment
+		 */
+		c = rnd(c, 8);
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SSTRING)
+				continue;
+			v = s->value;
+			while(v & 3)
+				v++;
+			s->value = c;
+			c += v;
+		}
+	}
+
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+
+/*
+ * when the first reference to the literal pool threatens
+ * to go out of range of a 12-bit PC-relative offset,
+ * drop the pool now, and branch round it.
+ * this happens only in extended basic blocks that exceed 4k.
+ */
+void
+checkpool(Prog *p)
+{
+	if(pool.size >= 0xffc || immaddr((p->pc+4)+4+pool.size - pool.start+8) == 0)
+		flushpool(p, 1);
+	else if(p->link == P)
+		flushpool(p, 2);
+}
+
+void
+flushpool(Prog *p, int skip)
+{
+	Prog *q;
+
+	if(blitrl) {
+		if(skip){
+			if(debug['v'] && skip == 1)
+				print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);
+			q = prg();
+			q->as = AB;
+			q->to.type = D_BRANCH;
+			q->cond = p->link;
+			q->link = blitrl;
+			blitrl = q;
+		}
+		else if(p->pc+pool.size-pool.start < 2048)
+			return;
+		elitrl->link = p->link;
+		p->link = blitrl;
+		blitrl = 0;	/* BUG: should refer back to values until out-of-range */
+		elitrl = 0;
+		pool.size = 0;
+		pool.start = 0;
+	}
+}
+
+void
+addpool(Prog *p, Adr *a)
+{
+	Prog *q, t;
+	int c;
+
+	c = aclass(a);
+
+	t = zprg;
+	t.as = AWORD;
+
+	switch(c) {
+	default:
+		t.to = *a;
+		break;
+
+	case C_SROREG:
+	case C_LOREG:
+	case C_ROREG:
+	case C_FOREG:
+	case C_SOREG:
+	case C_FAUTO:
+	case C_SAUTO:
+	case C_LAUTO:
+	case C_LACON:
+		t.to.type = D_CONST;
+		t.to.offset = instoffset;
+		break;
+	}
+
+	for(q = blitrl; q != P; q = q->link)	/* could hash on t.t0.offset */
+		if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {
+			p->cond = q;
+			return;
+		}
+
+	q = prg();
+	*q = t;
+	q->pc = pool.size;
+
+	if(blitrl == P) {
+		blitrl = q;
+		pool.start = p->pc;
+	} else
+		elitrl->link = q;
+	elitrl = q;
+	pool.size += 4;
+
+	p->cond = q;
+}
+
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+long
+immrot(ulong v)
+{
+	int i;
+
+	for(i=0; i<16; i++) {
+		if((v & ~0xff) == 0)
+			return (i<<8) | v | (1<<25);
+		v = (v<<2) | (v>>30);
+	}
+	return 0;
+}
+
+long
+immaddr(long v)
+{
+	if(v >= 0 && v <= 0xfff)
+		return (v & 0xfff) |
+			(1<<24) |	/* pre indexing */
+			(1<<23);	/* pre indexing, up */
+	if(v >= -0xfff && v < 0)
+		return (-v & 0xfff) |
+			(1<<24);	/* pre indexing */
+	return 0;
+}
+
+int
+immfloat(long v)
+{
+	return (v & 0xC03) == 0;	/* offset will fit in floating-point load/store */
+}
+
+int
+immhalf(long v)
+{
+	if(v >= 0 && v <= 0xff)
+		return v|
+			(1<<24)|	/* pre indexing */
+			(1<<23);	/* pre indexing, up */
+	if(v >= -0xff && v < 0)
+		return (-v & 0xff)|
+			(1<<24);	/* pre indexing */
+	return 0;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_REGREG:
+		return C_REGREG;
+
+	case D_SHIFT:
+		return C_SHIFT;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_FPCR:
+		return C_FCR;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			if(dlm) {
+				switch(t) {
+				default:
+					instoffset = s->value + a->offset + INITDAT;
+					break;
+				case SUNDEF:
+				case STEXT:
+				case SCONST:
+				case SLEAF:
+				case SSTRING:
+					instoffset = s->value + a->offset;
+					break;
+				}
+				return C_ADDR;
+			}
+			instoffset = s->value + a->offset - BIG;
+			t = immaddr(instoffset);
+			if(t) {
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFEXT : C_HEXT;
+				if(immfloat(t))
+					return C_FEXT;
+				return C_SEXT;
+			}
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			t = immaddr(instoffset);
+			if(t){
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFAUTO : C_HAUTO;
+				if(immfloat(t))
+					return C_FAUTO;
+				return C_SAUTO;
+			}
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			t = immaddr(instoffset);
+			if(t){
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFAUTO : C_HAUTO;
+				if(immfloat(t))
+					return C_FAUTO;
+				return C_SAUTO;
+			}
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			t = immaddr(instoffset);
+			if(t) {
+				if(immhalf(instoffset))		 /* n.b. that it will also satisfy immrot */
+					return immfloat(t) ? C_HFOREG : C_HOREG;
+				if(immfloat(t))
+					return C_FOREG; /* n.b. that it will also satisfy immrot */
+				t = immrot(instoffset);
+				if(t)
+					return C_SROREG;
+				if(immhalf(instoffset))
+					return C_HOREG;
+				return C_SOREG;
+			}
+			t = immrot(instoffset);
+			if(t)
+				return C_ROREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_PSR:
+		return C_PSR;
+
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF)
+				instoffset = s->value + a->offset;
+			return C_LCON;
+		}
+		return C_GOK;
+
+	case D_FCONST:
+		return C_FCON;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+			if(a->reg != NREG)
+				goto aconsize;
+
+			t = immrot(instoffset);
+			if(t)
+				return C_RCON;
+			t = immrot(~instoffset);
+			if(t)
+				return C_NCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SUNDEF:
+			case STEXT:
+			case SSTRING:
+			case SCONST:
+			case SLEAF:
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			if(!dlm) {
+				instoffset = s->value + a->offset - BIG;
+				t = immrot(instoffset);
+				if(t && instoffset != 0)
+					return C_RECON;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			goto aconsize;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+		aconsize:
+			t = immrot(instoffset);
+			if(t)
+				return C_RACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r;
+	char *c1, *c3;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0) {
+		a1 = opcross[repop[r]][a1][a2][a3];
+		if(a1) {
+			p->optab = a1+1;
+			return optab+a1;
+		}
+		o = oprange[r].stop; /* just generate an error */
+	}
+	if(0) {
+		print("oplook %A %d %d %d\n",
+			(int)p->as, a1, a2, a3);
+		print("		%d %d\n", p->from.type, p->to.type);
+	}
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, a1, a2, a3);
+	prasm(p);
+	if(o == 0)
+		o = optab;
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_RCON || b == C_NCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_RACON)
+			return 1;
+		break;
+	case C_LECON:
+		if(b == C_RECON)
+			return 1;
+		break;
+
+	case C_HFEXT:
+		return b == C_HEXT || b == C_FEXT;
+	case C_FEXT:
+	case C_HEXT:
+		return b == C_HFEXT;
+	case C_SEXT:
+		return cmp(C_HFEXT, b);
+	case C_LEXT:
+		return cmp(C_SEXT, b);
+
+	case C_HFAUTO:
+		return b == C_HAUTO || b == C_FAUTO;
+	case C_FAUTO:
+	case C_HAUTO:
+		return b == C_HFAUTO;
+	case C_SAUTO:
+		return cmp(C_HFAUTO, b);
+	case C_LAUTO:
+		return cmp(C_SAUTO, b);
+
+	case C_HFOREG:
+		return b == C_HOREG || b == C_FOREG;
+	case C_FOREG:
+	case C_HOREG:
+		return b == C_HFOREG;
+	case C_SROREG:
+		return cmp(C_SOREG, b) || cmp(C_ROREG, b);
+	case C_SOREG:
+	case C_ROREG:
+		return b == C_SROREG || cmp(C_HFOREG, b);
+	case C_LOREG:
+		return cmp(C_SROREG, b);
+
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = (p2->flag&V4) - (p1->flag&V4);	/* architecture version */
+	if(n)
+		return n;
+	n = (p2->flag&VFP) - (p1->flag&VFP);	/* floating point arch */
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	armv4 = !debug['h'];
+	vfp = debug['f'];
+	for(i=0; i<C_GOK; i++)
+		for(n=0; n<C_GOK; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++) {
+		if((optab[n].flag & VFP) && !vfp)
+			optab[n].as = AXXX;
+		if((optab[n].flag & V4) && !armv4) {
+			optab[n].as = AXXX;
+			break;
+		}
+	}
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case AXXX:
+			break;
+		case AADD:
+			oprange[AAND] = oprange[r];
+			oprange[AEOR] = oprange[r];
+			oprange[ASUB] = oprange[r];
+			oprange[ARSB] = oprange[r];
+			oprange[AADC] = oprange[r];
+			oprange[ASBC] = oprange[r];
+			oprange[ARSC] = oprange[r];
+			oprange[AORR] = oprange[r];
+			oprange[ABIC] = oprange[r];
+			break;
+		case ACMP:
+			oprange[ATST] = oprange[r];
+			oprange[ATEQ] = oprange[r];
+			oprange[ACMN] = oprange[r];
+			break;
+		case AMVN:
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			oprange[ABCS] = oprange[r];
+			oprange[ABHS] = oprange[r];
+			oprange[ABCC] = oprange[r];
+			oprange[ABLO] = oprange[r];
+			oprange[ABMI] = oprange[r];
+			oprange[ABPL] = oprange[r];
+			oprange[ABVS] = oprange[r];
+			oprange[ABVC] = oprange[r];
+			oprange[ABHI] = oprange[r];
+			oprange[ABLS] = oprange[r];
+			oprange[ABGE] = oprange[r];
+			oprange[ABLT] = oprange[r];
+			oprange[ABGT] = oprange[r];
+			oprange[ABLE] = oprange[r];
+			break;
+		case ASLL:
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			break;
+		case AMUL:
+			oprange[AMULU] = oprange[r];
+			break;
+		case ADIV:
+			oprange[AMOD] = oprange[r];
+			oprange[AMODU] = oprange[r];
+			oprange[ADIVU] = oprange[r];
+			break;
+		case AMOVW:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+			break;
+		case ASWPW:
+			oprange[ASWPBU] = oprange[r];
+			break;
+		case AB:
+		case ABL:
+		case ABX:
+		case ABXRET:
+		case ASWI:
+		case AWORD:
+		case AMOVM:
+		case ARFE:
+		case ATEXT:
+		case ACASE:
+		case ABCASE:
+			break;
+		case AADDF:
+			oprange[AADDD] = oprange[r];
+			oprange[ASUBF] = oprange[r];
+			oprange[ASUBD] = oprange[r];
+			oprange[AMULF] = oprange[r];
+			oprange[AMULD] = oprange[r];
+			oprange[ADIVF] = oprange[r];
+			oprange[ADIVD] = oprange[r];
+			oprange[AMOVFD] = oprange[r];
+			oprange[AMOVDF] = oprange[r];
+			break;
+			
+		case ACMPF:
+			oprange[ACMPD] = oprange[r];
+			break;
+
+		case AMOVF:
+			oprange[AMOVD] = oprange[r];
+			break;
+
+		case AMOVFW:
+			oprange[AMOVWF] = oprange[r];
+			oprange[AMOVWD] = oprange[r];
+			oprange[AMOVDW] = oprange[r];
+			break;
+
+		case AMULL:
+			oprange[AMULA] = oprange[r];
+			oprange[AMULAL] = oprange[r];
+			oprange[AMULLU] = oprange[r];
+			oprange[AMULALU] = oprange[r];
+			break;
+		}
+	}
+}
+
+/*
+void
+buildrep(int x, int as)
+{
+	Opcross *p;
+	Optab *e, *s, *o;
+	int a1, a2, a3, n;
+
+	if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
+		diag("assumptions fail in buildrep");
+		errorexit();
+	}
+	repop[as] = x;
+	p = (opcross + x);
+	s = oprange[as].start;
+	e = oprange[as].stop;
+	for(o=e-1; o>=s; o--) {
+		n = o-optab;
+		for(a2=0; a2<2; a2++) {
+			if(a2) {
+				if(o->a2 == C_NONE)
+					continue;
+			} else
+				if(o->a2 != C_NONE)
+					continue;
+			for(a1=0; a1<32; a1++) {
+				if(!xcmp[a1][o->a1])
+					continue;
+				for(a3=0; a3<32; a3++)
+					if(xcmp[a3][o->a3])
+						(*p)[a1][a2][a3] = n;
+			}
+		}
+	}
+	oprange[as].start = 0;
+}
+*/
+
+enum{
+	ABSD = 0,
+	ABSU = 1,
+	RELD = 2,
+	RELU = 3,
+};
+
+int modemap[4] = { 0, 1, -1, 2, };
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+	int n;
+	int t;
+	uchar *m;
+	ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+	int t;
+	uchar *m, *nm;
+	ulong *a, *na;
+
+	t = r->t;
+	r->t += 64;
+	m = r->m;
+	a = r->a;
+	r->m = nm = malloc(r->t*sizeof(uchar));
+	r->a = na = malloc(r->t*sizeof(ulong));
+	memmove(nm, m, t*sizeof(uchar));
+	memmove(na, a, t*sizeof(ulong));
+	free(m);
+	free(a);
+}
+
+void
+dynreloc(Sym *s, long v, int abs)
+{
+	int i, k, n;
+	uchar *m;
+	ulong *a;
+	Reloc *r;
+
+	if(v&3)
+		diag("bad relocation address");
+	v >>= 2;
+	if(s != S && s->type == SUNDEF)
+		k = abs ? ABSU : RELU;
+	else
+		k = abs ? ABSD : RELD;
+	/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
+	k = modemap[k];
+	r = &rels;
+	n = r->n;
+	if(n >= r->t)
+		grow(r);
+	m = r->m;
+	a = r->a;
+	for(i = n; i > 0; i--){
+		if(v < a[i-1]){	/* happens occasionally for data */
+			m[i] = m[i-1];
+			a[i] = a[i-1];
+		}
+		else
+			break;
+	}
+	m[i] = k;
+	a[i] = v;
+	r->n++;
+}
+
+static int
+sput(char *s)
+{
+	char *p;
+
+	p = s;
+	while(*s)
+		cput(*s++);
+	cput(0);
+	return  s-p+1;
+}
+
+void
+asmdyn()
+{
+	int i, n, t, c;
+	Sym *s;
+	ulong la, ra, *a;
+	vlong off;
+	uchar *m;
+	Reloc *r;
+
+	cflush();
+	off = seek(cout, 0, 1);
+	lput(0);
+	t = 0;
+	lput(imports);
+	t += 4;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type == SUNDEF){
+				lput(s->sig);
+				t += 4;
+				t += sput(s->name);
+			}
+	
+	la = 0;
+	r = &rels;
+	n = r->n;
+	m = r->m;
+	a = r->a;
+	lput(n);
+	t += 4;
+	for(i = 0; i < n; i++){
+		ra = *a-la;
+		if(*a < la)
+			diag("bad relocation order");
+		if(ra < 256)
+			c = 0;
+		else if(ra < 65536)
+			c = 1;
+		else
+			c = 2;
+		cput((c<<6)|*m++);
+		t++;
+		if(c == 0){
+			cput(ra);
+			t++;
+		}
+		else if(c == 1){
+			wput(ra);
+			t += 2;
+		}
+		else{
+			lput(ra);
+			t += 4;
+		}
+		la = *a++;
+	}
+
+	cflush();
+	seek(cout, off, 0);
+	lput(t);
+
+	if(debug['v']){
+		Bprint(&bso, "import table entries = %d\n", imports);
+		Bprint(&bso, "export table entries = %d\n", exports);
+	}
+}
--- /dev/null
+++ b/utils/6a/a.h
@@ -1,0 +1,194 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../6c/6.out.h"
+
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Ref	Ref;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+typedef	struct	Gen2 	Gen2;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		500
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	Ref*	ref;
+	char*	macro;
+	vlong	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+struct	Ref
+{
+	int	class;
+};
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	double	dval;
+	char	sval[8];
+	vlong	offset;
+	Sym*	sym;
+	short	type;
+	short	index;
+	short	scale;
+};
+struct	Gen2
+{
+	Gen	from;
+	Gen	to;
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	vlong	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+};
+
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	checkscale(int);
+void	pinit(char*);
+void	cclean(void);
+int	isreg(Gen*);
+void	outcode(int, Gen2*);
+void	outhist(void);
+void	zaddr(Gen*, int);
+void	zname(char*, int, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	macprag(void);
+void	maclin(void);
+void	macif(int);
+void	macend(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	Posix.c/Inferno.c/Nt.c
+ */
+enum	/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/6a/a.y
@@ -1,0 +1,562 @@
+%{
+#include "a.h"
+%}
+%union	{
+	Sym	*sym;
+	vlong	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+	Gen2	gen2;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
+%token	<lval>	LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT
+%token	<lval>	LCONST LFP LPC LSB
+%token	<lval>	LBREG LLREG LSREG LFREG LMREG LXREG
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST LSP
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset
+%type	<gen>	mem imm reg nam rel rem rim rom omem nmem
+%type	<gen2>	nonnon nonrel nonrem rimnon rimrem remrim spec10
+%type	<gen2>	spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+	LNAME '=' expr
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LTYPE0 nonnon	{ outcode($1, &$2); }
+|	LTYPE1 nonrem	{ outcode($1, &$2); }
+|	LTYPE2 rimnon	{ outcode($1, &$2); }
+|	LTYPE3 rimrem	{ outcode($1, &$2); }
+|	LTYPE4 remrim	{ outcode($1, &$2); }
+|	LTYPER nonrel	{ outcode($1, &$2); }
+|	LTYPED spec1	{ outcode($1, &$2); }
+|	LTYPET spec2	{ outcode($1, &$2); }
+|	LTYPEC spec3	{ outcode($1, &$2); }
+|	LTYPEN spec4	{ outcode($1, &$2); }
+|	LTYPES spec5	{ outcode($1, &$2); }
+|	LTYPEM spec6	{ outcode($1, &$2); }
+|	LTYPEI spec7	{ outcode($1, &$2); }
+|	LTYPEXC spec8	{ outcode($1, &$2); }
+|	LTYPEX spec9	{ outcode($1, &$2); }
+|	LTYPERT spec10	{ outcode($1, &$2); }
+
+nonnon:
+	{
+		$$.from = nullgen;
+		$$.to = nullgen;
+	}
+|	','
+	{
+		$$.from = nullgen;
+		$$.to = nullgen;
+	}
+
+rimrem:
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+remrim:
+	rem ',' rim
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+rimnon:
+	rim ','
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+
+nonrem:
+	',' rem
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rem
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+nonrel:
+	',' rel
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rel
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+spec1:	/* DATA */
+	nam '/' con ',' imm
+	{
+		$$.from = $1;
+		$$.from.scale = $3;
+		$$.to = $5;
+	}
+
+spec2:	/* TEXT */
+	mem ',' imm
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	mem ',' con ',' imm
+	{
+		$$.from = $1;
+		$$.from.scale = $3;
+		$$.to = $5;
+	}
+
+spec3:	/* JMP/CALL */
+	',' rom
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rom
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+spec4:	/* NOP */
+	nonnon
+|	nonrem
+
+spec5:	/* SHL/SHR */
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	rim ',' rem ':' LLREG
+	{
+		$$.from = $1;
+		$$.to = $3;
+		if($$.from.index != D_NONE)
+			yyerror("dp shift with lhs index");
+		$$.from.index = $5;
+	}
+
+spec6:	/* MOVW/MOVL */
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	rim ',' rem ':' LSREG
+	{
+		$$.from = $1;
+		$$.to = $3;
+		if($$.to.index != D_NONE)
+			yyerror("dp move with lhs index");
+		$$.to.index = $5;
+	}
+
+spec7:
+	rim ','
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+spec8:	/* CMPPS/CMPPD */
+	reg ',' rem ',' con
+	{
+		$$.from = $1;
+		$$.to = $3;
+		$$.from.offset = $5;
+	}
+
+spec9:	/* shufl */
+	imm ',' rem ',' reg
+	{
+		$$.from = $3;
+		$$.to = $5;
+		if($1.type != D_CONST)
+			yyerror("illegal constant");
+		$$.to.offset = $1.offset;
+	}
+
+spec10:	/* RET/RETF */
+	{
+		$$.from = nullgen;
+		$$.to = nullgen;
+	}
+|	imm
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+
+rem:
+	reg
+|	mem
+
+rom:
+	rel
+|	nmem
+|	'*' reg
+	{
+		$$ = $2;
+	}
+|	'*' omem
+	{
+		$$ = $2;
+	}
+|	reg
+|	omem
+
+rim:
+	rem
+|	imm
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+reg:
+	LBREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LFREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LLREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LMREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LSP
+	{
+		$$ = nullgen;
+		$$.type = D_SP;
+	}
+|	LSREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LXREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+
+imm:
+	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' nam
+	{
+		$$ = $2;
+		$$.index = $2.type;
+		$$.type = D_ADDR;
+		/*
+		if($2.type == D_AUTO || $2.type == D_PARAM)
+			yyerror("constant cannot be automatic: %s",
+				$2.sym->name);
+		 */
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '(' LFCONST ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $3;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+mem:
+	omem
+|	nmem
+
+omem:
+	con
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.offset = $1;
+	}
+|	con '(' LLREG ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$3;
+		$$.offset = $1;
+	}
+|	con '(' LSP ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_SP;
+		$$.offset = $1;
+	}
+|	con '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.offset = $1;
+		$$.index = $3;
+		$$.scale = $5;
+		checkscale($$.scale);
+	}
+|	con '(' LLREG ')' '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$3;
+		$$.offset = $1;
+		$$.index = $6;
+		$$.scale = $8;
+		checkscale($$.scale);
+	}
+|	'(' LLREG ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$2;
+	}
+|	'(' LSP ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_SP;
+	}
+|	'(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.index = $2;
+		$$.scale = $4;
+		checkscale($$.scale);
+	}
+|	'(' LLREG ')' '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$2;
+		$$.index = $5;
+		$$.scale = $7;
+		checkscale($$.scale);
+	}
+
+nmem:
+	nam
+	{
+		$$ = $1;
+	}
+|	nam '(' LLREG '*' con ')'
+	{
+		$$ = $1;
+		$$.index = $3;
+		$$.scale = $5;
+		checkscale($$.scale);
+	}
+
+nam:
+	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+	{
+		$$ = D_AUTO;
+	}
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/6a/lex.c
@@ -1,0 +1,1288 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = '6';
+	thestring = "amd64";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"AL",		LBREG,	D_AL,
+	"CL",		LBREG,	D_CL,
+	"DL",		LBREG,	D_DL,
+	"BL",		LBREG,	D_BL,
+/*	"SPB",		LBREG,	D_SPB,	*/
+	"SIB",		LBREG,	D_SIB,
+	"DIB",		LBREG,	D_DIB,
+	"BPB",		LBREG,	D_BPB,
+	"R8B",		LBREG,	D_R8B,
+	"R9B",		LBREG,	D_R9B,
+	"R10B",		LBREG,	D_R10B,
+	"R11B",		LBREG,	D_R11B,
+	"R12B",		LBREG,	D_R12B,
+	"R13B",		LBREG,	D_R13B,
+	"R14B",		LBREG,	D_R14B,
+	"R15B",		LBREG,	D_R15B,
+
+	"AH",		LBREG,	D_AH,
+	"CH",		LBREG,	D_CH,
+	"DH",		LBREG,	D_DH,
+	"BH",		LBREG,	D_BH,
+
+	"AX",		LLREG,	D_AX,
+	"CX",		LLREG,	D_CX,
+	"DX",		LLREG,	D_DX,
+	"BX",		LLREG,	D_BX,
+/*	"SP",		LLREG,	D_SP,	*/
+	"BP",		LLREG,	D_BP,
+	"SI",		LLREG,	D_SI,
+	"DI",		LLREG,	D_DI,
+	"R8",		LLREG,	D_R8,
+	"R9",		LLREG,	D_R9,
+	"R10",		LLREG,	D_R10,
+	"R11",		LLREG,	D_R11,
+	"R12",		LLREG,	D_R12,
+	"R13",		LLREG,	D_R13,
+	"R14",		LLREG,	D_R14,
+	"R15",		LLREG,	D_R15,
+
+	"RARG",		LLREG,	REGARG,
+
+	"F0",		LFREG,	D_F0+0,
+	"F1",		LFREG,	D_F0+1,
+	"F2",		LFREG,	D_F0+2,
+	"F3",		LFREG,	D_F0+3,
+	"F4",		LFREG,	D_F0+4,
+	"F5",		LFREG,	D_F0+5,
+	"F6",		LFREG,	D_F0+6,
+	"F7",		LFREG,	D_F0+7,
+
+	"M0",		LMREG,	D_M0+0,
+	"M1",		LMREG,	D_M0+1,
+	"M2",		LMREG,	D_M0+2,
+	"M3",		LMREG,	D_M0+3,
+	"M4",		LMREG,	D_M0+4,
+	"M5",		LMREG,	D_M0+5,
+	"M6",		LMREG,	D_M0+6,
+	"M7",		LMREG,	D_M0+7,
+
+	"X0",		LXREG,	D_X0+0,
+	"X1",		LXREG,	D_X0+1,
+	"X2",		LXREG,	D_X0+2,
+	"X3",		LXREG,	D_X0+3,
+	"X4",		LXREG,	D_X0+4,
+	"X5",		LXREG,	D_X0+5,
+	"X6",		LXREG,	D_X0+6,
+	"X7",		LXREG,	D_X0+7,
+	"X8",		LXREG,	D_X0+8,
+	"X9",		LXREG,	D_X0+9,
+	"X10",		LXREG,	D_X0+10,
+	"X11",		LXREG,	D_X0+11,
+	"X12",		LXREG,	D_X0+12,
+	"X13",		LXREG,	D_X0+13,
+	"X14",		LXREG,	D_X0+14,
+	"X15",		LXREG,	D_X0+15,
+
+	"CS",		LSREG,	D_CS,
+	"SS",		LSREG,	D_SS,
+	"DS",		LSREG,	D_DS,
+	"ES",		LSREG,	D_ES,
+	"FS",		LSREG,	D_FS,
+	"GS",		LSREG,	D_GS,
+
+	"GDTR",		LBREG,	D_GDTR,
+	"IDTR",		LBREG,	D_IDTR,
+	"LDTR",		LBREG,	D_LDTR,
+	"MSW",		LBREG,	D_MSW,
+	"TASK",		LBREG,	D_TASK,
+
+	"CR0",		LBREG,	D_CR+0,
+	"CR1",		LBREG,	D_CR+1,
+	"CR2",		LBREG,	D_CR+2,
+	"CR3",		LBREG,	D_CR+3,
+	"CR4",		LBREG,	D_CR+4,
+	"CR5",		LBREG,	D_CR+5,
+	"CR6",		LBREG,	D_CR+6,
+	"CR7",		LBREG,	D_CR+7,
+	"CR8",		LBREG,	D_CR+8,
+	"CR9",		LBREG,	D_CR+9,
+	"CR10",		LBREG,	D_CR+10,
+	"CR11",		LBREG,	D_CR+11,
+	"CR12",		LBREG,	D_CR+12,
+	"CR13",		LBREG,	D_CR+13,
+	"CR14",		LBREG,	D_CR+14,
+	"CR15",		LBREG,	D_CR+15,
+
+	"DR0",		LBREG,	D_DR+0,
+	"DR1",		LBREG,	D_DR+1,
+	"DR2",		LBREG,	D_DR+2,
+	"DR3",		LBREG,	D_DR+3,
+	"DR4",		LBREG,	D_DR+4,
+	"DR5",		LBREG,	D_DR+5,
+	"DR6",		LBREG,	D_DR+6,
+	"DR7",		LBREG,	D_DR+7,
+
+	"TR0",		LBREG,	D_TR+0,
+	"TR1",		LBREG,	D_TR+1,
+	"TR2",		LBREG,	D_TR+2,
+	"TR3",		LBREG,	D_TR+3,
+	"TR4",		LBREG,	D_TR+4,
+	"TR5",		LBREG,	D_TR+5,
+	"TR6",		LBREG,	D_TR+6,
+	"TR7",		LBREG,	D_TR+7,
+
+	"AAA",		LTYPE0,	AAAA,
+	"AAD",		LTYPE0,	AAAD,
+	"AAM",		LTYPE0,	AAAM,
+	"AAS",		LTYPE0,	AAAS,
+	"ADCB",		LTYPE3,	AADCB,
+	"ADCL",		LTYPE3,	AADCL,
+	"ADCQ",		LTYPE3,	AADCQ,
+	"ADCW",		LTYPE3,	AADCW,
+	"ADDB",		LTYPE3,	AADDB,
+	"ADDL",		LTYPE3,	AADDL,
+	"ADDQ",		LTYPE3,	AADDQ,
+	"ADDW",		LTYPE3,	AADDW,
+	"ADJSP",	LTYPE2,	AADJSP,
+	"ANDB",		LTYPE3,	AANDB,
+	"ANDL",		LTYPE3,	AANDL,
+	"ANDQ",		LTYPE3,	AANDQ,
+	"ANDW",		LTYPE3,	AANDW,
+	"ARPL",		LTYPE3,	AARPL,
+	"BOUNDL",	LTYPE3,	ABOUNDL,
+	"BOUNDW",	LTYPE3,	ABOUNDW,
+	"BSFL",		LTYPE3,	ABSFL,
+	"BSFQ",		LTYPE3,	ABSFQ,
+	"BSFW",		LTYPE3,	ABSFW,
+	"BSRL",		LTYPE3,	ABSRL,
+	"BSRQ",		LTYPE3,	ABSRQ,
+	"BSRW",		LTYPE3,	ABSRW,
+	"BTCL",		LTYPE3,	ABTCL,
+	"BTCQ",		LTYPE3,	ABTCQ,
+	"BTCW",		LTYPE3,	ABTCW,
+	"BTL",		LTYPE3,	ABTL,
+	"BTQ",		LTYPE3,	ABTQ,
+	"BTRL",		LTYPE3,	ABTRL,
+	"BTRQ",		LTYPE3,	ABTRQ,
+	"BTRW",		LTYPE3,	ABTRW,
+	"BTSL",		LTYPE3,	ABTSL,
+	"BTSQ",		LTYPE3,	ABTSQ,
+	"BTSW",		LTYPE3,	ABTSW,
+	"BTW",		LTYPE3,	ABTW,
+	"BYTE",		LTYPE2,	ABYTE,
+	"CALL",		LTYPEC,	ACALL,
+	"CLC",		LTYPE0,	ACLC,
+	"CLD",		LTYPE0,	ACLD,
+	"CLI",		LTYPE0,	ACLI,
+	"CLTS",		LTYPE0,	ACLTS,
+	"CMC",		LTYPE0,	ACMC,
+	"CMPB",		LTYPE4,	ACMPB,
+	"CMPL",		LTYPE4,	ACMPL,
+	"CMPQ",		LTYPE4,	ACMPQ,
+	"CMPW",		LTYPE4,	ACMPW,
+	"CMPSB",	LTYPE0,	ACMPSB,
+	"CMPSL",	LTYPE0,	ACMPSL,
+	"CMPSQ",	LTYPE0,	ACMPSQ,
+	"CMPSW",	LTYPE0,	ACMPSW,
+	"CMPXCHG8B",	LTYPE1,	ACMPXCHG8B,
+	"CMPXCHGB",	LTYPE3,	ACMPXCHGB,	/* LTYPE3? */
+	"CMPXCHGL",	LTYPE3,	ACMPXCHGL,
+	"CMPXCHGQ",	LTYPE3,	ACMPXCHGQ,
+	"CMPXCHGW",	LTYPE3,	ACMPXCHGW,
+	"CPUID",	LTYPE0,	ACPUID,
+	"DAA",		LTYPE0,	ADAA,
+	"DAS",		LTYPE0,	ADAS,
+	"DATA",		LTYPED,	ADATA,
+	"DECB",		LTYPE1,	ADECB,
+	"DECL",		LTYPE1,	ADECL,
+	"DECQ",		LTYPE1,	ADECQ,
+	"DECW",		LTYPE1,	ADECW,
+	"DIVB",		LTYPE2,	ADIVB,
+	"DIVL",		LTYPE2,	ADIVL,
+	"DIVQ",		LTYPE2,	ADIVQ,
+	"DIVW",		LTYPE2,	ADIVW,
+	"EMMS",		LTYPE0,	AEMMS,
+	"END",		LTYPE0,	AEND,
+	"ENTER",	LTYPE2,	AENTER,
+	"GLOBL",	LTYPET,	AGLOBL,
+	"HLT",		LTYPE0,	AHLT,
+	"IDIVB",	LTYPE2,	AIDIVB,
+	"IDIVL",	LTYPE2,	AIDIVL,
+	"IDIVQ",	LTYPE2,	AIDIVQ,
+	"IDIVW",	LTYPE2,	AIDIVW,
+	"IMULB",	LTYPEI,	AIMULB,
+	"IMULL",	LTYPEI,	AIMULL,
+	"IMULQ",	LTYPEI,	AIMULQ,
+	"IMULW",	LTYPEI,	AIMULW,
+	"INB",		LTYPE0,	AINB,
+	"INL",		LTYPE0,	AINL,
+	"INW",		LTYPE0,	AINW,
+	"INCB",		LTYPE1,	AINCB,
+	"INCL",		LTYPE1,	AINCL,
+	"INCQ",		LTYPE1,	AINCQ,
+	"INCW",		LTYPE1,	AINCW,
+	"INSB",		LTYPE0,	AINSB,
+	"INSL",		LTYPE0,	AINSL,
+	"INSW",		LTYPE0,	AINSW,
+	"INT",		LTYPE2,	AINT,
+	"INTO",		LTYPE0,	AINTO,
+	"INVD",		LTYPE0,	AINVD,
+	"INVLPG",	LTYPE2,	AINVLPG,
+	"IRETL",	LTYPE0,	AIRETL,
+	"IRETQ",	LTYPE0,	AIRETQ,
+	"IRETW",	LTYPE0,	AIRETW,
+
+	"JOS",		LTYPER,	AJOS,
+	"JO",		LTYPER,	AJOS,	/* alternate */
+	"JOC",		LTYPER,	AJOC,
+	"JNO",		LTYPER,	AJOC,	/* alternate */
+	"JCS",		LTYPER,	AJCS,
+	"JB",		LTYPER,	AJCS,	/* alternate */
+	"JC",		LTYPER,	AJCS,	/* alternate */
+	"JNAE",		LTYPER,	AJCS,	/* alternate */
+	"JLO",		LTYPER,	AJCS,	/* alternate */
+	"JCC",		LTYPER,	AJCC,
+	"JAE",		LTYPER,	AJCC,	/* alternate */
+	"JNB",		LTYPER,	AJCC,	/* alternate */
+	"JNC",		LTYPER,	AJCC,	/* alternate */
+	"JHS",		LTYPER,	AJCC,	/* alternate */
+	"JEQ",		LTYPER,	AJEQ,
+	"JE",		LTYPER,	AJEQ,	/* alternate */
+	"JZ",		LTYPER,	AJEQ,	/* alternate */
+	"JNE",		LTYPER,	AJNE,
+	"JNZ",		LTYPER,	AJNE,	/* alternate */
+	"JLS",		LTYPER,	AJLS,
+	"JBE",		LTYPER,	AJLS,	/* alternate */
+	"JNA",		LTYPER,	AJLS,	/* alternate */
+	"JHI",		LTYPER,	AJHI,
+	"JA",		LTYPER,	AJHI,	/* alternate */
+	"JNBE",		LTYPER,	AJHI,	/* alternate */
+	"JMI",		LTYPER,	AJMI,
+	"JS",		LTYPER,	AJMI,	/* alternate */
+	"JPL",		LTYPER,	AJPL,
+	"JNS",		LTYPER,	AJPL,	/* alternate */
+	"JPS",		LTYPER,	AJPS,
+	"JP",		LTYPER,	AJPS,	/* alternate */
+	"JPE",		LTYPER,	AJPS,	/* alternate */
+	"JPC",		LTYPER,	AJPC,
+	"JNP",		LTYPER,	AJPC,	/* alternate */
+	"JPO",		LTYPER,	AJPC,	/* alternate */
+	"JLT",		LTYPER,	AJLT,
+	"JL",		LTYPER,	AJLT,	/* alternate */
+	"JNGE",		LTYPER,	AJLT,	/* alternate */
+	"JGE",		LTYPER,	AJGE,
+	"JNL",		LTYPER,	AJGE,	/* alternate */
+	"JLE",		LTYPER,	AJLE,
+	"JNG",		LTYPER,	AJLE,	/* alternate */
+	"JGT",		LTYPER,	AJGT,
+	"JG",		LTYPER,	AJGT,	/* alternate */
+	"JNLE",		LTYPER,	AJGT,	/* alternate */
+
+	"JCXZ",		LTYPER,	AJCXZ,
+	"JMP",		LTYPEC,	AJMP,
+	"LAHF",		LTYPE0,	ALAHF,
+	"LARL",		LTYPE3,	ALARL,
+	"LARW",		LTYPE3,	ALARW,
+	"LEAL",		LTYPE3,	ALEAL,
+	"LEAQ",		LTYPE3,	ALEAQ,
+	"LEAW",		LTYPE3,	ALEAW,
+	"LEAVEL",	LTYPE0,	ALEAVEL,
+	"LEAVEQ",	LTYPE0,	ALEAVEQ,
+	"LEAVEW",	LTYPE0,	ALEAVEW,
+	"LFENCE",	LTYPE0,	ALFENCE,
+	"LOCK",		LTYPE0,	ALOCK,
+	"LODSB",	LTYPE0,	ALODSB,
+	"LODSL",	LTYPE0,	ALODSL,
+	"LODSQ",	LTYPE0,	ALODSQ,
+	"LODSW",	LTYPE0,	ALODSW,
+	"LONG",		LTYPE2,	ALONG,
+	"LOOP",		LTYPER,	ALOOP,
+	"LOOPEQ",	LTYPER,	ALOOPEQ,
+	"LOOPNE",	LTYPER,	ALOOPNE,
+	"LSLL",		LTYPE3,	ALSLL,
+	"LSLW",		LTYPE3,	ALSLW,
+	"MFENCE",	LTYPE0,	AMFENCE,
+	"MODE",		LTYPE2,	AMODE,
+	"MOVB",		LTYPE3,	AMOVB,
+	"MOVL",		LTYPEM,	AMOVL,
+	"MOVQ",		LTYPEM,	AMOVQ,
+	"MOVW",		LTYPEM,	AMOVW,
+	"MOVBLSX",	LTYPE3, AMOVBLSX,
+	"MOVBLZX",	LTYPE3, AMOVBLZX,
+	"MOVBQSX",	LTYPE3,	AMOVBQSX,
+	"MOVBQZX",	LTYPE3,	AMOVBQZX,
+	"MOVBWSX",	LTYPE3, AMOVBWSX,
+	"MOVBWZX",	LTYPE3, AMOVBWZX,
+	"MOVLQSX",	LTYPE3, AMOVLQSX,
+	"MOVLQZX",	LTYPE3, AMOVLQZX,
+	"MOVNTIL",	LTYPE3,	AMOVNTIL,
+	"MOVNTIQ",	LTYPE3,	AMOVNTIQ,
+	"MOVWLSX",	LTYPE3, AMOVWLSX,
+	"MOVWLZX",	LTYPE3, AMOVWLZX,
+	"MOVWQSX",	LTYPE3,	AMOVWQSX,
+	"MOVWQZX",	LTYPE3,	AMOVWQZX,
+	"MOVSB",	LTYPE0,	AMOVSB,
+	"MOVSL",	LTYPE0,	AMOVSL,
+	"MOVSQ",	LTYPE0,	AMOVSQ,
+	"MOVSW",	LTYPE0,	AMOVSW,
+	"MULB",		LTYPE2,	AMULB,
+	"MULL",		LTYPE2,	AMULL,
+	"MULQ",		LTYPE2,	AMULQ,
+	"MULW",		LTYPE2,	AMULW,
+	"NEGB",		LTYPE1,	ANEGB,
+	"NEGL",		LTYPE1,	ANEGL,
+	"NEGQ",		LTYPE1,	ANEGQ,
+	"NEGW",		LTYPE1,	ANEGW,
+	"NOP",		LTYPEN,	ANOP,
+	"NOTB",		LTYPE1,	ANOTB,
+	"NOTL",		LTYPE1,	ANOTL,
+	"NOTQ",		LTYPE1,	ANOTQ,
+	"NOTW",		LTYPE1,	ANOTW,
+	"ORB",		LTYPE3,	AORB,
+	"ORL",		LTYPE3,	AORL,
+	"ORQ",		LTYPE3,	AORQ,
+	"ORW",		LTYPE3,	AORW,
+	"OUTB",		LTYPE0,	AOUTB,
+	"OUTL",		LTYPE0,	AOUTL,
+	"OUTW",		LTYPE0,	AOUTW,
+	"OUTSB",	LTYPE0,	AOUTSB,
+	"OUTSL",	LTYPE0,	AOUTSL,
+	"OUTSW",	LTYPE0,	AOUTSW,
+	"POPAL",	LTYPE0,	APOPAL,
+	"POPAW",	LTYPE0,	APOPAW,
+	"POPFL",	LTYPE0,	APOPFL,
+	"POPFQ",	LTYPE0,	APOPFQ,
+	"POPFW",	LTYPE0,	APOPFW,
+	"POPL",		LTYPE1,	APOPL,
+	"POPQ",		LTYPE1,	APOPQ,
+	"POPW",		LTYPE1,	APOPW,
+	"PUSHAL",	LTYPE0,	APUSHAL,
+	"PUSHAW",	LTYPE0,	APUSHAW,
+	"PUSHFL",	LTYPE0,	APUSHFL,
+	"PUSHFQ",	LTYPE0,	APUSHFQ,
+	"PUSHFW",	LTYPE0,	APUSHFW,
+	"PUSHL",	LTYPE2,	APUSHL,
+	"PUSHQ",	LTYPE2,	APUSHQ,
+	"PUSHW",	LTYPE2,	APUSHW,
+	"RCLB",		LTYPE3,	ARCLB,
+	"RCLL",		LTYPE3,	ARCLL,
+	"RCLQ",		LTYPE3,	ARCLQ,
+	"RCLW",		LTYPE3,	ARCLW,
+	"RCRB",		LTYPE3,	ARCRB,
+	"RCRL",		LTYPE3,	ARCRL,
+	"RCRQ",		LTYPE3,	ARCRQ,
+	"RCRW",		LTYPE3,	ARCRW,
+	"RDMSR",	LTYPE0,	ARDMSR,
+	"RDPMC",	LTYPE0,	ARDPMC,
+	"RDTSC",	LTYPE0,	ARDTSC,
+	"REP",		LTYPE0,	AREP,
+	"REPN",		LTYPE0,	AREPN,
+	"RET",		LTYPE0,	ARET,
+	"RETFL",	LTYPERT,ARETFL,
+	"RETFW",	LTYPERT,ARETFW,
+	"RETFQ",	LTYPERT,ARETFQ,
+	"ROLB",		LTYPE3,	AROLB,
+	"ROLL",		LTYPE3,	AROLL,
+	"ROLQ",		LTYPE3,	AROLQ,
+	"ROLW",		LTYPE3,	AROLW,
+	"RORB",		LTYPE3,	ARORB,
+	"RORL",		LTYPE3,	ARORL,
+	"RORQ",		LTYPE3,	ARORQ,
+	"RORW",		LTYPE3,	ARORW,
+	"RSM",		LTYPE0,	ARSM,
+	"SAHF",		LTYPE0,	ASAHF,
+	"SALB",		LTYPE3,	ASALB,
+	"SALL",		LTYPE3,	ASALL,
+	"SALQ",		LTYPE3,	ASALQ,
+	"SALW",		LTYPE3,	ASALW,
+	"SARB",		LTYPE3,	ASARB,
+	"SARL",		LTYPE3,	ASARL,
+	"SARQ",		LTYPE3,	ASARQ,
+	"SARW",		LTYPE3,	ASARW,
+	"SBBB",		LTYPE3,	ASBBB,
+	"SBBL",		LTYPE3,	ASBBL,
+	"SBBQ",		LTYPE3,	ASBBQ,
+	"SBBW",		LTYPE3,	ASBBW,
+	"SCASB",	LTYPE0,	ASCASB,
+	"SCASL",	LTYPE0,	ASCASL,
+	"SCASQ",	LTYPE0,	ASCASQ,
+	"SCASW",	LTYPE0,	ASCASW,
+	"SETCC",	LTYPE1,	ASETCC,
+	"SETCS",	LTYPE1,	ASETCS,
+	"SETEQ",	LTYPE1,	ASETEQ,
+	"SETGE",	LTYPE1,	ASETGE,
+	"SETGT",	LTYPE1,	ASETGT,
+	"SETHI",	LTYPE1,	ASETHI,
+	"SETLE",	LTYPE1,	ASETLE,
+	"SETLS",	LTYPE1,	ASETLS,
+	"SETLT",	LTYPE1,	ASETLT,
+	"SETMI",	LTYPE1,	ASETMI,
+	"SETNE",	LTYPE1,	ASETNE,
+	"SETOC",	LTYPE1,	ASETOC,
+	"SETOS",	LTYPE1,	ASETOS,
+	"SETPC",	LTYPE1,	ASETPC,
+	"SETPL",	LTYPE1,	ASETPL,
+	"SETPS",	LTYPE1,	ASETPS,
+	"SFENCE",	LTYPE0,	ASFENCE,
+	"CDQ",		LTYPE0,	ACDQ,
+	"CWD",		LTYPE0,	ACWD,
+	"CQO",		LTYPE0,	ACQO,
+	"SHLB",		LTYPE3,	ASHLB,
+	"SHLL",		LTYPES,	ASHLL,
+	"SHLQ",		LTYPES,	ASHLQ,
+	"SHLW",		LTYPES,	ASHLW,
+	"SHRB",		LTYPE3,	ASHRB,
+	"SHRL",		LTYPES,	ASHRL,
+	"SHRQ",		LTYPES,	ASHRQ,
+	"SHRW",		LTYPES,	ASHRW,
+	"STC",		LTYPE0,	ASTC,
+	"STD",		LTYPE0,	ASTD,
+	"STI",		LTYPE0,	ASTI,
+	"STOSB",	LTYPE0,	ASTOSB,
+	"STOSL",	LTYPE0,	ASTOSL,
+	"STOSQ",	LTYPE0,	ASTOSQ,
+	"STOSW",	LTYPE0,	ASTOSW,
+	"SUBB",		LTYPE3,	ASUBB,
+	"SUBL",		LTYPE3,	ASUBL,
+	"SUBQ",		LTYPE3,	ASUBQ,
+	"SUBW",		LTYPE3,	ASUBW,
+	"SYSCALL",	LTYPE0,	ASYSCALL,
+	"SYSRET",	LTYPE0,	ASYSRET,
+	"SWAPGS",	LTYPE0,	ASWAPGS,
+	"TESTB",	LTYPE3,	ATESTB,
+	"TESTL",	LTYPE3,	ATESTL,
+	"TESTQ",	LTYPE3,	ATESTQ,
+	"TESTW",	LTYPE3,	ATESTW,
+	"TEXT",		LTYPET,	ATEXT,
+	"VERR",		LTYPE2,	AVERR,
+	"VERW",		LTYPE2,	AVERW,
+	"QUAD",		LTYPE2,	AQUAD,
+	"WAIT",		LTYPE0,	AWAIT,
+	"WBINVD",	LTYPE0,	AWBINVD,
+	"WRMSR",	LTYPE0,	AWRMSR,
+	"WORD",		LTYPE2,	AWORD,
+	"XADDB",	LTYPE3,	AXADDB,
+	"XADDL",	LTYPE3,	AXADDL,
+	"XADDQ",	LTYPE3,	AXADDQ,
+	"XADDW",	LTYPE3,	AXADDW,
+	"XCHGB",	LTYPE3,	AXCHGB,
+	"XCHGL",	LTYPE3,	AXCHGL,
+	"XCHGQ",	LTYPE3,	AXCHGQ,
+	"XCHGW",	LTYPE3,	AXCHGW,
+	"XLAT",		LTYPE2,	AXLAT,
+	"XORB",		LTYPE3,	AXORB,
+	"XORL",		LTYPE3,	AXORL,
+	"XORQ",		LTYPE3,	AXORQ,
+	"XORW",		LTYPE3,	AXORW,
+
+	"CMOVLCC",	LTYPE3,	ACMOVLCC,
+	"CMOVLCS",	LTYPE3,	ACMOVLCS,
+	"CMOVLEQ",	LTYPE3,	ACMOVLEQ,
+	"CMOVLGE",	LTYPE3,	ACMOVLGE,
+	"CMOVLGT",	LTYPE3,	ACMOVLGT,
+	"CMOVLHI",	LTYPE3,	ACMOVLHI,
+	"CMOVLLE",	LTYPE3,	ACMOVLLE,
+	"CMOVLLS",	LTYPE3,	ACMOVLLS,
+	"CMOVLLT",	LTYPE3,	ACMOVLLT,
+	"CMOVLMI",	LTYPE3,	ACMOVLMI,
+	"CMOVLNE",	LTYPE3,	ACMOVLNE,
+	"CMOVLOC",	LTYPE3,	ACMOVLOC,
+	"CMOVLOS",	LTYPE3,	ACMOVLOS,
+	"CMOVLPC",	LTYPE3,	ACMOVLPC,
+	"CMOVLPL",	LTYPE3,	ACMOVLPL,
+	"CMOVLPS",	LTYPE3,	ACMOVLPS,
+	"CMOVQCC",	LTYPE3,	ACMOVQCC,
+	"CMOVQCS",	LTYPE3,	ACMOVQCS,
+	"CMOVQEQ",	LTYPE3,	ACMOVQEQ,
+	"CMOVQGE",	LTYPE3,	ACMOVQGE,
+	"CMOVQGT",	LTYPE3,	ACMOVQGT,
+	"CMOVQHI",	LTYPE3,	ACMOVQHI,
+	"CMOVQLE",	LTYPE3,	ACMOVQLE,
+	"CMOVQLS",	LTYPE3,	ACMOVQLS,
+	"CMOVQLT",	LTYPE3,	ACMOVQLT,
+	"CMOVQMI",	LTYPE3,	ACMOVQMI,
+	"CMOVQNE",	LTYPE3,	ACMOVQNE,
+	"CMOVQOC",	LTYPE3,	ACMOVQOC,
+	"CMOVQOS",	LTYPE3,	ACMOVQOS,
+	"CMOVQPC",	LTYPE3,	ACMOVQPC,
+	"CMOVQPL",	LTYPE3,	ACMOVQPL,
+	"CMOVQPS",	LTYPE3,	ACMOVQPS,
+	"CMOVWCC",	LTYPE3,	ACMOVWCC,
+	"CMOVWCS",	LTYPE3,	ACMOVWCS,
+	"CMOVWEQ",	LTYPE3,	ACMOVWEQ,
+	"CMOVWGE",	LTYPE3,	ACMOVWGE,
+	"CMOVWGT",	LTYPE3,	ACMOVWGT,
+	"CMOVWHI",	LTYPE3,	ACMOVWHI,
+	"CMOVWLE",	LTYPE3,	ACMOVWLE,
+	"CMOVWLS",	LTYPE3,	ACMOVWLS,
+	"CMOVWLT",	LTYPE3,	ACMOVWLT,
+	"CMOVWMI",	LTYPE3,	ACMOVWMI,
+	"CMOVWNE",	LTYPE3,	ACMOVWNE,
+	"CMOVWOC",	LTYPE3,	ACMOVWOC,
+	"CMOVWOS",	LTYPE3,	ACMOVWOS,
+	"CMOVWPC",	LTYPE3,	ACMOVWPC,
+	"CMOVWPL",	LTYPE3,	ACMOVWPL,
+	"CMOVWPS",	LTYPE3,	ACMOVWPS,
+
+	"FMOVB",	LTYPE3, AFMOVB,
+	"FMOVBP",	LTYPE3, AFMOVBP,
+	"FMOVD",	LTYPE3, AFMOVD,
+	"FMOVDP",	LTYPE3, AFMOVDP,
+	"FMOVF",	LTYPE3, AFMOVF,
+	"FMOVFP",	LTYPE3, AFMOVFP,
+	"FMOVL",	LTYPE3, AFMOVL,
+	"FMOVLP",	LTYPE3, AFMOVLP,
+	"FMOVV",	LTYPE3, AFMOVV,
+	"FMOVVP",	LTYPE3, AFMOVVP,
+	"FMOVW",	LTYPE3, AFMOVW,
+	"FMOVWP",	LTYPE3, AFMOVWP,
+	"FMOVX",	LTYPE3, AFMOVX,
+	"FMOVXP",	LTYPE3, AFMOVXP,
+	"FCOMB",	LTYPE3, AFCOMB,
+	"FCOMBP",	LTYPE3, AFCOMBP,
+	"FCOMD",	LTYPE3, AFCOMD,
+	"FCOMDP",	LTYPE3, AFCOMDP,
+	"FCOMDPP",	LTYPE3, AFCOMDPP,
+	"FCOMF",	LTYPE3, AFCOMF,
+	"FCOMFP",	LTYPE3, AFCOMFP,
+	"FCOML",	LTYPE3, AFCOML,
+	"FCOMLP",	LTYPE3, AFCOMLP,
+	"FCOMW",	LTYPE3, AFCOMW,
+	"FCOMWP",	LTYPE3, AFCOMWP,
+	"FUCOM",	LTYPE3, AFUCOM,
+	"FUCOMP",	LTYPE3, AFUCOMP,
+	"FUCOMPP",	LTYPE3, AFUCOMPP,
+	"FADDW",	LTYPE3, AFADDW,
+	"FADDL",	LTYPE3, AFADDL,
+	"FADDF",	LTYPE3, AFADDF,
+	"FADDD",	LTYPE3, AFADDD,
+	"FADDDP",	LTYPE3, AFADDDP,
+	"FSUBDP",	LTYPE3, AFSUBDP,
+	"FSUBW",	LTYPE3, AFSUBW,
+	"FSUBL",	LTYPE3, AFSUBL,
+	"FSUBF",	LTYPE3, AFSUBF,
+	"FSUBD",	LTYPE3, AFSUBD,
+	"FSUBRDP",	LTYPE3, AFSUBRDP,
+	"FSUBRW",	LTYPE3, AFSUBRW,
+	"FSUBRL",	LTYPE3, AFSUBRL,
+	"FSUBRF",	LTYPE3, AFSUBRF,
+	"FSUBRD",	LTYPE3, AFSUBRD,
+	"FMULDP",	LTYPE3, AFMULDP,
+	"FMULW",	LTYPE3, AFMULW,
+	"FMULL",	LTYPE3, AFMULL,
+	"FMULF",	LTYPE3, AFMULF,
+	"FMULD",	LTYPE3, AFMULD,
+	"FDIVDP",	LTYPE3, AFDIVDP,
+	"FDIVW",	LTYPE3, AFDIVW,
+	"FDIVL",	LTYPE3, AFDIVL,
+	"FDIVF",	LTYPE3, AFDIVF,
+	"FDIVD",	LTYPE3, AFDIVD,
+	"FDIVRDP",	LTYPE3, AFDIVRDP,
+	"FDIVRW",	LTYPE3, AFDIVRW,
+	"FDIVRL",	LTYPE3, AFDIVRL,
+	"FDIVRF",	LTYPE3, AFDIVRF,
+	"FDIVRD",	LTYPE3, AFDIVRD,
+	"FXCHD",	LTYPE3, AFXCHD,
+	"FFREE",	LTYPE1, AFFREE,
+	"FLDCW",	LTYPE2, AFLDCW,
+	"FLDENV",	LTYPE1, AFLDENV,
+	"FRSTOR",	LTYPE2, AFRSTOR,
+	"FSAVE",	LTYPE1, AFSAVE,
+	"FSTCW",	LTYPE1, AFSTCW,
+	"FSTENV",	LTYPE1, AFSTENV,
+	"FSTSW",	LTYPE1, AFSTSW,
+	"F2XM1",	LTYPE0, AF2XM1,
+	"FABS",		LTYPE0, AFABS,
+	"FCHS",		LTYPE0, AFCHS,
+	"FCLEX",	LTYPE0, AFCLEX,
+	"FCOS",		LTYPE0, AFCOS,
+	"FDECSTP",	LTYPE0, AFDECSTP,
+	"FINCSTP",	LTYPE0, AFINCSTP,
+	"FINIT",	LTYPE0, AFINIT,
+	"FLD1",		LTYPE0, AFLD1,
+	"FLDL2E",	LTYPE0, AFLDL2E,
+	"FLDL2T",	LTYPE0, AFLDL2T,
+	"FLDLG2",	LTYPE0, AFLDLG2,
+	"FLDLN2",	LTYPE0, AFLDLN2,
+	"FLDPI",	LTYPE0, AFLDPI,
+	"FLDZ",		LTYPE0, AFLDZ,
+	"FNOP",		LTYPE0, AFNOP,
+	"FPATAN",	LTYPE0, AFPATAN,
+	"FPREM",	LTYPE0, AFPREM,
+	"FPREM1",	LTYPE0, AFPREM1,
+	"FPTAN",	LTYPE0, AFPTAN,
+	"FRNDINT",	LTYPE0, AFRNDINT,
+	"FSCALE",	LTYPE0, AFSCALE,
+	"FSIN",		LTYPE0, AFSIN,
+	"FSINCOS",	LTYPE0, AFSINCOS,
+	"FSQRT",	LTYPE0, AFSQRT,
+	"FTST",		LTYPE0, AFTST,
+	"FXAM",		LTYPE0, AFXAM,
+	"FXTRACT",	LTYPE0, AFXTRACT,
+	"FYL2X",	LTYPE0, AFYL2X,
+	"FYL2XP1",	LTYPE0, AFYL2XP1,
+
+	"ADDPD",	LTYPE3,	AADDPD,
+	"ADDPS",	LTYPE3,	AADDPS,
+	"ADDSD",	LTYPE3,	AADDSD,
+	"ADDSS",	LTYPE3,	AADDSS,
+	"ANDNPD",	LTYPE3,	AANDNPD,
+	"ANDNPS",	LTYPE3,	AANDNPS,
+	"ANDPD",	LTYPE3,	AANDPD,
+	"ANDPS",	LTYPE3,	AANDPS,
+	"CMPPD",	LTYPEXC,ACMPPD,
+	"CMPPS",	LTYPEXC,ACMPPS,
+	"CMPSD",	LTYPEXC,ACMPSD,
+	"CMPSS",	LTYPEXC,ACMPSS,
+	"COMISD",	LTYPE3,	ACOMISD,
+	"COMISS",	LTYPE3,	ACOMISS,
+	"CVTPL2PD",	LTYPE3,	ACVTPL2PD,
+	"CVTPL2PS",	LTYPE3,	ACVTPL2PS,
+	"CVTPD2PL",	LTYPE3,	ACVTPD2PL,
+	"CVTPD2PS",	LTYPE3,	ACVTPD2PS,
+	"CVTPS2PL",	LTYPE3,	ACVTPS2PL,
+	"PF2IW",	LTYPE3,	APF2IW,
+	"PF2IL",	LTYPE3,	APF2IL,
+	"PF2ID",	LTYPE3,	APF2IL,	/* syn */
+	"PI2FL",	LTYPE3,	API2FL,
+	"PI2FD",	LTYPE3,	API2FL,	/* syn */
+	"PI2FW",	LTYPE3,	API2FW,
+	"CVTPS2PD",	LTYPE3,	ACVTPS2PD,
+	"CVTSD2SL",	LTYPE3,	ACVTSD2SL,
+	"CVTSD2SQ",	LTYPE3,	ACVTSD2SQ,
+	"CVTSD2SS",	LTYPE3,	ACVTSD2SS,
+	"CVTSL2SD",	LTYPE3,	ACVTSL2SD,
+	"CVTSQ2SD",	LTYPE3,	ACVTSQ2SD,
+	"CVTSL2SS",	LTYPE3,	ACVTSL2SS,
+	"CVTSQ2SS",	LTYPE3,	ACVTSQ2SS,
+	"CVTSS2SD",	LTYPE3,	ACVTSS2SD,
+	"CVTSS2SL",	LTYPE3,	ACVTSS2SL,
+	"CVTSS2SQ",	LTYPE3,	ACVTSS2SQ,
+	"CVTTPD2PL",	LTYPE3,	ACVTTPD2PL,
+	"CVTTPS2PL",	LTYPE3,	ACVTTPS2PL,
+	"CVTTSD2SL",	LTYPE3,	ACVTTSD2SL,
+	"CVTTSD2SQ",	LTYPE3,	ACVTTSD2SQ,
+	"CVTTSS2SL",	LTYPE3,	ACVTTSS2SL,
+	"CVTTSS2SQ",	LTYPE3,	ACVTTSS2SQ,
+	"DIVPD",	LTYPE3,	ADIVPD,
+	"DIVPS",	LTYPE3,	ADIVPS,
+	"DIVSD",	LTYPE3,	ADIVSD,
+	"DIVSS",	LTYPE3,	ADIVSS,
+	"FXRSTOR",	LTYPE2,	AFXRSTOR,
+	"FXRSTOR64",	LTYPE2,	AFXRSTOR64,
+	"FXSAVE",	LTYPE1,	AFXSAVE,
+	"FXSAVE64",	LTYPE1,	AFXSAVE64,
+	"LDMXCSR",	LTYPE2,	ALDMXCSR,
+	"MASKMOVOU",	LTYPE3,	AMASKMOVOU,
+	"MASKMOVDQU",	LTYPE3,	AMASKMOVOU,	/* syn */
+	"MASKMOVQ",	LTYPE3,	AMASKMOVQ,
+	"MAXPD",	LTYPE3,	AMAXPD,
+	"MAXPS",	LTYPE3,	AMAXPS,
+	"MAXSD",	LTYPE3,	AMAXSD,
+	"MAXSS",	LTYPE3,	AMAXSS,
+	"MINPD",	LTYPE3,	AMINPD,
+	"MINPS",	LTYPE3,	AMINPS,
+	"MINSD",	LTYPE3,	AMINSD,
+	"MINSS",	LTYPE3,	AMINSS,
+	"MOVAPD",	LTYPE3,	AMOVAPD,
+	"MOVAPS",	LTYPE3,	AMOVAPS,
+	"MOVD",		LTYPE3,	AMOVQ,	/* syn */
+	"MOVDQ2Q",	LTYPE3,	AMOVQ,	/* syn */
+	"MOVO",		LTYPE3,	AMOVO,
+	"MOVOA",	LTYPE3,	AMOVO,	/* syn */
+	"MOVOU",	LTYPE3,	AMOVOU,
+	"MOVHLPS",	LTYPE3,	AMOVHLPS,
+	"MOVHPD",	LTYPE3,	AMOVHPD,
+	"MOVHPS",	LTYPE3,	AMOVHPS,
+	"MOVLHPS",	LTYPE3,	AMOVLHPS,
+	"MOVLPD",	LTYPE3,	AMOVLPD,
+	"MOVLPS",	LTYPE3,	AMOVLPS,
+	"MOVMSKPD",	LTYPE3,	AMOVMSKPD,
+	"MOVMSKPS",	LTYPE3,	AMOVMSKPS,
+	"MOVNTO",	LTYPE3,	AMOVNTO,
+	"MOVNTDQ",	LTYPE3,	AMOVNTO,	/* syn */
+	"MOVNTPD",	LTYPE3,	AMOVNTPD,
+	"MOVNTPS",	LTYPE3,	AMOVNTPS,
+	"MOVNTQ",	LTYPE3,	AMOVNTQ,
+	"MOVQOZX",	LTYPE3,	AMOVQOZX,
+	"MOVSD",	LTYPE3,	AMOVSD,
+	"MOVSS",	LTYPE3,	AMOVSS,
+	"MOVUPD",	LTYPE3,	AMOVUPD,
+	"MOVUPS",	LTYPE3,	AMOVUPS,
+	"MULPD",	LTYPE3,	AMULPD,
+	"MULPS",	LTYPE3,	AMULPS,
+	"MULSD",	LTYPE3,	AMULSD,
+	"MULSS",	LTYPE3,	AMULSS,
+	"ORPD",		LTYPE3,	AORPD,
+	"ORPS",		LTYPE3,	AORPS,
+	"PACKSSLW",	LTYPE3,	APACKSSLW,
+	"PACKSSWB",	LTYPE3,	APACKSSWB,
+	"PACKUSWB",	LTYPE3,	APACKUSWB,
+	"PADDB",	LTYPE3,	APADDB,
+	"PADDL",	LTYPE3,	APADDL,
+	"PADDQ",	LTYPE3,	APADDQ,
+	"PADDSB",	LTYPE3,	APADDSB,
+	"PADDSW",	LTYPE3,	APADDSW,
+	"PADDUSB",	LTYPE3,	APADDUSB,
+	"PADDUSW",	LTYPE3,	APADDUSW,
+	"PADDW",	LTYPE3,	APADDW,
+	"PAND",		LTYPE3, APAND,
+	"PANDB",	LTYPE3,	APANDB,
+	"PANDL",	LTYPE3,	APANDL,
+	"PANDSB",	LTYPE3,	APANDSB,
+	"PANDSW",	LTYPE3,	APANDSW,
+	"PANDUSB",	LTYPE3,	APANDUSB,
+	"PANDUSW",	LTYPE3,	APANDUSW,
+	"PANDW",	LTYPE3,	APANDW,
+	"PANDN",	LTYPE3, APANDN,
+	"PAVGB",	LTYPE3,	APAVGB,
+	"PAVGW",	LTYPE3,	APAVGW,
+	"PCMPEQB",	LTYPE3,	APCMPEQB,
+	"PCMPEQL",	LTYPE3,	APCMPEQL,
+	"PCMPEQW",	LTYPE3,	APCMPEQW,
+	"PCMPGTB",	LTYPE3,	APCMPGTB,
+	"PCMPGTL",	LTYPE3,	APCMPGTL,	
+	"PCMPGTW",	LTYPE3,	APCMPGTW,
+	"PEXTRW",	LTYPEX,	APEXTRW,
+	"PINSRW",	LTYPEX,	APINSRW,
+	"PMADDWL",	LTYPE3,	APMADDWL,
+	"PMAXSW",	LTYPE3,	APMAXSW,
+	"PMAXUB",	LTYPE3,	APMAXUB,
+	"PMINSW",	LTYPE3,	APMINSW,
+	"PMINUB",	LTYPE3,	APMINUB,
+	"PMOVMSKB",	LTYPE3,	APMOVMSKB,
+	"PMULHRW",	LTYPE3,	APMULHRW,
+	"PMULHUW",	LTYPE3,	APMULHUW,
+	"PMULHW",	LTYPE3,	APMULHW,
+	"PMULLW",	LTYPE3,	APMULLW,
+	"PMULULQ",	LTYPE3,	APMULULQ,
+	"POR",		LTYPE3,	APOR,
+	"PSADBW",	LTYPE3,	APSADBW,
+	"PSHUFHW",	LTYPEX,	APSHUFHW,
+	"PSHUFL",	LTYPEX,	APSHUFL,
+	"PSHUFLW",	LTYPEX,	APSHUFLW,
+	"PSHUFW",	LTYPEX, APSHUFW,
+	"PSLLO",	LTYPE3,	APSLLO,
+	"PSLLDQ",	LTYPE3,	APSLLO,	/* syn */
+	"PSLLL",	LTYPE3,	APSLLL,
+	"PSLLQ",	LTYPE3,	APSLLQ,
+	"PSLLW",	LTYPE3,	APSLLW,
+	"PSRAL",	LTYPE3,	APSRAL,
+	"PSRAW",	LTYPE3,	APSRAW,
+	"PSRLO",	LTYPE3,	APSRLO,
+	"PSRLDQ",	LTYPE3,	APSRLO,	/* syn */
+	"PSRLL",	LTYPE3,	APSRLL,
+	"PSRLQ",	LTYPE3,	APSRLQ,
+	"PSRLW",	LTYPE3,	APSRLW,
+	"PSUBB",	LTYPE3,	APSUBB,
+	"PSUBL",	LTYPE3,	APSUBL,
+	"PSUBQ",	LTYPE3,	APSUBQ,
+	"PSUBSB",	LTYPE3,	APSUBSB,
+	"PSUBSW",	LTYPE3,	APSUBSW,
+	"PSUBUSB",	LTYPE3,	APSUBUSB,
+	"PSUBUSW",	LTYPE3,	APSUBUSW,
+	"PSUBW",	LTYPE3,	APSUBW,
+	"PUNPCKHBW",	LTYPE3,	APUNPCKHBW,
+	"PUNPCKHLQ",	LTYPE3,	APUNPCKHLQ,
+	"PUNPCKHQDQ",	LTYPE3,	APUNPCKHQDQ,
+	"PUNPCKHWL",	LTYPE3,	APUNPCKHWL,
+	"PUNPCKLBW",	LTYPE3,	APUNPCKLBW,
+	"PUNPCKLLQ",	LTYPE3,	APUNPCKLLQ,
+	"PUNPCKLQDQ",	LTYPE3,	APUNPCKLQDQ,
+	"PUNPCKLWL",	LTYPE3,	APUNPCKLWL,
+	"PXOR",		LTYPE3,	APXOR,
+	"RCPPS",	LTYPE3,	ARCPPS,
+	"RCPSS",	LTYPE3,	ARCPSS,
+	"RSQRTPS",	LTYPE3,	ARSQRTPS,
+	"RSQRTSS",	LTYPE3,	ARSQRTSS,
+	"SHUFPD",	LTYPEX,	ASHUFPD,
+	"SHUFPS",	LTYPEX,	ASHUFPS,
+	"SQRTPD",	LTYPE3,	ASQRTPD,
+	"SQRTPS",	LTYPE3,	ASQRTPS,
+	"SQRTSD",	LTYPE3,	ASQRTSD,
+	"SQRTSS",	LTYPE3,	ASQRTSS,
+	"STMXCSR",	LTYPE1,	ASTMXCSR,
+	"SUBPD",	LTYPE3,	ASUBPD,
+	"SUBPS",	LTYPE3,	ASUBPS,
+	"SUBSD",	LTYPE3,	ASUBSD,
+	"SUBSS",	LTYPE3,	ASUBSS,
+	"UCOMISD",	LTYPE3,	AUCOMISD,
+	"UCOMISS",	LTYPE3,	AUCOMISS,
+	"UNPCKHPD",	LTYPE3,	AUNPCKHPD,
+	"UNPCKHPS",	LTYPE3,	AUNPCKHPS,
+	"UNPCKLPD",	LTYPE3,	AUNPCKLPD,
+	"UNPCKLPS",	LTYPE3,	AUNPCKLPS,
+	"XORPD",	LTYPE3,	AXORPD,
+	"XORPS",	LTYPE3,	AXORPS,
+
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+	nullgen.type = D_NONE;
+	nullgen.index = D_NONE;
+	nullgen.scale = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		if(s->type != LNAME)
+			yyerror("double initialization %s", itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+checkscale(int scale)
+{
+
+	switch(scale) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+		return;
+	}
+	yyerror("scale must be 1248: %d", scale);
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+void
+cclean(void)
+{
+	Gen2 g2;
+
+	g2.from = nullgen;
+	g2.to = nullgen;
+	outcode(AEND, &g2);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);		/* as(2) */
+	Bputc(&obuf, ANAME>>8);
+	Bputc(&obuf, t);		/* type */
+	Bputc(&obuf, s);		/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i, t;
+	char *n;
+	Ieee e;
+
+	t = 0;
+	if(a->index != D_NONE || a->scale != 0)
+		t |= T_INDEX;
+	if(a->offset != 0) {
+		t |= T_OFFSET;
+		l = a->offset;
+		if((vlong)l != a->offset)
+			t |= T_64;
+	}
+	if(s != 0)
+		t |= T_SYM;
+
+	switch(a->type) {
+	default:
+		t |= T_TYPE;
+		break;
+	case D_FCONST:
+		t |= T_FCONST;
+		break;
+	case D_SCONST:
+		t |= T_SCONST;
+		break;
+	case D_NONE:
+		break;
+	}
+	Bputc(&obuf, t);
+
+	if(t & T_INDEX) {	/* implies index, scale */
+		Bputc(&obuf, a->index);
+		Bputc(&obuf, a->scale);
+	}
+	if(t & T_OFFSET) {	/* implies offset */
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		if(t & T_64) {
+			l = a->offset>>32;
+			Bputc(&obuf, l);
+			Bputc(&obuf, l>>8);
+			Bputc(&obuf, l>>16);
+			Bputc(&obuf, l>>24);
+		}
+	}
+	if(t & T_SYM)		/* implies sym */
+		Bputc(&obuf, s);
+	if(t & T_FCONST) {
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		l = e.h;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		return;
+	}
+	if(t & T_SCONST) {
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		return;
+	}
+	if(t & T_TYPE)
+		Bputc(&obuf, a->type);
+}
+
+void
+outcode(int a, Gen2 *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+
+jackpot:
+	sf = 0;
+	s = g2->from.sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g2->from.type;
+		if(t == D_ADDR)
+			t = g2->from.index;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->to.sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->to.type;
+		if(t == D_ADDR)
+			t = g2->to.index;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(&g2->from, sf);
+	zaddr(&g2->to, st);
+
+out:
+	if(a != AGLOBL && a != ADATA && a != AMODE)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, ANAME>>8);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, AHISTORY>>8);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/6a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=6a
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../6c/6.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/6c/6.out.h
@@ -1,0 +1,820 @@
+#define	NSYM	50
+#define	NSNAME	8
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+/*
+ *	amd64
+ */
+
+enum	as
+{
+	AXXX,
+	AAAA,
+	AAAD,
+	AAAM,
+	AAAS,
+	AADCB,
+	AADCL,
+	AADCW,
+	AADDB,
+	AADDL,
+	AADDW,
+	AADJSP,
+	AANDB,
+	AANDL,
+	AANDW,
+	AARPL,
+	ABOUNDL,
+	ABOUNDW,
+	ABSFL,
+	ABSFW,
+	ABSRL,
+	ABSRW,
+	ABTL,
+	ABTW,
+	ABTCL,
+	ABTCW,
+	ABTRL,
+	ABTRW,
+	ABTSL,
+	ABTSW,
+	ABYTE,
+	ACALL,
+	ACLC,
+	ACLD,
+	ACLI,
+	ACLTS,
+	ACMC,
+	ACMPB,
+	ACMPL,
+	ACMPW,
+	ACMPSB,
+	ACMPSL,
+	ACMPSW,
+	ADAA,
+	ADAS,
+	ADATA,
+	ADECB,
+	ADECL,
+	ADECQ,
+	ADECW,
+	ADIVB,
+	ADIVL,
+	ADIVW,
+	AENTER,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AHLT,
+	AIDIVB,
+	AIDIVL,
+	AIDIVW,
+	AIMULB,
+	AIMULL,
+	AIMULW,
+	AINB,
+	AINL,
+	AINW,
+	AINCB,
+	AINCL,
+	AINCQ,
+	AINCW,
+	AINSB,
+	AINSL,
+	AINSW,
+	AINT,
+	AINTO,
+	AIRETL,
+	AIRETW,
+	AJCC,
+	AJCS,
+	AJCXZ,
+	AJEQ,
+	AJGE,
+	AJGT,
+	AJHI,
+	AJLE,
+	AJLS,
+	AJLT,
+	AJMI,
+	AJMP,
+	AJNE,
+	AJOC,
+	AJOS,
+	AJPC,
+	AJPL,
+	AJPS,
+	ALAHF,
+	ALARL,
+	ALARW,
+	ALEAL,
+	ALEAW,
+	ALEAVEL,
+	ALEAVEW,
+	ALOCK,
+	ALODSB,
+	ALODSL,
+	ALODSW,
+	ALONG,
+	ALOOP,
+	ALOOPEQ,
+	ALOOPNE,
+	ALSLL,
+	ALSLW,
+	AMOVB,
+	AMOVL,
+	AMOVW,
+	AMOVBLSX,
+	AMOVBLZX,
+	AMOVBQSX,
+	AMOVBQZX,
+	AMOVBWSX,
+	AMOVBWZX,
+	AMOVWLSX,
+	AMOVWLZX,
+	AMOVWQSX,
+	AMOVWQZX,
+	AMOVSB,
+	AMOVSL,
+	AMOVSW,
+	AMULB,
+	AMULL,
+	AMULW,
+	ANAME,
+	ANEGB,
+	ANEGL,
+	ANEGW,
+	ANOP,
+	ANOTB,
+	ANOTL,
+	ANOTW,
+	AORB,
+	AORL,
+	AORW,
+	AOUTB,
+	AOUTL,
+	AOUTW,
+	AOUTSB,
+	AOUTSL,
+	AOUTSW,
+	APOPAL,
+	APOPAW,
+	APOPFL,
+	APOPFW,
+	APOPL,
+	APOPW,
+	APUSHAL,
+	APUSHAW,
+	APUSHFL,
+	APUSHFW,
+	APUSHL,
+	APUSHW,
+	ARCLB,
+	ARCLL,
+	ARCLW,
+	ARCRB,
+	ARCRL,
+	ARCRW,
+	AREP,
+	AREPN,
+	ARET,
+	AROLB,
+	AROLL,
+	AROLW,
+	ARORB,
+	ARORL,
+	ARORW,
+	ASAHF,
+	ASALB,
+	ASALL,
+	ASALW,
+	ASARB,
+	ASARL,
+	ASARW,
+	ASBBB,
+	ASBBL,
+	ASBBW,
+	ASCASB,
+	ASCASL,
+	ASCASW,
+	ASETCC,
+	ASETCS,
+	ASETEQ,
+	ASETGE,
+	ASETGT,
+	ASETHI,
+	ASETLE,
+	ASETLS,
+	ASETLT,
+	ASETMI,
+	ASETNE,
+	ASETOC,
+	ASETOS,
+	ASETPC,
+	ASETPL,
+	ASETPS,
+	ACDQ,
+	ACWD,
+	ASHLB,
+	ASHLL,
+	ASHLW,
+	ASHRB,
+	ASHRL,
+	ASHRW,
+	ASTC,
+	ASTD,
+	ASTI,
+	ASTOSB,
+	ASTOSL,
+	ASTOSW,
+	ASUBB,
+	ASUBL,
+	ASUBW,
+	ASYSCALL,
+	ATESTB,
+	ATESTL,
+	ATESTW,
+	ATEXT,
+	AVERR,
+	AVERW,
+	AWAIT,
+	AWORD,
+	AXCHGB,
+	AXCHGL,
+	AXCHGW,
+	AXLAT,
+	AXORB,
+	AXORL,
+	AXORW,
+
+	AFMOVB,
+	AFMOVBP,
+	AFMOVD,
+	AFMOVDP,
+	AFMOVF,
+	AFMOVFP,
+	AFMOVL,
+	AFMOVLP,
+	AFMOVV,
+	AFMOVVP,
+	AFMOVW,
+	AFMOVWP,
+	AFMOVX,
+	AFMOVXP,
+
+	AFCOMB,
+	AFCOMBP,
+	AFCOMD,
+	AFCOMDP,
+	AFCOMDPP,
+	AFCOMF,
+	AFCOMFP,
+	AFCOML,
+	AFCOMLP,
+	AFCOMW,
+	AFCOMWP,
+	AFUCOM,
+	AFUCOMP,
+	AFUCOMPP,
+
+	AFADDDP,
+	AFADDW,
+	AFADDL,
+	AFADDF,
+	AFADDD,
+
+	AFMULDP,
+	AFMULW,
+	AFMULL,
+	AFMULF,
+	AFMULD,
+
+	AFSUBDP,
+	AFSUBW,
+	AFSUBL,
+	AFSUBF,
+	AFSUBD,
+
+	AFSUBRDP,
+	AFSUBRW,
+	AFSUBRL,
+	AFSUBRF,
+	AFSUBRD,
+
+	AFDIVDP,
+	AFDIVW,
+	AFDIVL,
+	AFDIVF,
+	AFDIVD,
+
+	AFDIVRDP,
+	AFDIVRW,
+	AFDIVRL,
+	AFDIVRF,
+	AFDIVRD,
+
+	AFXCHD,
+	AFFREE,
+
+	AFLDCW,
+	AFLDENV,
+	AFRSTOR,
+	AFSAVE,
+	AFSTCW,
+	AFSTENV,
+	AFSTSW,
+
+	AF2XM1,
+	AFABS,
+	AFCHS,
+	AFCLEX,
+	AFCOS,
+	AFDECSTP,
+	AFINCSTP,
+	AFINIT,
+	AFLD1,
+	AFLDL2E,
+	AFLDL2T,
+	AFLDLG2,
+	AFLDLN2,
+	AFLDPI,
+	AFLDZ,
+	AFNOP,
+	AFPATAN,
+	AFPREM,
+	AFPREM1,
+	AFPTAN,
+	AFRNDINT,
+	AFSCALE,
+	AFSIN,
+	AFSINCOS,
+	AFSQRT,
+	AFTST,
+	AFXAM,
+	AFXTRACT,
+	AFYL2X,
+	AFYL2XP1,
+
+	AEND,
+
+	ADYNT,
+	AINIT,
+
+	ASIGNAME,
+
+	/* extra 32-bit operations */
+	ACMPXCHGB,
+	ACMPXCHGL,
+	ACMPXCHGW,
+	ACMPXCHG8B,
+	ACPUID,
+	AINVD,
+	AINVLPG,
+	ALFENCE,
+	AMFENCE,
+	AMOVNTIL,
+	ARDMSR,
+	ARDPMC,
+	ARDTSC,
+	ARSM,
+	ASFENCE,
+	ASYSRET,
+	AWBINVD,
+	AWRMSR,
+	AXADDB,
+	AXADDL,
+	AXADDW,
+
+	/* conditional move */
+	ACMOVLCC,
+	ACMOVLCS,
+	ACMOVLEQ,
+	ACMOVLGE,
+	ACMOVLGT,
+	ACMOVLHI,
+	ACMOVLLE,
+	ACMOVLLS,
+	ACMOVLLT,
+	ACMOVLMI,
+	ACMOVLNE,
+	ACMOVLOC,
+	ACMOVLOS,
+	ACMOVLPC,
+	ACMOVLPL,
+	ACMOVLPS,
+	ACMOVQCC,
+	ACMOVQCS,
+	ACMOVQEQ,
+	ACMOVQGE,
+	ACMOVQGT,
+	ACMOVQHI,
+	ACMOVQLE,
+	ACMOVQLS,
+	ACMOVQLT,
+	ACMOVQMI,
+	ACMOVQNE,
+	ACMOVQOC,
+	ACMOVQOS,
+	ACMOVQPC,
+	ACMOVQPL,
+	ACMOVQPS,
+	ACMOVWCC,
+	ACMOVWCS,
+	ACMOVWEQ,
+	ACMOVWGE,
+	ACMOVWGT,
+	ACMOVWHI,
+	ACMOVWLE,
+	ACMOVWLS,
+	ACMOVWLT,
+	ACMOVWMI,
+	ACMOVWNE,
+	ACMOVWOC,
+	ACMOVWOS,
+	ACMOVWPC,
+	ACMOVWPL,
+	ACMOVWPS,
+
+	/* 64-bit */
+	AADCQ,
+	AADDQ,
+	AANDQ,
+	ABSFQ,
+	ABSRQ,
+	ABTCQ,
+	ABTQ,
+	ABTRQ,
+	ABTSQ,
+	ACMPQ,
+	ACMPSQ,
+	ACMPXCHGQ,
+	ACQO,
+	ADIVQ,
+	AIDIVQ,
+	AIMULQ,
+	AIRETQ,
+	ALEAQ,
+	ALEAVEQ,
+	ALODSQ,
+	AMOVQ,
+	AMOVLQSX,
+	AMOVLQZX,
+	AMOVNTIQ,
+	AMOVSQ,
+	AMULQ,
+	ANEGQ,
+	ANOTQ,
+	AORQ,
+	APOPFQ,
+	APOPQ,
+	APUSHFQ,
+	APUSHQ,
+	ARCLQ,
+	ARCRQ,
+	AROLQ,
+	ARORQ,
+	AQUAD,
+	ASALQ,
+	ASARQ,
+	ASBBQ,
+	ASCASQ,
+	ASHLQ,
+	ASHRQ,
+	ASTOSQ,
+	ASUBQ,
+	ATESTQ,
+	AXADDQ,
+	AXCHGQ,
+	AXORQ,
+
+	/* media */
+	AADDPD,
+	AADDPS,
+	AADDSD,
+	AADDSS,
+	AANDNPD,
+	AANDNPS,
+	AANDPD,
+	AANDPS,
+	ACMPPD,
+	ACMPPS,
+	ACMPSD,
+	ACMPSS,
+	ACOMISD,
+	ACOMISS,
+	ACVTPD2PL,
+	ACVTPD2PS,
+	ACVTPL2PD,
+	ACVTPL2PS,
+	ACVTPS2PD,
+	ACVTPS2PL,
+	ACVTSD2SL,
+	ACVTSD2SQ,
+	ACVTSD2SS,
+	ACVTSL2SD,
+	ACVTSL2SS,
+	ACVTSQ2SD,
+	ACVTSQ2SS,
+	ACVTSS2SD,
+	ACVTSS2SL,
+	ACVTSS2SQ,
+	ACVTTPD2PL,
+	ACVTTPS2PL,
+	ACVTTSD2SL,
+	ACVTTSD2SQ,
+	ACVTTSS2SL,
+	ACVTTSS2SQ,
+	ADIVPD,
+	ADIVPS,
+	ADIVSD,
+	ADIVSS,
+	AEMMS,
+	AFXRSTOR,
+	AFXRSTOR64,
+	AFXSAVE,
+	AFXSAVE64,
+	ALDMXCSR,
+	AMASKMOVOU,
+	AMASKMOVQ,
+	AMAXPD,
+	AMAXPS,
+	AMAXSD,
+	AMAXSS,
+	AMINPD,
+	AMINPS,
+	AMINSD,
+	AMINSS,
+	AMOVAPD,
+	AMOVAPS,
+	AMOVOU,
+	AMOVHLPS,
+	AMOVHPD,
+	AMOVHPS,
+	AMOVLHPS,
+	AMOVLPD,
+	AMOVLPS,
+	AMOVMSKPD,
+	AMOVMSKPS,
+	AMOVNTO,
+	AMOVNTPD,
+	AMOVNTPS,
+	AMOVNTQ,
+	AMOVO,
+	AMOVQOZX,
+	AMOVSD,
+	AMOVSS,
+	AMOVUPD,
+	AMOVUPS,
+	AMULPD,
+	AMULPS,
+	AMULSD,
+	AMULSS,
+	AORPD,
+	AORPS,
+	APACKSSLW,
+	APACKSSWB,
+	APACKUSWB,
+	APADDB,
+	APADDL,
+	APADDQ,
+	APADDSB,
+	APADDSW,
+	APADDUSB,
+	APADDUSW,
+	APADDW,
+	APANDB,
+	APANDL,
+	APANDSB,
+	APANDSW,
+	APANDUSB,
+	APANDUSW,
+	APANDW,
+	APAND,
+	APANDN,
+	APAVGB,
+	APAVGW,
+	APCMPEQB,
+	APCMPEQL,
+	APCMPEQW,
+	APCMPGTB,
+	APCMPGTL,
+	APCMPGTW,
+	APEXTRW,
+	APFACC,
+	APFADD,
+	APFCMPEQ,
+	APFCMPGE,
+	APFCMPGT,
+	APFMAX,
+	APFMIN,
+	APFMUL,
+	APFNACC,
+	APFPNACC,
+	APFRCP,
+	APFRCPIT1,
+	APFRCPI2T,
+	APFRSQIT1,
+	APFRSQRT,
+	APFSUB,
+	APFSUBR,
+	APINSRW,
+	APMADDWL,
+	APMAXSW,
+	APMAXUB,
+	APMINSW,
+	APMINUB,
+	APMOVMSKB,
+	APMULHRW,
+	APMULHUW,
+	APMULHW,
+	APMULLW,
+	APMULULQ,
+	APOR,
+	APSADBW,
+	APSHUFHW,
+	APSHUFL,
+	APSHUFLW,
+	APSHUFW,
+	APSLLO,
+	APSLLL,
+	APSLLQ,
+	APSLLW,
+	APSRAL,
+	APSRAW,
+	APSRLO,
+	APSRLL,
+	APSRLQ,
+	APSRLW,
+	APSUBB,
+	APSUBL,
+	APSUBQ,
+	APSUBSB,
+	APSUBSW,
+	APSUBUSB,
+	APSUBUSW,
+	APSUBW,
+	APSWAPL,
+	APUNPCKHBW,
+	APUNPCKHLQ,
+	APUNPCKHQDQ,
+	APUNPCKHWL,
+	APUNPCKLBW,
+	APUNPCKLLQ,
+	APUNPCKLQDQ,
+	APUNPCKLWL,
+	APXOR,
+	ARCPPS,
+	ARCPSS,
+	ARSQRTPS,
+	ARSQRTSS,
+	ASHUFPD,
+	ASHUFPS,
+	ASQRTPD,
+	ASQRTPS,
+	ASQRTSD,
+	ASQRTSS,
+	ASTMXCSR,
+	ASUBPD,
+	ASUBPS,
+	ASUBSD,
+	ASUBSS,
+	AUCOMISD,
+	AUCOMISS,
+	AUNPCKHPD,
+	AUNPCKHPS,
+	AUNPCKLPD,
+	AUNPCKLPS,
+	AXORPD,
+	AXORPS,
+
+	APF2IW,
+	APF2IL,
+	API2FW,
+	API2FL,
+	ARETFW,
+	ARETFL,
+	ARETFQ,
+	ASWAPGS,
+
+	AMODE,
+
+	ALAST
+};
+
+enum
+{
+
+	D_AL		= 0,
+	D_CL,
+	D_DL,
+	D_BL,
+	D_SPB,
+	D_BPB,
+	D_SIB,
+	D_DIB,
+	D_R8B,
+	D_R9B,
+	D_R10B,
+	D_R11B,
+	D_R12B,
+	D_R13B,
+	D_R14B,
+	D_R15B,
+
+	D_AX		= 16,
+	D_CX,
+	D_DX,
+	D_BX,
+	D_SP,
+	D_BP,
+	D_SI,
+	D_DI,
+	D_R8,
+	D_R9,
+	D_R10,
+	D_R11,
+	D_R12,
+	D_R13,
+	D_R14,
+	D_R15,
+
+	D_AH		= 32,
+	D_CH,
+	D_DH,
+	D_BH,
+
+	D_F0		= 36,
+
+	D_M0		= 44,
+
+	D_X0		= 52,
+
+	D_CS		= 68,
+	D_SS,
+	D_DS,
+	D_ES,
+	D_FS,
+	D_GS,
+
+	D_GDTR,		/* global descriptor table register */
+	D_IDTR,		/* interrupt descriptor table register */
+	D_LDTR,		/* local descriptor table register */
+	D_MSW,		/* machine status word */
+	D_TASK,		/* task register */
+
+	D_CR		= 79,
+	D_DR		= 95,
+	D_TR		= 103,
+
+	D_NONE		= 111,
+
+	D_BRANCH	= 112,
+	D_EXTERN	= 113,
+	D_STATIC		= 114,
+	D_AUTO		= 115,
+	D_PARAM		= 116,
+	D_CONST		= 117,
+	D_FCONST	= 118,
+	D_SCONST	= 119,
+	D_ADDR		= 120,
+
+	D_FILE,
+	D_FILE1,
+
+	D_INDIR,	/* additive */
+
+	T_TYPE		= 1<<0,
+	T_INDEX		= 1<<1,
+	T_OFFSET	= 1<<2,
+	T_FCONST	= 1<<3,
+	T_SYM		= 1<<4,
+	T_SCONST	= 1<<5,
+	T_64	= 1<<6,
+
+	REGARG		= D_BP,	/* MIGHT CHANGE */
+	REGRET		= D_AX,
+	FREGRET		= D_X0,
+	REGSP		= D_SP,
+	REGTMP		= D_DI,
+	REGEXT		= D_R15,	/* compiler allocates external registers R15 down */
+	FREGMIN		= D_X0+5,	/* first register variable */
+	FREGEXT		= D_X0+7	/* first external register */
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/6c/cgen.c
@@ -1,0 +1,1942 @@
+#include "gc.h"
+
+/* ,x/^(print|prtree)\(/i/\/\/ */
+int castup(Type*, Type*);
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r, *t;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o, hardleft;
+	long v, curs;
+	vlong c;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesu[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		if(cond(o) && typesu[l->type->etype])
+			break;
+
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gmove(&nod, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	hardleft = l->addable < INDEXED || l->complex >= FNX;
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case ONEG:
+	case OCOM:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		gopcode(o, n->type, Z, &nod);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(!hardleft) {
+			if(nn != Z || r->addable < INDEXED || hardconst(r)) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			if(l->op == OINDEX && immconst(r)) {
+				gmove(r, l);
+				break;
+			}
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED && !hardconst(r)) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gmove(&nod1, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(r->op == OCONST) {
+			if(r->vconst == 0) {
+				cgen(l, nn);
+				break;
+			}
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(o == OASHL && r->vconst == 1)
+				gopcode(OADD, n->type, &nod, &nod);
+			else
+				gopcode(o, n->type, r, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+
+		/*
+		 * get nod to be D_CX
+		 */
+		if(nodreg(&nod, nn, D_CX)) {
+			regsalloc(&nod1, n);
+			gmove(&nod, &nod1);
+			cgen(n, &nod);		/* probably a bug */
+			gmove(&nod, nn);
+			gmove(&nod1, &nod);
+			break;
+		}
+		reg[D_CX]++;
+		if(nn->op == OREGISTER && nn->reg == D_CX)
+			regalloc(&nod1, l, Z);
+		else
+			regalloc(&nod1, l, nn);
+		if(r->complex >= l->complex) {
+			cgen(r, &nod);
+			cgen(l, &nod1);
+		} else {
+			cgen(l, &nod1);
+			cgen(r, &nod);
+		}
+		gopcode(o, n->type, &nod, &nod1);
+		gmove(&nod1, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OAND:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(typefd[n->type->etype])
+			goto fop;
+		if(r->op == OCONST) {
+			if(r->vconst == 0 && o != OAND) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		if(n->op == OADD && l->op == OASHL && l->right->op == OCONST
+		&& (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) {
+			c = l->right->vconst;
+			if(c > 0 && c <= 3) {
+				if(l->left->complex >= r->complex) {
+					regalloc(&nod, l->left, nn);
+					cgen(l->left, &nod);
+					if(r->addable < INDEXED) {
+						regalloc(&nod1, r, Z);
+						cgen(r, &nod1);
+						genmuladd(&nod, &nod, 1 << c, &nod1);
+						regfree(&nod1);
+					}
+					else
+						genmuladd(&nod, &nod, 1 << c, r);
+				}
+				else {
+					regalloc(&nod, r, nn);
+					cgen(r, &nod);
+					regalloc(&nod1, l->left, Z);
+					cgen(l->left, &nod1);
+					genmuladd(&nod, &nod1, 1 << c, &nod);
+					regfree(&nod1);
+				}
+				gmove(&nod, nn);
+				regfree(&nod);
+				break;
+			}
+		}
+		if(r->addable >= INDEXED && !hardconst(r)) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, n->type, r, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, n->type, &nod1, &nod);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			regalloc(&nod, l, Z);
+			cgen(l, &nod);
+			gopcode(o, n->type, &nod1, &nod);
+		}
+		gmove(&nod, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OLMOD:
+	case OMOD:
+	case OLMUL:
+	case OLDIV:
+	case OMUL:
+	case ODIV:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(typefd[n->type->etype])
+			goto fop;
+		if(r->op == OCONST && typechl[n->type->etype]) {	/* TO DO */
+			SET(v);
+			switch(o) {
+			case ODIV:
+			case OMOD:
+				c = r->vconst;
+				if(c < 0)
+					c = -c;
+				v = log2(c);
+				if(v < 0)
+					break;
+				/* fall thru */
+			case OMUL:
+			case OLMUL:
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				switch(o) {
+				case OMUL:
+				case OLMUL:
+					mulgen(n->type, r, &nod);
+					break;
+				case ODIV:
+					sdiv2(r->vconst, v, l, &nod);
+					break;
+				case OMOD:
+					smod2(r->vconst, v, l, &nod);
+					break;
+				}
+				gmove(&nod, nn);
+				regfree(&nod);
+				goto done;
+			case OLDIV:
+				c = r->vconst;
+				if((c & 0x80000000) == 0)
+					break;
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+				regalloc(&nod, l, nn);
+				zeroregm(&nod);
+				gins(ACMPL, &nod1, nodconst(c));
+				gins(ASBBL, nodconst(-1), &nod);
+				regfree(&nod1);
+				gmove(&nod, nn);
+				regfree(&nod);
+				goto done;
+			}
+		}
+
+		if(o == OMUL) {
+			if(l->addable >= INDEXED) {
+				t = l;
+				l = r;
+				r = t;
+			}
+			/* should favour AX */
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(r->addable < INDEXED || hardconst(r)) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(OMUL, n->type, &nod1, &nod);
+				regfree(&nod1);
+			}else
+				gopcode(OMUL, n->type, r, &nod);	/* addressible */
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+
+		/*
+		 * get nod to be D_AX
+		 * get nod1 to be D_DX
+		 */
+		if(nodreg(&nod, nn, D_AX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod, &nod2);
+			v = reg[D_AX];
+			reg[D_AX] = 0;
+
+			if(isreg(l, D_AX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_AX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod);
+			reg[D_AX] = v;
+			break;
+		}
+		if(nodreg(&nod1, nn, D_DX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod1, &nod2);
+			v = reg[D_DX];
+			reg[D_DX] = 0;
+
+			if(isreg(l, D_DX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_DX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod1);
+			reg[D_DX] = v;
+			break;
+		}
+		reg[D_AX]++;
+
+		if(r->op == OCONST && (o == ODIV || o == OLDIV) && immconst(r) && typechl[r->type->etype]) {
+			reg[D_DX]++;
+			if(l->addable < INDEXED) {
+				regalloc(&nod2, l, Z);
+				cgen(l, &nod2);
+				l = &nod2;
+			}
+			if(o == ODIV)
+				sdivgen(l, r, &nod, &nod1);
+			else
+				udivgen(l, r, &nod, &nod1);
+			gmove(&nod1, nn);
+			if(l == &nod2)
+				regfree(l);
+			goto freeaxdx;
+		}
+
+		if(l->complex >= r->complex) {
+			cgen(l, &nod);
+			reg[D_DX]++;
+			if(o == ODIV || o == OMOD)
+				gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+			if(o == OLDIV || o == OLMOD)
+				zeroregm(&nod1);
+			if(r->addable < INDEXED || r->op == OCONST) {
+				regalloc(&nod3, r, Z);
+				cgen(r, &nod3);
+				gopcode(o, n->type, &nod3, Z);
+				regfree(&nod3);
+			} else
+				gopcode(o, n->type, r, Z);
+		} else {
+			regsalloc(&nod3, r);
+			cgen(r, &nod3);
+			cgen(l, &nod);
+			reg[D_DX]++;
+			if(o == ODIV || o == OMOD)
+				gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+			if(o == OLDIV || o == OLMOD)
+				zeroregm(&nod1);
+			gopcode(o, n->type, &nod3, Z);
+		}
+		if(o == OMOD || o == OLMOD)
+			gmove(&nod1, nn);
+		else
+			gmove(&nod, nn);
+	freeaxdx:
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+		if(r->op == OCONST)
+			goto asand;
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[n->type->etype])
+			goto asand;	/* can this happen? */
+
+		/*
+		 * get nod to be D_CX
+		 */
+		if(nodreg(&nod, nn, D_CX)) {
+			regsalloc(&nod1, n);
+			gmove(&nod, &nod1);
+			cgen(n, &nod);
+			if(nn != Z)
+				gmove(&nod, nn);
+			gmove(&nod1, &nod);
+			break;
+		}
+		reg[D_CX]++;
+
+		if(r->complex >= l->complex) {
+			cgen(r, &nod);
+			if(hardleft)
+				reglcgen(&nod1, l, Z);
+			else
+				nod1 = *l;
+		} else {
+			if(hardleft)
+				reglcgen(&nod1, l, Z);
+			else
+				nod1 = *l;
+			cgen(r, &nod);
+		}
+
+		gopcode(o, l->type, &nod, &nod1);
+		regfree(&nod);
+		if(nn != Z)
+			gmove(&nod1, nn);
+		if(hardleft)
+			regfree(&nod1);
+		break;
+
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+	asand:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[l->type->etype] || typefd[r->type->etype])
+			goto asfop;
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			if(!immconst(r)) {
+				regalloc(&nod1, r, nn);
+				cgen(r, &nod1);
+				gopcode(o, l->type, &nod1, &nod);
+				regfree(&nod1);
+			} else
+				gopcode(o, l->type, r, &nod);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			gopcode(o, l->type, &nod1, &nod);
+			regfree(&nod1);
+		}
+		if(nn != Z)
+			gmove(&nod, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	asfop:
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			if(r->addable < INDEXED){
+				regalloc(&nod1, r, nn);
+				cgen(r, &nod1);
+			}else
+				nod1 = *r;
+			regalloc(&nod2, r, Z);
+			gmove(&nod, &nod2);
+			gopcode(o, r->type, &nod1, &nod2);
+			gmove(&nod2, &nod);
+			regfree(&nod2);
+			if(r->addable < INDEXED)
+				regfree(&nod1);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			if(o != OASMUL && o != OASADD || !typefd[l->type->etype]) {
+				regalloc(&nod2, r, Z);
+				gmove(&nod, &nod2);
+				gopcode(o, r->type, &nod1, &nod2);
+				regfree(&nod1);
+				gmove(&nod2, &nod);
+				regfree(&nod2);
+			} else {
+				gopcode(o, r->type, &nod, &nod1);
+				gmove(&nod1, &nod);
+				regfree(&nod1);
+			}
+		}
+		if(nn != Z)
+			gmove(&nod, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[n->type->etype] || typefd[r->type->etype])
+			goto asfop;
+		if(r->op == OCONST && typechl[n->type->etype]) {
+			SET(v);
+			switch(o) {
+			case OASDIV:
+			case OASMOD:
+				c = r->vconst;
+				if(c < 0)
+					c = -c;
+				v = log2(c);
+				if(v < 0)
+					break;
+				/* fall thru */
+			case OASMUL:
+			case OASLMUL:
+				if(hardleft)
+					reglcgen(&nod2, l, Z);
+				else
+					nod2 = *l;
+				regalloc(&nod, l, nn);
+				cgen(&nod2, &nod);
+				switch(o) {
+				case OASMUL:
+				case OASLMUL:
+					mulgen(n->type, r, &nod);
+					break;
+				case OASDIV:
+					sdiv2(r->vconst, v, l, &nod);
+					break;
+				case OASMOD:
+					smod2(r->vconst, v, l, &nod);
+					break;
+				}
+			havev:
+				gmove(&nod, &nod2);
+				if(nn != Z)
+					gmove(&nod, nn);
+				if(hardleft)
+					regfree(&nod2);
+				regfree(&nod);
+				goto done;
+			case OASLDIV:
+				c = r->vconst;
+				if((c & 0x80000000) == 0)
+					break;
+				if(hardleft)
+					reglcgen(&nod2, l, Z);
+				else
+					nod2 = *l;
+				regalloc(&nod1, l, nn);
+				cgen(&nod2, &nod1);
+				regalloc(&nod, l, nn);
+				zeroregm(&nod);
+				gins(ACMPL, &nod1, nodconst(c));
+				gins(ASBBL, nodconst(-1), &nod);
+				regfree(&nod1);
+				goto havev;
+			}
+		}
+
+		if(o == OASMUL) {
+			/* should favour AX */
+			regalloc(&nod, l, nn);
+			if(r->complex >= FNX) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				r = &nod1;
+			}
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(r->addable < INDEXED || hardconst(r)) {
+				if(r->complex < FNX) {
+					regalloc(&nod1, r, Z);
+					cgen(r, &nod1);
+				}
+				gopcode(OASMUL, n->type, &nod1, &nod);
+				regfree(&nod1);
+			}
+			else
+				gopcode(OASMUL, n->type, r, &nod);
+			if(r == &nod1)
+				regfree(r);
+			gmove(&nod, &nod2);
+			if(nn != Z)
+				gmove(&nod, nn);
+			regfree(&nod);
+			if(hardleft)
+				regfree(&nod2);
+			break;
+		}
+
+		/*
+		 * get nod to be D_AX
+		 * get nod1 to be D_DX
+		 */
+		if(nodreg(&nod, nn, D_AX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod, &nod2);
+			v = reg[D_AX];
+			reg[D_AX] = 0;
+
+			if(isreg(l, D_AX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_AX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod);
+			reg[D_AX] = v;
+			break;
+		}
+		if(nodreg(&nod1, nn, D_DX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod1, &nod2);
+			v = reg[D_DX];
+			reg[D_DX] = 0;
+
+			if(isreg(l, D_DX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_DX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod1);
+			reg[D_DX] = v;
+			break;
+		}
+		reg[D_AX]++;
+		reg[D_DX]++;
+
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(r->op == OCONST && typechl[r->type->etype]) {
+				switch(o) {
+				case OASDIV:
+					sdivgen(&nod2, r, &nod, &nod1);
+					goto divdone;
+				case OASLDIV:
+					udivgen(&nod2, r, &nod, &nod1);
+				divdone:
+					gmove(&nod1, &nod2);
+					if(nn != Z)
+						gmove(&nod1, nn);
+					goto freelxaxdx;
+				}
+			}
+			if(o == OASDIV || o == OASMOD)
+				gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+			if(o == OASLDIV || o == OASLMOD)
+				zeroregm(&nod1);
+			if(r->addable < INDEXED || r->op == OCONST ||
+			   !typeil[r->type->etype]) {
+				regalloc(&nod3, r, Z);
+				cgen(r, &nod3);
+				gopcode(o, l->type, &nod3, Z);
+				regfree(&nod3);
+			} else
+				gopcode(o, n->type, r, Z);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(o == OASDIV || o == OASMOD)
+				gins(typechl[l->type->etype]? ACDQ: ACQO, Z, Z);
+			if(o == OASLDIV || o == OASLMOD)
+				zeroregm(&nod1);
+			gopcode(o, l->type, &nod3, Z);
+			regfree(&nod3);
+		}
+		if(o == OASMOD || o == OASLMOD) {
+			gmove(&nod1, &nod2);
+			if(nn != Z)
+				gmove(&nod1, nn);
+		} else {
+			gmove(&nod, &nod2);
+			if(nn != Z)
+				gmove(&nod, nn);
+		}
+	freelxaxdx:
+		if(hardleft)
+			regfree(&nod2);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	fop:
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(r->addable < INDEXED) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(o, n->type, &nod1, &nod);
+				regfree(&nod1);
+			} else
+				gopcode(o, n->type, r, &nod);
+		} else {
+			/* TO DO: could do better with r->addable >= INDEXED */
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, n->type, &nod1, &nod);
+			regfree(&nod1);
+		}
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+
+		{	/* TO DO: check floating point source */
+			Node onod;
+
+			/* incredible grot ... */
+			onod = nod3;
+			onod.op = o;
+			onod.complex = 2;
+			onod.addable = 0;
+			onod.type = tfield;
+			onod.left = &nod4;
+			onod.right = &nod3;
+			cgen(&onod, Z);
+		}
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gmove(&nod, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, nn);
+			nod.op = OREGISTER;
+			gopcode(OFUNC, n->type, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, n->type, Z, l);
+		if(REGARG)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gmove(&nod, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+			/* both null, gen l->nn */
+			cgen(l, nn);
+			break;
+		}
+		if(ewidth[n->type->etype] < ewidth[l->type->etype]){
+			if(l->type->etype == TIND && typechlp[n->type->etype])
+				warn(n, "conversion of pointer to shorter integer");
+		}else if(0){
+			if(nocast(n->type, nn->type) || castup(n->type, nn->type)){
+				if(typefd[l->type->etype] != typefd[nn->type->etype])
+					regalloc(&nod, l, nn);
+				else
+					regalloc(&nod, nn, nn);
+				cgen(l, &nod);
+				gmove(&nod, nn);
+				regfree(&nod);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gmove(&nod, &nod1);
+		gmove(&nod1, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn == Z)
+			break;
+		warn(n, "non-interruptable temporary");
+		nod = *nodrat;
+		if(!r || r->op != OCONST) {
+			diag(n, "DOT and no offset");
+			break;
+		}
+		nod.xoffset += (long)r->vconst;
+		nod.type = n->type;
+		cgen(&nod, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(hardleft)
+			reglcgen(&nod, l, Z);
+		else
+			nod = *l;
+
+		gmove(&nod, nn);
+		if(typefd[n->type->etype]) {
+			regalloc(&nod1, l, Z);
+			gmove(&nod, &nod1);
+			if(v < 0)
+				gopcode(OSUB, n->type, nodfconst(-v), &nod1);
+			else
+				gopcode(OADD, n->type, nodfconst(v), &nod1);
+			gmove(&nod1, &nod);
+			regfree(&nod1);
+		} else
+			gopcode(OADD, n->type, nodconst(v), &nod);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(hardleft)
+			reglcgen(&nod, l, Z);
+		else
+			nod = *l;
+		if(typefd[n->type->etype]) {
+			regalloc(&nod1, l, Z);
+			gmove(&nod, &nod1);
+			if(v < 0)
+				gopcode(OSUB, n->type, nodfconst(-v), &nod1);
+			else
+				gopcode(OADD, n->type, nodfconst(v), &nod1);
+			gmove(&nod1, &nod);
+			regfree(&nod1);
+		} else
+			gopcode(OADD, n->type, nodconst(v), &nod);
+		if(nn != Z)
+			gmove(&nod, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gmove(&nod, nn);
+			gopcode(OADD, tfield, nodconst(v), &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, tfield, nodconst(v), &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+done:
+	cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		gopcode(OADDR, n->type, n, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		o = ONE;
+		if(true)
+			o = OEQ;
+		/* bad, 13 is address of external that becomes constant */
+		if(n->addable >= INDEXED && n->addable != 13) {
+			if(typefd[n->type->etype]) {
+				regalloc(&nod1, n, Z);
+				gmove(nodfconst(0.0), &nod1);	/* TO DO: FREGZERO */
+				gopcode(o, n->type, n, &nod1);
+				regfree(&nod1);
+			} else
+				gopcode(o, n->type, n, nodconst(0));
+			goto com;
+		}
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		if(typefd[n->type->etype]) {
+			regalloc(&nod1, n, Z);
+			gmove(nodfconst(0.0), &nod1);	/* TO DO: FREGZERO */
+			gopcode(o, n->type, &nod, &nod1);
+			regfree(&nod1);
+		} else
+			gopcode(o, n->type, &nod, nodconst(0));
+		regfree(&nod);
+		goto com;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gmove(&nod, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(immconst(l)) {
+			o = invrel[relindex(o)];
+			/* bad, 13 is address of external that becomes constant */
+			if(r->addable < INDEXED || r->addable == 13) {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gopcode(o, l->type, &nod, l);
+				regfree(&nod);
+			} else
+				gopcode(o, l->type, r, l);
+			goto com;
+		}
+		if(typefd[l->type->etype])
+			o = invrel[relindex(logrel[relindex(o)])];
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(r->addable < INDEXED || hardconst(r) || typefd[l->type->etype]) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(o, l->type, &nod, &nod1);
+				regfree(&nod1);
+			} else
+				gopcode(o, l->type, &nod, r);
+			regfree(&nod);
+			goto com;
+		}
+		regalloc(&nod, r, nn);
+		cgen(r, &nod);
+		if(l->addable < INDEXED || l->addable == 13 || hardconst(l)) {
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			if(typechl[l->type->etype] && ewidth[l->type->etype] <= ewidth[TINT])
+				gopcode(o, types[TINT], &nod1, &nod);
+			else
+				gopcode(o, l->type, &nod1, &nod);
+			regfree(&nod1);
+		} else
+			gopcode(o, l->type, l, &nod);
+		regfree(&nod);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gmove(nodconst(1L), nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gmove(nodconst(0L), nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	int c, mt, mo;
+	vlong o0, o1;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn == Z)
+			break;
+		warn(n, "non-interruptable temporary");
+		nod1 = *nodrat;
+		r = n->right;
+		if(!r || r->op != OCONST) {
+			diag(n, "DOT and no offset");
+			break;
+		}
+		nod1.xoffset += (long)r->vconst;
+		nod1.type = n->type;
+		sugen(&nod1, nn, w);
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && side(nn)) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			cgen(&nod2, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = nil;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+			nod0.right = l;
+
+			/* prtree(&nod0, "hand craft"); /* */
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z) {
+		switch(n->op) {
+		case OASADD:
+		case OASSUB:
+		case OASAND:
+		case OASOR:
+		case OASXOR:
+
+		case OASMUL:
+		case OASLMUL:
+
+
+		case OASASHL:
+		case OASASHR:
+		case OASLSHR:
+			break;
+
+		case OPOSTINC:
+		case OPOSTDEC:
+		case OPREINC:
+		case OPREDEC:
+			break;
+
+		default:
+			return;
+		}
+	}
+
+	if(n->complex >= FNX && nn != nil && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gins(AMOVQ, &nod1, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	if(w <= 32) {
+		c = cursafe;
+		if(n->left != Z && n->left->complex >= FNX
+		&& n->right != Z && n->right->complex >= FNX) {
+			regsalloc(&nod1, n->right);
+			cgen(n->right, &nod1);
+			nod2 = *n;
+			nod2.right = &nod1;
+			cgen(&nod2, nn);
+			cursafe = c;
+			return;
+		}
+		if(w & 7) {
+			mt = TLONG;
+			mo = AMOVL;
+		} else {
+			mt = TVLONG;
+			mo = AMOVQ;
+		}
+		if(n->complex > nn->complex) {
+			t = n->type;
+			n->type = types[mt];
+			regalloc(&nod0, n, Z);
+			if(!vaddr(n, 0)) {
+				reglcgen(&nod1, n, Z);
+				n->type = t;
+				n = &nod1;
+			}
+			else
+				n->type = t;
+
+			t = nn->type;
+			nn->type = types[mt];
+			if(!vaddr(nn, 0)) {
+				reglcgen(&nod2, nn, Z);
+				nn->type = t;
+				nn = &nod2;
+			}
+			else
+				nn->type = t;
+		} else {
+			t = nn->type;
+			nn->type = types[mt];
+			regalloc(&nod0, nn, Z);
+			if(!vaddr(nn, 0)) {
+				reglcgen(&nod2, nn, Z);
+				nn->type = t;
+				nn = &nod2;
+			}
+			else
+				nn->type = t;
+
+			t = n->type;
+			n->type = types[mt];
+			if(!vaddr(n, 0)) {
+				reglcgen(&nod1, n, Z);
+				n->type = t;
+				n = &nod1;
+			}
+			else
+				n->type = t;
+		}
+		o0 = n->xoffset;
+		o1 = nn->xoffset;
+		w /= ewidth[mt];
+		while(--w >= 0) {
+			gins(mo, n, &nod0);
+			gins(mo, &nod0, nn);
+			n->xoffset += ewidth[mt];
+			nn->xoffset += ewidth[mt];
+		}
+		n->xoffset = o0;
+		nn->xoffset = o1;
+		if(nn == &nod2)
+			regfree(&nod2);
+		if(n == &nod1)
+			regfree(&nod1);
+		regfree(&nod0);
+		return;
+	}
+
+	/* botch, need to save in .safe */
+	c = 0;
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TIND];
+		nodreg(&nod1, n, D_SI);
+		if(reg[D_SI]) {
+			gins(APUSHQ, &nod1, Z);
+			c |= 1;
+			reg[D_SI]++;
+		}
+		lcgen(n, &nod1);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TIND];
+		nodreg(&nod2, nn, D_DI);
+		if(reg[D_DI]) {
+warn(Z, "DI botch");
+			gins(APUSHQ, &nod2, Z);
+			c |= 2;
+			reg[D_DI]++;
+		}
+		lcgen(nn, &nod2);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TIND];
+		nodreg(&nod2, nn, D_DI);
+		if(reg[D_DI]) {
+warn(Z, "DI botch");
+			gins(APUSHQ, &nod2, Z);
+			c |= 2;
+			reg[D_DI]++;
+		}
+		lcgen(nn, &nod2);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TIND];
+		nodreg(&nod1, n, D_SI);
+		if(reg[D_SI]) {
+			gins(APUSHQ, &nod1, Z);
+			c |= 1;
+			reg[D_SI]++;
+		}
+		lcgen(n, &nod1);
+		n->type = t;
+	}
+	nodreg(&nod3, n, D_CX);
+	if(reg[D_CX]) {
+		gins(APUSHQ, &nod3, Z);
+		c |= 4;
+		reg[D_CX]++;
+	}
+	gins(AMOVL, nodconst(w/SZ_INT), &nod3);
+	gins(ACLD, Z, Z);
+	gins(AREP, Z, Z);
+	gins(AMOVSL, Z, Z);
+	if(c & 4) {
+		gins(APOPQ, Z, &nod3);
+		reg[D_CX]--;
+	}
+	if(c & 2) {
+		gins(APOPQ, Z, &nod2);
+		reg[nod2.reg]--;
+	}
+	if(c & 1) {
+		gins(APOPQ, Z, &nod1);
+		reg[nod1.reg]--;
+	}
+}
+
+/*
+ * TO DO
+ */
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &lregnode, Z);
+	regalloc(&t2, &lregnode, Z);
+	if(c > 0) {
+		gmove(f, &t1);
+		f->xoffset += SZ_INT;
+	}
+	if(cn != Z)
+		gmove(nodconst(cv), cn);
+	if(c > 1) {
+		gmove(f, &t2);
+		f->xoffset += SZ_INT;
+	}
+	if(c > 0) {
+		gmove(&t1, t);
+		t->xoffset += SZ_INT;
+	}
+	if(c > 2) {
+		gmove(f, &t1);
+		f->xoffset += SZ_INT;
+	}
+	if(c > 1) {
+		gmove(&t2, t);
+		t->xoffset += SZ_INT;
+	}
+	if(c > 2) {
+		gmove(&t1, t);
+		t->xoffset += SZ_INT;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
+
+/*
+ * constant is not vlong or fits as 32-bit signed immediate
+ */
+int
+immconst(Node *n)
+{
+	long v;
+
+	if(n->op != OCONST || !typechlpv[n->type->etype])
+		return 0;
+	if(typechl[n->type->etype])
+		return 1;
+	v = n->vconst;
+	return n->vconst == (vlong)v;
+}
+
+/*
+ * if a constant and vlong, doesn't fit as 32-bit signed immediate
+ */
+int
+hardconst(Node *n)
+{
+	return n->op == OCONST && !immconst(n);
+}
+
+/*
+ * casting up to t2 covers an intermediate cast to t1
+ */
+int
+castup(Type *t1, Type *t2)
+{
+	int ft;
+
+	if(!nilcast(t1, t2))
+		return 0;
+	/* known to be small to large */
+	ft = t1->etype;
+	switch(t2->etype){
+	case TINT:
+	case TLONG:
+		return ft == TLONG || ft == TINT || ft == TSHORT || ft == TCHAR;
+	case TUINT:
+	case TULONG:
+		return ft == TULONG || ft == TUINT || ft == TUSHORT || ft == TUCHAR;
+	case TVLONG:
+		return ft == TLONG || ft == TINT || ft == TSHORT;
+	case TUVLONG:
+		return ft == TULONG || ft == TUINT || ft == TUSHORT;
+	}
+	return 0;
+}
+
+void
+zeroregm(Node *n)
+{
+	gins(AMOVL, nodconst(0), n);
+}
+
+/* do we need to load the address of a vlong? */
+int
+vaddr(Node *n, int a)
+{
+	switch(n->op) {
+	case ONAME:
+		if(a)
+			return 1;
+		return !(n->class == CEXTERN || n->class == CGLOBL || n->class == CSTATIC);
+
+	case OCONST:
+	case OREGISTER:
+	case OINDREG:
+		return 1;
+	}
+	return 0;
+}
+
+long
+hi64v(Node *n)
+{
+	if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+		return (long)(n->vconst) & ~0L;
+	else
+		return (long)((uvlong)n->vconst>>32) & ~0L;
+}
+
+long
+lo64v(Node *n)
+{
+	if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+		return (long)((uvlong)n->vconst>>32) & ~0L;
+	else
+		return (long)(n->vconst) & ~0L;
+}
+
+Node *
+hi64(Node *n)
+{
+	return nodconst(hi64v(n));
+}
+
+Node *
+lo64(Node *n)
+{
+	return nodconst(lo64v(n));
+}
+
+int
+cond(int op)
+{
+	switch(op) {
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/6c/div.c
@@ -1,0 +1,206 @@
+#include "gc.h"
+
+/*
+ * Based on: Granlund, T.; Montgomery, P.L.
+ * "Division by Invariant Integers using Multiplication".
+ * SIGPLAN Notices, Vol. 29, June 1994, page 61.
+ */
+
+#define	TN(n)	((uvlong)1 << (n))
+#define	T31	TN(31)
+#define	T32	TN(32)
+
+int
+multiplier(ulong d, int p, uvlong *mp)
+{
+	int l;
+	uvlong mlo, mhi, tlo, thi;
+
+	l = topbit(d - 1) + 1;
+	mlo = (((TN(l) - d) << 32) / d) + T32;
+	if(l + p == 64)
+		mhi = (((TN(l) + 1 - d) << 32) / d) + T32;
+	else
+		mhi = (TN(32 + l) + TN(32 + l - p)) / d;
+	/*assert(mlo < mhi);*/
+	while(l > 0) {
+		tlo = mlo >> 1;
+		thi = mhi >> 1;
+		if(tlo == thi)
+			break;
+		mlo = tlo;
+		mhi = thi;
+		l--;
+	}
+	*mp = mhi;
+	return l;
+}
+
+int
+sdiv(ulong d, ulong *mp, int *sp)
+{
+	int s;
+	uvlong m;
+
+	s = multiplier(d, 32 - 1, &m);
+	*mp = m;
+	*sp = s;
+	if(m >= T31)
+		return 1;
+	else
+		return 0;
+}
+
+int
+udiv(ulong d, ulong *mp, int *sp, int *pp)
+{
+	int p, s;
+	uvlong m;
+
+	s = multiplier(d, 32, &m);
+	p = 0;
+	if(m >= T32) {
+		while((d & 1) == 0) {
+			d >>= 1;
+			p++;
+		}
+		s = multiplier(d, 32 - p, &m);
+	}
+	*mp = m;
+	*pp = p;
+	if(m >= T32) {
+		/*assert(p == 0);*/
+		*sp = s - 1;
+		return 1;
+	}
+	else {
+		*sp = s;
+		return 0;
+	}
+}
+
+void
+sdivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+	int a, s;
+	ulong m;
+	vlong c;
+
+	c = r->vconst;
+	if(c < 0)
+		c = -c;
+	a = sdiv(c, &m, &s);
+//print("a=%d i=%ld s=%d m=%lux\n", a, (long)r->vconst, s, m);
+	gins(AMOVL, nodconst(m), ax);
+	gins(AIMULL, l, Z);
+	gins(AMOVL, l, ax);
+	if(a)
+		gins(AADDL, ax, dx);
+	gins(ASHRL, nodconst(31), ax);
+	gins(ASARL, nodconst(s), dx);
+	gins(AADDL, ax, dx);
+	if(r->vconst < 0)
+		gins(ANEGL, Z, dx);
+}
+
+void
+udivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+	int a, s, t;
+	ulong m;
+	Node nod;
+
+	a = udiv(r->vconst, &m, &s, &t);
+//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (long)r->vconst, t, s, m);
+	if(t != 0) {
+		gins(AMOVL, l, ax);
+		gins(ASHRL, nodconst(t), ax);
+		gins(AMOVL, nodconst(m), dx);
+		gins(AMULL, dx, Z);
+	}
+	else if(a) {
+		if(l->op != OREGISTER) {
+			regalloc(&nod, l, Z);
+			gins(AMOVL, l, &nod);
+			l = &nod;
+		}
+		gins(AMOVL, nodconst(m), ax);
+		gins(AMULL, l, Z);
+		gins(AADDL, l, dx);
+		gins(ARCRL, nodconst(1), dx);
+		if(l == &nod)
+			regfree(l);
+	}
+	else {
+		gins(AMOVL, nodconst(m), ax);
+		gins(AMULL, l, Z);
+	}
+	if(s != 0)
+		gins(ASHRL, nodconst(s), dx);
+}
+
+void
+sext(Node *d, Node *s, Node *l)
+{
+	if(s->reg == D_AX && !nodreg(d, Z, D_DX)) {
+		reg[D_DX]++;
+		gins(ACDQ, Z, Z);
+	}
+	else {
+		regalloc(d, l, Z);
+		gins(AMOVL, s, d);
+		gins(ASARL, nodconst(31), d);
+	}
+}
+
+void
+sdiv2(long c, int v, Node *l, Node *n)
+{
+	Node nod;
+
+	if(v > 0) {
+		if(v > 1) {
+			sext(&nod, n, l);
+			gins(AANDL, nodconst((1 << v) - 1), &nod);
+			gins(AADDL, &nod, n);
+			regfree(&nod);
+		}
+		else {
+			gins(ACMPL, n, nodconst(0x80000000));
+			gins(ASBBL, nodconst(-1), n);
+		}
+		gins(ASARL, nodconst(v), n);
+	}
+	if(c < 0)
+		gins(ANEGL, Z, n);
+}
+
+void
+smod2(long c, int v, Node *l, Node *n)
+{
+	Node nod;
+
+	if(c == 1) {
+		zeroregm(n);
+		return;
+	}
+
+	sext(&nod, n, l);
+	if(v == 0) {
+		zeroregm(n);
+		gins(AXORL, &nod, n);
+		gins(ASUBL, &nod, n);
+	}
+	else if(v > 1) {
+		gins(AANDL, nodconst((1 << v) - 1), &nod);
+		gins(AADDL, &nod, n);
+		gins(AANDL, nodconst((1 << v) - 1), n);
+		gins(ASUBL, &nod, n);
+	}
+	else {
+		gins(AANDL, nodconst(1), n);
+		gins(AXORL, &nod, n);
+		gins(ASUBL, &nod, n);
+	}
+	regfree(&nod);
+}
--- /dev/null
+++ b/utils/6c/enam.c
@@ -1,0 +1,669 @@
+char*	anames[] =
+{
+	"XXX",
+	"AAA",
+	"AAD",
+	"AAM",
+	"AAS",
+	"ADCB",
+	"ADCL",
+	"ADCW",
+	"ADDB",
+	"ADDL",
+	"ADDW",
+	"ADJSP",
+	"ANDB",
+	"ANDL",
+	"ANDW",
+	"ARPL",
+	"BOUNDL",
+	"BOUNDW",
+	"BSFL",
+	"BSFW",
+	"BSRL",
+	"BSRW",
+	"BTL",
+	"BTW",
+	"BTCL",
+	"BTCW",
+	"BTRL",
+	"BTRW",
+	"BTSL",
+	"BTSW",
+	"BYTE",
+	"CALL",
+	"CLC",
+	"CLD",
+	"CLI",
+	"CLTS",
+	"CMC",
+	"CMPB",
+	"CMPL",
+	"CMPW",
+	"CMPSB",
+	"CMPSL",
+	"CMPSW",
+	"DAA",
+	"DAS",
+	"DATA",
+	"DECB",
+	"DECL",
+	"DECQ",
+	"DECW",
+	"DIVB",
+	"DIVL",
+	"DIVW",
+	"ENTER",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"HLT",
+	"IDIVB",
+	"IDIVL",
+	"IDIVW",
+	"IMULB",
+	"IMULL",
+	"IMULW",
+	"INB",
+	"INL",
+	"INW",
+	"INCB",
+	"INCL",
+	"INCQ",
+	"INCW",
+	"INSB",
+	"INSL",
+	"INSW",
+	"INT",
+	"INTO",
+	"IRETL",
+	"IRETW",
+	"JCC",
+	"JCS",
+	"JCXZ",
+	"JEQ",
+	"JGE",
+	"JGT",
+	"JHI",
+	"JLE",
+	"JLS",
+	"JLT",
+	"JMI",
+	"JMP",
+	"JNE",
+	"JOC",
+	"JOS",
+	"JPC",
+	"JPL",
+	"JPS",
+	"LAHF",
+	"LARL",
+	"LARW",
+	"LEAL",
+	"LEAW",
+	"LEAVEL",
+	"LEAVEW",
+	"LOCK",
+	"LODSB",
+	"LODSL",
+	"LODSW",
+	"LONG",
+	"LOOP",
+	"LOOPEQ",
+	"LOOPNE",
+	"LSLL",
+	"LSLW",
+	"MOVB",
+	"MOVL",
+	"MOVW",
+	"MOVBLSX",
+	"MOVBLZX",
+	"MOVBQSX",
+	"MOVBQZX",
+	"MOVBWSX",
+	"MOVBWZX",
+	"MOVWLSX",
+	"MOVWLZX",
+	"MOVWQSX",
+	"MOVWQZX",
+	"MOVSB",
+	"MOVSL",
+	"MOVSW",
+	"MULB",
+	"MULL",
+	"MULW",
+	"NAME",
+	"NEGB",
+	"NEGL",
+	"NEGW",
+	"NOP",
+	"NOTB",
+	"NOTL",
+	"NOTW",
+	"ORB",
+	"ORL",
+	"ORW",
+	"OUTB",
+	"OUTL",
+	"OUTW",
+	"OUTSB",
+	"OUTSL",
+	"OUTSW",
+	"POPAL",
+	"POPAW",
+	"POPFL",
+	"POPFW",
+	"POPL",
+	"POPW",
+	"PUSHAL",
+	"PUSHAW",
+	"PUSHFL",
+	"PUSHFW",
+	"PUSHL",
+	"PUSHW",
+	"RCLB",
+	"RCLL",
+	"RCLW",
+	"RCRB",
+	"RCRL",
+	"RCRW",
+	"REP",
+	"REPN",
+	"RET",
+	"ROLB",
+	"ROLL",
+	"ROLW",
+	"RORB",
+	"RORL",
+	"RORW",
+	"SAHF",
+	"SALB",
+	"SALL",
+	"SALW",
+	"SARB",
+	"SARL",
+	"SARW",
+	"SBBB",
+	"SBBL",
+	"SBBW",
+	"SCASB",
+	"SCASL",
+	"SCASW",
+	"SETCC",
+	"SETCS",
+	"SETEQ",
+	"SETGE",
+	"SETGT",
+	"SETHI",
+	"SETLE",
+	"SETLS",
+	"SETLT",
+	"SETMI",
+	"SETNE",
+	"SETOC",
+	"SETOS",
+	"SETPC",
+	"SETPL",
+	"SETPS",
+	"CDQ",
+	"CWD",
+	"SHLB",
+	"SHLL",
+	"SHLW",
+	"SHRB",
+	"SHRL",
+	"SHRW",
+	"STC",
+	"STD",
+	"STI",
+	"STOSB",
+	"STOSL",
+	"STOSW",
+	"SUBB",
+	"SUBL",
+	"SUBW",
+	"SYSCALL",
+	"TESTB",
+	"TESTL",
+	"TESTW",
+	"TEXT",
+	"VERR",
+	"VERW",
+	"WAIT",
+	"WORD",
+	"XCHGB",
+	"XCHGL",
+	"XCHGW",
+	"XLAT",
+	"XORB",
+	"XORL",
+	"XORW",
+	"FMOVB",
+	"FMOVBP",
+	"FMOVD",
+	"FMOVDP",
+	"FMOVF",
+	"FMOVFP",
+	"FMOVL",
+	"FMOVLP",
+	"FMOVV",
+	"FMOVVP",
+	"FMOVW",
+	"FMOVWP",
+	"FMOVX",
+	"FMOVXP",
+	"FCOMB",
+	"FCOMBP",
+	"FCOMD",
+	"FCOMDP",
+	"FCOMDPP",
+	"FCOMF",
+	"FCOMFP",
+	"FCOML",
+	"FCOMLP",
+	"FCOMW",
+	"FCOMWP",
+	"FUCOM",
+	"FUCOMP",
+	"FUCOMPP",
+	"FADDDP",
+	"FADDW",
+	"FADDL",
+	"FADDF",
+	"FADDD",
+	"FMULDP",
+	"FMULW",
+	"FMULL",
+	"FMULF",
+	"FMULD",
+	"FSUBDP",
+	"FSUBW",
+	"FSUBL",
+	"FSUBF",
+	"FSUBD",
+	"FSUBRDP",
+	"FSUBRW",
+	"FSUBRL",
+	"FSUBRF",
+	"FSUBRD",
+	"FDIVDP",
+	"FDIVW",
+	"FDIVL",
+	"FDIVF",
+	"FDIVD",
+	"FDIVRDP",
+	"FDIVRW",
+	"FDIVRL",
+	"FDIVRF",
+	"FDIVRD",
+	"FXCHD",
+	"FFREE",
+	"FLDCW",
+	"FLDENV",
+	"FRSTOR",
+	"FSAVE",
+	"FSTCW",
+	"FSTENV",
+	"FSTSW",
+	"F2XM1",
+	"FABS",
+	"FCHS",
+	"FCLEX",
+	"FCOS",
+	"FDECSTP",
+	"FINCSTP",
+	"FINIT",
+	"FLD1",
+	"FLDL2E",
+	"FLDL2T",
+	"FLDLG2",
+	"FLDLN2",
+	"FLDPI",
+	"FLDZ",
+	"FNOP",
+	"FPATAN",
+	"FPREM",
+	"FPREM1",
+	"FPTAN",
+	"FRNDINT",
+	"FSCALE",
+	"FSIN",
+	"FSINCOS",
+	"FSQRT",
+	"FTST",
+	"FXAM",
+	"FXTRACT",
+	"FYL2X",
+	"FYL2XP1",
+	"END",
+	"DYNT",
+	"INIT",
+	"SIGNAME",
+	"CMPXCHGB",
+	"CMPXCHGL",
+	"CMPXCHGW",
+	"CMPXCHG8B",
+	"CPUID",
+	"INVD",
+	"INVLPG",
+	"LFENCE",
+	"MFENCE",
+	"MOVNTIL",
+	"RDMSR",
+	"RDPMC",
+	"RDTSC",
+	"RSM",
+	"SFENCE",
+	"SYSRET",
+	"WBINVD",
+	"WRMSR",
+	"XADDB",
+	"XADDL",
+	"XADDW",
+	"CMOVLCC",
+	"CMOVLCS",
+	"CMOVLEQ",
+	"CMOVLGE",
+	"CMOVLGT",
+	"CMOVLHI",
+	"CMOVLLE",
+	"CMOVLLS",
+	"CMOVLLT",
+	"CMOVLMI",
+	"CMOVLNE",
+	"CMOVLOC",
+	"CMOVLOS",
+	"CMOVLPC",
+	"CMOVLPL",
+	"CMOVLPS",
+	"CMOVQCC",
+	"CMOVQCS",
+	"CMOVQEQ",
+	"CMOVQGE",
+	"CMOVQGT",
+	"CMOVQHI",
+	"CMOVQLE",
+	"CMOVQLS",
+	"CMOVQLT",
+	"CMOVQMI",
+	"CMOVQNE",
+	"CMOVQOC",
+	"CMOVQOS",
+	"CMOVQPC",
+	"CMOVQPL",
+	"CMOVQPS",
+	"CMOVWCC",
+	"CMOVWCS",
+	"CMOVWEQ",
+	"CMOVWGE",
+	"CMOVWGT",
+	"CMOVWHI",
+	"CMOVWLE",
+	"CMOVWLS",
+	"CMOVWLT",
+	"CMOVWMI",
+	"CMOVWNE",
+	"CMOVWOC",
+	"CMOVWOS",
+	"CMOVWPC",
+	"CMOVWPL",
+	"CMOVWPS",
+	"ADCQ",
+	"ADDQ",
+	"ANDQ",
+	"BSFQ",
+	"BSRQ",
+	"BTCQ",
+	"BTQ",
+	"BTRQ",
+	"BTSQ",
+	"CMPQ",
+	"CMPSQ",
+	"CMPXCHGQ",
+	"CQO",
+	"DIVQ",
+	"IDIVQ",
+	"IMULQ",
+	"IRETQ",
+	"LEAQ",
+	"LEAVEQ",
+	"LODSQ",
+	"MOVQ",
+	"MOVLQSX",
+	"MOVLQZX",
+	"MOVNTIQ",
+	"MOVSQ",
+	"MULQ",
+	"NEGQ",
+	"NOTQ",
+	"ORQ",
+	"POPFQ",
+	"POPQ",
+	"PUSHFQ",
+	"PUSHQ",
+	"RCLQ",
+	"RCRQ",
+	"ROLQ",
+	"RORQ",
+	"QUAD",
+	"SALQ",
+	"SARQ",
+	"SBBQ",
+	"SCASQ",
+	"SHLQ",
+	"SHRQ",
+	"STOSQ",
+	"SUBQ",
+	"TESTQ",
+	"XADDQ",
+	"XCHGQ",
+	"XORQ",
+	"ADDPD",
+	"ADDPS",
+	"ADDSD",
+	"ADDSS",
+	"ANDNPD",
+	"ANDNPS",
+	"ANDPD",
+	"ANDPS",
+	"CMPPD",
+	"CMPPS",
+	"CMPSD",
+	"CMPSS",
+	"COMISD",
+	"COMISS",
+	"CVTPD2PL",
+	"CVTPD2PS",
+	"CVTPL2PD",
+	"CVTPL2PS",
+	"CVTPS2PD",
+	"CVTPS2PL",
+	"CVTSD2SL",
+	"CVTSD2SQ",
+	"CVTSD2SS",
+	"CVTSL2SD",
+	"CVTSL2SS",
+	"CVTSQ2SD",
+	"CVTSQ2SS",
+	"CVTSS2SD",
+	"CVTSS2SL",
+	"CVTSS2SQ",
+	"CVTTPD2PL",
+	"CVTTPS2PL",
+	"CVTTSD2SL",
+	"CVTTSD2SQ",
+	"CVTTSS2SL",
+	"CVTTSS2SQ",
+	"DIVPD",
+	"DIVPS",
+	"DIVSD",
+	"DIVSS",
+	"EMMS",
+	"FXRSTOR",
+	"FXRSTOR64",
+	"FXSAVE",
+	"FXSAVE64",
+	"LDMXCSR",
+	"MASKMOVOU",
+	"MASKMOVQ",
+	"MAXPD",
+	"MAXPS",
+	"MAXSD",
+	"MAXSS",
+	"MINPD",
+	"MINPS",
+	"MINSD",
+	"MINSS",
+	"MOVAPD",
+	"MOVAPS",
+	"MOVOU",
+	"MOVHLPS",
+	"MOVHPD",
+	"MOVHPS",
+	"MOVLHPS",
+	"MOVLPD",
+	"MOVLPS",
+	"MOVMSKPD",
+	"MOVMSKPS",
+	"MOVNTO",
+	"MOVNTPD",
+	"MOVNTPS",
+	"MOVNTQ",
+	"MOVO",
+	"MOVQOZX",
+	"MOVSD",
+	"MOVSS",
+	"MOVUPD",
+	"MOVUPS",
+	"MULPD",
+	"MULPS",
+	"MULSD",
+	"MULSS",
+	"ORPD",
+	"ORPS",
+	"PACKSSLW",
+	"PACKSSWB",
+	"PACKUSWB",
+	"PADDB",
+	"PADDL",
+	"PADDQ",
+	"PADDSB",
+	"PADDSW",
+	"PADDUSB",
+	"PADDUSW",
+	"PADDW",
+	"PANDB",
+	"PANDL",
+	"PANDSB",
+	"PANDSW",
+	"PANDUSB",
+	"PANDUSW",
+	"PANDW",
+	"PAND",
+	"PANDN",
+	"PAVGB",
+	"PAVGW",
+	"PCMPEQB",
+	"PCMPEQL",
+	"PCMPEQW",
+	"PCMPGTB",
+	"PCMPGTL",
+	"PCMPGTW",
+	"PEXTRW",
+	"PFACC",
+	"PFADD",
+	"PFCMPEQ",
+	"PFCMPGE",
+	"PFCMPGT",
+	"PFMAX",
+	"PFMIN",
+	"PFMUL",
+	"PFNACC",
+	"PFPNACC",
+	"PFRCP",
+	"PFRCPIT1",
+	"PFRCPI2T",
+	"PFRSQIT1",
+	"PFRSQRT",
+	"PFSUB",
+	"PFSUBR",
+	"PINSRW",
+	"PMADDWL",
+	"PMAXSW",
+	"PMAXUB",
+	"PMINSW",
+	"PMINUB",
+	"PMOVMSKB",
+	"PMULHRW",
+	"PMULHUW",
+	"PMULHW",
+	"PMULLW",
+	"PMULULQ",
+	"POR",
+	"PSADBW",
+	"PSHUFHW",
+	"PSHUFL",
+	"PSHUFLW",
+	"PSHUFW",
+	"PSLLO",
+	"PSLLL",
+	"PSLLQ",
+	"PSLLW",
+	"PSRAL",
+	"PSRAW",
+	"PSRLO",
+	"PSRLL",
+	"PSRLQ",
+	"PSRLW",
+	"PSUBB",
+	"PSUBL",
+	"PSUBQ",
+	"PSUBSB",
+	"PSUBSW",
+	"PSUBUSB",
+	"PSUBUSW",
+	"PSUBW",
+	"PSWAPL",
+	"PUNPCKHBW",
+	"PUNPCKHLQ",
+	"PUNPCKHQDQ",
+	"PUNPCKHWL",
+	"PUNPCKLBW",
+	"PUNPCKLLQ",
+	"PUNPCKLQDQ",
+	"PUNPCKLWL",
+	"PXOR",
+	"RCPPS",
+	"RCPSS",
+	"RSQRTPS",
+	"RSQRTSS",
+	"SHUFPD",
+	"SHUFPS",
+	"SQRTPD",
+	"SQRTPS",
+	"SQRTSD",
+	"SQRTSS",
+	"STMXCSR",
+	"SUBPD",
+	"SUBPS",
+	"SUBSD",
+	"SUBSS",
+	"UCOMISD",
+	"UCOMISS",
+	"UNPCKHPD",
+	"UNPCKHPS",
+	"UNPCKLPD",
+	"UNPCKLPS",
+	"XORPD",
+	"XORPS",
+	"PF2IW",
+	"PF2IL",
+	"PI2FW",
+	"PI2FL",
+	"RETFW",
+	"RETFL",
+	"RETFQ",
+	"SWAPGS",
+	"MODE",
+	"LAST",
+};
--- /dev/null
+++ b/utils/6c/gc.h
@@ -1,0 +1,380 @@
+#include	"../cc/cc.h"
+#include	"../6c/6.out.h"
+
+/*
+ * 6c/amd64
+ * Intel 386 with AMD64 extensions
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		8
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+typedef	struct	Renv	Renv;
+
+EXTERN	struct
+{
+	Node*	regtree;
+	Node*	basetree;
+	short	scale;
+	short	reg;
+	short	ptr;
+} idx;
+
+struct	Adr
+{
+	vlong	offset;
+	double	dval;
+	char	sval[NSNAME];
+
+	Sym*	sym;
+	uchar	type;
+	uchar	index;
+	uchar	etype;
+	uchar	scale;	/* doubles as width in DATA op */
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	short	as;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char	isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Var
+{
+	vlong	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+struct	Renv
+{
+	int	safe;
+	Node	base;
+	Node*	saved;
+	Node*	scope;
+};
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	Node	vconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	lregnode;
+EXTERN	Node	qregnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	int	reg[D_NONE];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+EXTERN	uchar	typechlpv[NTYPE];
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	5
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+EXTERN	int	suppress;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	usedset(Node*, int);
+void	xcom(Node*);
+void	indx(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	zeroregm(Node*);
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+int	needreg(Node*, int);
+int	hardconst(Node*);
+int	immconst(Node*);
+
+/*
+ * cgen64.c
+ */
+int	vaddr(Node*, int);
+void	loadpair(Node*, Node*);
+int	cgen64(Node*, Node*);
+void	testv(Node*, int);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nodfconst(double);
+Node*	nodgconst(vlong, Type*);
+int	nodreg(Node*, Node*, int);
+int	isreg(Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	naddr(Node*, Adr*);
+void	gcmp(int, Node*, vlong);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Type*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Rconv(Fmt*);
+int	Xconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Reg*, Adr*);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+#define	D_HI	D_NONE
+#define	D_LO	D_NONE
+
+#define	isregtype(t)	((t)>= D_AX && (t)<=D_R15)
+
+/*
+ * bound
+ */
+void	comtarg(void);
+
+/*
+ * com64
+ */
+int	cond(int);
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+long	lo64v(Node*);
+long	hi64v(Node*);
+Node*	lo64(Node*);
+Node*	hi64(Node*);
+
+/*
+ * div/mul
+ */
+void	sdivgen(Node*, Node*, Node*, Node*);
+void	udivgen(Node*, Node*, Node*, Node*);
+void	sdiv2(long, int, Node*, Node*);
+void	smod2(long, int, Node*, Node*);
+void	mulgen(Type*, Node*, Node*);
+void	genmuladd(Node*, Node*, int, Node*);
+void	shiftit(Type*, Node*, Node*);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"S"	char*
+
+#define	D_X7	(D_X0+7)
+
+void	fgopcode(int, Node*, Node*, int, int);
--- /dev/null
+++ b/utils/6c/list.c
@@ -1,0 +1,337 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%lld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+
+	p = va_arg(fp->args, Prog*);
+	if(p->as == ADATA)
+		sprint(str, "	%A	%D/%d,%D",
+			p->as, &p->from, p->from.scale, &p->to);
+	else if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D",
+			p->as, &p->from, p->from.scale, &p->to);
+	else
+		sprint(str, "	%A	%D,%D",
+			p->as, &p->from, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	int i;
+
+	i = va_arg(fp->args, int);
+	return fmtstrcpy(fp, anames[i]);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[40], s[20];
+	Adr *a;
+	int i;
+
+	a = va_arg(fp->args, Adr*);
+	i = a->type;
+	if(i >= D_INDIR) {
+		if(a->offset)
+			sprint(str, "%lld(%R)", a->offset, i-D_INDIR);
+		else
+			sprint(str, "(%R)", i-D_INDIR);
+		goto brk;
+	}
+	switch(i) {
+
+	default:
+		if(a->offset)
+			sprint(str, "$%lld,%R", a->offset, i);
+		else
+			sprint(str, "%R", i);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%lld(PC)", a->offset-pc);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%lld(SB)", a->sym->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%lld(SB)", a->sym->name,
+			a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s+%lld(SP)", a->sym->name, a->offset);
+		break;
+
+	case D_PARAM:
+		if(a->sym)
+			sprint(str, "%s+%lld(FP)", a->sym->name, a->offset);
+		else
+			sprint(str, "%lld(FP)", a->offset);
+		break;
+
+	case D_CONST:
+		sprint(str, "$%lld", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$(%.17e)", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+
+	case D_ADDR:
+		a->type = a->index;
+		a->index = D_NONE;
+		sprint(str, "$%D", a);
+		a->index = a->type;
+		a->type = D_ADDR;
+		goto conv;
+	}
+brk:
+	if(a->index != D_NONE) {
+		sprint(s, "(%R*%d)", (int)a->index, (int)a->scale);
+		strcat(str, s);
+	}
+conv:
+	return fmtstrcpy(fp, str);
+}
+
+char*	regstr[] =
+{
+	"AL",		/* [D_AL] */
+	"CL",
+	"DL",
+	"BL",
+	"SPB",
+	"BPB",
+	"SIB",
+	"DIB",
+	"R8B",
+	"R9B",
+	"R10B",
+	"R11B",
+	"R12B",
+	"R13B",
+	"R14B",
+	"R15B",
+
+	"AX",		/* [D_AX] */
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+
+	"AH",
+	"CH",
+	"DH",
+	"BH",
+
+	"F0",		/* [D_F0] */
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+
+	"M0",
+	"M1",
+	"M2",
+	"M3",
+	"M4",
+	"M5",
+	"M6",
+	"M7",
+
+	"X0",
+	"X1",
+	"X2",
+	"X3",
+	"X4",
+	"X5",
+	"X6",
+	"X7",
+	"X8",
+	"X9",
+	"X10",
+	"X11",
+	"X12",
+	"X13",
+	"X14",
+	"X15",
+
+	"CS",		/* [D_CS] */
+	"SS",
+	"DS",
+	"ES",
+	"FS",
+	"GS",
+
+	"GDTR",		/* [D_GDTR] */
+	"IDTR",		/* [D_IDTR] */
+	"LDTR",		/* [D_LDTR] */
+	"MSW",		/* [D_MSW] */
+	"TASK",		/* [D_TASK] */
+
+	"CR0",		/* [D_CR] */
+	"CR1",
+	"CR2",
+	"CR3",
+	"CR4",
+	"CR5",
+	"CR6",
+	"CR7",
+	"CR8",
+	"CR9",
+	"CR10",
+	"CR11",
+	"CR12",
+	"CR13",
+	"CR14",
+	"CR15",
+
+	"DR0",		/* [D_DR] */
+	"DR1",
+	"DR2",
+	"DR3",
+	"DR4",
+	"DR5",
+	"DR6",
+	"DR7",
+
+	"TR0",		/* [D_TR] */
+	"TR1",
+	"TR2",
+	"TR3",
+	"TR4",
+	"TR5",
+	"TR6",
+	"TR7",
+
+	"NONE",		/* [D_NONE] */
+};
+
+int
+Rconv(Fmt *fp)
+{
+	char str[20];
+	int r;
+
+	r = va_arg(fp->args, int);
+	if(r >= D_AL && r <= D_NONE)
+		sprint(str, "%s", regstr[r-D_AL]);
+	else
+		sprint(str, "gok(%d)", r);
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[30], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(double); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		default:
+			if(c < 040 || c >= 0177)
+				break;	/* not portable */
+			p[-1] = c;
+			continue;
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/6c/machcap.c
@@ -1,0 +1,78 @@
+#include "gc.h"
+
+int
+machcap(Node *n)
+{
+
+	if(n == Z)
+		return 1;	/* test */
+
+	switch(n->op) {
+	case OMUL:
+	case OLMUL:
+	case OASMUL:
+	case OASLMUL:
+		if(typechl[n->type->etype])
+			return 1;
+		if(typev[n->type->etype]) {
+				return 1;
+		}
+		break;
+
+	case OCOM:
+	case ONEG:
+	case OADD:
+	case OAND:
+	case OOR:
+	case OSUB:
+	case OXOR:
+	case OASHL:
+	case OLSHR:
+	case OASHR:
+		if(typechlv[n->left->type->etype])
+			return 1;
+		break;
+
+	case OCAST:
+		return 1;
+
+	case OCOND:
+	case OCOMMA:
+	case OLIST:
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OASADD:
+	case OASSUB:
+	case OASAND:
+	case OASOR:
+	case OASXOR:
+		return 1;
+
+	case OASASHL:
+	case OASASHR:
+	case OASLSHR:
+		return 1;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+	case OPREINC:
+	case OPREDEC:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OGT:
+	case OLT:
+	case OGE:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/6c/mkenam
@@ -1,0 +1,15 @@
+ed - ../6c/6.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/6c/mkfile
@@ -1,0 +1,42 @@
+<../../mkconfig
+
+TARG=6c
+
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+	reg.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	machcap.$O\
+	div.$O\
+	mul.$O\
+
+HFILES=\
+	gc.h\
+	6.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	6.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/6c/mul.c
@@ -1,0 +1,428 @@
+#include "gc.h"
+
+typedef struct	Malg	Malg;
+typedef struct	Mparam	Mparam;
+
+struct	Malg
+{
+	char	vals[10];
+};
+
+struct	Mparam
+{
+	ulong	value;
+	char	alg;
+	char	neg;
+	char	shift;
+	char	arg;
+	char	off;
+};
+
+static	Mparam	multab[32];
+static	int	mulptr;
+
+static	Malg	malgs[]	=
+{
+	{0, 100},
+	{-1, 1, 100},
+	{-9, -5, -3, 3, 5, 9, 100},
+	{6, 10, 12, 18, 20, 24, 36, 40, 72, 100},
+	{-8, -4, -2, 2, 4, 8, 100},
+};
+
+/*
+ * return position of lowest 1
+ */
+int
+lowbit(ulong v)
+{
+	int s, i;
+	ulong m;
+
+	s = 0;
+	m = 0xFFFFFFFFUL;
+	for(i = 16; i > 0; i >>= 1) {
+		m >>= i;
+		if((v & m) == 0) {
+			v >>= i;
+			s += i;
+		}
+	}
+	return s;
+}
+
+void
+genmuladd(Node *d, Node *s, int m, Node *a)
+{
+	Node nod;
+
+	nod.op = OINDEX;
+	nod.left = a;
+	nod.right = s;
+	nod.scale = m;
+	nod.type = types[TIND];
+	nod.xoffset = 0;
+	xcom(&nod);
+	gopcode(OADDR, d->type, &nod, d);
+}
+
+void
+mulparam(ulong m, Mparam *mp)
+{
+	int c, i, j, n, o, q, s;
+	int bc, bi, bn, bo, bq, bs, bt;
+	char *p;
+	long u;
+	ulong t;
+
+	bc = bq = 10;
+	bi = bn = bo = bs = bt = 0;
+	for(i = 0; i < nelem(malgs); i++) {
+		for(p = malgs[i].vals, j = 0; (o = p[j]) < 100; j++)
+		for(s = 0; s < 2; s++) {
+			c = 10;
+			q = 10;
+			u = m - o;
+			if(u == 0)
+				continue;
+			if(s) {
+				o = -o;
+				if(o > 0)
+					continue;
+				u = -u;
+			}
+			n = lowbit(u);
+			t = (ulong)u >> n;
+			switch(i) {
+			case 0:
+				if(t == 1) {
+					c = s + 1;
+					q = 0;
+					break;
+				}
+				switch(t) {
+				case 3:
+				case 5:
+				case 9:
+					c = s + 1;
+					if(n)
+						c++;
+					q = 0;
+					break;
+				}
+				if(s)
+					break;
+				switch(t) {
+				case 15:
+				case 25:
+				case 27:
+				case 45:
+				case 81:
+					c = 2;
+					if(n)
+						c++;
+					q = 1;
+					break;
+				}
+				break;
+			case 1:
+				if(t == 1) {
+					c = 3;
+					q = 3;
+					break;
+				}
+				switch(t) {
+				case 3:
+				case 5:
+				case 9:
+					c = 3;
+					q = 2;
+					break;
+				}
+				break;
+			case 2:
+				if(t == 1) {
+					c = 3;
+					q = 2;
+					break;
+				}
+				break;
+			case 3:
+				if(s)
+					break;
+				if(t == 1) {
+					c = 3;
+					q = 1;
+					break;
+				}
+				break;
+			case 4:
+				if(t == 1) {
+					c = 3;
+					q = 0;
+					break;
+				}
+				break;
+			}
+			if(c < bc || (c == bc && q > bq)) {
+				bc = c;
+				bi = i;
+				bn = n;
+				bo = o;
+				bq = q;
+				bs = s;
+				bt = t;
+			}
+		}
+	}
+	mp->value = m;
+	if(bc <= 3) {
+		mp->alg = bi;
+		mp->shift = bn;
+		mp->off = bo;
+		mp->neg = bs;
+		mp->arg = bt;
+	}
+	else
+		mp->alg = -1;
+}
+
+int
+m0(int a)
+{
+	switch(a) {
+	case -2:
+	case 2:
+		return 2;
+	case -3:
+	case 3:
+		return 2;
+	case -4:
+	case 4:
+		return 4;
+	case -5:
+	case 5:
+		return 4;
+	case 6:
+		return 2;
+	case -8:
+	case 8:
+		return 8;
+	case -9:
+	case 9:
+		return 8;
+	case 10:
+		return 4;
+	case 12:
+		return 2;
+	case 15:
+		return 2;
+	case 18:
+		return 8;
+	case 20:
+		return 4;
+	case 24:
+		return 2;
+	case 25:
+		return 4;
+	case 27:
+		return 2;
+	case 36:
+		return 8;
+	case 40:
+		return 4;
+	case 45:
+		return 4;
+	case 72:
+		return 8;
+	case 81:
+		return 8;
+	}
+	diag(Z, "bad m0");
+	return 0;
+}
+
+int
+m1(int a)
+{
+	switch(a) {
+	case 15:
+		return 4;
+	case 25:
+		return 4;
+	case 27:
+		return 8;
+	case 45:
+		return 8;
+	case 81:
+		return 8;
+	}
+	diag(Z, "bad m1");
+	return 0;
+}
+
+int
+m2(int a)
+{
+	switch(a) {
+	case 6:
+		return 2;
+	case 10:
+		return 2;
+	case 12:
+		return 4;
+	case 18:
+		return 2;
+	case 20:
+		return 4;
+	case 24:
+		return 8;
+	case 36:
+		return 4;
+	case 40:
+		return 8;
+	case 72:
+		return 8;
+	}
+	diag(Z, "bad m2");
+	return 0;
+}
+
+void
+shiftit(Type *t, Node *s, Node *d)
+{
+	long c;
+
+	c = (long)s->vconst & 31;
+	switch(c) {
+	case 0:
+		break;
+	case 1:
+		gopcode(OADD, t, d, d);
+		break;
+	default:
+		gopcode(OASHL, t, s, d);
+	}
+}
+
+static int
+mulgen1(ulong v, Node *n)
+{
+	int i, o;
+	Mparam *p;
+	Node nod, nods;
+
+	for(i = 0; i < nelem(multab); i++) {
+		p = &multab[i];
+		if(p->value == v)
+			goto found;
+	}
+
+	p = &multab[mulptr];
+	if(++mulptr == nelem(multab))
+		mulptr = 0;
+
+	mulparam(v, p);
+
+found:
+//	print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off);
+	if(p->alg < 0)
+		return 0;
+
+	nods = *nodconst(p->shift);
+
+	o = OADD;
+	if(p->alg > 0) {
+		regalloc(&nod, n, Z);
+		if(p->off < 0)
+			o = OSUB;
+	}
+
+	switch(p->alg) {
+	case 0:
+		switch(p->arg) {
+		case 1:
+			shiftit(n->type, &nods, n);
+			break;
+		case 15:
+		case 25:
+		case 27:
+		case 45:
+		case 81:
+			genmuladd(n, n, m1(p->arg), n);
+			/* fall thru */
+		case 3:
+		case 5:
+		case 9:
+			genmuladd(n, n, m0(p->arg), n);
+			shiftit(n->type, &nods, n);
+			break;
+		default:
+			goto bad;
+		}
+		if(p->neg == 1)
+			gins(ANEGL, Z, n);
+		break;
+	case 1:
+		switch(p->arg) {
+		case 1:
+			gmove(n, &nod);
+			shiftit(n->type, &nods, &nod);
+			break;
+		case 3:
+		case 5:
+		case 9:
+			genmuladd(&nod, n, m0(p->arg), n);
+			shiftit(n->type, &nods, &nod);
+			break;
+		default:
+			goto bad;
+		}
+		if(p->neg)
+			gopcode(o, n->type, &nod, n);
+		else {
+			gopcode(o, n->type, n, &nod);
+			gmove(&nod, n);
+		}
+		break;
+	case 2:
+		genmuladd(&nod, n, m0(p->off), n);
+		shiftit(n->type, &nods, n);
+		goto comop;
+	case 3:
+		genmuladd(&nod, n, m0(p->off), n);
+		shiftit(n->type, &nods, n);
+		genmuladd(n, &nod, m2(p->off), n);
+		break;
+	case 4:
+		genmuladd(&nod, n, m0(p->off), nodconst(0));
+		shiftit(n->type, &nods, n);
+		goto comop;
+	default:
+		diag(Z, "bad mul alg");
+		break;
+	comop:
+		if(p->neg) {
+			gopcode(o, n->type, n, &nod);
+			gmove(&nod, n);
+		}
+		else
+			gopcode(o, n->type, &nod, n);
+	}
+
+	if(p->alg > 0)
+		regfree(&nod);
+
+	return 1;
+
+bad:
+	diag(Z, "mulgen botch");
+	return 1;
+}
+
+void
+mulgen(Type *t, Node *r, Node *n)
+{
+	if(!mulgen1(r->vconst, n))
+		gopcode(OMUL, t, r, n);
+}
--- /dev/null
+++ b/utils/6c/peep.c
@@ -1,0 +1,846 @@
+#include "gc.h"
+
+static int
+needc(Prog *p)
+{
+	while(p != P) {
+		switch(p->as) {
+		case AADCL:
+		case AADCQ:
+		case ASBBL:
+		case ASBBQ:
+		case ARCRL:
+		case ARCRQ:
+			return 1;
+		case AADDL:
+		case AADDQ:
+		case ASUBL:
+		case ASUBQ:
+		case AJMP:
+		case ARET:
+		case ACALL:
+			return 0;
+		default:
+			if(p->to.type == D_BRANCH)
+				return 0;
+		}
+		p = p->link;
+	}
+	return 0;
+}
+
+static Reg*
+rnops(Reg *r)
+{
+	Prog *p;
+	Reg *r1;
+
+	if(r != R)
+	for(;;){
+		p = r->prog;
+		if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
+			break;
+		r1 = uniqs(r);
+		if(r1 == R)
+			break;
+		r = r1;
+	}
+	return r;
+}
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+
+	/*
+	 * complete R structure
+	 */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+	pc = 0;	/* speculating it won't kill */
+
+loop1:
+
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		case AMOVL:
+		case AMOVQ:
+		case AMOVSS:
+		case AMOVSD:
+			if(regtyp(&p->to))
+			if(regtyp(&p->from)) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			break;
+
+		case AMOVBLZX:
+		case AMOVWLZX:
+		case AMOVBLSX:
+		case AMOVWLSX:
+			if(regtyp(&p->to)) {
+				r1 = rnops(uniqs(r));
+				if(r1 != R) {
+					p1 = r1->prog;
+					if(p->as == p1->as && p->to.type == p1->from.type){
+						p1->as = AMOVL;
+						t++;
+					}
+				}
+			}
+			break;
+
+		case AMOVBQSX:
+		case AMOVBQZX:
+		case AMOVWQSX:
+		case AMOVWQZX:
+		case AMOVLQSX:
+		case AMOVLQZX:
+			if(regtyp(&p->to)) {
+				r1 = rnops(uniqs(r));
+				if(r1 != R) {
+					p1 = r1->prog;
+					if(p->as == p1->as && p->to.type == p1->from.type){
+						p1->as = AMOVQ;
+						t++;
+					}
+				}
+			}
+			break;
+
+		case AADDL:
+		case AADDQ:
+		case AADDW:
+			if(p->from.type != D_CONST || needc(p->link))
+				break;
+			if(p->from.offset == -1){
+				if(p->as == AADDQ)
+					p->as = ADECQ;
+				else if(p->as == AADDL)
+					p->as = ADECL;
+				else
+					p->as = ADECW;
+				p->from = zprog.from;
+			}
+			else if(p->from.offset == 1){
+				if(p->as == AADDQ)
+					p->as = AINCQ;
+				else if(p->as == AADDL)
+					p->as = AINCL;
+				else
+					p->as = AINCW;
+				p->from = zprog.from;
+			}
+			break;
+
+		case ASUBL:
+		case ASUBQ:
+		case ASUBW:
+			if(p->from.type != D_CONST || needc(p->link))
+				break;
+			if(p->from.offset == -1) {
+				if(p->as == ASUBQ)
+					p->as = AINCQ;
+				else if(p->as == ASUBL)
+					p->as = AINCL;
+				else
+					p->as = AINCW;
+				p->from = zprog.from;
+			}
+			else if(p->from.offset == 1){
+				if(p->as == ASUBQ)
+					p->as = ADECQ;
+				else if(p->as == ASUBL)
+					p->as = ADECL;
+				else
+					p->as = ADECW;
+				p->from = zprog.from;
+			}
+			break;
+		}
+	}
+	if(t)
+		goto loop1;
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+	int t;
+
+	t = a->type;
+	if(t >= D_AX && t <= D_R15)
+		return 1;
+	if(t >= D_X0 && t <= D_X0+15)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ACALL:
+			return 0;
+
+		case AIMULL:
+		case AIMULQ:
+		case AIMULW:
+			if(p->to.type != D_NONE)
+				break;
+
+		case ADIVB:
+		case ADIVL:
+		case ADIVQ:
+		case ADIVW:
+		case AIDIVB:
+		case AIDIVL:
+		case AIDIVQ:
+		case AIDIVW:
+		case AIMULB:
+		case AMULB:
+		case AMULL:
+		case AMULQ:
+		case AMULW:
+
+		case AROLB:
+		case AROLL:
+		case AROLQ:
+		case AROLW:
+		case ARORB:
+		case ARORL:
+		case ARORQ:
+		case ARORW:
+		case ASALB:
+		case ASALL:
+		case ASALQ:
+		case ASALW:
+		case ASARB:
+		case ASARL:
+		case ASARQ:
+		case ASARW:
+		case ASHLB:
+		case ASHLL:
+		case ASHLQ:
+		case ASHLW:
+		case ASHRB:
+		case ASHRL:
+		case ASHRQ:
+		case ASHRW:
+
+		case AREP:
+		case AREPN:
+
+		case ACWD:
+		case ACDQ:
+		case ACQO:
+
+		case AMOVSL:
+		case AMOVSQ:
+			return 0;
+
+		case AMOVL:
+		case AMOVQ:
+			if(p->to.type == v1->type)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->type;
+	v1->type = v2->type;
+	v2->type = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %D rar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %D set; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %D used+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %D used and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub %D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %D used+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %D set and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print("unknown op %A\n", p->as);
+		/* SBBL; ADCL; FLD1; SAHF */
+		return 2;
+
+
+	case ANEGB:
+	case ANEGW:
+	case ANEGL:
+	case ANEGQ:
+	case ANOTB:
+	case ANOTW:
+	case ANOTL:
+	case ANOTQ:
+		if(copyas(&p->to, v))
+			return 2;
+		break;
+
+	case ALEAL:	/* lhs addr, rhs store */
+	case ALEAQ:
+		if(copyas(&p->from, v))
+			return 2;
+
+
+	case ANOP:	/* rhs store */
+	case AMOVL:
+	case AMOVQ:
+	case AMOVBLSX:
+	case AMOVBLZX:
+	case AMOVBQSX:
+	case AMOVBQZX:
+	case AMOVLQSX:
+	case AMOVLQZX:
+	case AMOVWLSX:
+	case AMOVWLZX:
+	case AMOVWQSX:
+	case AMOVWQZX:
+
+	case AMOVSS:
+	case AMOVSD:
+	case ACVTSD2SL:
+	case ACVTSD2SQ:
+	case ACVTSD2SS:
+	case ACVTSL2SD:
+	case ACVTSL2SS:
+	case ACVTSQ2SD:
+	case ACVTSQ2SS:
+	case ACVTSS2SD:
+	case ACVTSS2SL:
+	case ACVTSS2SQ:
+	case ACVTTSD2SL:
+	case ACVTTSD2SQ:
+	case ACVTTSS2SL:
+	case ACVTTSS2SQ:
+		if(copyas(&p->to, v)) {
+			if(s != A)
+				return copysub(&p->from, v, s, 1);
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		goto caseread;
+
+	case AROLB:
+	case AROLL:
+	case AROLQ:
+	case AROLW:
+	case ARORB:
+	case ARORL:
+	case ARORQ:
+	case ARORW:
+	case ASALB:
+	case ASALL:
+	case ASALQ:
+	case ASALW:
+	case ASARB:
+	case ASARL:
+	case ASARQ:
+	case ASARW:
+	case ASHLB:
+	case ASHLL:
+	case ASHLQ:
+	case ASHLW:
+	case ASHRB:
+	case ASHRL:
+	case ASHRQ:
+	case ASHRW:
+		if(copyas(&p->to, v))
+			return 2;
+		if(copyas(&p->from, v))
+			if(p->from.type == D_CX)
+				return 2;
+		goto caseread;
+
+	case AADDB:	/* rhs rar */
+	case AADDL:
+	case AADDQ:
+	case AADDW:
+	case AANDB:
+	case AANDL:
+	case AANDQ:
+	case AANDW:
+	case ADECL:
+	case ADECQ:
+	case ADECW:
+	case AINCL:
+	case AINCQ:
+	case AINCW:
+	case ASUBB:
+	case ASUBL:
+	case ASUBQ:
+	case ASUBW:
+	case AORB:
+	case AORL:
+	case AORQ:
+	case AORW:
+	case AXORB:
+	case AXORL:
+	case AXORQ:
+	case AXORW:
+	case AMOVB:
+	case AMOVW:
+
+	case AADDSD:
+	case AADDSS:
+	case ACMPSD:
+	case ACMPSS:
+	case ADIVSD:
+	case ADIVSS:
+	case AMAXSD:
+	case AMAXSS:
+	case AMINSD:
+	case AMINSS:
+	case AMULSD:
+	case AMULSS:
+	case ARCPSS:
+	case ARSQRTSS:
+	case ASQRTSD:
+	case ASQRTSS:
+	case ASUBSD:
+	case ASUBSS:
+	case AXORPD:
+		if(copyas(&p->to, v))
+			return 2;
+		goto caseread;
+
+	case ACMPL:	/* read only */
+	case ACMPW:
+	case ACMPB:
+	case ACMPQ:
+
+	case ACOMISD:
+	case ACOMISS:
+	case AUCOMISD:
+	case AUCOMISS:
+	caseread:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub(&p->to, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		break;
+
+	case AJGE:	/* no reference */
+	case AJNE:
+	case AJLE:
+	case AJEQ:
+	case AJHI:
+	case AJLS:
+	case AJMI:
+	case AJPL:
+	case AJGT:
+	case AJLT:
+	case AJCC:
+	case AJCS:
+
+	case AADJSP:
+	case AWAIT:
+	case ACLD:
+		break;
+
+	case AIMULL:
+	case AIMULQ:
+	case AIMULW:
+		if(p->to.type != D_NONE) {
+			if(copyas(&p->to, v))
+				return 2;
+			goto caseread;
+		}
+
+	case ADIVB:
+	case ADIVL:
+	case ADIVQ:
+	case ADIVW:
+	case AIDIVB:
+	case AIDIVL:
+	case AIDIVQ:
+	case AIDIVW:
+	case AIMULB:
+	case AMULB:
+	case AMULL:
+	case AMULQ:
+	case AMULW:
+
+	case ACWD:
+	case ACDQ:
+	case ACQO:
+		if(v->type == D_AX || v->type == D_DX)
+			return 2;
+		goto caseread;
+
+	case AMOVSL:
+	case AMOVSQ:
+	case AREP:
+	case AREPN:
+		if(v->type == D_CX || v->type == D_DI || v->type == D_SI)
+			return 2;
+		goto caseread;
+
+	case AJMP:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == REGRET || v->type == FREGRET)
+			return 2;
+		if(s != A)
+			return 1;
+		return 3;
+
+	case ACALL:	/* funny */
+		if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
+			return 2;
+		if(REGARG && v->type == REGARG)
+			return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(REGARG && v->type == REGARG)
+			return 3;
+		return 0;
+	}
+	return 0;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+	if(a->type != v->type)
+		return 0;
+	if(regtyp(v))
+		return 1;
+	if(v->type == D_AUTO || v->type == D_PARAM)
+		if(v->offset == a->offset)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(regtyp(v)) {
+		if(a->type-D_INDIR == v->type)
+			return 1;
+		if(a->index == v->type)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+	int t;
+
+	if(copyas(a, v)) {
+		t = s->type;
+		if(t >= D_AX && t <= D_R15 || t >= D_X0 && t <= D_X0+15) {
+			if(f)
+				a->type = t;
+		}
+		return 0;
+	}
+	if(regtyp(v)) {
+		t = v->type;
+		if(a->type == t+D_INDIR) {
+			if((s->type == D_BP || s->type == D_R13) && a->index != D_NONE)
+				return 1;	/* can't use BP-base with index */
+			if(f)
+				a->type = s->type+D_INDIR;
+//			return 0;
+		}
+		if(a->index == t) {
+			if(f)
+				a->index = s->type;
+			return 0;
+		}
+		return 0;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/6c/reg.c
@@ -1,0 +1,1358 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = RtoB(D_SP) | RtoB(D_AX) | RtoB(D_X0);
+	if(REGEXT)
+		regbits |= RtoB(REGEXT) | RtoB(REGEXT-1);
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AJMP:
+		case AIRETL:
+		case AIRETQ:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		bit = mkvar(r, &p->from);
+		if(bany(&bit))
+		switch(p->as) {
+		/*
+		 * funny
+		 */
+		case ALEAL:
+		case ALEAQ:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * left side read
+		 */
+		default:
+			for(z=0; z<BITS; z++)
+				r->use1.b[z] |= bit.b[z];
+			break;
+		}
+
+		bit = mkvar(r, &p->to);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown op: %A", p->as);
+			break;
+
+		/*
+		 * right side read
+		 */
+		case ACMPB:
+		case ACMPL:
+		case ACMPQ:
+		case ACMPW:
+		case ACOMISS:
+		case ACOMISD:
+		case AUCOMISS:
+		case AUCOMISD:
+			for(z=0; z<BITS; z++)
+				r->use2.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVL:
+		case AMOVQ:
+		case AMOVB:
+		case AMOVW:
+		case AMOVBLSX:
+		case AMOVBLZX:
+		case AMOVBQSX:
+		case AMOVBQZX:
+		case AMOVLQSX:
+		case AMOVLQZX:
+		case AMOVWLSX:
+		case AMOVWLZX:
+		case AMOVWQSX:
+		case AMOVWQZX:
+
+		case AMOVSS:
+		case AMOVSD:
+		case ACVTSD2SL:
+		case ACVTSD2SQ:
+		case ACVTSD2SS:
+		case ACVTSL2SD:
+		case ACVTSL2SS:
+		case ACVTSQ2SD:
+		case ACVTSQ2SS:
+		case ACVTSS2SD:
+		case ACVTSS2SL:
+		case ACVTSS2SQ:
+		case ACVTTSD2SL:
+		case ACVTTSD2SQ:
+		case ACVTTSS2SL:
+		case ACVTTSS2SQ:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * right side read+write
+		 */
+		case AADDB:
+		case AADDL:
+		case AADDQ:
+		case AADDW:
+		case AANDB:
+		case AANDL:
+		case AANDQ:
+		case AANDW:
+		case ASUBB:
+		case ASUBL:
+		case ASUBQ:
+		case ASUBW:
+		case AORB:
+		case AORL:
+		case AORQ:
+		case AORW:
+		case AXORB:
+		case AXORL:
+		case AXORQ:
+		case AXORW:
+		case ASALB:
+		case ASALL:
+		case ASALQ:
+		case ASALW:
+		case ASARB:
+		case ASARL:
+		case ASARQ:
+		case ASARW:
+		case AROLB:
+		case AROLL:
+		case AROLQ:
+		case AROLW:
+		case ARORB:
+		case ARORL:
+		case ARORQ:
+		case ARORW:
+		case ASHLB:
+		case ASHLL:
+		case ASHLQ:
+		case ASHLW:
+		case ASHRB:
+		case ASHRL:
+		case ASHRQ:
+		case ASHRW:
+		case AIMULL:
+		case AIMULQ:
+		case AIMULW:
+		case ANEGL:
+		case ANEGQ:
+		case ANOTL:
+		case ANOTQ:
+		case AADCL:
+		case AADCQ:
+		case ASBBL:
+		case ASBBQ:
+
+		case AADDSD:
+		case AADDSS:
+		case ACMPSD:
+		case ACMPSS:
+		case ADIVSD:
+		case ADIVSS:
+		case AMAXSD:
+		case AMAXSS:
+		case AMINSD:
+		case AMINSS:
+		case AMULSD:
+		case AMULSS:
+		case ARCPSS:
+		case ARSQRTSS:
+		case ASQRTSD:
+		case ASQRTSS:
+		case ASUBSD:
+		case ASUBSS:
+		case AXORPD:
+			for(z=0; z<BITS; z++) {
+				r->set.b[z] |= bit.b[z];
+				r->use2.b[z] |= bit.b[z];
+			}
+			break;
+
+		/*
+		 * funny
+		 */
+		case ACALL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+
+		switch(p->as) {
+		case AIMULL:
+		case AIMULQ:
+		case AIMULW:
+			if(p->to.type != D_NONE)
+				break;
+
+		case AIDIVB:
+		case AIDIVL:
+		case AIDIVQ:
+		case AIDIVW:
+		case AIMULB:
+		case ADIVB:
+		case ADIVL:
+		case ADIVQ:
+		case ADIVW:
+		case AMULB:
+		case AMULL:
+		case AMULQ:
+		case AMULW:
+
+		case ACWD:
+		case ACDQ:
+		case ACQO:
+			r->regu |= RtoB(D_AX) | RtoB(D_DX);
+			break;
+
+		case AREP:
+		case AREPN:
+		case ALOOP:
+		case ALOOPEQ:
+		case ALOOPNE:
+			r->regu |= RtoB(D_CX);
+			break;
+
+		case AMOVSB:
+		case AMOVSL:
+		case AMOVSQ:
+		case AMOVSW:
+		case ACMPSB:
+		case ACMPSL:
+		case ACMPSQ:
+		case ACMPSW:
+			r->regu |= RtoB(D_SI) | RtoB(D_DI);
+			break;
+
+		case ASTOSB:
+		case ASTOSL:
+		case ASTOSQ:
+		case ASTOSW:
+		case ASCASB:
+		case ASCASL:
+		case ASCASQ:
+		case ASCASW:
+			r->regu |= RtoB(D_AX) | RtoB(D_DI);
+			break;
+
+		case AINSB:
+		case AINSL:
+		case AINSW:
+		case AOUTSB:
+		case AOUTSL:
+		case AOUTSW:
+			r->regu |= RtoB(D_DI) | RtoB(D_DX);
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+	if(debug['R'] && debug['v']) {
+		print("\nlooping structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->use1.b[z] |
+					   r->use2.b[z] |
+					   r->set.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+	if(debug['R'] && debug['v'])
+		print("\nprop structure:\n");
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		if(debug['R'] && debug['v']) {
+			print("%P\t", r->prog);
+			if(bany(&r->set))
+				print("s:%B ", r->set);
+			if(bany(&r->refahead))
+				print("ra:%B ", r->refahead);
+			if(bany(&r->calahead))
+				print("ca:%B ", r->calahead);
+			print("\n");
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L$%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			print("%L$%d %R: %B\n",
+				rgp->enter->prog->lineno,
+				rgp->cost,
+				rgp->regno,
+				bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = v->name;
+
+	p1->as = AMOVL;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVW;
+	if(v->etype == TVLONG || v->etype == TUVLONG || v->etype == TIND)
+		p1->as = AMOVQ;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVSS;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVSD;
+
+	p1->from.type = rn;
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = rn;
+		if(v->etype == TUCHAR)
+			p1->as = AMOVB;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVW;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+ulong
+doregbits(int r)
+{
+	ulong b;
+
+	b = 0;
+	if(r >= D_INDIR)
+		r -= D_INDIR;
+	if(r >= D_AX && r <= D_R15)
+		b |= RtoB(r);
+	else
+	if(r >= D_AL && r <= D_R15B)
+		b |= RtoB(r-D_AL+D_AX);
+	else
+	if(r >= D_AH && r <= D_BH)
+		b |= RtoB(r-D_AH+D_AX);
+	else
+	if(r >= D_X0 && r <= D_X0+15)
+		b |= FtoB(r);
+	return b;
+}
+
+Bits
+mkvar(Reg *r, Adr *a)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	/*
+	 * mark registers used
+	 */
+	t = a->type;
+	r->regu |= doregbits(t);
+	r->regu |= doregbits(a->index);
+
+	switch(t) {
+	default:
+		goto none;
+	case D_ADDR:
+		a->type = a->index;
+		bit = mkvar(r, a);
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+		a->type = t;
+		goto none;
+	case D_EXTERN:
+	case D_STATIC:
+	case D_PARAM:
+	case D_AUTO:
+		n = t;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		goto none;
+	if(s->name[0] == '.')
+		goto none;
+	et = a->etype;
+	o = a->offset;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->name = n;
+	v->etype = et;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !(typechlpfd[et] || typev[et]))	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ACALL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+regset(Reg *r, ulong bb)
+{
+	ulong b, set;
+	Adr v;
+	int c;
+
+	set = 0;
+	v = zprog.from;
+	while(b = bb & ~(bb-1)) {
+		v.type = b & 0xFFFF? BtoR(b): BtoF(b);
+		if(v.type == 0)
+			diag(Z, "zero v.type for %#lux", b);
+		c = copyu(r->prog, &v, A);
+		if(c == 3)
+			set |= b;
+		bb &= ~b;
+	}
+	return set;
+}
+
+ulong
+reguse(Reg *r, ulong bb)
+{
+	ulong b, set;
+	Adr v;
+	int c;
+
+	set = 0;
+	v = zprog.from;
+	while(b = bb & ~(bb-1)) {
+		v.type = b & 0xFFFF? BtoR(b): BtoF(b);
+		c = copyu(r->prog, &v, A);
+		if(c == 1 || c == 2 || c == 4)
+			set |= b;
+		bb &= ~b;
+	}
+	return set;
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg, x;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+
+	bb = vreg;
+	for(; r; r=r->s1) {
+		x = r->regu & ~bb;
+		if(x) {
+			vreg |= reguse(r, x);
+			bb |= regset(r, x);
+		}
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->offset = 0;
+	a->type = rn;
+}
+
+long
+RtoB(int r)
+{
+
+	if(r < D_AX || r > D_R15)
+		return 0;
+	return 1L << (r-D_AX);
+}
+
+int
+BtoR(long b)
+{
+
+	b &= 0xffffL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + D_AX;
+}
+
+/*
+ *	bit	reg
+ *	16	X5
+ *	17	X6
+ *	18	X7
+ */
+long
+FtoB(int f)
+{
+	if(f < FREGMIN || f > FREGEXT)
+		return 0;
+	return 1L << (f - FREGMIN + 16);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0x70000L;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 16 + FREGMIN;
+}
--- /dev/null
+++ b/utils/6c/sgen.c
@@ -1,0 +1,435 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = FREGRET;
+	}
+}
+
+/* welcome to commute */
+static void
+commute(Node *n)
+{
+	Node *l, *r;
+
+	l = n->left;
+	r = n->right;
+	if(r->complex > l->complex) {
+		n->left = r;
+		n->right = l;
+	}
+}
+
+void
+indexshift(Node *n)
+{
+	int g;
+
+	if(!typechlpv[n->type->etype])
+		return;
+	simplifyshift(n);
+	if(n->op == OASHL && n->right->op == OCONST){
+		g = vconst(n->right);
+		if(g >= 0 && g <= 3)
+			n->addable = 7;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		NAME ==> 10/11		name+value(SB/SP)
+ *		REGISTER ==> 12		register
+ *		CONST ==> 20		$value
+ *		*(20) ==> 21		value
+ *		&(10) ==> 13		$name+value(SB)
+ *		&(11) ==> 1		$name+value(SP)
+ *		(13) + (20) ==> 13	fold constants
+ *		(1) + (20) ==> 1	fold constants
+ *		*(13) ==> 10		back to name
+ *		*(1) ==> 11		back to name
+ *
+ *		(20) * (X) ==> 7	multiplier in indexing
+ *		(X,7) + (13,1) ==> 8	adder in indexing (addresses)
+ *		(8) ==> &9(OINDEX)	index, almost addressable
+ *
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int g;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->complex = 0;
+	n->addable = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		break;
+
+	case ONAME:
+		n->addable = 10;
+		if(n->class == CPARAM || n->class == CAUTO)
+			n->addable = 11;
+		break;
+
+	case OREGISTER:
+		n->addable = 12;
+		break;
+
+	case OINDREG:
+		n->addable = 12;
+		break;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 13;
+		else
+		if(l->addable == 11)
+			n->addable = 1;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(n->type->etype != TIND)
+			break;
+
+		switch(r->addable) {
+		case 20:
+			switch(l->addable) {
+			case 1:
+			case 13:
+			commadd:
+				l->type = n->type;
+				*n = *l;
+				l = new(0, Z, Z);
+				*l = *(n->left);
+				l->xoffset += r->vconst;
+				n->left = l;
+				r = n->right;
+				goto brk;
+			}
+			break;
+
+		case 1:
+		case 13:
+		case 10:
+		case 11:
+			/* l is the base, r is the index */
+			if(l->addable != 20)
+				n->addable = 8;
+			break;
+		}
+		switch(l->addable) {
+		case 20:
+			switch(r->addable) {
+			case 13:
+			case 1:
+				r = n->left;
+				l = n->right;
+				n->left = l;
+				n->right = r;
+				goto commadd;
+			}
+			break;
+
+		case 13:
+		case 1:
+		case 10:
+		case 11:
+			/* r is the base, l is the index */
+			if(r->addable != 20)
+				n->addable = 8;
+			break;
+		}
+		if(n->addable == 8 && !side(n)) {
+			indx(n);
+			l = new1(OINDEX, idx.basetree, idx.regtree);
+			l->scale = idx.scale;
+			l->addable = 9;
+			l->complex = l->right->complex;
+			l->type = l->left->type;
+			n->op = OADDR;
+			n->left = l;
+			n->right = Z;
+			n->addable = 8;
+			break;
+		}
+		break;
+
+	case OINDEX:
+		xcom(l);
+		xcom(r);
+		n->addable = 9;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->op == OADDR) {
+			l = l->left;
+			l->type = n->type;
+			*n = *l;
+			return;
+		}
+		switch(l->addable) {
+		case 20:
+			n->addable = 21;
+			break;
+		case 1:
+			n->addable = 11;
+			break;
+		case 13:
+			n->addable = 10;
+			break;
+		}
+		break;
+
+	case OASHL:
+		xcom(l);
+		xcom(r);
+		indexshift(n);
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		g = vlog(l);
+		if(g >= 0) {
+			n->left = r;
+			n->right = l;
+			l = r;
+			r = n->right;
+		}
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASHL;
+			r->vconst = g;
+			r->type = types[TINT];
+			indexshift(n);
+			break;
+		}
+		commute(n);
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASLSHR;
+			r->vconst = g;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OLSHR;
+			r->vconst = g;
+			r->type = types[TINT];
+			indexshift(n);
+			break;
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASASHL;
+			r->vconst = g;
+		}
+		break;
+
+	case OLSHR:
+	case OASHR:
+		xcom(l);
+		xcom(r);
+		indexshift(n);
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+brk:
+	if(n->addable >= 10)
+		return;
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	switch(n->op) {
+
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OCAST:
+		if(l->type->etype == TUVLONG && typefd[n->type->etype])
+			n->complex += 2;
+		break;
+
+	case OLMOD:
+	case OMOD:
+	case OLMUL:
+	case OLDIV:
+	case OMUL:
+	case ODIV:
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(r->complex >= l->complex) {
+			n->complex = l->complex + 3;
+			if(r->complex > n->complex)
+				n->complex = r->complex;
+		} else {
+			n->complex = r->complex + 3;
+			if(l->complex > n->complex)
+				n->complex = l->complex;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+		if(r->complex >= l->complex) {
+			n->complex = l->complex + 2;
+			if(r->complex > n->complex)
+				n->complex = r->complex;
+		} else {
+			n->complex = r->complex + 2;
+			if(l->complex > n->complex)
+				n->complex = l->complex;
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * compare operators, make const on left
+		 */
+		if(r->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+	}
+}
+
+void
+indx(Node *n)
+{
+	Node *l, *r;
+
+	if(debug['x'])
+		prtree(n, "indx");
+
+	l = n->left;
+	r = n->right;
+	if(l->addable == 1 || l->addable == 13 || r->complex > l->complex) {
+		n->right = l;
+		n->left = r;
+		l = r;
+		r = n->right;
+	}
+	if(l->addable != 7) {
+		idx.regtree = l;
+		idx.scale = 1;
+	} else
+	if(l->right->addable == 20) {
+		idx.regtree = l->left;
+		idx.scale = 1 << l->right->vconst;
+	} else
+	if(l->left->addable == 20) {
+		idx.regtree = l->right;
+		idx.scale = 1 << l->left->vconst;
+	} else
+		diag(n, "bad index");
+
+	idx.basetree = r;
+	if(debug['x']) {
+		print("scale = %d\n", idx.scale);
+		prtree(idx.regtree, "index");
+		prtree(idx.basetree, "base");
+	}
+}
--- /dev/null
+++ b/utils/6c/swt.c
@@ -1,0 +1,513 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gcmp(OEQ, n, q->val);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(debug['K'])
+		print("case > %.8llux\n", r->val);
+	gcmp(OGT, n, r->val);
+	sp = p;
+	gbranch(OGOTO);
+	p->as = AJEQ;
+	patch(p, r->label);
+	swit1(q, i, def, n);
+
+	if(debug['K'])
+		print("case < %.8llux\n", r->val);
+	patch(sp, pc);
+	swit1(r+1, nc-i-1, def, n);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gmove(n2, n3);
+		gmove(n3, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, tfield, nodconst(v), n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, tfield, nodconst(sh), n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, tfield, nodconst(sh), n1);
+			else
+				gopcode(OASHR, tfield, nodconst(sh), n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod;
+	int sh;
+
+	regalloc(&nod, b->left, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, types[TLONG], nodconst(v), n1);
+	gmove(n1, &nod);
+	if(nn != Z)
+		gmove(n1, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, types[TLONG], nodconst(sh), &nod);
+	v <<= sh;
+	gopcode(OAND, types[TLONG], nodconst(~v), n3);
+	gopcode(OOR, types[TLONG], n3, &nod);
+	gmove(&nod, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->from.scale = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+	if(0 && a->op == OCONST && typev[a->type->etype]) {
+		gpseudo(ADATA, s, lo64(a));
+		p->from.offset += o;
+		p->from.scale = 4;
+		gpseudo(ADATA, s, hi64(a));
+		p->from.offset += o + 4;
+		p->from.scale = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->from.scale = w;
+	switch(p->to.type) {
+	default:
+		p->to.index = p->to.type;
+		p->to.type = D_ADDR;
+	case D_CONST:
+	case D_FCONST:
+	case D_ADDR:
+		break;
+	}
+}
+
+void	zname(Biobuf*, Sym*, int);
+void	zaddr(Biobuf*, Adr*, int);
+void	outhist(Biobuf*);
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int f, sf, st, t, sym;
+	Biobuf b;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	f = open(outfile, OWRITE);
+	if(f < 0) {
+		diag(Z, "cannot open %s", outfile);
+		return;
+	}
+	Binit(&b, f, OWRITE);
+	Bseek(&b, 0L, 2);
+	outhist(&b);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.type;
+			if(t == D_ADDR)
+				t = p->from.index;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&b, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.type;
+			if(t == D_ADDR)
+				t = p->to.index;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&b, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		Bputc(&b, p->as);
+		Bputc(&b, p->as>>8);
+		Bputc(&b, p->lineno);
+		Bputc(&b, p->lineno>>8);
+		Bputc(&b, p->lineno>>16);
+		Bputc(&b, p->lineno>>24);
+		zaddr(&b, &p->from, sf);
+		zaddr(&b, &p->to, st);
+	}
+	Bflush(&b);
+	close(f);
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, ANAME>>8);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		Bputc(b, pg.as);
+		Bputc(b, pg.as>>8);
+		Bputc(b, pg.lineno);
+		Bputc(b, pg.lineno>>8);
+		Bputc(b, pg.lineno>>16);
+		Bputc(b, pg.lineno>>24);
+		zaddr(b, &pg.from, 0);
+		zaddr(b, &pg.to, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n;
+	ulong sig;
+
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		Bputc(b, ASIGNAME);
+		Bputc(b, ASIGNAME>>8);
+		Bputc(b, sig);
+		Bputc(b, sig>>8);
+		Bputc(b, sig>>16);
+		Bputc(b, sig>>24);
+		s->sig = SIGDONE;
+	}
+	else{
+		Bputc(b, ANAME);	/* as */
+		Bputc(b, ANAME>>8);	/* as */
+	}
+	Bputc(b, t);			/* type */
+	Bputc(b, s->sym);		/* sym */
+	n = s->name;
+	while(*n) {
+		Bputc(b, *n);
+		n++;
+	}
+	Bputc(b, 0);
+}
+
+void
+zaddr(Biobuf *b, Adr *a, int s)
+{
+	long l;
+	int i, t;
+	char *n;
+	Ieee e;
+
+	t = 0;
+	if(a->index != D_NONE || a->scale != 0)
+		t |= T_INDEX;
+	if(s != 0)
+		t |= T_SYM;
+
+	switch(a->type) {
+	default:
+		t |= T_TYPE;
+	case D_NONE:
+		if(a->offset != 0) {
+			t |= T_OFFSET;
+			l = a->offset;
+			if((vlong)l != a->offset)
+				t |= T_64;
+		}
+		break;
+	case D_FCONST:
+		t |= T_FCONST;
+		break;
+	case D_SCONST:
+		t |= T_SCONST;
+		break;
+	}
+	Bputc(b, t);
+
+	if(t & T_INDEX) {	/* implies index, scale */
+		Bputc(b, a->index);
+		Bputc(b, a->scale);
+	}
+	if(t & T_OFFSET) {	/* implies offset */
+		l = a->offset;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		if(t & T_64) {
+			l = a->offset>>32;
+			Bputc(b, l);
+			Bputc(b, l>>8);
+			Bputc(b, l>>16);
+			Bputc(b, l>>24);
+		}
+	}
+	if(t & T_SYM)		/* implies sym */
+		Bputc(b, s);
+	if(t & T_FCONST) {
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		l = e.h;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		return;
+	}
+	if(t & T_SCONST) {
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(b, *n);
+			n++;
+		}
+		return;
+	}
+	if(t & T_TYPE)
+		Bputc(b, a->type);
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial align of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_VLONG)
+			w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesu[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial align of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_VLONG) {
+			w = SZ_VLONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_VLONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_VLONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/6c/txt.c
@@ -1,0 +1,1522 @@
+#include "gc.h"
+
+static	int	resvreg[nelem(reg)];
+
+void
+ginit(void)
+{
+	int i;
+	Type *t;
+
+	thechar = '6';
+	thestring = "amd64";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TINT];
+
+	typeword = typechlvp;
+	typeswitch = typechlv;
+	typecmplx = typesu;
+
+	/* TO DO */
+	memmove(typechlpv, typechlp, sizeof(typechlpv));
+	typechlpv[TVLONG] = 1;
+	typechlpv[TUVLONG] = 1;
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.from.type = D_NONE;
+	zprog.from.index = D_NONE;
+	zprog.from.scale = 0;
+	zprog.to = zprog.from;
+
+	lregnode.op = OREGISTER;
+	lregnode.class = CEXREG;
+	lregnode.reg = REGTMP;
+	lregnode.complex = 0;
+	lregnode.addable = 11;
+	lregnode.type = types[TLONG];
+
+	qregnode = lregnode;
+	qregnode.type = types[TVLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	vconstnode = constnode;
+	vconstnode.type = types[TVLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	if(0)
+		com64init();
+
+	memset(reg, 0, sizeof(reg));
+	for(i=0; i<nelem(reg); i++) {
+		reg[i] = 1;
+		if(i >= D_AX && i <= D_R15 && i != D_SP)
+			reg[i] = 0;
+		if(i >= D_X0 && i <= D_X7)
+			reg[i] = 0;
+	}
+	/* keep two external registers */
+	reg[REGEXT] = 1;
+	reg[REGEXT-1] = 1;
+	memmove(resvreg, reg, sizeof(resvreg));
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	reg[D_SP]--;
+	for(i=D_AX; i<=D_R15; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "reg %R left allocated", i);
+	for(i=D_X0; i<=D_X7; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "reg %R left allocated", i);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+int
+nareg(void)
+{
+	int i, n;
+
+	n = 0;
+	for(i=D_AX; i<=D_R15; i++)
+		if(reg[i] == 0 && !resvreg[i])
+			n++;
+	return n;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesu[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG && curarg == 0 && typechlpv[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gmove(n, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gmove(tn1, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodgconst(vlong v, Type *t)
+{
+	if(!typev[t->etype])
+		return nodconst((long)v);
+	vconstnode.vconst = v;
+	return &vconstnode;
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+int
+isreg(Node *n, int r)
+{
+
+	if(n->op == OREGISTER)
+		if(n->reg == r)
+			return 1;
+	return 0;
+}
+
+int
+nodreg(Node *n, Node *nn, int r)
+{
+	int et;
+
+	*n = qregnode;
+	n->reg = r;
+	if(nn != Z){
+		et = nn->type->etype;
+		if(!typefd[et] && nn->type->width <= SZ_LONG && 0)
+			n->type = typeu[et]? types[TUINT]: types[TINT];
+		else
+			n->type = nn->type;
+//print("nodreg %s [%s]\n", tnames[et], tnames[n->type->etype]);
+		n->lineno = nn->lineno;
+	}
+	if(reg[r] == 0)
+		return 0;
+	if(nn != Z) {
+		if(nn->op == OREGISTER)
+		if(nn->reg == r)
+			return 0;
+	}
+	return 1;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= D_AX && i <= D_R15)
+				goto out;
+		}
+		for(i=D_AX; i<=D_R15; i++)
+			if(reg[i] == 0 && !resvreg[i])
+				goto out;
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= D_X0 && i <= D_X7)
+				goto out;
+		}
+		for(i=D_X0; i<=D_X7; i++)
+			if(reg[i] == 0 && !resvreg[i])
+				goto out;
+		diag(tn, "out of float registers");
+		goto out;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	i = 0;
+out:
+	if(i)
+		reg[i]++;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %R", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O %D", n->op, a);
+		break;
+
+	case OREGISTER:
+		a->type = n->reg;
+		a->sym = S;
+		break;
+
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type >= D_AX && a->type <= D_R15)
+			a->type += D_INDIR;
+		else
+		if(a->type == D_CONST)
+			a->type = D_NONE+D_INDIR;
+		else
+		if(a->type == D_ADDR) {
+			a->type = a->index;
+			a->index = D_NONE;
+		} else
+			goto bad;
+		break;
+
+	case OINDEX:
+		a->type = idx.ptr;
+		if(n->left->op == OADDR || n->left->op == OCONST)
+			naddr(n->left, a);
+		if(a->type >= D_AX && a->type <= D_R15)
+			a->type += D_INDIR;
+		else
+		if(a->type == D_CONST)
+			a->type = D_NONE+D_INDIR;
+		else
+		if(a->type == D_ADDR) {
+			a->type = a->index;
+			a->index = D_NONE;
+		} else
+			goto bad;
+		a->index = idx.reg;
+		a->scale = n->scale;
+		a->offset += n->xoffset;
+		break;
+
+	case OINDREG:
+		a->type = n->reg+D_INDIR;
+		a->sym = S;
+		a->offset = n->xoffset;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->type = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->type = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->type = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+			break;
+		}
+		a->sym = S;
+		a->type = D_CONST;
+		if(typev[n->type->etype] || n->type->etype == TIND)
+			a->offset = n->vconst;
+		else
+			a->offset = convvtox(n->vconst, typeu[n->type->etype]? TULONG: TLONG);
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type >= D_INDIR) {
+			a->type -= D_INDIR;
+			break;
+		}
+		if(a->type == D_EXTERN || a->type == D_STATIC ||
+		   a->type == D_AUTO || a->type == D_PARAM)
+			if(a->index == D_NONE) {
+				a->index = a->type;
+				a->type = D_ADDR;
+				break;
+			}
+		goto bad;
+
+	case OADD:
+		if(n->right->op == OCONST) {
+			v = n->right->vconst;
+			naddr(n->left, a);
+		} else
+		if(n->left->op == OCONST) {
+			v = n->left->vconst;
+			naddr(n->right, a);
+		} else
+			goto bad;
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+gcmp(int op, Node *n, vlong val)
+{
+	Node *cn, nod;
+
+	cn = nodgconst(val, n->type);
+	if(!immconst(cn)){
+		regalloc(&nod, n, Z);
+		gmove(cn, &nod);
+		gopcode(op, n->type, n, &nod);
+		regfree(&nod);
+	}else
+		gopcode(op, n->type, n, cn);
+}
+
+#define	CASE(a,b)	((a<<8)|(b<<0))
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, t64, a;
+	Node nod, nod1, nod2, nod3;
+	Prog *p1, *p2;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+	t64 = tt == TVLONG || tt == TUVLONG || tt == TIND;
+	if(debug['M'])
+		print("gop: %O %O[%s],%O[%s]\n", OAS,
+			f->op, tnames[ft], t->op, tnames[tt]);
+	if(typefd[ft] && f->op == OCONST) {
+		/* TO DO: pick up special constants, possibly preloaded */
+		if(f->fconst == 0.0){
+			regalloc(&nod, t, t);
+			gins(AXORPD, &nod, &nod);
+			gmove(&nod, t);
+			regfree(&nod);
+			return;
+		}
+	}
+/*
+ * load
+ */
+	if(f->op == ONAME || f->op == OINDREG ||
+	   f->op == OIND || f->op == OINDEX)
+	switch(ft) {
+	case TCHAR:
+		a = AMOVBLSX;
+		if(t64)
+			a = AMOVBQSX;
+		goto ld;
+	case TUCHAR:
+		a = AMOVBLZX;
+		if(t64)
+			a = AMOVBQZX;
+		goto ld;
+	case TSHORT:
+		a = AMOVWLSX;
+		if(t64)
+			a = AMOVWQSX;
+		goto ld;
+	case TUSHORT:
+		a = AMOVWLZX;
+		if(t64)
+			a = AMOVWQZX;
+		goto ld;
+	case TINT:
+	case TLONG:
+		if(typefd[tt]) {
+			regalloc(&nod, t, t);
+			if(tt == TDOUBLE)
+				a = ACVTSL2SD;
+			else
+				a = ACVTSL2SS;
+			gins(a, f, &nod);
+			gmove(&nod, t);
+			regfree(&nod);
+			return;
+		}
+		a = AMOVL;
+		if(t64)
+			a = AMOVLQSX;
+		goto ld;
+	case TUINT:
+	case TULONG:
+		a = AMOVL;
+		if(t64)
+			a = AMOVLQZX;	/* could probably use plain MOVL */
+		goto ld;
+	case TVLONG:
+		if(typefd[tt]) {
+			regalloc(&nod, t, t);
+			if(tt == TDOUBLE)
+				a = ACVTSQ2SD;
+			else
+				a = ACVTSQ2SS;
+			gins(a, f, &nod);
+			gmove(&nod, t);
+			regfree(&nod);
+			return;
+		}
+	case TUVLONG:
+		a = AMOVQ;
+		goto ld;
+	case TIND:
+		a = AMOVQ;
+
+	ld:
+		regalloc(&nod, f, t);
+		nod.type = t64? types[TVLONG]: types[TINT];
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+
+	case TFLOAT:
+		a = AMOVSS;
+		goto fld;
+	case TDOUBLE:
+		a = AMOVSD;
+	fld:
+		regalloc(&nod, f, t);
+		if(tt != TDOUBLE && tt != TFLOAT){	/* TO DO: why is this here */
+			prtree(f, "odd tree");
+			nod.type = t64? types[TVLONG]: types[TINT];
+		}
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+/*
+ * store
+ */
+	if(t->op == ONAME || t->op == OINDREG ||
+	   t->op == OIND || t->op == OINDEX)
+	switch(tt) {
+	case TCHAR:
+	case TUCHAR:
+		a = AMOVB;	goto st;
+	case TSHORT:
+	case TUSHORT:
+		a = AMOVW;	goto st;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+		a = AMOVL;	goto st;
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+		a = AMOVQ;	goto st;
+
+	st:
+		if(f->op == OCONST) {
+			gins(a, f, t);
+			return;
+		}
+	fst:
+		regalloc(&nod, t, f);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+
+	case TFLOAT:
+		a = AMOVSS;
+		goto fst;
+	case TDOUBLE:
+		a = AMOVSD;
+		goto fst;
+	}
+
+/*
+ * convert
+ */
+	switch(CASE(ft,tt)) {
+	default:
+/*
+ * integer to integer
+ ********
+		a = AGOK;	break;
+
+	case CASE(	TCHAR,	TCHAR):
+	case CASE(	TUCHAR,	TCHAR):
+	case CASE(	TSHORT,	TCHAR):
+	case CASE(	TUSHORT,TCHAR):
+	case CASE(	TINT,	TCHAR):
+	case CASE(	TUINT,	TCHAR):
+	case CASE(	TLONG,	TCHAR):
+	case CASE(	TULONG,	TCHAR):
+	case CASE(	TIND,	TCHAR):
+
+	case CASE(	TCHAR,	TUCHAR):
+	case CASE(	TUCHAR,	TUCHAR):
+	case CASE(	TSHORT,	TUCHAR):
+	case CASE(	TUSHORT,TUCHAR):
+	case CASE(	TINT,	TUCHAR):
+	case CASE(	TUINT,	TUCHAR):
+	case CASE(	TLONG,	TUCHAR):
+	case CASE(	TULONG,	TUCHAR):
+	case CASE(	TIND,	TUCHAR):
+
+	case CASE(	TSHORT,	TSHORT):
+	case CASE(	TUSHORT,TSHORT):
+	case CASE(	TINT,	TSHORT):
+	case CASE(	TUINT,	TSHORT):
+	case CASE(	TLONG,	TSHORT):
+	case CASE(	TULONG,	TSHORT):
+	case CASE(	TIND,	TSHORT):
+
+	case CASE(	TSHORT,	TUSHORT):
+	case CASE(	TUSHORT,TUSHORT):
+	case CASE(	TINT,	TUSHORT):
+	case CASE(	TUINT,	TUSHORT):
+	case CASE(	TLONG,	TUSHORT):
+	case CASE(	TULONG,	TUSHORT):
+	case CASE(	TIND,	TUSHORT):
+
+	case CASE(	TINT,	TINT):
+	case CASE(	TUINT,	TINT):
+	case CASE(	TLONG,	TINT):
+	case CASE(	TULONG,	TINT):
+	case CASE(	TIND,	TINT):
+
+	case CASE(	TINT,	TUINT):
+	case CASE(	TUINT,	TUINT):
+	case CASE(	TLONG,	TUINT):
+	case CASE(	TULONG,	TUINT):
+	case CASE(	TIND,	TUINT):
+
+	case CASE(	TUINT,	TIND):
+	case CASE(	TVLONG,	TUINT):
+	case CASE(	TVLONG,	TULONG):
+	case CASE(	TUVLONG, TUINT):
+	case CASE(	TUVLONG, TULONG):
+ *****/
+		a = AMOVL;
+		break;
+
+	case CASE(	TVLONG,	TCHAR):
+	case	CASE(	TVLONG,	TSHORT):
+	case CASE(	TVLONG,	TINT):
+	case CASE(	TVLONG,	TLONG):
+	case CASE(	TUVLONG, TCHAR):
+	case	CASE(	TUVLONG, TSHORT):
+	case CASE(	TUVLONG, TINT):
+	case CASE(	TUVLONG, TLONG):
+	case CASE(	TINT,	TVLONG):
+	case CASE(	TINT,	TUVLONG):
+	case CASE(	TLONG,	TVLONG):
+	case CASE(	TINT,	TIND):
+	case CASE(	TLONG,	TIND):
+		a = AMOVLQSX;
+		if(f->op == OCONST) {
+			f->vconst &= (uvlong)0xffffffffU;
+			if(f->vconst & 0x80000000)
+				f->vconst |= (vlong)0xffffffff << 32;
+			a = AMOVQ;
+		}
+		break;
+
+	case CASE(	TUINT,	TIND):
+	case CASE(	TUINT,	TVLONG):
+	case CASE(	TUINT,	TUVLONG):
+	case CASE(	TULONG,	TVLONG):
+	case CASE(	TULONG,	TUVLONG):
+	case CASE(	TULONG,	TIND):
+		a = AMOVL;	/* same effect as AMOVLQZX */
+		if(f->op == OCONST) {
+			f->vconst &= (uvlong)0xffffffffU;
+			a = AMOVQ;
+		}
+		break;
+
+	case CASE(	TIND,	TVLONG):
+	case CASE(	TVLONG,	TVLONG):
+	case CASE(	TUVLONG,	TVLONG):
+	case CASE(	TVLONG,	TUVLONG):
+	case CASE(	TUVLONG,	TUVLONG):
+	case CASE(	TIND,	TUVLONG):
+	case CASE(	TVLONG,	TIND):
+	case CASE(	TUVLONG,	TIND):
+	case CASE(	TIND,	TIND):
+		a = AMOVQ;
+		break;
+
+	case CASE(	TSHORT,	TINT):
+	case CASE(	TSHORT,	TUINT):
+	case CASE(	TSHORT,	TLONG):
+	case CASE(	TSHORT,	TULONG):
+		a = AMOVWLSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			if(f->vconst & 0x8000)
+				f->vconst |= 0xffff0000;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TSHORT,	TVLONG):
+	case CASE(	TSHORT,	TUVLONG):
+	case CASE(	TSHORT,	TIND):
+		a = AMOVWQSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			if(f->vconst & 0x8000){
+				f->vconst |= 0xffff0000;
+				f->vconst |= (vlong)~0 << 32;
+			}
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TUSHORT,TINT):
+	case CASE(	TUSHORT,TUINT):
+	case CASE(	TUSHORT,TLONG):
+	case CASE(	TUSHORT,TULONG):
+		a = AMOVWLZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TUSHORT,TVLONG):
+	case CASE(	TUSHORT,TUVLONG):
+	case CASE(	TUSHORT,TIND):
+		a = AMOVWQZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			a = AMOVL;	/* MOVL also zero-extends to 64 bits */
+		}
+		break;
+
+	case CASE(	TCHAR,	TSHORT):
+	case CASE(	TCHAR,	TUSHORT):
+	case CASE(	TCHAR,	TINT):
+	case CASE(	TCHAR,	TUINT):
+	case CASE(	TCHAR,	TLONG):
+	case CASE(	TCHAR,	TULONG):
+		a = AMOVBLSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			if(f->vconst & 0x80)
+				f->vconst |= 0xffffff00;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TCHAR,	TVLONG):
+	case CASE(	TCHAR,	TUVLONG):
+	case CASE(	TCHAR,	TIND):
+		a = AMOVBQSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			if(f->vconst & 0x80){
+				f->vconst |= 0xffffff00;
+				f->vconst |= (vlong)~0 << 32;
+			}
+			a = AMOVQ;
+		}
+		break;
+
+	case CASE(	TUCHAR,	TSHORT):
+	case CASE(	TUCHAR,	TUSHORT):
+	case CASE(	TUCHAR,	TINT):
+	case CASE(	TUCHAR,	TUINT):
+	case CASE(	TUCHAR,	TLONG):
+	case CASE(	TUCHAR,	TULONG):
+		a = AMOVBLZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TUCHAR,	TVLONG):
+	case CASE(	TUCHAR,	TUVLONG):
+	case CASE(	TUCHAR,	TIND):
+		a = AMOVBQZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			a = AMOVL;	/* zero-extends to 64-bits */
+		}
+		break;
+
+/*
+ * float to fix
+ */
+	case CASE(	TFLOAT,	TCHAR):
+	case CASE(	TFLOAT,	TUCHAR):
+	case CASE(	TFLOAT,	TSHORT):
+	case CASE(	TFLOAT,	TUSHORT):
+	case CASE(	TFLOAT,	TINT):
+	case CASE(	TFLOAT,	TUINT):
+	case CASE(	TFLOAT,	TLONG):
+	case CASE(	TFLOAT,	TULONG):
+	case CASE(	TFLOAT,	TVLONG):
+	case CASE(	TFLOAT,	TUVLONG):
+	case CASE(	TFLOAT,	TIND):
+
+	case CASE(	TDOUBLE,TCHAR):
+	case CASE(	TDOUBLE,TUCHAR):
+	case CASE(	TDOUBLE,TSHORT):
+	case CASE(	TDOUBLE,TUSHORT):
+	case CASE(	TDOUBLE,TINT):
+	case CASE(	TDOUBLE,TUINT):
+	case CASE(	TDOUBLE,TLONG):
+	case CASE(	TDOUBLE,TULONG):
+	case CASE(	TDOUBLE,TVLONG):
+	case CASE(	TDOUBLE,TUVLONG):
+	case CASE(	TDOUBLE,TIND):
+		regalloc(&nod, t, Z);
+		if(ewidth[tt] == SZ_VLONG || typeu[tt] && ewidth[tt] == SZ_INT){
+			if(ft == TFLOAT)
+				a = ACVTTSS2SQ;
+			else
+				a = ACVTTSD2SQ;
+		}else{
+			if(ft == TFLOAT)
+				a = ACVTTSS2SL;
+			else
+				a = ACVTTSD2SL;
+		}
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+
+/*
+ * ulong to float
+ */
+	case CASE(	TUVLONG,	TDOUBLE):
+	case CASE(	TUVLONG,	TFLOAT):
+		a = ACVTSQ2SS;
+		if(tt == TDOUBLE)
+			a = ACVTSQ2SD;
+		regalloc(&nod, f, f);
+		gmove(f, &nod);
+		regalloc(&nod1, t, t);
+		gins(ACMPQ, &nod, nodconst(0));
+		gins(AJLT, Z, Z);
+		p1 = p;
+		gins(a, &nod, &nod1);
+		gins(AJMP, Z, Z);
+		p2 = p;
+		patch(p1, pc);
+		regalloc(&nod2, f, Z);
+		regalloc(&nod3, f, Z);
+		gmove(&nod, &nod2);
+		gins(ASHRQ, nodconst(1), &nod2);
+		gmove(&nod, &nod3);
+		gins(AANDL, nodconst(1), &nod3);
+		gins(AORQ, &nod3, &nod2);
+		gins(a, &nod2, &nod1);
+		gins(tt == TDOUBLE? AADDSD: AADDSS, &nod1, &nod1);
+		regfree(&nod2);
+		regfree(&nod3);
+		patch(p2, pc);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+	case CASE(	TULONG,	TDOUBLE):
+	case CASE(	TUINT,	TDOUBLE):
+	case CASE(	TULONG,	TFLOAT):
+	case CASE(	TUINT,	TFLOAT):
+		a = ACVTSQ2SS;
+		if(tt == TDOUBLE)
+			a = ACVTSQ2SD;
+		regalloc(&nod, f, f);
+		gins(AMOVLQZX, f, &nod);
+		regalloc(&nod1, t, t);
+		gins(a, &nod, &nod1);
+		gmove(&nod1, t);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+/*
+ * fix to float
+ */
+	case CASE(	TCHAR,	TFLOAT):
+	case CASE(	TUCHAR,	TFLOAT):
+	case CASE(	TSHORT,	TFLOAT):
+	case CASE(	TUSHORT,TFLOAT):
+	case CASE(	TINT,	TFLOAT):
+	case CASE(	TLONG,	TFLOAT):
+	case	CASE(	TVLONG,	TFLOAT):
+	case CASE(	TIND,	TFLOAT):
+
+	case CASE(	TCHAR,	TDOUBLE):
+	case CASE(	TUCHAR,	TDOUBLE):
+	case CASE(	TSHORT,	TDOUBLE):
+	case CASE(	TUSHORT,TDOUBLE):
+	case CASE(	TINT,	TDOUBLE):
+	case CASE(	TLONG,	TDOUBLE):
+	case CASE(	TVLONG,	TDOUBLE):
+	case CASE(	TIND,	TDOUBLE):
+		regalloc(&nod, t, t);
+		if(ewidth[ft] == SZ_VLONG){
+			if(tt == TFLOAT)
+				a = ACVTSQ2SS;
+			else
+				a = ACVTSQ2SD;
+		}else{
+			if(tt == TFLOAT)
+				a = ACVTSL2SS;
+			else
+				a = ACVTSL2SD;
+		}
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+
+/*
+ * float to float
+ */
+	case CASE(	TFLOAT,	TFLOAT):
+		a = AMOVSS;
+		break;
+	case CASE(	TDOUBLE,TFLOAT):
+		a = ACVTSD2SS;
+		break;
+	case CASE(	TFLOAT,	TDOUBLE):
+		a = ACVTSS2SD;
+		break;
+	case CASE(	TDOUBLE,TDOUBLE):
+		a = AMOVSD;
+		break;
+	}
+	if(a == AMOVQ || a == AMOVSD || a == AMOVSS || a == AMOVL && ewidth[ft] == ewidth[tt])	/* TO DO: check AMOVL */
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+doindex(Node *n)
+{
+	Node nod, nod1;
+	long v;
+
+if(debug['Y'])
+prtree(n, "index");
+
+if(n->left->complex >= FNX)
+print("botch in doindex\n");
+
+	regalloc(&nod, &qregnode, Z);
+	v = constnode.vconst;
+	cgen(n->right, &nod);
+	idx.ptr = D_NONE;
+	if(n->left->op == OCONST)
+		idx.ptr = D_CONST;
+	else if(n->left->op == OREGISTER)
+		idx.ptr = n->left->reg;
+	else if(n->left->op != OADDR) {
+		reg[D_BP]++;	// cant be used as a base
+		regalloc(&nod1, &qregnode, Z);
+		cgen(n->left, &nod1);
+		idx.ptr = nod1.reg;
+		regfree(&nod1);
+		reg[D_BP]--;
+	}
+	idx.reg = nod.reg;
+	regfree(&nod);
+	constnode.vconst = v;
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	if(f != Z && f->op == OINDEX)
+		doindex(f);
+	if(t != Z && t->op == OINDEX)
+		doindex(t);
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Type *ty, Node *f, Node *t)
+{
+	int a, et;
+
+	et = TLONG;
+	if(ty != T)
+		et = ty->etype;
+	if(debug['M']) {
+		if(f != Z && f->type != T)
+			print("gop: %O %O[%s],", o, f->op, tnames[et]);
+		else
+			print("gop: %O Z,", o);
+		if(t != Z && t->type != T)
+			print("%O[%s]\n", t->op, tnames[t->type->etype]);
+		else
+			print("Z\n");
+	}
+	a = AGOK;
+	switch(o) {
+	case OCOM:
+		a = ANOTL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ANOTB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ANOTW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ANOTQ;
+		break;
+
+	case ONEG:
+		a = ANEGL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ANEGB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ANEGW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ANEGQ;
+		break;
+
+	case OADDR:
+		a = ALEAQ;
+		break;
+
+	case OASADD:
+	case OADD:
+		a = AADDL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AADDB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AADDW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AADDQ;
+		if(et == TFLOAT)
+			a = AADDSS;
+		if(et == TDOUBLE)
+			a = AADDSD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASUBB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASUBW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ASUBQ;
+		if(et == TFLOAT)
+			a = ASUBSS;
+		if(et == TDOUBLE)
+			a = ASUBSD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AORL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AORB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AORW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AORQ;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AANDL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AANDB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AANDW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AANDQ;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXORL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AXORB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AXORW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AXORQ;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASHRL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASHRB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASHRW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ASHRQ;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASARL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASARB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASARW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ASARQ;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASALL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASALB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASALW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ASALQ;
+		break;
+
+	case OFUNC:
+		a = ACALL;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(f->op == OREGISTER && t != Z && isreg(t, D_AX) && reg[D_DX] == 0)
+			t = Z;
+		a = AIMULL;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AIMULQ;
+		if(et == TFLOAT)
+			a = AMULSS;
+		if(et == TDOUBLE)
+			a = AMULSD;
+		break;
+
+	case OASMOD:
+	case OMOD:
+	case OASDIV:
+	case ODIV:
+		a = AIDIVL;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AIDIVQ;
+		if(et == TFLOAT)
+			a = ADIVSS;
+		if(et == TDOUBLE)
+			a = ADIVSD;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULL;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AMULQ;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVL;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ADIVQ;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		a = ACMPL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ACMPB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ACMPW;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ACMPQ;
+		if(et == TFLOAT)
+			a = AUCOMISS;
+		if(et == TDOUBLE)
+			a = AUCOMISD;
+		gins(a, f, t);
+		switch(o) {
+		case OEQ:	a = AJEQ; break;
+		case ONE:	a = AJNE; break;
+		case OLT:	a = AJLT; break;
+		case OLE:	a = AJLE; break;
+		case OGE:	a = AJGE; break;
+		case OGT:	a = AJGT; break;
+		case OLO:	a = AJCS; break;
+		case OLS:	a = AJLS; break;
+		case OHS:	a = AJCC; break;
+		case OHI:	a = AJHI; break;
+		}
+		gins(a, Z, Z);
+		return;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	gins(a, f, t);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+	return f->op == OREGISTER && t->op == OREGISTER && f->reg == t->reg;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_EXTERN;
+	p->from.sym = s;
+	p->from.scale = (profileflg ? 0 : NOPROF);
+	if(s->class == CSTATIC)
+		p->from.type = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	long v;
+
+	if(n->op == OCONST && !typefd[n->type->etype]) {
+		v = n->vconst;
+		if(v >= -32766L && v < 32766L)
+			return 1;
+	}
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlpv[t->etype]) {
+		if(exregoffset <= REGEXT-4)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/*[TXXX]*/	
+	SZ_CHAR,	/*[TCHAR]*/	
+	SZ_CHAR,	/*[TUCHAR]*/
+	SZ_SHORT,	/*[TSHORT]*/
+	SZ_SHORT,	/*[TUSHORT]*/
+	SZ_INT,		/*[TINT]*/
+	SZ_INT,		/*[TUINT]*/
+	SZ_LONG,	/*[TLONG]*/
+	SZ_LONG,	/*[TULONG]*/
+	SZ_VLONG,	/*[TVLONG]*/
+	SZ_VLONG,	/*[TUVLONG]*/
+	SZ_FLOAT,	/*[TFLOAT]*/
+	SZ_DOUBLE,	/*[TDOUBLE]*/
+	SZ_IND,		/*[TIND]*/
+	0,		/*[TFUNC]*/
+	-1,		/*[TARRAY]*/
+	0,		/*[TVOID]*/
+	-1,		/*[TSTRUCT]*/
+	-1,		/*[TUNION]*/
+	SZ_INT,		/*[TENUM]*/
+};
+long	ncast[NTYPE] =
+{
+	0,				/*[TXXX]*/
+	BCHAR|BUCHAR,			/*[TCHAR]*/
+	BCHAR|BUCHAR,			/*[TUCHAR]*/	
+	BSHORT|BUSHORT,			/*[TSHORT]*/
+	BSHORT|BUSHORT,			/*[TUSHORT]*/
+	BINT|BUINT|BLONG|BULONG,	/*[TINT]*/		
+	BINT|BUINT|BLONG|BULONG,	/*[TUINT]*/
+	BINT|BUINT|BLONG|BULONG,	/*[TLONG]*/
+	BINT|BUINT|BLONG|BULONG,	/*[TULONG]*/
+	BVLONG|BUVLONG|BIND,			/*[TVLONG]*/
+	BVLONG|BUVLONG|BIND,			/*[TUVLONG]*/
+	BFLOAT,				/*[TFLOAT]*/
+	BDOUBLE,			/*[TDOUBLE]*/
+	BVLONG|BUVLONG|BIND,		/*[TIND]*/
+	0,				/*[TFUNC]*/
+	0,				/*[TARRAY]*/
+	0,				/*[TVOID]*/
+	BSTRUCT,			/*[TSTRUCT]*/
+	BUNION,				/*[TUNION]*/
+	0,				/*[TENUM]*/
+};
--- /dev/null
+++ b/utils/6l/asm.c
@@ -1,0 +1,413 @@
+#include	"l.h"
+
+#define	Dbufslop	100
+
+#define PADDR(a)	((a) & ~0xfffffffff0000000ull)
+
+vlong
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	switch(s->type) {
+	case STEXT:
+		break;
+	case SDATA:
+		if(dlm)
+			return s->value+INITDAT;
+	default:
+		diag("entry not text: %s", s->name);
+	}
+	return s->value;
+}
+
+/* these need to take long arguments to be compatible with elf.c */void
+wputl(long w)
+{
+	cput(w);
+	cput(w>>8);
+}
+
+void
+wput(long w)
+{
+	cput(w>>8);
+	cput(w);
+}
+
+void
+lput(long l)
+{
+	cput(l>>24);
+	cput(l>>16);
+	cput(l>>8);
+	cput(l);
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+lputl(long l)
+{
+	cput(l);
+	cput(l>>8);
+	cput(l>>16);
+	cput(l>>24);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s && n > 0; s++){
+		cput(*s);
+		n--;
+	}
+	while(n > 0){
+		cput(0);
+		n--;
+	}
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long v, magic;
+	int a;
+	uchar *op1;
+	vlong vl;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asmb\n", cputime());
+	Bflush(&bso);
+
+	seek(cout, HEADR, 0);
+	pc = INITTEXT;
+	curp = firstp;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->pc != pc) {
+			if(!debug['a'])
+				print("%P\n", curp);
+			diag("phase error %llux sb %llux in %s", p->pc, pc, TNAME);
+			pc = p->pc;
+		}
+		curp = p;
+		asmins(p);
+		a = (andptr - and);
+		if(cbc < a)
+			cflush();
+		if(debug['a']) {
+			Bprint(&bso, pcstr, pc);
+			for(op1 = and; op1 < andptr; op1++)
+				Bprint(&bso, "%.2ux", *op1 & 0xff);
+			Bprint(&bso, "\t%P\n", curp);
+		}
+		if(dlm) {
+			if(p->as == ATEXT)
+				reloca = nil;
+			else if(reloca != nil)
+				diag("reloc failure: %P", curp);
+		}
+		memmove(cbp, and, a);
+		cbp += a;
+		pc += a;
+		cbc -= a;
+	}
+	cflush();
+	switch(HEADTYPE) {
+	default:
+		diag("unknown header type %ld", HEADTYPE);
+	case 2:
+	case 5:
+	case 6:
+		seek(cout, HEADR+textsize, 0);
+		break;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f datblk\n", cputime());
+	Bflush(&bso);
+
+	if(dlm){
+		char buf[8];
+
+		write(cout, buf, INITDAT-textsize);
+		textsize = INITDAT;
+	}
+
+	for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) {
+		if(datsize-v > sizeof(buf)-Dbufslop)
+			datblk(v, sizeof(buf)-Dbufslop);
+		else
+			datblk(v, datsize-v);
+	}
+
+	symsize = 0;
+	spsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		default:
+		case 2:
+		case 5:
+		case 6:
+			seek(cout, HEADR+textsize+datsize, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sp\n", cputime());
+		Bflush(&bso);
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		if(dlm)
+			asmdyn();
+		cflush();
+	}
+	else if(dlm){
+		seek(cout, HEADR+textsize+datsize, 0);
+		asmdyn();
+		cflush();
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f headr\n", cputime());
+	Bflush(&bso);
+	seek(cout, 0L, 0);
+	switch(HEADTYPE) {
+	default:
+	case 2:	/* plan9 */
+		magic = 4*26*26+7;
+		magic |= 0x00008000;		/* fat header */
+		if(dlm)
+			magic |= 0x80000000;	/* dlm */
+		lput(magic);			/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		vl = entryvalue();
+		lput(PADDR(vl));		/* va of entry */
+		lput(spsize);			/* sp offsets */
+		lput(lcsize);			/* line offsets */
+		llput(vl);			/* va of entry */
+		break;
+	case 5:
+		elf32(debug['8']? I386: AMD64, ELFDATA2LSB, 0, nil);
+		break;
+	case 6:
+		elf64(AMD64, ELFDATA2LSB, 0, nil);
+		break;
+	}
+	cflush();
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+datblk(long s, long n)
+{
+	Prog *p;
+	uchar *cast;
+	long l, fl, j;
+	vlong o;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+Dbufslop);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->from.scale;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(&p->to.ieee);
+				cast = (uchar*)&fl;
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[fnuxi4[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (uchar*)&p->to.ieee;
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[fnuxi8[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			if(debug['a'] && i == 0) {
+				Bprint(&bso, pcstr, l+s+INITDAT);
+				for(j=0; j<c; j++)
+					Bprint(&bso, "%.2ux", p->to.scon[j] & 0xff);
+				Bprint(&bso, "\t%P\n", curp);
+			}
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.scon[i];
+				l++;
+			}
+			break;
+		default:
+			o = p->to.offset;
+			if(p->to.type == D_ADDR) {
+				if(p->to.index != D_STATIC && p->to.index != D_EXTERN)
+					diag("DADDR type%P", p);
+				if(p->to.sym) {
+					if(p->to.sym->type == SUNDEF)
+						ckoff(p->to.sym, o);
+					o += p->to.sym->value;
+					if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF)
+						o += INITDAT;
+					if(dlm)
+						dynreloc(p->to.sym, l+s+INITDAT, 1);
+				}
+			}
+			fl = o;
+			cast = (uchar*)&fl;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi1[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi2[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi4[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (uchar*)&o;
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi8[j]]);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+vlong
+rnd(vlong v, vlong r)
+{
+	vlong c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/utils/6l/l.h
@@ -1,0 +1,400 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../6c/6.out.h"
+#include	"../ld/elf.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext?curtext->from.sym->name:noname)
+#define	cput(c)\
+	{ *cbp++ = c;\
+	if(--cbc <= 0)\
+		cflush(); }
+
+#define	LIBNAMELEN	300
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Sym	Sym;
+typedef	struct	Auto	Auto;
+typedef	struct	Optab	Optab;
+typedef	struct	Movtab	Movtab;
+
+struct	Adr
+{
+	union
+	{
+		vlong	u0offset;
+		char	u0scon[8];
+		Prog	*u0cond;	/* not used, but should be D_BRANCH */
+		Ieee	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	short	type;
+	char	index;
+	char	scale;
+};
+
+#define	offset	u0.u0offset
+#define	scon	u0.u0scon
+#define	cond	u0.u0cond
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog	*forwd;
+	Prog*	link;
+	Prog*	pcond;	/* work on this */
+	vlong	pc;
+	long	line;
+	uchar	mark;	/* work on these */
+	uchar	back;
+
+	short	as;
+	char	width;		/* fake for DATA */
+	char	mode;	/* 16, 32, or 64 */
+};
+struct	Auto
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	uchar	subtype;
+	ushort	file;
+	vlong	value;
+	long	sig;
+	Sym*	link;
+};
+struct	Optab
+{
+	short	as;
+	uchar*	ytab;
+	uchar	prefix;
+	uchar	op[20];
+};
+struct	Movtab
+{
+	short	as;
+	uchar	ft;
+	uchar	tt;
+	uchar	code;
+	uchar	op[4];
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SFILE,
+	SCONST,
+	SUNDEF,
+
+	SIMPORT,
+	SEXPORT,
+
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 8,
+	STRINGSZ	= 200,
+	MINLC		= 1,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+
+	Yxxx		= 0,
+	Ynone,
+	Yi0,
+	Yi1,
+	Yi8,
+	Ys32,
+	Yi32,
+	Yi64,
+	Yiauto,
+	Yal,
+	Ycl,
+	Yax,
+	Ycx,
+	Yrb,
+	Yrl,
+	Yrf,
+	Yf0,
+	Yrx,
+	Ymb,
+	Yml,
+	Ym,
+	Ybr,
+	Ycol,
+
+	Ycs,	Yss,	Yds,	Yes,	Yfs,	Ygs,
+	Ygdtr,	Yidtr,	Yldtr,	Ymsw,	Ytask,
+	Ycr0,	Ycr1,	Ycr2,	Ycr3,	Ycr4,	Ycr5,	Ycr6,	Ycr7,	Ycr8,
+	Ydr0,	Ydr1,	Ydr2,	Ydr3,	Ydr4,	Ydr5,	Ydr6,	Ydr7,
+	Ytr0,	Ytr1,	Ytr2,	Ytr3,	Ytr4,	Ytr5,	Ytr6,	Ytr7,	Yrl32,	Yrl64,
+	Ymr, Ymm,
+	Yxr, Yxm,
+	Ymax,
+
+	Zxxx		= 0,
+
+	Zlit,
+	Z_rp,
+	Zbr,
+	Zcall,
+	Zib_,
+	Zib_rp,
+	Zibo_m,
+	Zibo_m_xm,
+	Zil_,
+	Zil_rp,
+	Ziq_rp,
+	Zilo_m,
+	Ziqo_m,
+	Zjmp,
+	Zloop,
+	Zo_iw,
+	Zm_o,
+	Zm_r,
+	Zm_r_xm,
+	Zm_r_i_xm,
+	Zm_r_3d,
+	Zm_r_xm_nr,
+	Zr_m_xm_nr,
+	Zibm_r,	/* mmx1,mmx2/mem64,imm8 */
+	Zmb_r,
+	Zaut_r,
+	Zo_m,
+	Zo_m64,
+	Zpseudo,
+	Zr_m,
+	Zr_m_xm,
+	Zr_m_i_xm,
+	Zrp_,
+	Z_ib,
+	Z_il,
+	Zm_ibo,
+	Zm_ilo,
+	Zib_rr,
+	Zil_rr,
+	Zclr,
+	Zbyte,
+	Zmax,
+
+	Px		= 0,
+	P32		= 0x32,	/* 32-bit only */
+	Pe		= 0x66,	/* operand escape */
+	Pm		= 0x0f,	/* 2byte opcode escape */
+	Pq		= 0xff,	/* both escape */
+	Pb		= 0xfe,	/* byte operands */
+	Pf2		= 0xf2,	/* xmm escape 1 */
+	Pf3		= 0xf3,	/* xmm escape 2 */
+	Pw		= 0x48,	/* Rex.w */
+	Py		= 0x80,	/* defaults to 64-bit mode */
+
+	Rxf		= 1<<9,	/* internal flag for Rxr on from */
+	Rxt		= 1<<8,	/* internal flag for Rxr on to */
+	Rxw		= 1<<3,	/* =1, 64-bit operand size */
+	Rxr		= 1<<2,	/* extend modrm reg */
+	Rxx		= 1<<1,	/* extend sib index */
+	Rxb		= 1<<0,	/* extend modrm r/m, sib base, or opcode reg */
+
+	Roffset	= 22,		/* no. bits for offset in relocation address */
+	Rindex	= 10,		/* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+EXTERN	long	HEADR;
+EXTERN	long	HEADTYPE;
+EXTERN	vlong	INITDAT;
+EXTERN	long	INITRND;
+EXTERN	vlong	INITTEXT;
+EXTERN	vlong	INITTEXTP;
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	char*	pcstr;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	Prog*	edatap;
+EXTERN	vlong	datsize;
+EXTERN	char	debug[128];
+EXTERN	char	literal[32];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	uchar	fnuxi8[8];
+EXTERN	uchar	fnuxi4[4];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	uchar	inuxi1[1];
+EXTERN	uchar	inuxi2[2];
+EXTERN	uchar	inuxi4[4];
+EXTERN	uchar	inuxi8[8];
+EXTERN	char	ycover[Ymax*Ymax];
+EXTERN	uchar*	andptr;
+EXTERN	uchar*	rexptr;
+EXTERN	uchar	and[30];
+EXTERN	int	reg[D_NONE];
+EXTERN	int	regrex[D_NONE+1];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	nsymbol;
+EXTERN	char*	noname;
+EXTERN	char*	outfile;
+EXTERN	vlong	pc;
+EXTERN	long	spsize;
+EXTERN	Sym*	symlist;
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	vlong	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+EXTERN	char*	paramspace;
+
+EXTERN	Adr*	reloca;
+EXTERN	int	doexp, dlm;
+EXTERN	int	imports, nimports;
+EXTERN	int	exports, nexports;
+EXTERN	char*	EXPTAB;
+EXTERN	Prog	undefp;
+
+#define	UP	(&undefp)
+
+extern	Optab	optab[];
+extern	Optab*	opindex[];
+extern	char*	anames[];
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Pconv(Fmt*);
+int	Rconv(Fmt*);
+int	Sconv(Fmt*);
+void	addhist(long, int);
+void	addlibpath(char*);
+Prog*	appendp(Prog*);
+void	asmb(void);
+void	asmdyn(void);
+void	asmins(Prog*);
+void	asmlc(void);
+void	asmsp(void);
+void	asmsym(void);
+vlong	atolwhex(char*);
+Prog*	brchain(Prog*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	cflush(void);
+void	ckoff(Sym*, long);
+Prog*	copyp(Prog*);
+double	cputime(void);
+void	datblk(long, long);
+void	diag(char*, ...);
+void	dodata(void);
+void	doinit(void);
+void	doprof1(void);
+void	doprof2(void);
+void	dostkoff(void);
+void	dynreloc(Sym*, ulong, int);
+vlong	entryvalue(void);
+void	errorexit(void);
+void	export(void);
+int	fileexists(char*);
+int	find1(long, int);
+int	find2(long, int);
+char*	findlib(char*);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+void	import(void);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	llput(vlong v);
+void	llputl(vlong v);
+void	lput(long);
+void	lputl(long);
+void	main(int, char*[]);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	nuxiinit(void);
+void	objfile(char*);
+int	opsize(Prog*);
+void	patch(void);
+Prog*	prg(void);
+void	readundefs(char*, int);
+int	relinv(int);
+long	reuse(Prog*, Sym*);
+vlong	rnd(vlong, vlong);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	undefsym(Sym*);
+vlong	vaddr(Adr*);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, vlong);
+void	xfol(Prog*);
+int	zaddr(uchar*, Adr*, Sym*[]);
+void	zerosig(char*);
--- /dev/null
+++ b/utils/6l/list.c
@@ -1,0 +1,350 @@
+#include	"l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('R', Rconv);
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('P', Pconv);
+}
+
+static	Prog	*bigP;
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+
+	p = va_arg(fp->args, Prog*);
+	bigP = p;
+	switch(p->as) {
+	case ATEXT:
+		if(p->from.scale) {
+			sprint(str, "(%ld)	%A	%D,%d,%D",
+				p->line, p->as, &p->from, p->from.scale, &p->to);
+			break;
+		}
+	default:
+		sprint(str, "(%ld)	%A	%D,%D",
+			p->line, p->as, &p->from, &p->to);
+		break;
+	case ADATA:
+	case AINIT:
+	case ADYNT:
+		sprint(str, "(%ld)	%A	%D/%d,%D",
+			p->line, p->as, &p->from, p->from.scale, &p->to);
+		break;
+	}
+	bigP = P;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	int i;
+
+	i = va_arg(fp->args, int);
+	return fmtstrcpy(fp, anames[i]);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[40], s[20];
+	Adr *a;
+	int i;
+
+	a = va_arg(fp->args, Adr*);
+	i = a->type;
+	if(i >= D_INDIR) {
+		if(a->offset)
+			sprint(str, "%lld(%R)", a->offset, i-D_INDIR);
+		else
+			sprint(str, "(%R)", i-D_INDIR);
+		goto brk;
+	}
+	switch(i) {
+
+	default:
+		if(a->offset)
+			sprint(str, "$%lld,%R", a->offset, i);
+		else
+			sprint(str, "%R", i);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		break;
+
+	case D_BRANCH:
+		if(bigP != P && bigP->pcond != P)
+			if(a->sym != S)
+				sprint(str, "%llux+%s", bigP->pcond->pc,
+					a->sym->name);
+			else
+				sprint(str, "%llux", bigP->pcond->pc);
+		else
+			sprint(str, "%lld(PC)", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%lld(SB)", a->sym->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<%d>+%lld(SB)", a->sym->name,
+			a->sym->version, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s+%lld(SP)", a->sym->name, a->offset);
+		break;
+
+	case D_PARAM:
+		if(a->sym)
+			sprint(str, "%s+%lld(%s)", a->sym->name, a->offset, paramspace);
+		else
+			sprint(str, "%lld(%s)", a->offset, paramspace);
+		break;
+
+	case D_CONST:
+		sprint(str, "$%lld", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->scon);
+		break;
+
+	case D_ADDR:
+		a->type = a->index;
+		a->index = D_NONE;
+		sprint(str, "$%D", a);
+		a->index = a->type;
+		a->type = D_ADDR;
+		goto conv;
+	}
+brk:
+	if(a->index != D_NONE) {
+		sprint(s, "(%R*%d)", a->index, a->scale);
+		strcat(str, s);
+	}
+conv:
+	return fmtstrcpy(fp, str);
+}
+
+char*	regstr[] =
+{
+	"AL",		/* [D_AL] */
+	"CL",
+	"DL",
+	"BL",
+	"SPB",
+	"BPB",
+	"SIB",
+	"DIB",
+	"R8B",
+	"R9B",
+	"R10B",
+	"R11B",
+	"R12B",
+	"R13B",
+	"R14B",
+	"R15B",
+
+	"AX",		/* [D_AX] */
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+
+	"AH",
+	"CH",
+	"DH",
+	"BH",
+
+	"F0",		/* [D_F0] */
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+
+	"M0",
+	"M1",
+	"M2",
+	"M3",
+	"M4",
+	"M5",
+	"M6",
+	"M7",
+
+	"X0",
+	"X1",
+	"X2",
+	"X3",
+	"X4",
+	"X5",
+	"X6",
+	"X7",
+	"X8",
+	"X9",
+	"X10",
+	"X11",
+	"X12",
+	"X13",
+	"X14",
+	"X15",
+
+	"CS",		/* [D_CS] */
+	"SS",
+	"DS",
+	"ES",
+	"FS",
+	"GS",
+
+	"GDTR",		/* [D_GDTR] */
+	"IDTR",		/* [D_IDTR] */
+	"LDTR",		/* [D_LDTR] */
+	"MSW",		/* [D_MSW] */
+	"TASK",		/* [D_TASK] */
+
+	"CR0",		/* [D_CR] */
+	"CR1",
+	"CR2",
+	"CR3",
+	"CR4",
+	"CR5",
+	"CR6",
+	"CR7",
+	"CR8",
+	"CR9",
+	"CR10",
+	"CR11",
+	"CR12",
+	"CR13",
+	"CR14",
+	"CR15",
+
+	"DR0",		/* [D_DR] */
+	"DR1",
+	"DR2",
+	"DR3",
+	"DR4",
+	"DR5",
+	"DR6",
+	"DR7",
+
+	"TR0",		/* [D_TR] */
+	"TR1",
+	"TR2",
+	"TR3",
+	"TR4",
+	"TR5",
+	"TR6",
+	"TR7",
+
+	"NONE",		/* [D_NONE] */
+};
+
+int
+Rconv(Fmt *fp)
+{
+	char str[20];
+	int r;
+
+	r = va_arg(fp->args, int);
+	if(r >= D_AL && r <= D_NONE)
+		sprint(str, "%s", regstr[r-D_AL]);
+	else
+		sprint(str, "gok(%d)", r);
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[30], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(double); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		default:
+			if(c < 040 || c >= 0177)
+				break;	/* not portable */
+			p[-1] = c;
+			continue;
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 20) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/6l/mkfile
@@ -1,0 +1,35 @@
+<../../mkconfig
+
+TARG=6l
+
+OFILES=\
+	asm.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	list.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+	elf.$O\
+
+HFILES=\
+	l.h\
+	../6c/6.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include -I.
+
+enam.$O:	../6c/enam.c
+	$CC $CFLAGS ../6c/enam.c
+elf.$O:	../ld/elf.c
+	$CC $CFLAGS ../ld/elf.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/6l/obj.c
@@ -1,0 +1,1674 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= '6';
+char	*thestring 	= "amd64";
+char	*paramspace	= "FP";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H2 -T0x200028 -R0x200000	is plan9 format (was -T4136 -R4096)
+ *	-H5 -T0x80110000 -R4096		is ELF32
+ *	-H6 -T0x2000e8 -R0x200000	is ELF64
+ *
+ *	options used: 189BLPQSVWabcjlnpsvz
+ */
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+static int
+isobjfile(char *f)
+{
+	int n, v;
+	Biobuf *b;
+	char buf1[5], buf2[SARMAG];
+
+	b = Bopen(f, OREAD);
+	if(b == nil)
+		return 0;
+	n = Bread(b, buf1, 5);
+	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+		v = 1;	/* good enough for our purposes */
+	else{
+		Bseek(b, 0, 0);
+		n = Bread(b, buf2, SARMAG);
+		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+	}
+	Bterm(b);
+	return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, c;
+	char *a;
+	char name[LIBNAMELEN];
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	memset(debug, 0, sizeof(debug));
+	nerrors = 0;
+	outfile = "6.out";
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITTEXTP = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o': /* output to (next arg) */
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'x':	/* produce export table */
+		doexp = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SEXPORT);
+		break;
+	case 'u':	/* produce dynamically loadable module */
+		dlm = 1;
+		debug['l']++;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SIMPORT);
+		break;
+	} ARGEND
+	USED(argc);
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	a = getenv("ccroot");
+	if(a != nil && *a != '\0') {
+		if(!fileexists(a)) {
+			diag("nonexistent $ccroot: %s", a);
+			errorexit();
+		}
+	}else
+		a = "";
+	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+	addlibpath(name);
+	if(HEADTYPE == -1) {
+		if(debug['B'])
+			HEADTYPE = 2;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+	case 2:	/* plan 9 */
+		HEADR = 32L+8L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x200000+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 0x200000;
+		break;
+	case 5:	/* elf32 executable */
+		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0xf0110000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 6:	/* ELF64 executable */
+		HEADR = rnd(Ehdr64sz+3*Phdr64sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0x200000+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 0x200000;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%llux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H%ld -T0x%llux -D0x%llux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	for(i=1; optab[i].as; i++) {
+		c = optab[i].as;
+		if(opindex[c] != nil) {
+			diag("phase error in optab: %d (%A)", i, c);
+			errorexit();
+		}
+		opindex[c] = &optab[i];
+	}
+
+	for(i=0; i<Ymax; i++)
+		ycover[i*Ymax + i] = 1;
+
+	ycover[Yi0*Ymax + Yi8] = 1;
+	ycover[Yi1*Ymax + Yi8] = 1;
+
+	ycover[Yi0*Ymax + Ys32] = 1;
+	ycover[Yi1*Ymax + Ys32] = 1;
+	ycover[Yi8*Ymax + Ys32] = 1;
+
+	ycover[Yi0*Ymax + Yi32] = 1;
+	ycover[Yi1*Ymax + Yi32] = 1;
+	ycover[Yi8*Ymax + Yi32] = 1;
+	ycover[Ys32*Ymax + Yi32] = 1;
+
+	ycover[Yi0*Ymax + Yi64] = 1;
+	ycover[Yi1*Ymax + Yi64] = 1;
+	ycover[Yi8*Ymax + Yi64] = 1;
+	ycover[Ys32*Ymax + Yi64] = 1;
+	ycover[Yi32*Ymax + Yi64] = 1;
+
+	ycover[Yal*Ymax + Yrb] = 1;
+	ycover[Ycl*Ymax + Yrb] = 1;
+	ycover[Yax*Ymax + Yrb] = 1;
+	ycover[Ycx*Ymax + Yrb] = 1;
+	ycover[Yrx*Ymax + Yrb] = 1;
+	ycover[Yrl*Ymax + Yrb] = 1;
+
+	ycover[Ycl*Ymax + Ycx] = 1;
+
+	ycover[Yax*Ymax + Yrx] = 1;
+	ycover[Ycx*Ymax + Yrx] = 1;
+
+	ycover[Yax*Ymax + Yrl] = 1;
+	ycover[Ycx*Ymax + Yrl] = 1;
+	ycover[Yrx*Ymax + Yrl] = 1;
+
+	ycover[Yf0*Ymax + Yrf] = 1;
+
+	ycover[Yal*Ymax + Ymb] = 1;
+	ycover[Ycl*Ymax + Ymb] = 1;
+	ycover[Yax*Ymax + Ymb] = 1;
+	ycover[Ycx*Ymax + Ymb] = 1;
+	ycover[Yrx*Ymax + Ymb] = 1;
+	ycover[Yrb*Ymax + Ymb] = 1;
+	ycover[Yrl*Ymax + Ymb] = 1;
+	ycover[Ym*Ymax + Ymb] = 1;
+
+	ycover[Yax*Ymax + Yml] = 1;
+	ycover[Ycx*Ymax + Yml] = 1;
+	ycover[Yrx*Ymax + Yml] = 1;
+	ycover[Yrl*Ymax + Yml] = 1;
+	ycover[Ym*Ymax + Yml] = 1;
+
+	ycover[Yax*Ymax + Ymm] = 1;
+	ycover[Ycx*Ymax + Ymm] = 1;
+	ycover[Yrx*Ymax + Ymm] = 1;
+	ycover[Yrl*Ymax + Ymm] = 1;
+	ycover[Ym*Ymax + Ymm] = 1;
+	ycover[Ymr*Ymax + Ymm] = 1;
+
+	ycover[Yax*Ymax + Yxm] = 1;
+	ycover[Ycx*Ymax + Yxm] = 1;
+	ycover[Yrx*Ymax + Yxm] = 1;
+	ycover[Yrl*Ymax + Yxm] = 1;
+	ycover[Ym*Ymax + Yxm] = 1;
+	ycover[Yxr*Ymax + Yxm] = 1;
+
+	for(i=0; i<D_NONE; i++) {
+		reg[i] = -1;
+		if(i >= D_AL && i <= D_R15B) {
+			reg[i] = (i-D_AL) & 7;
+			if(i >= D_SPB && i <= D_DIB)
+				regrex[i] = 0x40;
+			if(i >= D_R8B && i <= D_R15B)
+				regrex[i] = Rxr | Rxx | Rxb;
+		}
+		if(i >= D_AH && i<= D_BH)
+			reg[i] = 4 + ((i-D_AH) & 7);
+		if(i >= D_AX && i <= D_R15) {
+			reg[i] = (i-D_AX) & 7;
+			if(i >= D_R8)
+				regrex[i] = Rxr | Rxx | Rxb;
+		}
+		if(i >= D_F0 && i <= D_F0+7)
+			reg[i] = (i-D_F0) & 7;
+		if(i >= D_M0 && i <= D_M0+7)
+			reg[i] = (i-D_M0) & 7;
+		if(i >= D_X0 && i <= D_X0+15) {
+			reg[i] = (i-D_X0) & 7;
+			if(i >= D_X0+8)
+				regrex[i] = Rxr | Rxx | Rxb;
+		}
+		if(i >= D_CR+8 && i <= D_CR+15) 
+			regrex[i] = Rxr;
+	}
+
+	zprg.link = P;
+	zprg.pcond = P;
+	zprg.back = 2;
+	zprg.as = AGOK;
+	zprg.from.type = D_NONE;
+	zprg.from.index = D_NONE;
+	zprg.from.scale = 1;
+	zprg.to = zprg.from;
+	zprg.mode = 64;
+
+	pcstr = "%.6llux ";
+	nuxiinit();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	edatap = P;
+	pc = 0;
+	dtype = 4;
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		errorexit();
+	if(doexp || dlm){
+		EXPTAB = "_exporttab";
+		zerosig(EXPTAB);
+		zerosig("etext");
+		zerosig("edata");
+		zerosig("end");
+		if(dlm){
+			import();
+			HEADTYPE = 2;
+			INITTEXT = 0;
+			INITDAT = 0;
+			INITRND = 8;
+			INITENTRY = EXPTAB;
+		}
+		export();
+	}
+	patch();
+	follow();
+	dodata();
+	dostkoff();
+	paramspace = "SP";	/* (FP) now (SP) on output */
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	span();
+	doinit();
+	asmb();
+	undef();
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld symbols\n", nsymbol);
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+	int i;
+	char name[LIBNAMELEN];
+
+	for(i = 0; i < nlibdir; i++) {
+		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+		if(fileexists(name))
+			return libdir[i];
+	}
+	return nil;
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[LIBNAMELEN], pname[LIBNAMELEN];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	if(file[0] == '-' && file[1] == 'l') {
+		snprint(pname, sizeof(pname), "lib%s.a", file+2);
+		e = findlib(pname);
+		if(e == nil) {
+			diag("cannot find library: %s", file);
+			errorexit();
+		}
+		snprint(name, sizeof(name), "%s/%s", e, pname);
+		file = name;
+	}
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open %s: %r", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			/* need readn to read the dumps (at least) */
+			l = readn(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int c, t, i;
+	int l;
+	Sym *s;
+	Auto *u;
+
+	t = p[0];
+	c = 1;
+	if(t & T_INDEX) {
+		a->index = p[c];
+		a->scale = p[c+1];
+		c += 2;
+	} else {
+		a->index = D_NONE;
+		a->scale = 0;
+	}
+	a->offset = 0;
+	if(t & T_OFFSET) {
+		/*
+		 * Hack until Charles fixes the compiler.
+		a->offset = (long)(p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24));
+		 */
+		l = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
+		a->offset = l;
+		c += 4;
+		if(t & T_64) {
+			l = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
+			a->offset = ((vlong)l<<32) | (a->offset & 0xFFFFFFFFUL);
+			c += 4;
+		}
+	}
+	a->sym = S;
+	if(t & T_SYM) {
+		a->sym = h[p[c]];
+		c++;
+	}
+	a->type = D_NONE;
+	if(t & T_FCONST) {
+		a->ieee.l = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
+		a->ieee.h = p[c+4] | (p[c+5]<<8) | (p[c+6]<<16) | (p[c+7]<<24);
+		c += 8;
+		a->type = D_FCONST;
+	} else
+	if(t & T_SCONST) {
+		for(i=0; i<NSNAME; i++)
+			a->scon[i] = p[c+i];
+		c += NSNAME;
+		a->type = D_SCONST;
+	}
+	if(t & T_TYPE) {
+		a->type = p[c];
+		c++;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+
+	t = a->type;
+	if(t != D_AUTO && t != D_PARAM)
+		return c;
+	l = a->offset;
+	for(u=curauto; u; u=u->link) {
+		if(u->asym == s)
+		if(u->type == t) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+	}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = t;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+
+	cleanname(name);
+
+	if(search){
+		p = findlib(name);
+		if(p != nil){
+			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+			name = fn2;
+		}
+	}
+
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	vlong ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	int v, o, r, skip, mode;
+	Sym *h[NSYM], *s, *di;
+	ulong sig;
+	static int files;
+	static char **filen;
+	char **nfilen;
+
+	if((files&15) == 0){
+		nfilen = malloc((files+16)*sizeof(char*));
+		memmove(nfilen, filen, files*sizeof(char*));
+		free(filen);
+		filen = nfilen;
+	}
+	filen[files++] = strdup(pn);
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+	mode = 64;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0] | (bloc[1] << 8);
+	if(o <= AXXX || o >= ALAST) {
+		if(o < 0)
+			goto eof;
+		diag("%s: opcode out of range %d", pn, o);
+		print("	probably not a .6 file\n");
+		errorexit();
+	}
+
+	if(o == ANAME || o == ASIGNAME) {
+		sig = 0;
+		if(o == ASIGNAME) {
+			sig = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[2];	/* type */
+		o = bloc[3];	/* sym */
+		bloc += 4;
+		c -= 4;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['S'] && r == 0)
+			sig = 1729;
+		if(sig != 0){
+			if(s->sig != 0 && s->sig != sig)
+				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+			s->sig = sig;
+			s->file = files-1;
+		}
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->line = bloc[2] | (bloc[3] << 8) | (bloc[4] << 16) | (bloc[5] << 24);
+	p->back = 2;
+	p->mode = mode;
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(p->as) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("%s: redefinition: %s in %s",
+				pn, s->name, TNAME);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		goto loop;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->from.scale = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_ADDR;
+		p->to.index = D_EXTERN;
+		goto data;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		goto data;
+
+	case ADATA:
+	data:
+		if(edatap == P)
+			datap = p;
+		else
+			edatap->link = p;
+		edatap = p;
+		p->link = P;
+		goto loop;
+
+	case AGOK:
+		diag("%s: GOK opcode in %s", pn, TNAME);
+		pc++;
+		goto loop;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		s = p->from.sym;
+		if(s == S) {
+			diag("%s: no TEXT symbol: %P", pn, p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->from.scale & DUPOK) {
+				skip = 1;
+				goto casdef;
+			}
+			diag("%s: redefinition: %s\n%P", pn, s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->pcond = p;
+		etextp = p;
+		goto loop;
+
+	case AMODE:
+		if(p->from.type == D_CONST || p->from.type == D_INDIR+D_NONE){
+			switch((int)p->from.offset){
+			case 16: case 32: case 64:
+				mode = p->from.offset;
+				break;
+			}
+		}
+		goto loop;
+
+	case AFMOVF:
+	case AFADDF:
+	case AFSUBF:
+	case AFSUBRF:
+	case AFMULF:
+	case AFDIVF:
+	case AFDIVRF:
+	case AFCOMF:
+	case AFCOMFP:
+	case AMOVSS:
+	case AADDSS:
+	case ASUBSS:
+	case AMULSS:
+	case ADIVSS:
+	case ACOMISS:
+	case AUCOMISS:
+		if(skip)
+			goto casdef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(&p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_EXTERN;
+				t->from.sym = s;
+				t->from.scale = 4;
+				t->to = p->from;
+				if(edatap == P)
+					datap = t;
+				else
+					edatap->link = t;
+				edatap = t;
+				t->link = P;
+			}
+			p->from.type = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = 0;
+		}
+		goto casdef;
+
+	case AFMOVD:
+	case AFADDD:
+	case AFSUBD:
+	case AFSUBRD:
+	case AFMULD:
+	case AFDIVD:
+	case AFDIVRD:
+	case AFCOMD:
+	case AFCOMDP:
+	case AMOVSD:
+	case AADDSD:
+	case ASUBSD:
+	case AMULSD:
+	case ADIVSD:
+	case ACOMISD:
+	case AUCOMISD:
+		if(skip)
+			goto casdef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee.l, p->from.ieee.h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_EXTERN;
+				t->from.sym = s;
+				t->from.scale = 8;
+				t->to = p->from;
+				if(edatap == P)
+					datap = t;
+				else
+					edatap->link = t;
+				edatap = t;
+				t->link = P;
+			}
+			p->from.type = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = 0;
+		}
+		goto casdef;
+
+	casdef:
+	default:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		goto loop;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int l, c;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	h &= 0xffffff;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l + 1);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->sig = 0;
+	hash[h] = s;
+	nsymbol++;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+Prog*
+copyp(Prog *q)
+{
+	Prog *p;
+
+	p = prg();
+	*p = *q;
+	return p;
+}
+
+Prog*
+appendp(Prog *q)
+{
+	Prog *p;
+
+	p = prg();
+	p->link = q->link;
+	q->link = p;
+	p->line = q->line;
+	p->mode = q->mode;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->from.scale = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADDL;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_EXTERN;
+	q->from.sym = s;
+	q->from.scale = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				p->from.scale = 1;
+				ps2 = p;
+			}
+			if(p->from.sym == s4) {
+				p->from.scale = 1;
+				ps4 = p;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+
+			if(p->from.scale & NOPROF) {	/* dont profile */
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JMPL	profin
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = AJMP;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->pcond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = ACALL;
+			p->to.type = D_BRANCH;
+			p->pcond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JAL	profout
+			 */
+			p->as = ACALL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->pcond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		inuxi8[i] = c;
+		inuxi8[i+4] = c+4;
+		fnuxi4[i] = c;
+		fnuxi8[i] = c;
+		fnuxi8[i+4] = c+4;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", inuxi8[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+int
+find2(long l, int c)
+{
+	short *p;
+	int i;
+
+	p = (short*)&l;
+	for(i=0; i<4; i+=2) {
+		if(((*p >> 8) & 0xff) == c)
+			return i;
+		if((*p++ & 0xff) == c)
+			return i+1;
+	}
+	return 0;
+}
+
+long
+ieeedtof(Ieee *e)
+{
+	int exp;
+	long v;
+
+	if(e->h == 0)
+		return 0;
+	exp = (e->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (e->h & 0xfffffL) << 3;
+	v |= (e->l >> 29) & 0x7L;
+	if((e->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= e->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
+
+void
+undefsym(Sym *s)
+{
+	int n;
+
+	n = imports;
+	if(s->value != 0)
+		diag("value != 0 on SXREF");
+	if(n >= 1<<Rindex)
+		diag("import index %d out of range", n);
+	s->value = n<<Roffset;
+	s->type = SUNDEF;
+	imports++;
+}
+
+void
+zerosig(char *sp)
+{
+	Sym *s;
+
+	s = lookup(sp, 0);
+	s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+	int i, n;
+	Sym *s;
+	Biobuf *b;
+	char *l, buf[256], *fields[64];
+
+	if(f == nil)
+		return;
+	b = Bopen(f, OREAD);
+	if(b == nil){
+		diag("could not open %s: %r", f);
+		errorexit();
+	}
+	while((l = Brdline(b, '\n')) != nil){
+		n = Blinelen(b);
+		if(n >= sizeof(buf)){
+			diag("%s: line too long", f);
+			errorexit();
+		}
+		memmove(buf, l, n);
+		buf[n-1] = '\0';
+		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+		if(n == nelem(fields)){
+			diag("%s: bad format", f);
+			errorexit();
+		}
+		for(i = 0; i < n; i++){
+			s = lookup(fields[i], 0);
+			s->type = SXREF;
+			s->subtype = t;
+			if(t == SIMPORT)
+				nimports++;
+			else
+				nexports++;
+		}
+	}
+	Bterm(b);
+}
--- /dev/null
+++ b/utils/6l/optab.c
@@ -1,0 +1,1185 @@
+#include	"l.h"
+
+uchar	ynone[] =
+{
+	Ynone,	Ynone,	Zlit,	1,
+	0
+};
+uchar	ytext[] =
+{
+	Ymb,	Yi32,	Zpseudo,1,
+	0
+};
+uchar	ynop[] =
+{
+	Ynone,	Ynone,	Zpseudo,1,
+	Ynone,	Yml,	Zpseudo,1,
+	Ynone,	Yrf,	Zpseudo,1,
+	Ynone,	Yxr,	Zpseudo,1,
+	Yml,	Ynone,	Zpseudo,1,
+	Yrf,	Ynone,	Zpseudo,1,
+	Yxr,	Ynone,	Zpseudo,1,
+	0
+};
+uchar	yxorb[] =
+{
+	Yi32,	Yal,	Zib_,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	yxorl[] =
+{
+	Yi8,	Yml,	Zibo_m,	2,
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yaddl[] =
+{
+	Yi8,	Yml,	Zibo_m,	2,
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yincb[] =
+{
+	Ynone,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yincw[] =
+{
+	Ynone,	Yml,	Zo_m,	2,
+	0
+};
+uchar	yincl[] =
+{
+	Ynone,	Yml,	Zo_m,	2,
+	0
+};
+uchar	ycmpb[] =
+{
+	Yal,	Yi32,	Z_ib,	1,
+	Ymb,	Yi32,	Zm_ibo,	2,
+	Ymb,	Yrb,	Zm_r,	1,
+	Yrb,	Ymb,	Zr_m,	1,
+	0
+};
+uchar	ycmpl[] =
+{
+	Yml,	Yi8,	Zm_ibo,	2,
+	Yax,	Yi32,	Z_il,	1,
+	Yml,	Yi32,	Zm_ilo,	2,
+	Yml,	Yrl,	Zm_r,	1,
+	Yrl,	Yml,	Zr_m,	1,
+	0
+};
+uchar	yshb[] =
+{
+	Yi1,	Ymb,	Zo_m,	2,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Ycx,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yshl[] =
+{
+	Yi1,	Yml,	Zo_m,	2,
+	Yi32,	Yml,	Zibo_m,	2,
+	Ycl,	Yml,	Zo_m,	2,
+	Ycx,	Yml,	Zo_m,	2,
+	0
+};
+uchar	ytestb[] =
+{
+	Yi32,	Yal,	Zib_,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	ytestl[] =
+{
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	ymovb[] =
+{
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	Yi32,	Yrb,	Zib_rp,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	0
+};
+uchar	ymbs[] =
+{
+	Ymb,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ybtl[] =
+{
+	Yi8,	Yml,	Zibo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	0
+};
+uchar	ymovw[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	Yi0,	Yrl,	Zclr,	1,
+	Yi32,	Yrl,	Zil_rp,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yiauto,	Yrl,	Zaut_r,	2,
+	0
+};
+uchar	ymovl[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	Yi0,	Yrl,	Zclr,	1,
+	Yi32,	Yrl,	Zil_rp,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yml,	Ymr,	Zm_r_xm,	1,	// MMX MOVD
+	Ymr,	Yml,	Zr_m_xm,	1,	// MMX MOVD
+	Yml,	Yxr,	Zm_r_xm,	2,	// XMM MOVD (32 bit)
+	Yxr,	Yml,	Zr_m_xm,	2,	// XMM MOVD (32 bit)
+	Yiauto,	Yrl,	Zaut_r,	2,
+	0
+};
+uchar	yret[] =
+{
+	Ynone,	Ynone,	Zo_iw,	1,
+	Yi32,	Ynone,	Zo_iw,	1,
+	0
+};
+uchar	ymovq[] =
+{
+	Yrl,	Yml,	Zr_m,	1,	// 0x89
+	Yml,	Yrl,	Zm_r,	1,	// 0x8b
+	Yi0,	Yrl,	Zclr,	1,	// 0x31
+	Ys32,	Yrl,	Zilo_m,	2,	// 32 bit signed 0xc7,(0)
+	Yi64,	Yrl,	Ziq_rp,	1,	// 0xb8 -- 32/64 bit immediate
+	Yi32,	Yml,	Zilo_m,	2,	// 0xc7,(0)
+	Ym,	Ymr,	Zm_r_xm_nr,	1,	// MMX MOVQ (shorter encoding)
+	Ymr,	Ym,	Zr_m_xm_nr,	1,	// MMX MOVQ
+	Ymm,	Ymr,	Zm_r_xm,	1,	// MMX MOVD
+	Ymr,	Ymm,	Zr_m_xm,	1,	// MMX MOVD
+	Yxr,	Ymr,	Zm_r_xm_nr,	2,	// MOVDQ2Q
+	Yxr,	Ym,	Zr_m_xm_nr,	2,	// MOVQ xmm store
+	Yml,	Yxr,	Zm_r_xm,	2,	// MOVD xmm load
+	Yxr,	Yml,	Zr_m_xm,	2,	// MOVD xmm store
+	Yiauto,	Yrl,	Zaut_r,	2,	// built-in LEAQ
+	0
+};
+uchar	ym_rl[] =
+{
+	Ym,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yrl_m[] =
+{
+	Yrl,	Ym,	Zr_m,	1,
+	0
+};
+uchar	ymb_rl[] =
+{
+	Ymb,	Yrl,	Zmb_r,	1,
+	0
+};
+uchar	yml_rl[] =
+{
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yrl_ml[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	0
+};
+uchar	yml_mb[] =
+{
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	yrb_mb[] =
+{
+	Yrb,	Ymb,	Zr_m,	1,
+	0
+};
+uchar	yml_ml[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	ydivl[] =
+{
+	Yml,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ydivb[] =
+{
+	Ymb,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	yimul[] =
+{
+	Yml,	Ynone,	Zm_o,	2,
+	Yi8,	Yrl,	Zib_rr,	1,
+	Yi32,	Yrl,	Zil_rr,	1,
+	Yml,	Yrl,	Zm_r,	2,
+	0
+};
+uchar	ybyte[] =
+{
+	Yi64,	Ynone,	Zbyte,	1,
+	0
+};
+uchar	yin[] =
+{
+	Yi32,	Ynone,	Zib_,	1,
+	Ynone,	Ynone,	Zlit,	1,
+	0
+};
+uchar	yint[] =
+{
+	Yi32,	Ynone,	Zib_,	1,
+	0
+};
+uchar	ypushl[] =
+{
+	Yrl,	Ynone,	Zrp_,	1,
+	Ym,	Ynone,	Zm_o,	2,
+	Yi8,	Ynone,	Zib_,	1,
+	Yi32,	Ynone,	Zil_,	1,
+	0
+};
+uchar	ypopl[] =
+{
+	Ynone,	Yrl,	Z_rp,	1,
+	Ynone,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yscond[] =
+{
+	Ynone,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yjcond[] =
+{
+	Ynone,	Ybr,	Zbr,	1,
+	0
+};
+uchar	yloop[] =
+{
+	Ynone,	Ybr,	Zloop,	1,
+	0
+};
+uchar	ycall[] =
+{
+	Ynone,	Yml,	Zo_m64,	2,
+	Ynone,	Ybr,	Zcall,	1,
+	0
+};
+uchar	yjmp[] =
+{
+	Ynone,	Yml,	Zo_m64,	2,
+	Ynone,	Ybr,	Zjmp,	1,
+	0
+};
+
+uchar	yfmvd[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yf0,	Ym,	Zo_m,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfmvdp[] =
+{
+	Yf0,	Ym,	Zo_m,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfmvf[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yf0,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yfmvx[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	0
+};
+uchar	yfmvp[] =
+{
+	Yf0,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yfadd[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfaddp[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfxch[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	0
+};
+uchar	ycompp[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,	/* botch is really f0,f1 */
+	0
+};
+uchar	ystsw[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ynone,	Yax,	Zlit,	1,
+	0
+};
+uchar	ystcw[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ym,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ysvrs[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ym,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ymm[] = 
+{
+	Ymm,	Ymr,	Zm_r_xm,	1,
+	Yxm,	Yxr,	Zm_r_xm,	2,
+	0
+};
+uchar	yxm[] = 
+{
+	Yxm,	Yxr,	Zm_r_xm,	1,
+	0
+};
+uchar	yxcvm1[] = 
+{
+	Yxm,	Yxr,	Zm_r_xm,	2,
+	Yxm,	Ymr,	Zm_r_xm,	2,
+	0
+};
+uchar	yxcvm2[] =
+{
+	Yxm,	Yxr,	Zm_r_xm,	2,
+	Ymm,	Yxr,	Zm_r_xm,	2,
+	0
+};
+uchar	yxmq[] = 
+{
+	Yxm,	Yxr,	Zm_r_xm,	2,
+	0
+};
+uchar	yxr[] = 
+{
+	Yxr,	Yxr,	Zm_r_xm,	1,
+	0
+};
+uchar	yxr_ml[] =
+{
+	Yxr,	Yml,	Zr_m_xm,	1,
+	0
+};
+uchar	ymr[] =
+{
+	Ymr,	Ymr,	Zm_r,	1,
+	0
+};
+uchar	ymr_ml[] =
+{
+	Ymr,	Yml,	Zr_m_xm,	1,
+	0
+};
+uchar	yxcmp[] =
+{
+	Yxm,	Yxr, Zm_r_xm,	1,
+	0
+};
+uchar	yxcmpi[] =
+{
+	Yxm,	Yxr, Zm_r_i_xm,	2,
+	0
+};
+uchar	yxmov[] =
+{
+	Yxm,	Yxr,	Zm_r_xm,	1,
+	Yxr,	Yxm,	Zr_m_xm,	1,
+	0
+};
+uchar	yxcvfl[] = 
+{
+	Yxm,	Yrl,	Zm_r_xm,	1,
+	0
+};
+uchar	yxcvlf[] =
+{
+	Yml,	Yxr,	Zm_r_xm,	1,
+	0
+};
+uchar	yxcvfq[] = 
+{
+	Yxm,	Yrl,	Zm_r_xm,	2,
+	0
+};
+uchar	yxcvqf[] =
+{
+	Yml,	Yxr,	Zm_r_xm,	2,
+	0
+};
+uchar	yps[] = 
+{
+	Ymm,	Ymr,	Zm_r_xm,	1,
+	Yi8,	Ymr,	Zibo_m_xm,	2,
+	Yxm,	Yxr,	Zm_r_xm,	2,
+	Yi8,	Yxr,	Zibo_m_xm,	3,
+	0
+};
+uchar	yxrrl[] =
+{
+	Yxr,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	ymfp[] =
+{
+	Ymm,	Ymr,	Zm_r_3d,	1,
+	0,
+};
+uchar	ymrxr[] =
+{
+	Ymr,	Yxr,	Zm_r,	1,
+	Yxm,	Yxr,	Zm_r_xm,	1,
+	0
+};
+uchar	ymshuf[] =
+{
+	Ymm,	Ymr,	Zibm_r,	1,
+	0
+};
+uchar	yxshuf[] =
+{
+	Yxm,	Yxr,	Zibm_r,	1,
+	0
+};
+uchar	yextrw[] =
+{
+	Yxr,	Yrl,	Zibm_r,	1,
+	0
+};
+uchar	ypsdq[] =
+{
+	Yi8,	Yxr,	Zibo_m,	2,
+	0
+};
+uchar	ymskb[] =
+{
+	Yxr,	Yrl,	Zm_r_xm,	2,
+	Ymr,	Yrl,	Zm_r_xm,	1,
+	0
+};
+
+Optab optab[] =
+/*	as, ytab, andproto, opcode */
+{
+	{ AXXX },
+	{ AAAA,		ynone,	P32, 0x37 },
+	{ AAAD,		ynone,	P32, 0xd5,0x0a },
+	{ AAAM,		ynone,	P32, 0xd4,0x0a },
+	{ AAAS,		ynone,	P32, 0x3f },
+	{ AADCB,	yxorb,	Pb, 0x14,0x80,(02),0x10,0x10 },
+	{ AADCL,	yxorl,	Px, 0x83,(02),0x15,0x81,(02),0x11,0x13 },
+	{ AADCQ,	yxorl,	Pw, 0x83,(02),0x15,0x81,(02),0x11,0x13 },
+	{ AADCW,	yxorl,	Pe, 0x83,(02),0x15,0x81,(02),0x11,0x13 },
+	{ AADDB,	yxorb,	Pb, 0x04,0x80,(00),0x00,0x02 },
+	{ AADDL,	yaddl,	Px, 0x83,(00),0x05,0x81,(00),0x01,0x03 },
+	{ AADDPD,	yxm,	Pq, 0x58 },
+	{ AADDPS,	yxm,	Pm, 0x58 },
+	{ AADDQ,	yaddl,	Pw, 0x83,(00),0x05,0x81,(00),0x01,0x03 },
+	{ AADDSD,	yxm,	Pf2, 0x58 },
+	{ AADDSS,	yxm,	Pf3, 0x58 },
+	{ AADDW,	yaddl,	Pe, 0x83,(00),0x05,0x81,(00),0x01,0x03 },
+	{ AADJSP },
+	{ AANDB,	yxorb,	Pb, 0x24,0x80,(04),0x20,0x22 },
+	{ AANDL,	yxorl,	Px, 0x83,(04),0x25,0x81,(04),0x21,0x23 },
+	{ AANDNPD,	yxm,	Pq, 0x55 },
+	{ AANDNPS,	yxm,	Pm, 0x55 },
+	{ AANDPD,	yxm,	Pq, 0x54 },
+	{ AANDPS,	yxm,	Pq, 0x54 },
+	{ AANDQ,	yxorl,	Pw, 0x83,(04),0x25,0x81,(04),0x21,0x23 },
+	{ AANDW,	yxorl,	Pe, 0x83,(04),0x25,0x81,(04),0x21,0x23 },
+	{ AARPL,	yrl_ml,	P32, 0x63 },
+	{ ABOUNDL,	yrl_m,	P32, 0x62 },
+	{ ABOUNDW,	yrl_m,	Pe, 0x62 },
+	{ ABSFL,	yml_rl,	Pm, 0xbc },
+	{ ABSFQ,	yml_rl,	Pw, 0x0f,0xbc },
+	{ ABSFW,	yml_rl,	Pq, 0xbc },
+	{ ABSRL,	yml_rl,	Pm, 0xbd },
+	{ ABSRQ,	yml_rl,	Pw, 0x0f,0xbd },
+	{ ABSRW,	yml_rl,	Pq, 0xbd },
+	{ ABTCL,	ybtl,	Pm, 0xba,(07),0xbb },
+	{ ABTCQ,	ybtl,	Pw, 0x0f,0xba,(07),0x0f,0xbb },
+	{ ABTCW,	ybtl,	Pq, 0xba,(07),0xbb },
+	{ ABTL,		ybtl,	Pm, 0xba,(04),0xa3 },
+	{ ABTQ,		ybtl,	Pw, 0x0f,0xba,(04),0x0f,0xa3},
+	{ ABTRL,	ybtl,	Pm, 0xba,(06),0xb3 },
+	{ ABTRQ,	ybtl,	Pw, 0x0f,0xba,(06),0x0f,0xb3 },
+	{ ABTRW,	ybtl,	Pq, 0xba,(06),0xb3 },
+	{ ABTSL,	ybtl,	Pm, 0xba,(05),0xab  },
+	{ ABTSQ,	ybtl,	Pw, 0x0f,0xba,(05),0x0f,0xab },
+	{ ABTSW,	ybtl,	Pq, 0xba,(05),0xab  },
+	{ ABTW,		ybtl,	Pq, 0xba,(04),0xa3 },
+	{ ABYTE,	ybyte,	Px, 1 },
+	{ ACALL,	ycall,	Px, 0xff,(02),0xe8 },
+	{ ACDQ,		ynone,	Px, 0x99 },
+	{ ACLC,		ynone,	Px, 0xf8 },
+	{ ACLD,		ynone,	Px, 0xfc },
+	{ ACLI,		ynone,	Px, 0xfa },
+	{ ACLTS,	ynone,	Pm, 0x06 },
+	{ ACMC,		ynone,	Px, 0xf5 },
+	{ ACMOVLCC,	yml_rl,	Pm, 0x43 },
+	{ ACMOVLCS,	yml_rl,	Pm, 0x42 },
+	{ ACMOVLEQ,	yml_rl,	Pm, 0x44 },
+	{ ACMOVLGE,	yml_rl,	Pm, 0x4d },
+	{ ACMOVLGT,	yml_rl,	Pm, 0x4f },
+	{ ACMOVLHI,	yml_rl,	Pm, 0x47 },
+	{ ACMOVLLE,	yml_rl,	Pm, 0x4e },
+	{ ACMOVLLS,	yml_rl,	Pm, 0x46 },
+	{ ACMOVLLT,	yml_rl,	Pm, 0x4c },
+	{ ACMOVLMI,	yml_rl,	Pm, 0x48 },
+	{ ACMOVLNE,	yml_rl,	Pm, 0x45 },
+	{ ACMOVLOC,	yml_rl,	Pm, 0x41 },
+	{ ACMOVLOS,	yml_rl,	Pm, 0x40 },
+	{ ACMOVLPC,	yml_rl,	Pm, 0x4b },
+	{ ACMOVLPL,	yml_rl,	Pm, 0x49 },
+	{ ACMOVLPS,	yml_rl,	Pm, 0x4a },
+	{ ACMOVQCC,	yml_rl,	Pw, 0x0f,0x43 },
+	{ ACMOVQCS,	yml_rl,	Pw, 0x0f,0x42 },
+	{ ACMOVQEQ,	yml_rl,	Pw, 0x0f,0x44 },
+	{ ACMOVQGE,	yml_rl,	Pw, 0x0f,0x4d },
+	{ ACMOVQGT,	yml_rl,	Pw, 0x0f,0x4f },
+	{ ACMOVQHI,	yml_rl,	Pw, 0x0f,0x47 },
+	{ ACMOVQLE,	yml_rl,	Pw, 0x0f,0x4e },
+	{ ACMOVQLS,	yml_rl,	Pw, 0x0f,0x46 },
+	{ ACMOVQLT,	yml_rl,	Pw, 0x0f,0x4c },
+	{ ACMOVQMI,	yml_rl,	Pw, 0x0f,0x48 },
+	{ ACMOVQNE,	yml_rl,	Pw, 0x0f,0x45 },
+	{ ACMOVQOC,	yml_rl,	Pw, 0x0f,0x41 },
+	{ ACMOVQOS,	yml_rl,	Pw, 0x0f,0x40 },
+	{ ACMOVQPC,	yml_rl,	Pw, 0x0f,0x4b },
+	{ ACMOVQPL,	yml_rl,	Pw, 0x0f,0x49 },
+	{ ACMOVQPS,	yml_rl,	Pw, 0x0f,0x4a },
+	{ ACMOVWCC,	yml_rl,	Pq, 0x43 },
+	{ ACMOVWCS,	yml_rl,	Pq, 0x42 },
+	{ ACMOVWEQ,	yml_rl,	Pq, 0x44 },
+	{ ACMOVWGE,	yml_rl,	Pq, 0x4d },
+	{ ACMOVWGT,	yml_rl,	Pq, 0x4f },
+	{ ACMOVWHI,	yml_rl,	Pq, 0x47 },
+	{ ACMOVWLE,	yml_rl,	Pq, 0x4e },
+	{ ACMOVWLS,	yml_rl,	Pq, 0x46 },
+	{ ACMOVWLT,	yml_rl,	Pq, 0x4c },
+	{ ACMOVWMI,	yml_rl,	Pq, 0x48 },
+	{ ACMOVWNE,	yml_rl,	Pq, 0x45 },
+	{ ACMOVWOC,	yml_rl,	Pq, 0x41 },
+	{ ACMOVWOS,	yml_rl,	Pq, 0x40 },
+	{ ACMOVWPC,	yml_rl,	Pq, 0x4b },
+	{ ACMOVWPL,	yml_rl,	Pq, 0x49 },
+	{ ACMOVWPS,	yml_rl,	Pq, 0x4a },
+	{ ACMPB,	ycmpb,	Pb, 0x3c,0x80,(07),0x38,0x3a },
+	{ ACMPL,	ycmpl,	Px, 0x83,(07),0x3d,0x81,(07),0x39,0x3b },
+	{ ACMPPD,	yxcmpi,	Px, Pe,0xc2 },
+	{ ACMPPS,	yxcmpi,	Pm, 0xc2,0 },
+	{ ACMPQ,	ycmpl,	Pw, 0x83,(07),0x3d,0x81,(07),0x39,0x3b },
+	{ ACMPSB,	ynone,	Pb, 0xa6 },
+	{ ACMPSD,	yxcmpi,	Px, Pf2,0xc2 },
+	{ ACMPSL,	ynone,	Px, 0xa7 },
+	{ ACMPSQ,	ynone,	Pw, 0xa7 },
+	{ ACMPSS,	yxcmpi,	Px, Pf3,0xc2 },
+	{ ACMPSW,	ynone,	Pe, 0xa7 },
+	{ ACMPW,	ycmpl,	Pe, 0x83,(07),0x3d,0x81,(07),0x39,0x3b },
+	{ ACOMISD,	yxcmp,	Pe, 0x2f },
+	{ ACOMISS,	yxcmp,	Pm, 0x2f },
+	{ ACPUID,	ynone,	Pm, 0xa2 },
+	{ ACVTPL2PD,	yxcvm2,	Px, Pf3,0xe6,Pe,0x2a },
+	{ ACVTPL2PS,	yxcvm2,	Pm, 0x5b,0,0x2a,0, },
+	{ ACVTPD2PL,	yxcvm1,	Px, Pf2,0xe6,Pe,0x2d },
+	{ ACVTPD2PS,	yxm,	Pe, 0x5a },
+	{ ACVTPS2PL,	yxcvm1, Px, Pe,0x5b,Pm,0x2d },
+	{ ACVTPS2PD,	yxm,	Pm, 0x5a },
+	{ API2FW,	ymfp,	Px, 0x0c },
+	{ ACVTSD2SL,	yxcvfl, Pf2, 0x2d },
+	{ ACVTSD2SQ,	yxcvfq, Pw, Pf2,0x2d },
+	{ ACVTSD2SS,	yxm,	Pf2, 0x5a },
+	{ ACVTSL2SD,	yxcvlf, Pf2, 0x2a },
+	{ ACVTSQ2SD,	yxcvqf, Pw, Pf2,0x2a },
+	{ ACVTSL2SS,	yxcvlf, Pf3, 0x2a },
+	{ ACVTSQ2SS,	yxcvqf, Pw, Pf3,0x2a },
+	{ ACVTSS2SD,	yxm,	Pf3, 0x5a },
+	{ ACVTSS2SL,	yxcvfl, Pf3, 0x2d },
+	{ ACVTSS2SQ,	yxcvfq, Pw, Pf3,0x2d },
+	{ ACVTTPD2PL,	yxcvm1,	Px, Pe,0xe6,Pe,0x2c },
+	{ ACVTTPS2PL,	yxcvm1,	Px, Pf3,0x5b,Pm,0x2c },
+	{ ACVTTSD2SL,	yxcvfl, Pf2, 0x2c },
+	{ ACVTTSD2SQ,	yxcvfq, Pw, Pf2,0x2c },
+	{ ACVTTSS2SL,	yxcvfl,	Pf3, 0x2c },
+	{ ACVTTSS2SQ,	yxcvfq, Pw, Pf3,0x2c },
+	{ ACWD,		ynone,	Pe, 0x99 },
+	{ ACQO,		ynone,	Pw, 0x99 },
+	{ ADAA,		ynone,	P32, 0x27 },
+	{ ADAS,		ynone,	P32, 0x2f },
+	{ ADATA },
+	{ ADECB,	yincb,	Pb, 0xfe,(01) },
+	{ ADECL,	yincl,	Px, 0xff,(01) },
+	{ ADECQ,	yincl,	Pw, 0xff,(01) },
+	{ ADECW,	yincw,	Pe, 0xff,(01) },
+	{ ADIVB,	ydivb,	Pb, 0xf6,(06) },
+	{ ADIVL,	ydivl,	Px, 0xf7,(06) },
+	{ ADIVPD,	yxm,	Pe, 0x5e },
+	{ ADIVPS,	yxm,	Pm, 0x5e },
+	{ ADIVQ,	ydivl,	Pw, 0xf7,(06) },
+	{ ADIVSD,	yxm,	Pf2, 0x5e },
+	{ ADIVSS,	yxm,	Pf3, 0x5e },
+	{ ADIVW,	ydivl,	Pe, 0xf7,(06) },
+	{ AEMMS,	ynone,	Pm, 0x77 },
+	{ AENTER },				/* botch */
+	{ AFXRSTOR,	ysvrs,	Pm, 0xae,(01),0xae,(01) },
+	{ AFXSAVE,	ysvrs,	Pm, 0xae,(00),0xae,(00) },
+	{ AFXRSTOR64,	ysvrs,	Pw, 0x0f,0xae,(01),0x0f,0xae,(01) },
+	{ AFXSAVE64,	ysvrs,	Pw, 0x0f,0xae,(00),0x0f,0xae,(00) },
+	{ AGLOBL },
+	{ AGOK },
+	{ AHISTORY },
+	{ AHLT,		ynone,	Px, 0xf4 },
+	{ AIDIVB,	ydivb,	Pb, 0xf6,(07) },
+	{ AIDIVL,	ydivl,	Px, 0xf7,(07) },
+	{ AIDIVQ,	ydivl,	Pw, 0xf7,(07) },
+	{ AIDIVW,	ydivl,	Pe, 0xf7,(07) },
+	{ AIMULB,	ydivb,	Pb, 0xf6,(05) },
+	{ AIMULL,	yimul,	Px, 0xf7,(05),0x6b,0x69,Pm,0xaf },
+	{ AIMULQ,	yimul,	Pw, 0xf7,(05),0x6b,0x69,Pm,0xaf },
+	{ AIMULW,	yimul,	Pe, 0xf7,(05),0x6b,0x69,Pm,0xaf },
+	{ AINB,		yin,	Pb, 0xe4,0xec },
+	{ AINCB,	yincb,	Pb, 0xfe,(00) },
+	{ AINCL,	yincl,	Px, 0xff,(00) },
+	{ AINCQ,	yincl,	Pw, 0xff,(00) },
+	{ AINCW,	yincw,	Pe, 0xff,(00) },
+	{ AINL,		yin,	Px, 0xe5,0xed },
+	{ AINSB,	ynone,	Pb, 0x6c },
+	{ AINSL,	ynone,	Px, 0x6d },
+	{ AINSW,	ynone,	Pe, 0x6d },
+	{ AINT,		yint,	Px, 0xcd },
+	{ AINTO,	ynone,	P32, 0xce },
+	{ AINW,		yin,	Pe, 0xe5,0xed },
+	{ AIRETL,	ynone,	Px, 0xcf },
+	{ AIRETQ,	ynone,	Pw, 0xcf },
+	{ AIRETW,	ynone,	Pe, 0xcf },
+	{ AJCC,		yjcond,	Px, 0x73,0x83,(00) },
+	{ AJCS,		yjcond,	Px, 0x72,0x82 },
+	{ AJCXZ,	yloop,	Px, 0xe3 },
+	{ AJEQ,		yjcond,	Px, 0x74,0x84 },
+	{ AJGE,		yjcond,	Px, 0x7d,0x8d },
+	{ AJGT,		yjcond,	Px, 0x7f,0x8f },
+	{ AJHI,		yjcond,	Px, 0x77,0x87 },
+	{ AJLE,		yjcond,	Px, 0x7e,0x8e },
+	{ AJLS,		yjcond,	Px, 0x76,0x86 },
+	{ AJLT,		yjcond,	Px, 0x7c,0x8c },
+	{ AJMI,		yjcond,	Px, 0x78,0x88 },
+	{ AJMP,		yjmp,	Px, 0xff,(04),0xeb,0xe9 },
+	{ AJNE,		yjcond,	Px, 0x75,0x85 },
+	{ AJOC,		yjcond,	Px, 0x71,0x81,(00) },
+	{ AJOS,		yjcond,	Px, 0x70,0x80,(00) },
+	{ AJPC,		yjcond,	Px, 0x7b,0x8b },
+	{ AJPL,		yjcond,	Px, 0x79,0x89 },
+	{ AJPS,		yjcond,	Px, 0x7a,0x8a },
+	{ ALAHF,	ynone,	Px, 0x9f },
+	{ ALARL,	yml_rl,	Pm, 0x02 },
+	{ ALARW,	yml_rl,	Pq, 0x02 },
+	{ ALDMXCSR,	ysvrs,	Pm, 0xae,(02),0xae,(02) },
+	{ ALEAL,	ym_rl,	Px, 0x8d },
+	{ ALEAQ,	ym_rl,	Pw, 0x8d },
+	{ ALEAVEL,	ynone,	P32, 0xc9 },
+	{ ALEAVEQ,	ynone,	Py, 0xc9 },
+	{ ALEAVEW,	ynone,	Pe, 0xc9 },
+	{ ALEAW,	ym_rl,	Pe, 0x8d },
+	{ ALOCK,	ynone,	Px, 0xf0 },
+	{ ALODSB,	ynone,	Pb, 0xac },
+	{ ALODSL,	ynone,	Px, 0xad },
+	{ ALODSQ,	ynone,	Pw, 0xad },
+	{ ALODSW,	ynone,	Pe, 0xad },
+	{ ALONG,	ybyte,	Px, 4 },
+	{ ALOOP,	yloop,	Px, 0xe2 },
+	{ ALOOPEQ,	yloop,	Px, 0xe1 },
+	{ ALOOPNE,	yloop,	Px, 0xe0 },
+	{ ALSLL,	yml_rl,	Pm, 0x03  },
+	{ ALSLW,	yml_rl,	Pq, 0x03  },
+	{ AMASKMOVOU,	yxr,	Pe, 0xf7 },
+	{ AMASKMOVQ,	ymr,	Pm, 0xf7 },
+	{ AMAXPD,	yxm,	Pe, 0x5f },
+	{ AMAXPS,	yxm,	Pm, 0x5f },
+	{ AMAXSD,	yxm,	Pf2, 0x5f },
+	{ AMAXSS,	yxm,	Pf3, 0x5f },
+	{ AMINPD,	yxm,	Pe, 0x5d },
+	{ AMINPS,	yxm,	Pm, 0x5d },
+	{ AMINSD,	yxm,	Pf2, 0x5d },
+	{ AMINSS,	yxm,	Pf3, 0x5d },
+	{ AMOVAPD,	yxmov,	Pe, 0x28,0x29 },
+	{ AMOVAPS,	yxmov,	Pm, 0x28,0x29 },
+	{ AMOVB,	ymovb,	Pb, 0x88,0x8a,0xb0,0xc6,(00) },
+	{ AMOVBLSX,	ymb_rl,	Pm, 0xbe },
+	{ AMOVBLZX,	ymb_rl,	Pm, 0xb6 },
+	{ AMOVBQSX,	ymb_rl,	Pw, 0x0f,0xbe },
+	{ AMOVBQZX,	ymb_rl,	Pw, 0x0f,0xb6 },
+	{ AMOVBWSX,	ymb_rl,	Pq, 0xbe },
+	{ AMOVBWZX,	ymb_rl,	Pq, 0xb6 },
+	{ AMOVO,	yxmov,	Pe, 0x6f,0x7f },
+	{ AMOVOU,	yxmov,	Pf3, 0x6f,0x7f },
+	{ AMOVHLPS,	yxr,	Pm, 0x12 },
+	{ AMOVHPD,	yxmov,	Pe, 0x16,0x17 },
+	{ AMOVHPS,	yxmov,	Pm, 0x16,0x17 },
+	{ AMOVL,	ymovl,	Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e },
+	{ AMOVLHPS,	yxr,	Pm, 0x16 },
+	{ AMOVLPD,	yxmov,	Pe, 0x12,0x13 },
+	{ AMOVLPS,	yxmov,	Pm, 0x12,0x13 },
+	{ AMOVLQSX,	yml_rl,	Pw, 0x63 },
+	{ AMOVLQZX,	yml_rl,	Px, 0x8b },
+	{ AMOVMSKPD,	yxrrl,	Pq, 0x50 },
+	{ AMOVMSKPS,	yxrrl,	Pm, 0x50 },
+	{ AMOVNTO,	yxr_ml,	Pe, 0xe7 },
+	{ AMOVNTPD,	yxr_ml,	Pe, 0x2b },
+	{ AMOVNTPS,	yxr_ml,	Pm, 0x2b },
+	{ AMOVNTQ,	ymr_ml,	Pm, 0xe7 },
+	{ AMOVQ,	ymovq,	Pw, 0x89,0x8b,0x31,0xc7,(00),0xb8,0xc7,(00),0x6f,0x7f,0x6e,0x7e,Pf2,0xd6,Pe,0xd6,Pe,0x6e,Pe,0x7e },
+	{ AMOVQOZX,	ymrxr,	Pf3, 0xd6,0x7e },
+	{ AMOVSB,	ynone,	Pb, 0xa4 },
+	{ AMOVSD,	yxmov,	Pf2, 0x10,0x11 },
+	{ AMOVSL,	ynone,	Px, 0xa5 },
+	{ AMOVSQ,	ynone,	Pw, 0xa5 },
+	{ AMOVSS,	yxmov,	Pf3, 0x10,0x11 },
+	{ AMOVSW,	ynone,	Pe, 0xa5 },
+	{ AMOVUPD,	yxmov,	Pe, 0x10,0x11 },
+	{ AMOVUPS,	yxmov,	Pm, 0x10,0x11 },
+	{ AMOVW,	ymovw,	Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00) },
+	{ AMOVWLSX,	yml_rl,	Pm, 0xbf },
+	{ AMOVWLZX,	yml_rl,	Pm, 0xb7 },
+	{ AMOVWQSX,	yml_rl,	Pw, 0x0f,0xbf },
+	{ AMOVWQZX,	yml_rl,	Pw, 0x0f,0xb7 },
+	{ AMULB,	ydivb,	Pb, 0xf6,(04) },
+	{ AMULL,	ydivl,	Px, 0xf7,(04) },
+	{ AMULPD,	yxm,	Pe, 0x59 },
+	{ AMULPS,	yxm,	Ym, 0x59 },
+	{ AMULQ,	ydivl,	Pw, 0xf7,(04) },
+	{ AMULSD,	yxm,	Pf2, 0x59 },
+	{ AMULSS,	yxm,	Pf3, 0x59 },
+	{ AMULW,	ydivl,	Pe, 0xf7,(04) },
+	{ ANAME },
+	{ ANEGB,	yscond,	Pb, 0xf6,(03) },
+	{ ANEGL,	yscond,	Px, 0xf7,(03) },
+	{ ANEGQ,	yscond,	Pw, 0xf7,(03) },
+	{ ANEGW,	yscond,	Pe, 0xf7,(03) },
+	{ ANOP,		ynop,	Px, 0,0 },
+	{ ANOTB,	yscond,	Pb, 0xf6,(02) },
+	{ ANOTL,	yscond,	Px, 0xf7,(02) },
+	{ ANOTQ,	yscond,	Pw, 0xf7,(02) },
+	{ ANOTW,	yscond,	Pe, 0xf7,(02) },
+	{ AORB,		yxorb,	Pb, 0x0c,0x80,(01),0x08,0x0a },
+	{ AORL,		yxorl,	Px, 0x83,(01),0x0d,0x81,(01),0x09,0x0b },
+	{ AORPD,	yxm,	Pq, 0x56 },
+	{ AORPS,	yxm,	Pm, 0x56 },
+	{ AORQ,		yxorl,	Pw, 0x83,(01),0x0d,0x81,(01),0x09,0x0b },
+	{ AORW,		yxorl,	Pe, 0x83,(01),0x0d,0x81,(01),0x09,0x0b },
+	{ AOUTB,	yin,	Pb, 0xe6,0xee },
+	{ AOUTL,	yin,	Px, 0xe7,0xef },
+	{ AOUTSB,	ynone,	Pb, 0x6e },
+	{ AOUTSL,	ynone,	Px, 0x6f },
+	{ AOUTSW,	ynone,	Pe, 0x6f },
+	{ AOUTW,	yin,	Pe, 0xe7,0xef },
+	{ APACKSSLW,	ymm,	Py, 0x6b,Pe,0x6b },
+	{ APACKSSWB,	ymm,	Py, 0x63,Pe,0x63 },
+	{ APACKUSWB,	ymm,	Py, 0x67,Pe,0x67 },
+	{ APADDB,	ymm,	Py, 0xfc,Pe,0xfc },
+	{ APADDL,	ymm,	Py, 0xfe,Pe,0xfe },
+	{ APADDQ,	yxm,	Pe, 0xd4 },
+	{ APADDSB,	ymm,	Py, 0xec,Pe,0xec },
+	{ APADDSW,	ymm,	Py, 0xed,Pe,0xed },
+	{ APADDUSB,	ymm,	Py, 0xdc,Pe,0xdc },
+	{ APADDUSW,	ymm,	Py, 0xdd,Pe,0xdd },
+	{ APADDW,	ymm,	Py, 0xfd,Pe,0xfd },
+	{ APAND,	ymm,	Py, 0xdb,Pe,0xdb },
+	{ APANDN,	ymm,	Py, 0xdf,Pe,0xdf },
+	{ APAVGB,	ymm,	Py, 0xe0,Pe,0xe0 },
+	{ APAVGW,	ymm,	Py, 0xe3,Pe,0xe3 },
+	{ APCMPEQB,	ymm,	Py, 0x74,Pe,0x74 },
+	{ APCMPEQL,	ymm,	Py, 0x76,Pe,0x76 },
+	{ APCMPEQW,	ymm,	Py, 0x75,Pe,0x75 },
+	{ APCMPGTB,	ymm,	Py, 0x64,Pe,0x64 },
+	{ APCMPGTL,	ymm,	Py, 0x66,Pe,0x66 },
+	{ APCMPGTW,	ymm,	Py, 0x65,Pe,0x65 },
+	{ APEXTRW,	yextrw,	Pq, 0xc5 },
+	{ APF2IL,	ymfp,	Px, 0x1d },
+	{ APF2IW,	ymfp,	Px, 0x1c },
+	{ API2FL,	ymfp,	Px, 0x0d },
+	{ APFACC,	ymfp,	Px, 0xae },
+	{ APFADD,	ymfp,	Px, 0x9e },
+	{ APFCMPEQ,	ymfp,	Px, 0xb0 },
+	{ APFCMPGE,	ymfp,	Px, 0x90 },
+	{ APFCMPGT,	ymfp,	Px, 0xa0 },
+	{ APFMAX,	ymfp,	Px, 0xa4 },
+	{ APFMIN,	ymfp,	Px, 0x94 },
+	{ APFMUL,	ymfp,	Px, 0xb4 },
+	{ APFNACC,	ymfp,	Px, 0x8a },
+	{ APFPNACC,	ymfp,	Px, 0x8e },
+	{ APFRCP,	ymfp,	Px, 0x96 },
+	{ APFRCPIT1,	ymfp,	Px, 0xa6 },
+	{ APFRCPI2T,	ymfp,	Px, 0xb6 },
+	{ APFRSQIT1,	ymfp,	Px, 0xa7 },
+	{ APFRSQRT,	ymfp,	Px, 0x97 },
+	{ APFSUB,	ymfp,	Px, 0x9a },
+	{ APFSUBR,	ymfp,	Px, 0xaa },
+	{ APINSRW,	yextrw,	Pq, 0xc4 },
+	{ APMADDWL,	ymm,	Py, 0xf5,Pe,0xf5 },
+	{ APMAXSW,	yxm,	Pe, 0xee },
+	{ APMAXUB,	yxm,	Pe, 0xde },
+	{ APMINSW,	yxm,	Pe, 0xea },
+	{ APMINUB,	yxm,	Pe, 0xda },
+	{ APMOVMSKB,	ymskb,	Px, Pe,0xd7,0xd7 },
+	{ APMULHRW,	ymfp,	Px, 0xb7 },
+	{ APMULHUW,	ymm,	Py, 0xe4,Pe,0xe4 },
+	{ APMULHW,	ymm,	Py, 0xe5,Pe,0xe5 },
+	{ APMULLW,	ymm,	Py, 0xd5,Pe,0xd5 },
+	{ APMULULQ,	ymm,	Py, 0xf4,Pe,0xf4 },
+	{ APOPAL,	ynone,	P32, 0x61 },
+	{ APOPAW,	ynone,	Pe, 0x61 },
+	{ APOPFL,	ynone,	P32, 0x9d },
+	{ APOPFQ,	ynone,	Py, 0x9d },
+	{ APOPFW,	ynone,	Pe, 0x9d },
+	{ APOPL,	ypopl,	P32, 0x58,0x8f,(00) },
+	{ APOPQ,	ypopl,	Py, 0x58,0x8f,(00) },
+	{ APOPW,	ypopl,	Pe, 0x58,0x8f,(00) },
+	{ APOR,		ymm,	Py, 0xeb,Pe,0xeb },
+	{ APSADBW,	yxm,	Pq, 0xf6 },
+	{ APSHUFHW,	yxshuf,	Pf3, 0x70 },
+	{ APSHUFL,	yxshuf,	Pq, 0x70 },
+	{ APSHUFLW,	yxshuf,	Pf2, 0x70 },
+	{ APSHUFW,	ymshuf,	Pm, 0x70 },
+	{ APSLLO,	ypsdq,	Pq, 0x73,(07) },
+	{ APSLLL,	yps,	Py, 0xf2, 0x72,(06), Pe,0xf2, Pe,0x72,(06) },
+	{ APSLLQ,	yps,	Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x7e,(06) },
+	{ APSLLW,	yps,	Py, 0xf1, 0x71,(06), Pe,0xf1, Pe,0x71,(06) },
+	{ APSRAL,	yps,	Py, 0xe2, 0x72,(04), Pe,0xe2, Pe,0x72,(04) },
+	{ APSRAW,	yps,	Py, 0xe1, 0x71,(04), Pe,0xe1, Pe,0x71,(04) },
+	{ APSRLO,	ypsdq,	Pq, 0x73,(03) },
+	{ APSRLL,	yps,	Py, 0xd2, 0x72,(02), Pe,0xd2, Pe,0x72,(02) },
+	{ APSRLQ,	yps,	Py, 0xd3, 0x73,(02), Pe,0xd3, Pe,0x73,(02) },
+	{ APSRLW,	yps,	Py, 0xd1, 0x71,(02), Pe,0xe1, Pe,0x71,(02) },
+	{ APSUBB,	yxm,	Pe, 0xf8 },
+	{ APSUBL,	yxm,	Pe, 0xfa },
+	{ APSUBQ,	yxm,	Pe, 0xfb },
+	{ APSUBSB,	yxm,	Pe, 0xe8 },
+	{ APSUBSW,	yxm,	Pe, 0xe9 },
+	{ APSUBUSB,	yxm,	Pe, 0xd8 },
+	{ APSUBUSW,	yxm,	Pe, 0xd9 },
+	{ APSUBW,	yxm,	Pe, 0xf9 },
+	{ APSWAPL,	ymfp,	Px, 0xbb },
+	{ APUNPCKHBW,	ymm,	Py, 0x68,Pe,0x68 },
+	{ APUNPCKHLQ,	ymm,	Py, 0x6a,Pe,0x6a },
+	{ APUNPCKHQDQ,	yxm,	Pe, 0x6d },
+	{ APUNPCKHWL,	ymm,	Py, 0x69,Pe,0x69 },
+	{ APUNPCKLBW,	ymm,	Py, 0x60,Pe,0x60 },
+	{ APUNPCKLLQ,	ymm,	Py, 0x62,Pe,0x62 },
+	{ APUNPCKLQDQ,	yxm,	Pe, 0x6c },
+	{ APUNPCKLWL,	ymm,	Py, 0x61,Pe,0x61 },
+	{ APUSHAL,	ynone,	P32, 0x60 },
+	{ APUSHAW,	ynone,	Pe, 0x60 },
+	{ APUSHFL,	ynone,	P32, 0x9c },
+	{ APUSHFQ,	ynone,	Py, 0x9c },
+	{ APUSHFW,	ynone,	Pe, 0x9c },
+	{ APUSHL,	ypushl,	P32, 0x50,0xff,(06),0x6a,0x68 },
+	{ APUSHQ,	ypushl,	Py, 0x50,0xff,(06),0x6a,0x68 },
+	{ APUSHW,	ypushl,	Pe, 0x50,0xff,(06),0x6a,0x68 },
+	{ APXOR,	ymm,	Py, 0xef,Pe,0xef },
+	{ AQUAD,	ybyte,	Px, 8 },
+	{ ARCLB,	yshb,	Pb, 0xd0,(02),0xc0,(02),0xd2,(02) },
+	{ ARCLL,	yshl,	Px, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) },
+	{ ARCLQ,	yshl,	Pw, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) },
+	{ ARCLW,	yshl,	Pe, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) },
+	{ ARCPPS,	yxm,	Pm, 0x53 },
+	{ ARCPSS,	yxm,	Pf3, 0x53 },
+	{ ARCRB,	yshb,	Pb, 0xd0,(03),0xc0,(03),0xd2,(03) },
+	{ ARCRL,	yshl,	Px, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) },
+	{ ARCRQ,	yshl,	Pw, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) },
+	{ ARCRW,	yshl,	Pe, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) },
+	{ AREP,		ynone,	Px, 0xf3 },
+	{ AREPN,	ynone,	Px, 0xf2 },
+	{ ARET,		ynone,	Px, 0xc3 },
+	{ ARETFW,	yret,	Pe, 0xcb,0xca },
+	{ ARETFL,	yret,	Px, 0xcb,0xca },
+	{ ARETFQ,	yret,	Pw, 0xcb,0xca },
+	{ AROLB,	yshb,	Pb, 0xd0,(00),0xc0,(00),0xd2,(00) },
+	{ AROLL,	yshl,	Px, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) },
+	{ AROLQ,	yshl,	Pw, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) },
+	{ AROLW,	yshl,	Pe, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) },
+	{ ARORB,	yshb,	Pb, 0xd0,(01),0xc0,(01),0xd2,(01) },
+	{ ARORL,	yshl,	Px, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) },
+	{ ARORQ,	yshl,	Pw, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) },
+	{ ARORW,	yshl,	Pe, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) },
+	{ ARSQRTPS,	yxm,	Pm, 0x52 },
+	{ ARSQRTSS,	yxm,	Pf3, 0x52 },
+	{ ASAHF,	ynone,	Px, 0x86,0xe0,0x50,0x9d },	/* XCHGB AH,AL; PUSH AX; POPFL */
+	{ ASALB,	yshb,	Pb, 0xd0,(04),0xc0,(04),0xd2,(04) },
+	{ ASALL,	yshl,	Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASALQ,	yshl,	Pw, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASALW,	yshl,	Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASARB,	yshb,	Pb, 0xd0,(07),0xc0,(07),0xd2,(07) },
+	{ ASARL,	yshl,	Px, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) },
+	{ ASARQ,	yshl,	Pw, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) },
+	{ ASARW,	yshl,	Pe, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) },
+	{ ASBBB,	yxorb,	Pb, 0x1c,0x80,(03),0x18,0x1a },
+	{ ASBBL,	yxorl,	Px, 0x83,(03),0x1d,0x81,(03),0x19,0x1b },
+	{ ASBBQ,	yxorl,	Pw, 0x83,(03),0x1d,0x81,(03),0x19,0x1b },
+	{ ASBBW,	yxorl,	Pe, 0x83,(03),0x1d,0x81,(03),0x19,0x1b },
+	{ ASCASB,	ynone,	Pb, 0xae },
+	{ ASCASL,	ynone,	Px, 0xaf },
+	{ ASCASQ,	ynone,	Pw, 0xaf },
+	{ ASCASW,	ynone,	Pe, 0xaf },
+	{ ASETCC,	yscond,	Pm, 0x93,(00) },
+	{ ASETCS,	yscond,	Pm, 0x92,(00) },
+	{ ASETEQ,	yscond,	Pm, 0x94,(00) },
+	{ ASETGE,	yscond,	Pm, 0x9d,(00) },
+	{ ASETGT,	yscond,	Pm, 0x9f,(00) },
+	{ ASETHI,	yscond,	Pm, 0x97,(00) },
+	{ ASETLE,	yscond,	Pm, 0x9e,(00) },
+	{ ASETLS,	yscond,	Pm, 0x96,(00) },
+	{ ASETLT,	yscond,	Pm, 0x9c,(00) },
+	{ ASETMI,	yscond,	Pm, 0x98,(00) },
+	{ ASETNE,	yscond,	Pm, 0x95,(00) },
+	{ ASETOC,	yscond,	Pm, 0x91,(00) },
+	{ ASETOS,	yscond,	Pm, 0x90,(00) },
+	{ ASETPC,	yscond,	Pm, 0x96,(00) },
+	{ ASETPL,	yscond,	Pm, 0x99,(00) },
+	{ ASETPS,	yscond,	Pm, 0x9a,(00) },
+	{ ASHLB,	yshb,	Pb, 0xd0,(04),0xc0,(04),0xd2,(04) },
+	{ ASHLL,	yshl,	Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASHLQ,	yshl,	Pw, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASHLW,	yshl,	Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASHRB,	yshb,	Pb, 0xd0,(05),0xc0,(05),0xd2,(05) },
+	{ ASHRL,	yshl,	Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) },
+	{ ASHRQ,	yshl,	Pw, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) },
+	{ ASHRW,	yshl,	Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) },
+	{ ASHUFPD,	yxshuf,	Pq, 0xc6 },
+	{ ASHUFPS,	yxshuf,	Pm, 0xc6 },
+	{ ASQRTPD,	yxm,	Pe, 0x51 },
+	{ ASQRTPS,	yxm,	Pm, 0x51 },
+	{ ASQRTSD,	yxm,	Pf2, 0x51 },
+	{ ASQRTSS,	yxm,	Pf3, 0x51 },
+	{ ASTC,		ynone,	Px, 0xf9 },
+	{ ASTD,		ynone,	Px, 0xfd },
+	{ ASTI,		ynone,	Px, 0xfb },
+	{ ASTMXCSR,	ysvrs,	Pm, 0xae,(03),0xae,(03) },
+	{ ASTOSB,	ynone,	Pb, 0xaa },
+	{ ASTOSL,	ynone,	Px, 0xab },
+	{ ASTOSQ,	ynone,	Pw, 0xab },
+	{ ASTOSW,	ynone,	Pe, 0xab },
+	{ ASUBB,	yxorb,	Pb, 0x2c,0x80,(05),0x28,0x2a },
+	{ ASUBL,	yaddl,	Px, 0x83,(05),0x2d,0x81,(05),0x29,0x2b },
+	{ ASUBPD,	yxm,	Pe, 0x5c },
+	{ ASUBPS,	yxm,	Pm, 0x5c },
+	{ ASUBQ,	yaddl,	Pw, 0x83,(05),0x2d,0x81,(05),0x29,0x2b },
+	{ ASUBSD,	yxm,	Pf2, 0x5c },
+	{ ASUBSS,	yxm,	Pf3, 0x5c },
+	{ ASUBW,	yaddl,	Pe, 0x83,(05),0x2d,0x81,(05),0x29,0x2b },
+	{ ASWAPGS,	ynone,	Pm, 0x01,0xf8 },
+	{ ASYSCALL,	ynone,	Px, 0x0f,0x05 },	/* fast syscall */
+	{ ATESTB,	ytestb,	Pb, 0xa8,0xf6,(00),0x84,0x84 },
+	{ ATESTL,	ytestl,	Px, 0xa9,0xf7,(00),0x85,0x85 },
+	{ ATESTQ,	ytestl,	Pw, 0xa9,0xf7,(00),0x85,0x85 },
+	{ ATESTW,	ytestl,	Pe, 0xa9,0xf7,(00),0x85,0x85 },
+	{ ATEXT,	ytext,	Px },
+	{ AUCOMISD,	yxcmp,	Pe, 0x2e },
+	{ AUCOMISS,	yxcmp,	Pm, 0x2e },
+	{ AUNPCKHPD,	yxm,	Pe, 0x15 },
+	{ AUNPCKHPS,	yxm,	Pm, 0x15 },
+	{ AUNPCKLPD,	yxm,	Pe, 0x14 },
+	{ AUNPCKLPS,	yxm,	Pm, 0x14 },
+	{ AVERR,	ydivl,	Pm, 0x00,(04) },
+	{ AVERW,	ydivl,	Pm, 0x00,(05) },
+	{ AWAIT,	ynone,	Px, 0x9b },
+	{ AWORD,	ybyte,	Px, 2 },
+	{ AXCHGB,	yml_mb,	Pb, 0x86,0x86 },
+	{ AXCHGL,	yml_ml,	Px, 0x87,0x87 },
+	{ AXCHGQ,	yml_ml,	Pw, 0x87,0x87 },
+	{ AXCHGW,	yml_ml,	Pe, 0x87,0x87 },
+	{ AXLAT,	ynone,	Px, 0xd7 },
+	{ AXORB,	yxorb,	Pb, 0x34,0x80,(06),0x30,0x32 },
+	{ AXORL,	yxorl,	Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
+	{ AXORPD,	yxm,	Pe, 0x57 },
+	{ AXORPS,	yxm,	Pm, 0x57 },
+	{ AXORQ,	yxorl,	Pw, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
+	{ AXORW,	yxorl,	Pe, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
+
+	{ AFMOVB,	yfmvx,	Px, 0xdf,(04) },
+	{ AFMOVBP,	yfmvp,	Px, 0xdf,(06) },
+	{ AFMOVD,	yfmvd,	Px, 0xdd,(00),0xdd,(02),0xd9,(00),0xdd,(02) },
+	{ AFMOVDP,	yfmvdp,	Px, 0xdd,(03),0xdd,(03) },
+	{ AFMOVF,	yfmvf,	Px, 0xd9,(00),0xd9,(02) },
+	{ AFMOVFP,	yfmvp,	Px, 0xd9,(03) },
+	{ AFMOVL,	yfmvf,	Px, 0xdb,(00),0xdb,(02) },
+	{ AFMOVLP,	yfmvp,	Px, 0xdb,(03) },
+	{ AFMOVV,	yfmvx,	Px, 0xdf,(05) },
+	{ AFMOVVP,	yfmvp,	Px, 0xdf,(07) },
+	{ AFMOVW,	yfmvf,	Px, 0xdf,(00),0xdf,(02) },
+	{ AFMOVWP,	yfmvp,	Px, 0xdf,(03) },
+	{ AFMOVX,	yfmvx,	Px, 0xdb,(05) },
+	{ AFMOVXP,	yfmvp,	Px, 0xdb,(07) },
+
+	{ AFCOMB },
+	{ AFCOMBP },
+	{ AFCOMD,	yfadd,	Px, 0xdc,(02),0xd8,(02),0xdc,(02) },	/* botch */
+	{ AFCOMDP,	yfadd,	Px, 0xdc,(03),0xd8,(03),0xdc,(03) },	/* botch */
+	{ AFCOMDPP,	ycompp,	Px, 0xde,(03) },
+	{ AFCOMF,	yfmvx,	Px, 0xd8,(02) },
+	{ AFCOMFP,	yfmvx,	Px, 0xd8,(03) },
+	{ AFCOML,	yfmvx,	Px, 0xda,(02) },
+	{ AFCOMLP,	yfmvx,	Px, 0xda,(03) },
+	{ AFCOMW,	yfmvx,	Px, 0xde,(02) },
+	{ AFCOMWP,	yfmvx,	Px, 0xde,(03) },
+
+	{ AFUCOM,	ycompp,	Px, 0xdd,(04) },
+	{ AFUCOMP,	ycompp, Px, 0xdd,(05) },
+	{ AFUCOMPP,	ycompp,	Px, 0xda,(13) },
+
+	{ AFADDDP,	yfaddp,	Px, 0xde,(00) },
+	{ AFADDW,	yfmvx,	Px, 0xde,(00) },
+	{ AFADDL,	yfmvx,	Px, 0xda,(00) },
+	{ AFADDF,	yfmvx,	Px, 0xd8,(00) },
+	{ AFADDD,	yfadd,	Px, 0xdc,(00),0xd8,(00),0xdc,(00) },
+
+	{ AFMULDP,	yfaddp,	Px, 0xde,(01) },
+	{ AFMULW,	yfmvx,	Px, 0xde,(01) },
+	{ AFMULL,	yfmvx,	Px, 0xda,(01) },
+	{ AFMULF,	yfmvx,	Px, 0xd8,(01) },
+	{ AFMULD,	yfadd,	Px, 0xdc,(01),0xd8,(01),0xdc,(01) },
+
+	{ AFSUBDP,	yfaddp,	Px, 0xde,(05) },
+	{ AFSUBW,	yfmvx,	Px, 0xde,(04) },
+	{ AFSUBL,	yfmvx,	Px, 0xda,(04) },
+	{ AFSUBF,	yfmvx,	Px, 0xd8,(04) },
+	{ AFSUBD,	yfadd,	Px, 0xdc,(04),0xd8,(04),0xdc,(05) },
+
+	{ AFSUBRDP,	yfaddp,	Px, 0xde,(04) },
+	{ AFSUBRW,	yfmvx,	Px, 0xde,(05) },
+	{ AFSUBRL,	yfmvx,	Px, 0xda,(05) },
+	{ AFSUBRF,	yfmvx,	Px, 0xd8,(05) },
+	{ AFSUBRD,	yfadd,	Px, 0xdc,(05),0xd8,(05),0xdc,(04) },
+
+	{ AFDIVDP,	yfaddp,	Px, 0xde,(07) },
+	{ AFDIVW,	yfmvx,	Px, 0xde,(06) },
+	{ AFDIVL,	yfmvx,	Px, 0xda,(06) },
+	{ AFDIVF,	yfmvx,	Px, 0xd8,(06) },
+	{ AFDIVD,	yfadd,	Px, 0xdc,(06),0xd8,(06),0xdc,(07) },
+
+	{ AFDIVRDP,	yfaddp,	Px, 0xde,(06) },
+	{ AFDIVRW,	yfmvx,	Px, 0xde,(07) },
+	{ AFDIVRL,	yfmvx,	Px, 0xda,(07) },
+	{ AFDIVRF,	yfmvx,	Px, 0xd8,(07) },
+	{ AFDIVRD,	yfadd,	Px, 0xdc,(07),0xd8,(07),0xdc,(06) },
+
+	{ AFXCHD,	yfxch,	Px, 0xd9,(01),0xd9,(01) },
+	{ AFFREE },
+	{ AFLDCW,	ystcw,	Px, 0xd9,(05),0xd9,(05) },
+	{ AFLDENV,	ystcw,	Px, 0xd9,(04),0xd9,(04) },
+	{ AFRSTOR,	ysvrs,	Px, 0xdd,(04),0xdd,(04) },
+	{ AFSAVE,	ysvrs,	Px, 0xdd,(06),0xdd,(06) },
+	{ AFSTCW,	ystcw,	Px, 0xd9,(07),0xd9,(07) },
+	{ AFSTENV,	ystcw,	Px, 0xd9,(06),0xd9,(06) },
+	{ AFSTSW,	ystsw,	Px, 0xdd,(07),0xdf,0xe0 },
+	{ AF2XM1,	ynone,	Px, 0xd9, 0xf0 },
+	{ AFABS,	ynone,	Px, 0xd9, 0xe1 },
+	{ AFCHS,	ynone,	Px, 0xd9, 0xe0 },
+	{ AFCLEX,	ynone,	Px, 0xdb, 0xe2 },
+	{ AFCOS,	ynone,	Px, 0xd9, 0xff },
+	{ AFDECSTP,	ynone,	Px, 0xd9, 0xf6 },
+	{ AFINCSTP,	ynone,	Px, 0xd9, 0xf7 },
+	{ AFINIT,	ynone,	Px, 0xdb, 0xe3 },
+	{ AFLD1,	ynone,	Px, 0xd9, 0xe8 },
+	{ AFLDL2E,	ynone,	Px, 0xd9, 0xea },
+	{ AFLDL2T,	ynone,	Px, 0xd9, 0xe9 },
+	{ AFLDLG2,	ynone,	Px, 0xd9, 0xec },
+	{ AFLDLN2,	ynone,	Px, 0xd9, 0xed },
+	{ AFLDPI,	ynone,	Px, 0xd9, 0xeb },
+	{ AFLDZ,	ynone,	Px, 0xd9, 0xee },
+	{ AFNOP,	ynone,	Px, 0xd9, 0xd0 },
+	{ AFPATAN,	ynone,	Px, 0xd9, 0xf3 },
+	{ AFPREM,	ynone,	Px, 0xd9, 0xf8 },
+	{ AFPREM1,	ynone,	Px, 0xd9, 0xf5 },
+	{ AFPTAN,	ynone,	Px, 0xd9, 0xf2 },
+	{ AFRNDINT,	ynone,	Px, 0xd9, 0xfc },
+	{ AFSCALE,	ynone,	Px, 0xd9, 0xfd },
+	{ AFSIN,	ynone,	Px, 0xd9, 0xfe },
+	{ AFSINCOS,	ynone,	Px, 0xd9, 0xfb },
+	{ AFSQRT,	ynone,	Px, 0xd9, 0xfa },
+	{ AFTST,	ynone,	Px, 0xd9, 0xe4 },
+	{ AFXAM,	ynone,	Px, 0xd9, 0xe5 },
+	{ AFXTRACT,	ynone,	Px, 0xd9, 0xf4 },
+	{ AFYL2X,	ynone,	Px, 0xd9, 0xf1 },
+	{ AFYL2XP1,	ynone,	Px, 0xd9, 0xf9 },
+
+	{ ACMPXCHGB,	yrb_mb,	Pb, 0x0f,0xb0 },
+	{ ACMPXCHGL,	yrl_ml,	Px, 0x0f,0xb1 },
+	{ ACMPXCHGW,	yrl_ml,	Pe, 0x0f,0xb1 },
+	{ ACMPXCHGQ,	yrl_ml,	Pw, 0x0f,0xb1 },
+	{ ACMPXCHG8B,	yscond,	Pm, 0xc7,(01) },
+	{ AINVD,	ynone,	Pm, 0x08 },
+	{ AINVLPG,	ymbs,	Pm, 0x01,(07) },
+	{ ALFENCE,	ynone,	Pm, 0xae,0xe8 },
+	{ AMFENCE,	ynone,	Pm, 0xae,0xf0 },
+	{ AMOVNTIL,	yrl_ml,	Pm, 0xc3 },
+	{ AMOVNTIQ,	yrl_ml, Pw, 0x0f,0xc3 },
+	{ ARDMSR,	ynone,	Pm, 0x32 },
+	{ ARDPMC,	ynone,	Pm, 0x33 },
+	{ ARDTSC,	ynone,	Pm, 0x31 },
+	{ ARSM,		ynone,	Pm, 0xaa },
+	{ ASFENCE,	ynone,	Pm, 0xae,0xf8 },
+	{ ASYSRET,	ynone,	Pm, 0x07 },
+	{ AWBINVD,	ynone,	Pm, 0x09 },
+	{ AWRMSR,	ynone,	Pm, 0x30 },
+
+	{ AXADDB,	yrb_mb,	Pb, 0x0f,0xc0 },
+	{ AXADDL,	yrl_ml,	Px, 0x0f,0xc1 },
+	{ AXADDQ,	yrl_ml,	Pw, 0x0f,0xc1 },
+	{ AXADDW,	yrl_ml,	Pe, 0x0f,0xc1 },
+
+	{ AEND },
+	0
+};
+
+Optab*	opindex[ALAST+1];
+
+/*
+AMOVD	0f 6e/r	mmx,reg/mem32[mem64-rex?]
+AMOVD	0f 7e/r	reg/mem32[64],mmx	STORE
+AMOVQ	0f 6f/r	mmx1,mmx2/mem64
+AMOVQ	0f 7f/r	mmx1/mem64,mmx2
+*/
--- /dev/null
+++ b/utils/6l/pass.c
@@ -1,0 +1,790 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i;
+	Sym *s;
+	Prog *p;
+	long t, u;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		t = p->from.offset + p->width;
+		if(t > s->value)
+			diag("initialize bounds (%lld): %s\n%P",
+				s->value, s->name, p);
+	}
+	/* allocate small guys */
+	datsize = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SDATA)
+		if(s->type != SBSS)
+			continue;
+		t = s->value;
+		if(t == 0) {
+			diag("%s: no size", s->name);
+			t = 1;
+		}
+		t = rnd(t, 4);
+		s->value = t;
+		if(t > MINSIZ)
+			continue;
+		if(t >= 8)
+			datsize = rnd(datsize, 8);
+		s->value = datsize;
+		datsize += t;
+		s->type = SDATA1;
+	}
+
+	/* allocate the rest of the data */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SDATA) {
+			if(s->type == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		t = s->value;
+		if(t >= 8)
+			datsize = rnd(datsize, 8);
+		s->value = datsize;
+		datsize += t;
+	}
+	if(datsize)
+		datsize = rnd(datsize, 8);
+
+	if(debug['j']) {
+		/*
+		 * pad data with bss that fits up to next
+		 * 8k boundary, then push data to 8k
+		 */
+		u = rnd(datsize, 8192);
+		u -= datsize;
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SBSS)
+				continue;
+			t = s->value;
+			if(t > u)
+				continue;
+			u -= t;
+			s->value = datsize;
+			s->type = SDATA;
+			datsize += t;
+		}
+		datsize += u;
+	}
+
+	/* now the bss */
+	bsssize = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		t = s->value;
+		if(t >= 8)
+			bsssize = rnd(bsssize, 8);
+		s->value = bsssize + datsize;
+		bsssize += t;
+	}
+	xdefine("edata", SBSS, datsize);
+	xdefine("end", SBSS, bsssize + datsize);
+}
+
+Prog*
+brchain(Prog *p)
+{
+	int i;
+
+	for(i=0; i<20; i++) {
+		if(p == P || p->as != AJMP)
+			return p;
+		p = p->pcond;
+	}
+	return P;
+}
+
+void
+follow(void)
+{
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+	lastp->link = P;
+	firstp = firstp->link;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q;
+	int i;
+	enum as a;
+
+loop:
+	if(p == P)
+		return;
+	if(p->as == ATEXT)
+		curtext = p;
+	if(p->as == AJMP)
+	if((q = p->pcond) != P) {
+		p->mark = 1;
+		p = q;
+		if(p->mark == 0)
+			goto loop;
+	}
+	if(p->mark) {
+		/* copy up to 4 instructions to avoid branch */
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == P)
+				break;
+			if(q == lastp)
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			switch(a) {
+			case AJMP:
+			case ARET:
+			case AIRETL:
+			case AIRETQ:
+			case AIRETW:
+			case ARETFL:
+			case ARETFQ:
+			case ARETFW:
+
+			case APUSHL:
+			case APUSHFL:
+			case APUSHQ:
+			case APUSHFQ:
+			case APUSHW:
+			case APUSHFW:
+			case APOPL:
+			case APOPFL:
+			case APOPQ:
+			case APOPFQ:
+			case APOPW:
+			case APOPFW:
+				goto brk;
+			}
+			if(q->pcond == P || q->pcond->mark)
+				continue;
+			if(a == ACALL || a == ALOOP)
+				continue;
+			for(;;) {
+				if(p->as == ANOP) {
+					p = p->link;
+					continue;
+				}
+				q = copyp(p);
+				p = p->link;
+				q->mark = 1;
+				lastp->link = q;
+				lastp = q;
+				if(q->as != a || q->pcond == P || q->pcond->mark)
+					continue;
+
+				q->as = relinv(q->as);
+				p = q->pcond;
+				q->pcond = q->link;
+				q->link = p;
+				xfol(q->link);
+				p = q->link;
+				if(p->mark)
+					return;
+				goto loop;
+			}
+		} /* */
+	brk:;
+		q = prg();
+		q->as = AJMP;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->pcond = p;
+		p = q;
+	}
+	p->mark = 1;
+	lastp->link = p;
+	lastp = p;
+	a = p->as;
+	if(a == AJMP || a == ARET || a == AIRETL || a == AIRETQ || a == AIRETW ||
+	   a == ARETFL || a == ARETFQ || a == ARETFW)
+		return;
+	if(p->pcond != P)
+	if(a != ACALL) {
+		q = brchain(p->link);
+		if(q != P && q->mark)
+		if(a != ALOOP) {
+			p->as = relinv(a);
+			p->link = p->pcond;
+			p->pcond = q;
+		}
+		xfol(p->link);
+		q = brchain(p->pcond);
+		if(q->mark) {
+			p->pcond = q;
+			return;
+		}
+		p = q;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+int
+relinv(int a)
+{
+
+	switch(a) {
+	case AJEQ:	return AJNE;
+	case AJNE:	return AJEQ;
+	case AJLE:	return AJGT;
+	case AJLS:	return AJHI;
+	case AJLT:	return AJGE;
+	case AJMI:	return AJPL;
+	case AJGE:	return AJLT;
+	case AJPL:	return AJMI;
+	case AJGT:	return AJLE;
+	case AJHI:	return AJLS;
+	case AJCS:	return AJCC;
+	case AJCC:	return AJCS;
+	case AJPS:	return AJPC;
+	case AJPC:	return AJPS;
+	case AJOS:	return AJOC;
+	case AJOC:	return AJOS;
+	}
+	diag("unknown relation: %s in %s", anames[a], TNAME);
+	return a;
+}
+
+void
+doinit(void)
+{
+	Sym *s;
+	Prog *p;
+	int x;
+
+	for(p = datap; p != P; p = p->link) {
+		x = p->to.type;
+		if(x != D_EXTERN && x != D_STATIC)
+			continue;
+		s = p->to.sym;
+		if(s->type == 0 || s->type == SXREF)
+			diag("undefined %s initializer of %s",
+				s->name, p->from.sym->name);
+		p->to.offset += s->value;
+		p->to.type = D_CONST;
+		if(s->type == SDATA || s->type == SBSS)
+			p->to.offset += INITDAT;
+	}
+}
+
+void
+patch(void)
+{
+	long c;
+	Prog *p, *q;
+	Sym *s;
+	long vexit;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f mkfwd\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->as == ACALL || p->as == ARET) {
+			s = p->to.sym;
+			if(s) {
+				if(debug['c'])
+					Bprint(&bso, "%s calls %s\n", TNAME, s->name);
+				switch(s->type) {
+				default:
+					diag("undefined: %s in %s", s->name, TNAME);
+					s->type = STEXT;
+					s->value = vexit;
+					break;	/* or fall through to set offset? */
+				case STEXT:
+					p->to.offset = s->value;
+					break;
+				case SUNDEF:
+					p->pcond = UP;
+					p->to.offset = 0;
+					break;
+				}
+				p->to.type = D_BRANCH;
+			}
+		}
+		if(p->to.type != D_BRANCH || p->pcond == UP)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range in %s\n%P", TNAME, p);
+			p->to.type = D_NONE;
+		}
+		p->pcond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		p->mark = 0;	/* initialization for follow */
+		if(p->pcond != P && p->pcond != UP) {
+			p->pcond = brloop(p->pcond);
+			if(p->pcond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->pcond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	int i;
+	long dwn[LOG], cnt[LOG];
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	int c;
+	Prog *q;
+
+	c = 0;
+	for(q = p; q != P; q = q->pcond) {
+		if(q->as != AJMP)
+			break;
+		c++;
+		if(c >= 5000)
+			return P;
+	}
+	return q;
+}
+
+void
+dostkoff(void)
+{
+	Prog *p, *q;
+	long autoffset, deltasp;
+	int a, f, curframe, curbecome, maxbecome, pcsize;
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == (D_INDIR+D_SP))
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			curtext = p;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+			break;
+		}
+	}
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case ACALL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				f = maxbecome - curtext->from.sym->frame;
+				if(f <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += f;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, f);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	autoffset = 0;
+	deltasp = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autoffset = p->to.offset;
+			if(autoffset < 0)
+				autoffset = 0;
+			if(autoffset) {
+				p = appendp(p);
+				p->as = AADJSP;
+				p->from.type = D_CONST;
+				p->from.offset = autoffset;
+			}
+			deltasp = autoffset;
+		}
+		pcsize = p->mode/8;
+		a = p->from.type;
+		if(a == D_AUTO)
+			p->from.offset += deltasp;
+		if(a == D_PARAM)
+			p->from.offset += deltasp + pcsize;
+		a = p->to.type;
+		if(a == D_AUTO)
+			p->to.offset += deltasp;
+		if(a == D_PARAM)
+			p->to.offset += deltasp + pcsize;
+
+		switch(p->as) {
+		default:
+			continue;
+		case APUSHL:
+		case APUSHFL:
+			deltasp += 4;
+			continue;
+		case APUSHQ:
+		case APUSHFQ:
+			deltasp += 8;
+			continue;
+		case APUSHW:
+		case APUSHFW:
+			deltasp += 2;
+			continue;
+		case APOPL:
+		case APOPFL:
+			deltasp -= 4;
+			continue;
+		case APOPQ:
+		case APOPFQ:
+			deltasp -= 8;
+			continue;
+		case APOPW:
+		case APOPFW:
+			deltasp -= 2;
+			continue;
+		case ARET:
+			break;
+		}
+
+		if(autoffset != deltasp)
+			diag("unbalanced PUSH/POP");
+		if(p->from.type == D_CONST)
+			goto become;
+
+		if(autoffset) {
+			q = p;
+			p = appendp(p);
+			p->as = ARET;
+
+			q->as = AADJSP;
+			q->from.type = D_CONST;
+			q->from.offset = -autoffset;
+		}
+		continue;
+
+	become:
+		q = p;
+		p = appendp(p);
+		p->as = AJMP;
+		p->to = q->to;
+		p->pcond = q->pcond;
+
+		q->as = AADJSP;
+		q->from = zprg.from;
+		q->from.type = D_CONST;
+		q->from.offset = -autoffset;
+		q->to = zprg.to;
+		continue;
+	}
+}
+
+vlong
+atolwhex(char *s)
+{
+	vlong n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+void
+import(void)
+{
+	int i;
+	Sym *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+				if(s->value != 0)
+					diag("value != 0 on SXREF");
+				undefsym(s);
+				Bprint(&bso, "IMPORT: %s sig=%lux v=%lld\n", s->name, s->sig, s->value);
+				if(debug['S'])
+					s->sig = 0;
+			}
+}
+
+void
+ckoff(Sym *s, long v)
+{
+	if(v < 0 || v >= 1<<Roffset)
+		diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+	Prog *p;
+
+	p = prg();
+	if(edatap == P)
+		datap = p;
+	else
+		edatap->link = p;
+	edatap = p;
+	p->as = ADATA;
+	p->width = w;
+	p->from.scale = w;
+	p->from.type = t;
+	p->from.sym = s;
+	p->from.offset = o;
+	p->to.type = D_CONST;
+	return p;
+}
+
+void
+export(void)
+{
+	int i, j, n, off, nb, sv, ne;
+	Sym *s, *et, *str, **esyms;
+	Prog *p;
+	char buf[NSNAME], *t;
+
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				n++;
+	esyms = malloc(n*sizeof(Sym*));
+	ne = n;
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				esyms[n++] = s;
+	for(i = 0; i < ne-1; i++)
+		for(j = i+1; j < ne; j++)
+			if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+				s = esyms[i];
+				esyms[i] = esyms[j];
+				esyms[j] = s;
+			}
+
+	nb = 0;
+	off = 0;
+	et = lookup(EXPTAB, 0);
+	if(et->type != 0 && et->type != SXREF)
+		diag("%s already defined", EXPTAB);
+	et->type = SDATA;
+	str = lookup(".string", 0);
+	if(str->type == 0)
+		str->type = SDATA;
+	sv = str->value;
+	for(i = 0; i < ne; i++){
+		s = esyms[i];
+		if(debug['S'])
+			s->sig = 0;
+		/* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */
+
+		/* signature */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.offset = s->sig;
+
+		/* address */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.type = D_ADDR;
+		p->to.index = D_EXTERN;
+		p->to.sym = s;
+
+		/* string */
+		t = s->name;
+		n = strlen(t)+1;
+		for(;;){
+			buf[nb++] = *t;
+			sv++;
+			if(nb >= NSNAME){
+				p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+				p->to.type = D_SCONST;
+				memmove(p->to.scon, buf, NSNAME);
+				nb = 0;
+			}
+			if(*t++ == 0)
+				break;
+		}
+
+		/* name */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.type = D_ADDR;
+		p->to.index = D_STATIC;
+		p->to.sym = str;
+		p->to.offset = sv-n;
+	}
+
+	if(nb > 0){
+		p = newdata(str, sv-nb, nb, D_STATIC);
+		p->to.type = D_SCONST;
+		memmove(p->to.scon, buf, nb);
+	}
+
+	for(i = 0; i < 3; i++){
+		newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+	}
+	et->value = off;
+	if(sv == 0)
+		sv = 1;
+	str->value = sv;
+	exports = ne;
+	free(esyms);
+}
--- /dev/null
+++ b/utils/6l/span.c
@@ -1,0 +1,1754 @@
+#include	"l.h"
+
+static int	rexflag;
+static int	asmode;
+
+void
+span(void)
+{
+	Prog *p, *q;
+	long v;
+	vlong c, idat;
+	int m, n, again;
+
+	xdefine("etext", STEXT, 0L);
+	idat = INITDAT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		n = 0;
+		if(p->to.type == D_BRANCH)
+			if(p->pcond == P)
+				p->pcond = p;
+		if((q = p->pcond) != P)
+			if(q->back != 2)
+				n = 1;
+		p->back = n;
+		if(p->as == AADJSP) {
+			p->to.type = D_SP;
+			v = -p->from.offset;
+			p->from.offset = v;
+			p->as = p->mode != 64? AADDL: AADDQ;
+			if(v < 0) {
+				p->as = p->mode != 64? ASUBL: ASUBQ;
+				v = -v;
+				p->from.offset = v;
+			}
+			if(v == 0)
+				p->as = ANOP;
+		}
+	}
+	n = 0;
+
+start:
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+	c = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->to.type == D_BRANCH)
+			if(p->back)
+				p->pc = c;
+		asmins(p);
+		p->pc = c;
+		m = andptr-and;
+		p->mark = m;
+		c += m;
+	}
+
+loop:
+	n++;
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span %d\n", cputime(), n);
+	Bflush(&bso);
+	if(n > 50) {
+		print("span must be looping\n");
+		errorexit();
+	}
+	again = 0;
+	c = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->to.type == D_BRANCH || p->back & 0100) {
+			if(p->back)
+				p->pc = c;
+			asmins(p);
+			m = andptr-and;
+			if(m != p->mark) {
+				p->mark = m;
+				again++;
+			}
+		}
+		p->pc = c;
+		c += p->mark;
+	}
+	if(again) {
+		textsize = c;
+		goto loop;
+	}
+	if(INITRND) {
+		INITDAT = rnd(c, INITRND);
+		if(INITDAT != idat) {
+			idat = INITDAT;
+			goto start;
+		}
+	}
+	xdefine("etext", STEXT, c);
+	if(debug['v'])
+		Bprint(&bso, "etext = %llux\n", c);
+	Bflush(&bso);
+	for(p = textp; p != P; p = p->pcond)
+		p->from.sym->value = p->pc;
+	textsize = c - INITTEXT;
+}
+
+void
+xdefine(char *p, int t, vlong v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+	if(s->type == STEXT && s->value == 0)
+		s->value = v;
+}
+
+void
+putsymb(char *s, int t, vlong v, int ver)
+{
+	int i, f, l;
+
+	if(t == 'f')
+		s++;
+	l = 4;
+	switch(HEADTYPE){
+	default:
+		break;
+	case 5:
+		if(debug['8'])
+			break;
+	case 2:
+	case 6:
+		lput(v>>32);
+		l = 8;
+		break;
+	}
+	lput(v);
+	if(ver)
+		t += 'a' - 'A';
+	cput(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		cput(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			cput(s[i]);
+			cput(s[i+1]);
+		}
+		cput(0);
+		cput(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			cput(s[i]);
+		cput(0);
+	}
+	symsize += l + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8llux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8llux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8llux %s\n", t, v, s);
+	}
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->pcond) {
+		s = p->from.sym;
+		if(s->type != STEXT)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		putsymb(s->name, 'T', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+8, 0);
+
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+asmlc(void)
+{
+	vlong oldpc;
+	Prog *p;
+	long oldlc, v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6llux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			cput(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			cput(0);	/* 0 vv +lc */
+			cput(s>>24);
+			cput(s>>16);
+			cput(s>>8);
+			cput(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6llux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			cput(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6llux %P\n",
+					p->pc, p);
+			}
+		} else {
+			cput(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6llux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		cput(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+int
+oclass(Adr *a)
+{
+	vlong v;
+	long l;
+
+	if(a->type >= D_INDIR || a->index != D_NONE) {
+		if(a->index != D_NONE && a->scale == 0) {
+			if(a->type == D_ADDR) {
+				switch(a->index) {
+				case D_EXTERN:
+				case D_STATIC:
+					return Yi32;	/* TO DO: Yi64 */
+				case D_AUTO:
+				case D_PARAM:
+					return Yiauto;
+				}
+				return Yxxx;
+			}
+			return Ycol;
+		}
+		return Ym;
+	}
+	switch(a->type)
+	{
+	case D_AL:
+		return Yal;
+
+	case D_AX:
+		return Yax;
+
+/*
+	case D_SPB:
+*/
+	case D_BPB:
+	case D_SIB:
+	case D_DIB:
+	case D_R8B:
+	case D_R9B:
+	case D_R10B:
+	case D_R11B:
+	case D_R12B:
+	case D_R13B:
+	case D_R14B:
+	case D_R15B:
+		if(asmode != 64)
+			return Yxxx;
+	case D_DL:
+	case D_BL:
+	case D_AH:
+	case D_CH:
+	case D_DH:
+	case D_BH:
+		return Yrb;
+
+	case D_CL:
+		return Ycl;
+
+	case D_CX:
+		return Ycx;
+
+	case D_DX:
+	case D_BX:
+		return Yrx;
+
+	case D_R8:	/* not really Yrl */
+	case D_R9:
+	case D_R10:
+	case D_R11:
+	case D_R12:
+	case D_R13:
+	case D_R14:
+	case D_R15:
+		if(asmode != 64)
+			return Yxxx;
+	case D_SP:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		return Yrl;
+
+	case D_F0+0:
+		return	Yf0;
+
+	case D_F0+1:
+	case D_F0+2:
+	case D_F0+3:
+	case D_F0+4:
+	case D_F0+5:
+	case D_F0+6:
+	case D_F0+7:
+		return	Yrf;
+
+	case D_M0+0:
+	case D_M0+1:
+	case D_M0+2:
+	case D_M0+3:
+	case D_M0+4:
+	case D_M0+5:
+	case D_M0+6:
+	case D_M0+7:
+		return	Ymr;
+
+	case D_X0+0:
+	case D_X0+1:
+	case D_X0+2:
+	case D_X0+3:
+	case D_X0+4:
+	case D_X0+5:
+	case D_X0+6:
+	case D_X0+7:
+	case D_X0+8:
+	case D_X0+9:
+	case D_X0+10:
+	case D_X0+11:
+	case D_X0+12:
+	case D_X0+13:
+	case D_X0+14:
+	case D_X0+15:
+		return	Yxr;
+
+	case D_NONE:
+		return Ynone;
+
+	case D_CS:	return	Ycs;
+	case D_SS:	return	Yss;
+	case D_DS:	return	Yds;
+	case D_ES:	return	Yes;
+	case D_FS:	return	Yfs;
+	case D_GS:	return	Ygs;
+
+	case D_GDTR:	return	Ygdtr;
+	case D_IDTR:	return	Yidtr;
+	case D_LDTR:	return	Yldtr;
+	case D_MSW:	return	Ymsw;
+	case D_TASK:	return	Ytask;
+
+	case D_CR+0:	return	Ycr0;
+	case D_CR+1:	return	Ycr1;
+	case D_CR+2:	return	Ycr2;
+	case D_CR+3:	return	Ycr3;
+	case D_CR+4:	return	Ycr4;
+	case D_CR+5:	return	Ycr5;
+	case D_CR+6:	return	Ycr6;
+	case D_CR+7:	return	Ycr7;
+	case D_CR+8:	return	Ycr8;
+
+	case D_DR+0:	return	Ydr0;
+	case D_DR+1:	return	Ydr1;
+	case D_DR+2:	return	Ydr2;
+	case D_DR+3:	return	Ydr3;
+	case D_DR+4:	return	Ydr4;
+	case D_DR+5:	return	Ydr5;
+	case D_DR+6:	return	Ydr6;
+	case D_DR+7:	return	Ydr7;
+
+	case D_TR+0:	return	Ytr0;
+	case D_TR+1:	return	Ytr1;
+	case D_TR+2:	return	Ytr2;
+	case D_TR+3:	return	Ytr3;
+	case D_TR+4:	return	Ytr4;
+	case D_TR+5:	return	Ytr5;
+	case D_TR+6:	return	Ytr6;
+	case D_TR+7:	return	Ytr7;
+
+	case D_EXTERN:
+	case D_STATIC:
+	case D_AUTO:
+	case D_PARAM:
+		return Ym;
+
+	case D_CONST:
+	case D_ADDR:
+		if(a->sym == S) {
+			v = a->offset;
+			if(v == 0)
+				return Yi0;
+			if(v == 1)
+				return Yi1;
+			if(v >= -128 && v <= 127)
+				return Yi8;
+			l = v;
+			if((vlong)l == v)
+				return Ys32;	/* can sign extend */
+			if((v>>32) == 0)
+				return Yi32;	/* unsigned */
+			return Yi64;
+		}
+		return Yi32;	/* TO DO: D_ADDR as Yi64 */
+
+	case D_BRANCH:
+		return Ybr;
+	}
+	return Yxxx;
+}
+
+void
+asmidx(Adr *a, int base)
+{
+	int i;
+
+	switch(a->index) {
+	default:
+		goto bad;
+
+	case D_NONE:
+		i = 4 << 3;
+		goto bas;
+
+	case D_R8:
+	case D_R9:
+	case D_R10:
+	case D_R11:
+	case D_R12:
+	case D_R13:
+	case D_R14:
+	case D_R15:
+		if(asmode != 64)
+			goto bad;
+	case D_AX:
+	case D_CX:
+	case D_DX:
+	case D_BX:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		i = reg[a->index] << 3;
+		break;
+	}
+	switch(a->scale) {
+	default:
+		goto bad;
+	case 1:
+		break;
+	case 2:
+		i |= (1<<6);
+		break;
+	case 4:
+		i |= (2<<6);
+		break;
+	case 8:
+		i |= (3<<6);
+		break;
+	}
+bas:
+	switch(base) {
+	default:
+		goto bad;
+	case D_NONE:	/* must be mod=00 */
+		i |= 5;
+		break;
+	case D_R8:
+	case D_R9:
+	case D_R10:
+	case D_R11:
+	case D_R12:
+	case D_R13:
+	case D_R14:
+	case D_R15:
+		if(asmode != 64)
+			goto bad;
+	case D_AX:
+	case D_CX:
+	case D_DX:
+	case D_BX:
+	case D_SP:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		i |= reg[base];
+		break;
+	}
+	*andptr++ = i;
+	return;
+bad:
+	diag("asmidx: bad address %D", a);
+	*andptr++ = 0;
+	return;
+}
+
+static void
+put4(long v)
+{
+	if(dlm && curp != P && reloca != nil){
+		dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1);
+		reloca = nil;
+	}
+	andptr[0] = v;
+	andptr[1] = v>>8;
+	andptr[2] = v>>16;
+	andptr[3] = v>>24;
+	andptr += 4;
+}
+
+static void
+put8(vlong v)
+{
+	if(dlm && curp != P && reloca != nil){
+		dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1);	/* TO DO */
+		reloca = nil;
+	}
+	andptr[0] = v;
+	andptr[1] = v>>8;
+	andptr[2] = v>>16;
+	andptr[3] = v>>24;
+	andptr[4] = v>>32;
+	andptr[5] = v>>40;
+	andptr[6] = v>>48;
+	andptr[7] = v>>56;
+	andptr += 8;
+}
+
+vlong
+vaddr(Adr *a)
+{
+	int t;
+	vlong v;
+	Sym *s;
+
+	t = a->type;
+	v = a->offset;
+	if(t == D_ADDR)
+		t = a->index;
+	switch(t) {
+	case D_STATIC:
+	case D_EXTERN:
+		s = a->sym;
+		if(s != nil) {
+			if(dlm && curp != P)
+				reloca = a;
+			switch(s->type) {
+			case SUNDEF:
+				ckoff(s, v);
+			case STEXT:
+			case SCONST:
+				if((uvlong)s->value < (uvlong)INITTEXT)
+					v += INITTEXT;	/* TO DO */
+				v += s->value;
+				break;
+			default:
+				v += INITDAT + s->value;
+			}
+		}
+	}
+	return v;
+}
+
+static void
+asmandsz(Adr *a, int r, int rex, int m64)
+{
+	long v;
+	int t;
+	Adr aa;
+
+	rex &= (0x40 | Rxr);
+	v = a->offset;
+	t = a->type;
+	if(a->index != D_NONE) {
+		if(t >= D_INDIR) {
+			t -= D_INDIR;
+			rexflag |= (regrex[a->index] & Rxx) | (regrex[t] & Rxb) | rex;
+			if(t == D_NONE) {
+				*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				put4(v);
+				return;
+			}
+			if(v == 0 && t != D_BP && t != D_R13) {
+				*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				*andptr++ = (1 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				*andptr++ = v;
+				return;
+			}
+			*andptr++ = (2 << 6) | (4 << 0) | (r << 3);
+			asmidx(a, t);
+			put4(v);
+			return;
+		}
+		switch(t) {
+		default:
+			goto bad;
+		case D_STATIC:
+		case D_EXTERN:
+			aa.type = D_NONE+D_INDIR;
+			break;
+		case D_AUTO:
+		case D_PARAM:
+			aa.type = D_SP+D_INDIR;
+			break;
+		}
+		aa.offset = vaddr(a);
+		aa.index = a->index;
+		aa.scale = a->scale;
+		asmandsz(&aa, r, rex, m64);
+		return;
+	}
+	if(t >= D_AL && t <= D_X0+15) {
+		if(v)
+			goto bad;
+		*andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3);
+		rexflag |= (regrex[t] & (0x40 | Rxb)) | rex;
+		return;
+	}
+	if(t >= D_INDIR) {
+		t -= D_INDIR;
+		rexflag |= (regrex[t] & Rxb) | rex;
+		if(t == D_NONE) {
+			if(asmode != 64){
+				*andptr++ = (0 << 6) | (5 << 0) | (r << 3);
+				put4(v);
+				return;
+			}
+			/* temporary */
+			*andptr++ = (0 <<  6) | (4 << 0) | (r << 3);	/* sib present */
+			*andptr++ = (0 << 6) | (4 << 3) | (5 << 0);	/* DS:d32 */
+			put4(v);
+			return;
+		}
+		if(t == D_SP || t == D_R12) {
+			if(v == 0) {
+				*andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3);
+				asmidx(a, t);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				*andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3);
+				asmidx(a, t);
+				*andptr++ = v;
+				return;
+			}
+			*andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3);
+			asmidx(a, t);
+			put4(v);
+			return;
+		}
+		if(t >= D_AX && t <= D_R15) {
+			if(v == 0 && t != D_BP && t != D_R13) {
+				*andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3);
+				andptr[1] = v;
+				andptr += 2;
+				return;
+			}
+			*andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3);
+			put4(v);
+			return;
+		}
+		goto bad;
+	}
+	switch(a->type) {
+	default:
+		goto bad;
+	case D_STATIC:
+	case D_EXTERN:
+		aa.type = D_NONE+D_INDIR;
+		break;
+	case D_AUTO:
+	case D_PARAM:
+		aa.type = D_SP+D_INDIR;
+		break;
+	}
+	aa.index = D_NONE;
+	aa.scale = 1;
+	aa.offset = vaddr(a);
+	asmandsz(&aa, r, rex, m64);
+	return;
+bad:
+	diag("asmand: bad address %D", a);
+	return;
+}
+
+void
+asmand(Adr *a, Adr *ra)
+{
+	asmandsz(a, reg[ra->type], regrex[ra->type], 0);
+}
+
+void
+asmando(Adr *a, int o)
+{
+	asmandsz(a, o, 0, 0);
+}
+
+static void
+bytereg(Adr *a)
+{
+	if(a->index == D_NONE && (a->type >= D_AX && a->type <= D_R15))
+		a->type = D_AL + (a->type-D_AX);
+}
+
+#define	E	0xff
+Movtab	ymovtab[] =
+{
+/* push */
+	{APUSHL,	Ycs,	Ynone,	0,	0x0e,E,0,0},
+	{APUSHL,	Yss,	Ynone,	0,	0x16,E,0,0},
+	{APUSHL,	Yds,	Ynone,	0,	0x1e,E,0,0},
+	{APUSHL,	Yes,	Ynone,	0,	0x06,E,0,0},
+	{APUSHL,	Yfs,	Ynone,	0,	0x0f,0xa0,E,0},
+	{APUSHL,	Ygs,	Ynone,	0,	0x0f,0xa8,E,0},
+	{APUSHQ,	Yfs,	Ynone,	0,	0x0f,0xa0,E,0},
+	{APUSHQ,	Ygs,	Ynone,	0,	0x0f,0xa8,E,0},
+
+	{APUSHW,	Ycs,	Ynone,	0,	Pe,0x0e,E,0},
+	{APUSHW,	Yss,	Ynone,	0,	Pe,0x16,E,0},
+	{APUSHW,	Yds,	Ynone,	0,	Pe,0x1e,E,0},
+	{APUSHW,	Yes,	Ynone,	0,	Pe,0x06,E,0},
+	{APUSHW,	Yfs,	Ynone,	0,	Pe,0x0f,0xa0,E},
+	{APUSHW,	Ygs,	Ynone,	0,	Pe,0x0f,0xa8,E},
+
+/* pop */
+	{APOPL,	Ynone,	Yds,	0,	0x1f,E,0,0},
+	{APOPL,	Ynone,	Yes,	0,	0x07,E,0,0},
+	{APOPL,	Ynone,	Yss,	0,	0x17,E,0,0},
+	{APOPL,	Ynone,	Yfs,	0,	0x0f,0xa1,E,0},
+	{APOPL,	Ynone,	Ygs,	0,	0x0f,0xa9,E,0},
+	{APOPQ,	Ynone,	Yfs,	0,	0x0f,0xa1,E,0},
+	{APOPQ,	Ynone,	Ygs,	0,	0x0f,0xa9,E,0},
+
+	{APOPW,	Ynone,	Yds,	0,	Pe,0x1f,E,0},
+	{APOPW,	Ynone,	Yes,	0,	Pe,0x07,E,0},
+	{APOPW,	Ynone,	Yss,	0,	Pe,0x17,E,0},
+	{APOPW,	Ynone,	Yfs,	0,	Pe,0x0f,0xa1,E},
+	{APOPW,	Ynone,	Ygs,	0,	Pe,0x0f,0xa9,E},
+
+/* mov seg */
+	{AMOVW,	Yes,	Yml,	1,	0x8c,0,0,0},
+	{AMOVW,	Ycs,	Yml,	1,	0x8c,1,0,0},
+	{AMOVW,	Yss,	Yml,	1,	0x8c,2,0,0},
+	{AMOVW,	Yds,	Yml,	1,	0x8c,3,0,0},
+	{AMOVW,	Yfs,	Yml,	1,	0x8c,4,0,0},
+	{AMOVW,	Ygs,	Yml,	1,	0x8c,5,0,0},
+
+	{AMOVW,	Yml,	Yes,	2,	0x8e,0,0,0},
+	{AMOVW,	Yml,	Ycs,	2,	0x8e,1,0,0},
+	{AMOVW,	Yml,	Yss,	2,	0x8e,2,0,0},
+	{AMOVW,	Yml,	Yds,	2,	0x8e,3,0,0},
+	{AMOVW,	Yml,	Yfs,	2,	0x8e,4,0,0},
+	{AMOVW,	Yml,	Ygs,	2,	0x8e,5,0,0},
+
+/* mov cr */
+	{AMOVL,	Ycr0,	Yml,	3,	0x0f,0x20,0,0},
+	{AMOVL,	Ycr2,	Yml,	3,	0x0f,0x20,2,0},
+	{AMOVL,	Ycr3,	Yml,	3,	0x0f,0x20,3,0},
+	{AMOVL,	Ycr4,	Yml,	3,	0x0f,0x20,4,0},
+	{AMOVL,	Ycr8,	Yml,	3,	0x0f,0x20,8,0},
+	{AMOVQ,	Ycr0,	Yml,	3,	0x0f,0x20,0,0},
+	{AMOVQ,	Ycr2,	Yml,	3,	0x0f,0x20,2,0},
+	{AMOVQ,	Ycr3,	Yml,	3,	0x0f,0x20,3,0},
+	{AMOVQ,	Ycr4,	Yml,	3,	0x0f,0x20,4,0},
+	{AMOVQ,	Ycr8,	Yml,	3,	0x0f,0x20,8,0},
+
+	{AMOVL,	Yml,	Ycr0,	4,	0x0f,0x22,0,0},
+	{AMOVL,	Yml,	Ycr2,	4,	0x0f,0x22,2,0},
+	{AMOVL,	Yml,	Ycr3,	4,	0x0f,0x22,3,0},
+	{AMOVL,	Yml,	Ycr4,	4,	0x0f,0x22,4,0},
+	{AMOVL,	Yml,	Ycr8,	4,	0x0f,0x22,8,0},
+	{AMOVQ,	Yml,	Ycr0,	4,	0x0f,0x22,0,0},
+	{AMOVQ,	Yml,	Ycr2,	4,	0x0f,0x22,2,0},
+	{AMOVQ,	Yml,	Ycr3,	4,	0x0f,0x22,3,0},
+	{AMOVQ,	Yml,	Ycr4,	4,	0x0f,0x22,4,0},
+	{AMOVQ,	Yml,	Ycr8,	4,	0x0f,0x22,8,0},
+
+/* mov dr */
+	{AMOVL,	Ydr0,	Yml,	3,	0x0f,0x21,0,0},
+	{AMOVL,	Ydr6,	Yml,	3,	0x0f,0x21,6,0},
+	{AMOVL,	Ydr7,	Yml,	3,	0x0f,0x21,7,0},
+	{AMOVQ,	Ydr0,	Yml,	3,	0x0f,0x21,0,0},
+	{AMOVQ,	Ydr6,	Yml,	3,	0x0f,0x21,6,0},
+	{AMOVQ,	Ydr7,	Yml,	3,	0x0f,0x21,7,0},
+
+	{AMOVL,	Yml,	Ydr0,	4,	0x0f,0x23,0,0},
+	{AMOVL,	Yml,	Ydr6,	4,	0x0f,0x23,6,0},
+	{AMOVL,	Yml,	Ydr7,	4,	0x0f,0x23,7,0},
+	{AMOVQ,	Yml,	Ydr0,	4,	0x0f,0x23,0,0},
+	{AMOVQ,	Yml,	Ydr6,	4,	0x0f,0x23,6,0},
+	{AMOVQ,	Yml,	Ydr7,	4,	0x0f,0x23,7,0},
+
+/* mov tr */
+	{AMOVL,	Ytr6,	Yml,	3,	0x0f,0x24,6,0},
+	{AMOVL,	Ytr7,	Yml,	3,	0x0f,0x24,7,0},
+
+	{AMOVL,	Yml,	Ytr6,	4,	0x0f,0x26,6,E},
+	{AMOVL,	Yml,	Ytr7,	4,	0x0f,0x26,7,E},
+
+/* lgdt, sgdt, lidt, sidt */
+	{AMOVL,	Ym,	Ygdtr,	4,	0x0f,0x01,2,0},
+	{AMOVL,	Ygdtr,	Ym,	3,	0x0f,0x01,0,0},
+	{AMOVL,	Ym,	Yidtr,	4,	0x0f,0x01,3,0},
+	{AMOVL,	Yidtr,	Ym,	3,	0x0f,0x01,1,0},
+	{AMOVQ,	Ym,	Ygdtr,	4,	0x0f,0x01,2,0},
+	{AMOVQ,	Ygdtr,	Ym,	3,	0x0f,0x01,0,0},
+	{AMOVQ,	Ym,	Yidtr,	4,	0x0f,0x01,3,0},
+	{AMOVQ,	Yidtr,	Ym,	3,	0x0f,0x01,1,0},
+
+/* lldt, sldt */
+	{AMOVW,	Yml,	Yldtr,	4,	0x0f,0x00,2,0},
+	{AMOVW,	Yldtr,	Yml,	3,	0x0f,0x00,0,0},
+
+/* lmsw, smsw */
+	{AMOVW,	Yml,	Ymsw,	4,	0x0f,0x01,6,0},
+	{AMOVW,	Ymsw,	Yml,	3,	0x0f,0x01,4,0},
+
+/* ltr, str */
+	{AMOVW,	Yml,	Ytask,	4,	0x0f,0x00,3,0},
+	{AMOVW,	Ytask,	Yml,	3,	0x0f,0x00,1,0},
+
+/* load full pointer */
+	{AMOVL,	Yml,	Ycol,	5,	0,0,0,0},
+	{AMOVW,	Yml,	Ycol,	5,	Pe,0,0,0},
+
+/* double shift */
+	{ASHLL,	Ycol,	Yml,	6,	0xa4,0xa5,0,0},
+	{ASHRL,	Ycol,	Yml,	6,	0xac,0xad,0,0},
+	{ASHLQ,	Ycol,	Yml,	6,	Pw,0xa4,0xa5,0},
+	{ASHRQ,	Ycol,	Yml,	6,	Pw,0xac,0xad,0},
+	{ASHLW,	Ycol,	Yml,	6,	Pe,0xa4,0xa5,0},
+	{ASHRW,	Ycol,	Yml,	6,	Pe,0xac,0xad,0},
+	0
+};
+
+int
+isax(Adr *a)
+{
+
+	switch(a->type) {
+	case D_AX:
+	case D_AL:
+	case D_AH:
+	case D_INDIR+D_AX:
+		return 1;
+	}
+	if(a->index == D_AX)
+		return 1;
+	return 0;
+}
+
+void
+subreg(Prog *p, int from, int to)
+{
+
+	if(debug['Q'])
+		print("\n%P	s/%R/%R/\n", p, from, to);
+
+	if(p->from.type == from)
+		p->from.type = to;
+	if(p->to.type == from)
+		p->to.type = to;
+
+	if(p->from.index == from)
+		p->from.index = to;
+	if(p->to.index == from)
+		p->to.index = to;
+
+	from += D_INDIR;
+	if(p->from.type == from)
+		p->from.type = to+D_INDIR;
+	if(p->to.type == from)
+		p->to.type = to+D_INDIR;
+
+	if(debug['Q'])
+		print("%P\n", p);
+}
+
+static int
+mediaop(Optab *o, int op, int osize, int z)
+{
+	switch(op){
+	case Pm:
+	case Pe:
+	case Pf2:
+	case Pf3:
+		if(osize != 1){
+			if(op != Pm)
+				*andptr++ = op;
+			*andptr++ = Pm;
+			op = o->op[++z];
+			break;
+		}
+	default:
+		if(andptr == and || andptr[-1] != Pm)
+			*andptr++ = Pm;
+		break;
+	}
+	*andptr++ = op;
+	return z;
+}
+
+void
+doasm(Prog *p)
+{
+	Optab *o;
+	Prog *q, pp;
+	uchar *t;
+	Movtab *mo;
+	int z, op, ft, tt, xo, l;
+	vlong v;
+
+	o = opindex[p->as];
+	if(o == nil) {
+		diag("asmins: missing op %P", p);
+		return;
+	}
+	ft = oclass(&p->from) * Ymax;
+	tt = oclass(&p->to) * Ymax;
+	t = o->ytab;
+	if(t == 0) {
+		diag("asmins: noproto %P", p);
+		return;
+	}
+	xo = o->op[0] == 0x0f;
+	for(z=0; *t; z+=t[3]+xo,t+=4)
+		if(ycover[ft+t[0]])
+		if(ycover[tt+t[1]])
+			goto found;
+	goto domov;
+
+found:
+	switch(o->prefix) {
+	case Pq:	/* 16 bit escape and opcode escape */
+		*andptr++ = Pe;
+		*andptr++ = Pm;
+		break;
+
+	case Pf2:	/* xmm opcode escape */
+	case Pf3:
+		*andptr++ = o->prefix;
+		*andptr++ = Pm;
+		break;
+
+	case Pm:	/* opcode escape */
+		*andptr++ = Pm;
+		break;
+
+	case Pe:	/* 16 bit escape */
+		*andptr++ = Pe;
+		break;
+
+	case Pw:	/* 64-bit escape */
+		if(p->mode != 64)
+			diag("asmins: illegal 64: %P", p);
+		rexflag |= Pw;
+		break;
+
+	case Pb:	/* botch */
+		bytereg(&p->from);
+		bytereg(&p->to);
+		break;
+
+	case P32:	/* 32 bit but illegal if 64-bit mode */
+		if(p->mode == 64)
+			diag("asmins: illegal in 64-bit mode: %P", p);
+		break;
+
+	case Py:	/* 64-bit only, no prefix */
+		if(p->mode != 64)
+			diag("asmins: illegal in %d-bit mode: %P", p->mode, p);
+		break;
+	}
+	v = vaddr(&p->from);
+	op = o->op[z];
+	if(op == 0x0f) {
+		*andptr++ = op;
+		op = o->op[++z];
+	}
+	switch(t[2]) {
+	default:
+		diag("asmins: unknown z %d %P", t[2], p);
+		return;
+
+	case Zpseudo:
+		break;
+
+	case Zlit:
+		for(; op = o->op[z]; z++)
+			*andptr++ = op;
+		break;
+
+	case Zmb_r:
+		bytereg(&p->from);
+		/* fall through */
+	case Zm_r:
+		*andptr++ = op;
+		asmand(&p->from, &p->to);
+		break;
+
+	case Zm_r_xm:
+		mediaop(o, op, t[3], z);
+		asmand(&p->from, &p->to);
+		break;
+
+	case Zm_r_xm_nr:
+		rexflag = 0;
+		mediaop(o, op, t[3], z);
+		asmand(&p->from, &p->to);
+		break;
+
+	case Zm_r_i_xm:
+		mediaop(o, op, t[3], z);
+		asmand(&p->from, &p->to);
+		*andptr++ = p->to.offset;
+		break;
+
+	case Zm_r_3d:
+		*andptr++ = 0x0f;
+		*andptr++ = 0x0f;
+		asmand(&p->from, &p->to);
+		*andptr++ = op;
+		break;
+
+	case Zibm_r:
+		*andptr++ = op;
+		asmand(&p->from, &p->to);
+		*andptr++ = p->to.offset;
+		break;
+
+	case Zaut_r:
+		*andptr++ = 0x8d;	/* leal */
+		if(p->from.type != D_ADDR)
+			diag("asmins: Zaut sb type ADDR");
+		p->from.type = p->from.index;
+		p->from.index = D_NONE;
+		asmand(&p->from, &p->to);
+		p->from.index = p->from.type;
+		p->from.type = D_ADDR;
+		break;
+
+	case Zm_o:
+		*andptr++ = op;
+		asmando(&p->from, o->op[z+1]);
+		break;
+
+	case Zr_m:
+		*andptr++ = op;
+		asmand(&p->to, &p->from);
+		break;
+
+	case Zr_m_xm:
+		mediaop(o, op, t[3], z);
+		asmand(&p->to, &p->from);
+		break;
+
+	case Zr_m_xm_nr:
+		rexflag = 0;
+		mediaop(o, op, t[3], z);
+		asmand(&p->to, &p->from);
+		break;
+
+	case Zr_m_i_xm:
+		mediaop(o, op, t[3], z);
+		asmand(&p->to, &p->from);
+		*andptr++ = p->from.offset;
+		break;
+
+	case Zo_m:
+		*andptr++ = op;
+		asmando(&p->to, o->op[z+1]);
+		break;
+
+	case Zo_m64:
+		*andptr++ = op;
+		asmandsz(&p->to, o->op[z+1], 0, 1);
+		break;
+
+	case Zm_ibo:
+		v = vaddr(&p->to);
+		*andptr++ = op;
+		asmando(&p->from, o->op[z+1]);
+		*andptr++ = v;
+		break;
+
+	case Zibo_m:
+		*andptr++ = op;
+		asmando(&p->to, o->op[z+1]);
+		*andptr++ = v;
+		break;
+
+	case Zibo_m_xm:
+		z = mediaop(o, op, t[3], z);
+		asmando(&p->to, o->op[z+1]);
+		*andptr++ = v;
+		break;
+
+	case Z_ib:
+		v = vaddr(&p->to);
+	case Zib_:
+		*andptr++ = op;
+		*andptr++ = v;
+		break;
+
+	case Zib_rp:
+		rexflag |= regrex[p->to.type] & (Rxb|0x40);
+		*andptr++ = op + reg[p->to.type];
+		*andptr++ = v;
+		break;
+
+	case Zil_rp:
+		rexflag |= regrex[p->to.type] & Rxb;
+		*andptr++ = op + reg[p->to.type];
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zo_iw:
+		*andptr++ = op;
+		if(p->from.type != D_NONE){
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		break;
+
+	case Ziq_rp:
+		l = v>>32;
+		if(l == 0){
+			//p->mark |= 0100;
+			//print("zero: %llux %P\n", v, p);
+			rexflag &= ~(0x40|Rxw);
+			rexflag |= regrex[p->to.type] & Rxb;
+			*andptr++ = 0xb8 + reg[p->to.type];
+			put4(v);
+		}else if(l == -1 && (v&((uvlong)1<<31))!=0){	/* sign extend */
+			//p->mark |= 0100;
+			//print("sign: %llux %P\n", v, p);
+			*andptr ++ = 0xc7;
+			asmando(&p->to, 0);
+			put4(v);
+		}else{	/* need all 8 */
+			//print("all: %llux %P\n", v, p);
+			rexflag |= regrex[p->to.type] & Rxb;
+			*andptr++ = op + reg[p->to.type];
+			put8(v);
+		}
+		break;
+
+	case Zib_rr:
+		*andptr++ = op;
+		asmand(&p->to, &p->to);
+		*andptr++ = v;
+		break;
+
+	case Z_il:
+		v = vaddr(&p->to);
+	case Zil_:
+		*andptr++ = op;
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zm_ilo:
+		v = vaddr(&p->to);
+		*andptr++ = op;
+		asmando(&p->from, o->op[z+1]);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zilo_m:
+		*andptr++ = op;
+		asmando(&p->to, o->op[z+1]);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zil_rr:
+		*andptr++ = op;
+		asmand(&p->to, &p->to);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Z_rp:
+		rexflag |= regrex[p->to.type] & (Rxb|0x40);
+		*andptr++ = op + reg[p->to.type];
+		break;
+
+	case Zrp_:
+		rexflag |= regrex[p->from.type] & (Rxb|0x40);
+		*andptr++ = op + reg[p->from.type];
+		break;
+
+	case Zclr:
+		*andptr++ = op;
+		asmand(&p->to, &p->to);
+		break;
+
+	case Zbr:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v >= -128 && v <= 127) {
+				*andptr++ = op;
+				*andptr++ = v;
+			} else {
+				v -= 6-2;
+				*andptr++ = 0x0f;
+				*andptr++ = o->op[z+1];
+				*andptr++ = v;
+				*andptr++ = v>>8;
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+			}
+		}
+		break;
+
+	case Zcall:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 5;
+			if(dlm && curp != P && p->to.sym->type == SUNDEF){
+				/* v = 0 - p->pc - 5; */
+				v = 0;
+				ckoff(p->to.sym, v);
+				v += p->to.sym->value;
+				dynreloc(p->to.sym, p->pc+1, 0);
+			}
+			*andptr++ = op;
+			*andptr++ = v;
+			*andptr++ = v>>8;
+			*andptr++ = v>>16;
+			*andptr++ = v>>24;
+		}
+		break;
+
+	case Zjmp:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v >= -128 && v <= 127) {
+				*andptr++ = op;
+				*andptr++ = v;
+			} else {
+				v -= 5-2;
+				*andptr++ = o->op[z+1];
+				*andptr++ = v;
+				*andptr++ = v>>8;
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+			}
+		}
+		break;
+
+	case Zloop:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v < -128 || v > 127)
+				diag("loop too far: %P", p);
+			*andptr++ = op;
+			*andptr++ = v;
+		}
+		break;
+
+	case Zbyte:
+		*andptr++ = v;
+		if(op > 1) {
+			*andptr++ = v>>8;
+			if(op > 2) {
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+				if(op > 4) {
+					*andptr++ = v>>32;
+					*andptr++ = v>>40;
+					*andptr++ = v>>48;
+					*andptr++ = v>>56;
+				}
+			}
+		}
+		break;
+	}
+	return;
+
+domov:
+	for(mo=ymovtab; mo->as; mo++)
+		if(p->as == mo->as)
+		if(ycover[ft+mo->ft])
+		if(ycover[tt+mo->tt]){
+			t = mo->op;
+			goto mfound;
+		}
+bad:
+	if(p->mode != 64){
+		/*
+		 * here, the assembly has failed.
+		 * if its a byte instruction that has
+		 * unaddressable registers, try to
+		 * exchange registers and reissue the
+		 * instruction with the operands renamed.
+		 */
+		pp = *p;
+		z = p->from.type;
+		if(z >= D_BP && z <= D_DI) {
+			if(isax(&p->to)) {
+				*andptr++ = 0x87;			/* xchg lhs,bx */
+				asmando(&p->from, reg[D_BX]);
+				subreg(&pp, z, D_BX);
+				doasm(&pp);
+				*andptr++ = 0x87;			/* xchg lhs,bx */
+				asmando(&p->from, reg[D_BX]);
+			} else {
+				*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
+				subreg(&pp, z, D_AX);
+				doasm(&pp);
+				*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
+			}
+			return;
+		}
+		z = p->to.type;
+		if(z >= D_BP && z <= D_DI) {
+			if(isax(&p->from)) {
+				*andptr++ = 0x87;			/* xchg rhs,bx */
+				asmando(&p->to, reg[D_BX]);
+				subreg(&pp, z, D_BX);
+				doasm(&pp);
+				*andptr++ = 0x87;			/* xchg rhs,bx */
+				asmando(&p->to, reg[D_BX]);
+			} else {
+				*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
+				subreg(&pp, z, D_AX);
+				doasm(&pp);
+				*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
+			}
+			return;
+		}
+	}
+	diag("doasm: notfound from=%ux to=%ux %P", p->from.type, p->to.type, p);
+	return;
+
+mfound:
+	switch(mo->code) {
+	default:
+		diag("asmins: unknown mov %d %P", mo->code, p);
+		break;
+
+	case 0:	/* lit */
+		for(z=0; t[z]!=E; z++)
+			*andptr++ = t[z];
+		break;
+
+	case 1:	/* r,m */
+		*andptr++ = t[0];
+		asmando(&p->to, t[1]);
+		break;
+
+	case 2:	/* m,r */
+		*andptr++ = t[0];
+		asmando(&p->from, t[1]);
+		break;
+
+	case 3:	/* r,m - 2op */
+		*andptr++ = t[0];
+		*andptr++ = t[1];
+		asmando(&p->to, t[2]);
+		rexflag |= regrex[p->from.type] & (Rxr|0x40);
+		break;
+
+	case 4:	/* m,r - 2op */
+		*andptr++ = t[0];
+		*andptr++ = t[1];
+		asmando(&p->from, t[2]);
+		rexflag |= regrex[p->to.type] & (Rxr|0x40);
+		break;
+
+	case 5:	/* load full pointer, trash heap */
+		if(t[0])
+			*andptr++ = t[0];
+		switch(p->to.index) {
+		default:
+			goto bad;
+		case D_DS:
+			*andptr++ = 0xc5;
+			break;
+		case D_SS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb2;
+			break;
+		case D_ES:
+			*andptr++ = 0xc4;
+			break;
+		case D_FS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb4;
+			break;
+		case D_GS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb5;
+			break;
+		}
+		asmand(&p->from, &p->to);
+		break;
+
+	case 6:	/* double shift */
+		if(t[0] == Pw){
+			if(p->mode != 64)
+				diag("asmins: illegal 64: %P", p);
+			rexflag |= Pw;
+			t++;
+		}else if(t[0] == Pe){
+			*andptr++ = Pe;
+			t++;
+		}
+		z = p->from.type;
+		switch(z) {
+		default:
+			goto bad;
+		case D_CONST:
+			*andptr++ = 0x0f;
+			*andptr++ = t[0];
+			asmandsz(&p->to, reg[p->from.index], regrex[p->from.index], 0);
+			*andptr++ = p->from.offset;
+			break;
+		case D_CL:
+		case D_CX:
+			*andptr++ = 0x0f;
+			*andptr++ = t[1];
+			asmandsz(&p->to, reg[p->from.index], regrex[p->from.index], 0);
+			break;
+		}
+		break;
+	}
+}
+
+void
+asmins(Prog *p)
+{
+	int n, np, c;
+
+	rexflag = 0;
+	andptr = and;
+	asmode = p->mode;
+	doasm(p);
+	if(rexflag){
+		/*
+		 * as befits the whole approach of the architecture,
+		 * the rex prefix must appear before the first opcode byte
+		 * (and thus after any 66/67/f2/f3/26/2e/3e prefix bytes, but
+		 * before the 0f opcode escape!), or it might be ignored.
+		 * note that the handbook often misleadingly shows 66/f2/f3 in `opcode'.
+		 */
+		if(p->mode != 64)
+			diag("asmins: illegal in mode %d: %P", p->mode, p);
+		n = andptr - and;
+		for(np = 0; np < n; np++) {
+			c = and[np];
+			if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26)
+				break;
+		}
+		memmove(and+np+1, and+np, n-np);
+		and[np] = 0x40 | rexflag;
+		andptr++;
+	}
+}
+
+enum{
+	ABSD = 0,
+	ABSU = 1,
+	RELD = 2,
+	RELU = 3,
+};
+
+int modemap[4] = { 0, 1, -1, 2, };
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+	int n;
+	int t;
+	uchar *m;
+	ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+	int t;
+	uchar *m, *nm;
+	ulong *a, *na;
+
+	t = r->t;
+	r->t += 64;
+	m = r->m;
+	a = r->a;
+	r->m = nm = malloc(r->t*sizeof(uchar));
+	r->a = na = malloc(r->t*sizeof(ulong));
+	memmove(nm, m, t*sizeof(uchar));
+	memmove(na, a, t*sizeof(ulong));
+	free(m);
+	free(a);
+}
+
+void
+dynreloc(Sym *s, ulong v, int abs)
+{
+	int i, k, n;
+	uchar *m;
+	ulong *a;
+	Reloc *r;
+
+	if(s->type == SUNDEF)
+		k = abs ? ABSU : RELU;
+	else
+		k = abs ? ABSD : RELD;
+	/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */
+	k = modemap[k];
+	r = &rels;
+	n = r->n;
+	if(n >= r->t)
+		grow(r);
+	m = r->m;
+	a = r->a;
+	for(i = n; i > 0; i--){
+		if(v < a[i-1]){	/* happens occasionally for data */
+			m[i] = m[i-1];
+			a[i] = a[i-1];
+		}
+		else
+			break;
+	}
+	m[i] = k;
+	a[i] = v;
+	r->n++;
+}
+
+static int
+sput(char *s)
+{
+	char *p;
+
+	p = s;
+	while(*s)
+		cput(*s++);
+	cput(0);
+	return s-p+1;
+}
+
+void
+asmdyn()
+{
+	int i, n, t, c;
+	Sym *s;
+	ulong la, ra, *a;
+	vlong off;
+	uchar *m;
+	Reloc *r;
+
+	cflush();
+	off = seek(cout, 0, 1);
+	lput(0);
+	t = 0;
+	lput(imports);
+	t += 4;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type == SUNDEF){
+				lput(s->sig);
+				t += 4;
+				t += sput(s->name);
+			}
+
+	la = 0;
+	r = &rels;
+	n = r->n;
+	m = r->m;
+	a = r->a;
+	lput(n);
+	t += 4;
+	for(i = 0; i < n; i++){
+		ra = *a-la;
+		if(*a < la)
+			diag("bad relocation order");
+		if(ra < 256)
+			c = 0;
+		else if(ra < 65536)
+			c = 1;
+		else
+			c = 2;
+		cput((c<<6)|*m++);
+		t++;
+		if(c == 0){
+			cput(ra);
+			t++;
+		}
+		else if(c == 1){
+			wput(ra);
+			t += 2;
+		}
+		else{
+			lput(ra);
+			t += 4;
+		}
+		la = *a++;
+	}
+
+	cflush();
+	seek(cout, off, 0);
+	lput(t);
+
+	if(debug['v']){
+		Bprint(&bso, "import table entries = %d\n", imports);
+		Bprint(&bso, "export table entries = %d\n", exports);
+	}
+}
--- /dev/null
+++ b/utils/8a/a.h
@@ -1,0 +1,194 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../8c/8.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Ref	Ref;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+typedef	struct	Gen2 	Gen2;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		500
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	Ref*	ref;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+struct	Ref
+{
+	int	class;
+};
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	double	dval;
+	char	sval[8];
+	long	offset;
+	long	offset2;
+	Sym*	sym;
+	short	type;
+	short	index;
+	short	scale;
+};
+struct	Gen2
+{
+	Gen	from;
+	Gen	to;
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+};
+
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	checkscale(int);
+void	pinit(char*);
+void	cclean(void);
+int	isreg(Gen*);
+void	outcode(int, Gen2*);
+void	outhist(void);
+void	zaddr(Gen*, int);
+void	zname(char*, int, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	macprag(void);
+void	maclin(void);
+void	macif(int);
+void	macend(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+enum	/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/8a/a.y
@@ -1,0 +1,577 @@
+%{
+#include "a.h"
+%}
+%union	{
+	Sym	*sym;
+	long	lval;
+	struct {
+		long v1;
+		long v2;
+	} con2;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+	Gen2	gen2;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
+%token	<lval>	LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG
+%token	<lval>	LCONST LFP LPC LSB
+%token	<lval>	LBREG LLREG LSREG LFREG
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST LSP
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset
+%type	<con2>	con2
+%type	<gen>	mem imm imm2 reg nam rel rem rim rom omem nmem
+%type	<gen2>	nonnon nonrel nonrem rimnon rimrem remrim
+%type	<gen2>	spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+	LNAME '=' expr
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LTYPE0 nonnon	{ outcode($1, &$2); }
+|	LTYPE1 nonrem	{ outcode($1, &$2); }
+|	LTYPE2 rimnon	{ outcode($1, &$2); }
+|	LTYPE3 rimrem	{ outcode($1, &$2); }
+|	LTYPE4 remrim	{ outcode($1, &$2); }
+|	LTYPER nonrel	{ outcode($1, &$2); }
+|	LTYPED spec1	{ outcode($1, &$2); }
+|	LTYPET spec2	{ outcode($1, &$2); }
+|	LTYPEC spec3	{ outcode($1, &$2); }
+|	LTYPEN spec4	{ outcode($1, &$2); }
+|	LTYPES spec5	{ outcode($1, &$2); }
+|	LTYPEM spec6	{ outcode($1, &$2); }
+|	LTYPEI spec7	{ outcode($1, &$2); }
+|	LTYPEG spec8	{ outcode($1, &$2); }
+
+nonnon:
+	{
+		$$.from = nullgen;
+		$$.to = nullgen;
+	}
+|	','
+	{
+		$$.from = nullgen;
+		$$.to = nullgen;
+	}
+
+rimrem:
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+remrim:
+	rem ',' rim
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+rimnon:
+	rim ','
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+
+nonrem:
+	',' rem
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rem
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+nonrel:
+	',' rel
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rel
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+spec1:	/* DATA */
+	nam '/' con ',' imm
+	{
+		$$.from = $1;
+		$$.from.scale = $3;
+		$$.to = $5;
+	}
+
+spec2:	/* TEXT */
+	mem ',' imm
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	mem ',' con ',' imm
+	{
+		$$.from = $1;
+		$$.from.scale = $3;
+		$$.to = $5;
+	}
+
+spec3:	/* JMP/CALL */
+	',' rom
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+	}
+|	rom
+	{
+		$$.from = nullgen;
+		$$.to = $1;
+	}
+
+spec4:	/* NOP */
+	nonnon
+|	nonrem
+
+spec5:	/* SHL/SHR */
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	rim ',' rem ':' LLREG
+	{
+		$$.from = $1;
+		$$.to = $3;
+		if($$.from.index != D_NONE)
+			yyerror("dp shift with lhs index");
+		$$.from.index = $5;
+	}
+
+spec6:	/* MOVW/MOVL */
+	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	rim ',' rem ':' LSREG
+	{
+		$$.from = $1;
+		$$.to = $3;
+		if($$.to.index != D_NONE)
+			yyerror("dp move with lhs index");
+		$$.to.index = $5;
+	}
+
+spec7:
+	rim ','
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim
+	{
+		$$.from = $1;
+		$$.to = nullgen;
+	}
+|	rim ',' rem
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+
+spec8:	/* GLOBL */
+	mem ',' imm
+	{
+		$$.from = $1;
+		$$.to = $3;
+	}
+|	mem ',' con ',' imm
+	{
+		$$.from = $1;
+		$$.from.scale = $3;
+		$$.to = $5;
+	}
+
+rem:
+	reg
+|	mem
+
+rom:
+	rel
+|	nmem
+|	'*' reg
+	{
+		$$ = $2;
+	}
+|	'*' omem
+	{
+		$$ = $2;
+	}
+|	reg
+|	omem
+|	imm
+
+rim:
+	rem
+|	imm
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+reg:
+	LBREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LFREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LLREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+|	LSP
+	{
+		$$ = nullgen;
+		$$.type = D_SP;
+	}
+|	LSREG
+	{
+		$$ = nullgen;
+		$$.type = $1;
+	}
+
+imm:
+	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' nam
+	{
+		$$ = $2;
+		$$.index = $2.type;
+		$$.type = D_ADDR;
+		/*
+		if($2.type == D_AUTO || $2.type == D_PARAM)
+			yyerror("constant cannot be automatic: %s",
+				$2.sym->name);
+		 */
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '(' LFCONST ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $3;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+imm2:
+	'$' con2
+	{
+		$$ = nullgen;
+		$$.type = D_CONST2;
+		$$.offset = $2.v1;
+		$$.offset2 = $2.v2;
+	}
+
+con2:
+	LCONST
+	{
+		$$.v1 = $1;
+		$$.v2 = 0;
+	}
+|	'-' LCONST
+	{
+		$$.v1 = -$2;
+		$$.v2 = 0;
+	}
+|	LCONST '-' LCONST
+	{
+		$$.v1 = $1;
+		$$.v2 = $3;
+	}
+|	'-' LCONST '-' LCONST
+	{
+		$$.v1 = -$2;
+		$$.v2 = $4;
+	}
+
+mem:
+	omem
+|	nmem
+
+omem:
+	con
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.offset = $1;
+	}
+|	con '(' LLREG ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$3;
+		$$.offset = $1;
+	}
+|	con '(' LSP ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_SP;
+		$$.offset = $1;
+	}
+|	con '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.offset = $1;
+		$$.index = $3;
+		$$.scale = $5;
+		checkscale($$.scale);
+	}
+|	con '(' LLREG ')' '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$3;
+		$$.offset = $1;
+		$$.index = $6;
+		$$.scale = $8;
+		checkscale($$.scale);
+	}
+|	'(' LLREG ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$2;
+	}
+|	'(' LSP ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_SP;
+	}
+|	con '(' LSREG ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$3;
+		$$.offset = $1;
+	}
+|	'(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+D_NONE;
+		$$.index = $2;
+		$$.scale = $4;
+		checkscale($$.scale);
+	}
+|	'(' LLREG ')' '(' LLREG '*' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_INDIR+$2;
+		$$.index = $5;
+		$$.scale = $7;
+		checkscale($$.scale);
+	}
+
+nmem:
+	nam
+	{
+		$$ = $1;
+	}
+|	nam '(' LLREG '*' con ')'
+	{
+		$$ = $1;
+		$$.index = $3;
+		$$.scale = $5;
+		checkscale($$.scale);
+	}
+
+nam:
+	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+	{
+		$$ = D_AUTO;
+	}
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/8a/l.s
@@ -1,0 +1,704 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		(20)			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+
+/*
+ *  Address spaces
+ *
+ *  User is at 0-2GB
+ *  Kernel is at 2GB-4GB
+ *
+ *  To avoid an extra page map, both the user stack (USTKTOP) and
+ *  the temporary user stack (TSTKTOP) should be in the the same
+ *  4 meg.
+ */
+#define	UZERO		0			/* base of user address space */
+#define	UTZERO		(UZERO+BY2PG)		/* first address in user text */
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		KZERO			/* first address in kernel text */
+#define	USERADDR	0xC0000000		/* struct User */
+#define	UREGADDR	(USERADDR+BY2PG-4*19)	
+#define	TSTKTOP		USERADDR		/* end of new stack in sysexec */
+#define TSTKSIZ 10
+#define	USTKTOP		(TSTKTOP-TSTKSIZ*BY2PG)	/* byte just beyond user stack */
+#define	USTKSIZE	(16*1024*1024 - TSTKSIZ*BY2PG)	/* size of user stack */
+#define ROMBIOS		(KZERO|0xF0000)
+
+#define	MACHSIZE	4096
+
+#define isphys(x) (((ulong)x)&KZERO)
+
+/*
+ *  known 80386 segments (in GDT) and their selectors
+ */
+#define	NULLSEG	0	/* null segment */
+#define	KDSEG	1	/* kernel data/stack */
+#define	KESEG	2	/* kernel executable */	
+#define	UDSEG	3	/* user data/stack */
+#define	UESEG	4	/* user executable */
+#define TSSSEG	5	/* task segment */
+
+#define SELGDT	(0<<3)	/* selector is in gdt */
+#define	SELLDT	(1<<3)	/* selector is in ldt */
+
+#define SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
+
+#define NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
+#define KESEL	SELECTOR(KESEG, SELGDT, 0)
+#define KDSEL	SELECTOR(KDSEG, SELGDT, 0)
+#define UESEL	SELECTOR(UESEG, SELGDT, 3)
+#define UDSEL	SELECTOR(UDSEG, SELGDT, 3)
+#define TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
+
+/*
+ *  fields in segment descriptors
+ */
+#define SEGDATA	(0x10<<8)	/* data/stack segment */
+#define SEGEXEC	(0x18<<8)	/* executable segment */
+#define	SEGTSS	(0x9<<8)	/* TSS segment */
+#define SEGCG	(0x0C<<8)	/* call gate */
+#define	SEGIG	(0x0E<<8)	/* interrupt gate */
+#define SEGTG	(0x0F<<8)	/* task gate */
+#define SEGTYPE	(0x1F<<8)
+
+#define SEGP	(1<<15)		/* segment present */
+#define SEGPL(x) ((x)<<13)	/* priority level */
+#define SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
+#define SEGG	(1<<23)		/* granularity 1==4k (for other) */
+#define SEGE	(1<<10)		/* expand down */
+#define SEGW	(1<<9)		/* writable (for data/stack) */
+#define	SEGR	(1<<9)		/* readable (for code) */
+#define SEGD	(1<<22)		/* default 1==32bit (for code) */
+
+/*
+ *  virtual MMU
+ */
+#define PTEMAPMEM	(1024*1024)	/* ??? */	
+#define SEGMAPSIZE	16		/* ??? */
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)	/* ??? */
+#define PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ *  physical MMU
+ */
+#define	PTEVALID	(1<<0)
+#define	PTEUNCACHED	0		/* everything is uncached */
+#define PTEWRITE	(1<<1)
+#define	PTERONLY	(0<<1)
+#define	PTEKERNEL	(0<<2)
+#define	PTEUSER		(1<<2)
+
+/*
+ *  flag register bits that we care about
+ */
+#define IFLAG	0x200
+
+#define OP16	BYTE	$0x66
+
+/*
+ *	about to walk all over ms/dos - turn off interrupts
+ */
+TEXT	origin(SB),$0
+
+	CLI
+
+#ifdef BOOT
+/*
+ *	This part of l.s is used only in the boot kernel.
+ *	It assumes that we are in real address mode, i.e.,
+ *	that we look like an 8086.
+ */
+/*
+ *	relocate everything to a half meg and jump there
+ *	- looks wierd because it is being assembled by a 32 bit
+ *	  assembler for a 16 bit world
+ */
+	MOVL	$0,BX
+	INCL	BX
+	SHLL	$15,BX
+	MOVL	BX,CX
+	MOVW	BX,ES
+	MOVL	$0,SI
+	MOVL	SI,DI
+	CLD; REP; MOVSL
+/*	JMPFAR	0X8000:$lowcore(SB) /**/
+	 BYTE	$0xEA
+	 WORD	$lowcore(SB)
+	 WORD	$0X8000
+
+TEXT	lowcore(SB),$0
+
+/*
+ *	now that we're in low core, update the DS
+ */
+
+	MOVW	BX,DS
+
+/*
+ * 	goto protected mode
+ */
+/*	MOVL	tgdtptr(SB),GDTR /**/
+	 BYTE	$0x0f
+	 BYTE	$0x01
+	 BYTE	$0x16
+	 WORD	$tgdtptr(SB)
+	MOVL	CR0,AX
+	ORL	$1,AX
+	MOVL	AX,CR0
+
+/*
+ *	clear prefetch queue (wierd code to avoid optimizations)
+ */
+	CLC
+	JCC	flush
+	MOVL	AX,AX
+flush:
+
+/*
+ *	set all segs
+ */
+/*	MOVW	$SELECTOR(1, SELGDT, 0),AX	/**/
+	 BYTE	$0xc7
+	 BYTE	$0xc0
+	 WORD	$SELECTOR(1, SELGDT, 0)
+	MOVW	AX,DS
+	MOVW	AX,SS
+	MOVW	AX,ES
+	MOVW	AX,FS
+	MOVW	AX,GS
+
+/*	JMPFAR	SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
+	 BYTE	$0x66
+	 BYTE	$0xEA
+	 LONG	$mode32bit-KZERO(SB)
+	 WORD	$SELECTOR(2, SELGDT, 0)
+
+TEXT	mode32bit(SB),$0
+
+#endif BOOT
+
+	/*
+	 * Clear BSS
+	 */
+	LEAL	edata-KZERO(SB),SI
+	MOVL	SI,DI
+	ADDL	$4,DI
+	MOVL	$0,AX
+	MOVL	AX,(SI)
+	LEAL	end-KZERO(SB),CX
+	SUBL	DI,CX
+	SHRL	$2,CX
+	CLD; REP; MOVSL
+
+	/*
+	 *  make a bottom level page table page that maps the first
+	 *  16 meg of physical memory
+	 */
+	LEAL	tpt-KZERO(SB),AX	/* get phys addr of temporary page table */
+	ADDL	$(BY2PG-1),AX		/* must be page aligned */
+	ANDL	$(~(BY2PG-1)),AX	/* ... */
+	MOVL	$(4*1024),CX		/* pte's per page */
+	MOVL	$((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
+setpte:
+	MOVL	BX,-4(AX)(CX*4)
+	SUBL	$(1<<PGSHIFT),BX
+	LOOP	setpte
+
+	/*
+	 *  make a top level page table page that maps the first
+	 *  16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
+	 */
+	MOVL	AX,BX
+	ADDL	$(4*BY2PG),AX
+	ADDL	$(PTEVALID|PTEKERNEL|PTEWRITE),BX
+	MOVL	BX,0(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,4(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,8(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
+	ADDL	$BY2PG,BX
+	MOVL	BX,12(AX)
+	MOVL	BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
+
+	/*
+	 *  point processor to top level page & turn on paging
+	 */
+	MOVL	AX,CR3
+	MOVL	CR0,AX
+	ORL	$0X80000000,AX
+	ANDL	$~(0x8|0x2),AX	/* TS=0, MP=0 */
+	MOVL	AX,CR0
+
+	/*
+	 *  use a jump to an absolute location to get the PC into
+	 *  KZERO.
+	 */
+	LEAL	tokzero(SB),AX
+	JMP*	AX
+
+TEXT	tokzero(SB),$0
+
+	/*
+	 *  stack and mach
+	 */
+	MOVL	$mach0(SB),SP
+	MOVL	SP,m(SB)
+	MOVL	$0,0(SP)
+	ADDL	$(MACHSIZE-4),SP	/* start stack under machine struct */
+	MOVL	$0, u(SB)
+
+	/*
+	 *  clear flags
+	 */
+	MOVL	$0,AX
+	PUSHL	AX
+	POPFL
+
+	CALL	main(SB)
+
+loop:
+	JMP	loop
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	u(SB), $4
+GLOBL	m(SB), $4
+GLOBL	tpt(SB), $(BY2PG*6)
+
+/*
+ *  gdt to get us to 32-bit/segmented/unpaged mode
+ */
+TEXT	tgdt(SB),$0
+
+	/* null descriptor */
+	LONG	$0
+	LONG	$0
+
+	/* data segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+
+	/* exec segment descriptor for 4 gigabytes (PL 0) */
+	LONG	$(0xFFFF)
+	LONG	$(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+/*
+ *  pointer to initial gdt
+ */
+TEXT	tgdtptr(SB),$0
+
+	WORD	$(3*8)
+	LONG	$tgdt-KZERO(SB)
+
+/*
+ *  input a byte
+ */
+TEXT	inb(SB),$0
+
+	MOVL	p+0(FP),DX
+	XORL	AX,AX
+	INB
+	RET
+
+/*
+ *  output a byte
+ */
+TEXT	outb(SB),$0
+
+	MOVL	p+0(FP),DX
+	MOVL	b+4(FP),AX
+	OUTB
+	RET
+
+/*
+ *  input a string of shorts from a port
+ */
+TEXT	inss(SB),$0
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),DI
+	MOVL	c+8(FP),CX
+	CLD; REP; OP16; INSL
+	RET
+
+/*
+ *  output a string of shorts to a port
+ */
+TEXT	outss(SB),$0
+	MOVL	p+0(FP),DX
+	MOVL	a+4(FP),SI
+	MOVL	c+8(FP),CX
+	CLD; REP; OP16; OUTSL
+	RET
+
+/*
+ *  test and set
+ */
+TEXT	tas(SB),$0
+	MOVL	$0xdeadead,AX
+	MOVL	l+0(FP),BX
+	XCHGL	AX,(BX)
+	RET
+
+/*
+ *  routines to load/read various system registers
+ */
+GLOBL	idtptr(SB),$6
+TEXT	putidt(SB),$0		/* interrupt descriptor table */
+	MOVL	t+0(FP),AX
+	MOVL	AX,idtptr+2(SB)
+	MOVL	l+4(FP),AX
+	MOVW	AX,idtptr(SB)
+	MOVL	idtptr(SB),IDTR
+	RET
+
+GLOBL	gdtptr(SB),$6
+TEXT	putgdt(SB),$0		/* global descriptor table */
+	MOVL	t+0(FP),AX
+	MOVL	AX,gdtptr+2(SB)
+	MOVL	l+4(FP),AX
+	MOVW	AX,gdtptr(SB)
+	MOVL	gdtptr(SB),GDTR
+	RET
+
+TEXT	putcr3(SB),$0		/* top level page table pointer */
+	MOVL	t+0(FP),AX
+	MOVL	AX,CR3
+	RET
+
+TEXT	puttr(SB),$0		/* task register */
+	MOVL	t+0(FP),AX
+	MOVW	AX,TASK
+	RET
+
+TEXT	getcr0(SB),$0		/* coprocessor bits */
+	MOVL	CR0,AX
+	RET
+
+TEXT	getcr2(SB),$0		/* fault address */
+	MOVL	CR2,AX
+	RET
+
+#define	FPOFF\
+	WAIT;\
+	MOVL	CR0,AX;\
+	ORL	$0x4,AX		/* EM=1 */;\
+	MOVL	AX,CR0
+
+#define	FPON\
+	MOVL	CR0,AX;\
+	ANDL	$~0x4,AX	/* EM=0 */;\
+	MOVL	AX,CR0
+	
+TEXT	fpoff(SB),$0		/* turn off floating point */
+	FPOFF
+	RET
+
+TEXT	fpinit(SB),$0		/* turn on & init the floating point */
+	FPON
+	FINIT
+	WAIT
+	PUSHW	$0x0330
+	FLDCW	0(SP)		/* ignore underflow/precision, signal others */
+	POPW	AX
+	WAIT
+	RET
+
+TEXT	fpsave(SB),$0		/* save floating point state and turn off */
+	MOVL	p+0(FP),AX
+	WAIT
+	FSAVE	0(AX)
+	FPOFF
+	RET
+
+TEXT	fprestore(SB),$0	/* turn on floating point and restore regs */
+	FPON
+	MOVL	p+0(FP),AX
+	FRSTOR	0(AX)
+	WAIT
+	RET
+
+TEXT	fpstatus(SB),$0		/* get floating point status */
+	FSTSW	AX
+	RET
+
+/*
+ *  special traps
+ */
+TEXT	intr0(SB),$0
+	PUSHL	$0
+	PUSHL	$0
+	JMP	intrcommon
+TEXT	intr1(SB),$0
+	PUSHL	$0
+	PUSHL	$1
+	JMP	intrcommon
+TEXT	intr2(SB),$0
+	PUSHL	$0
+	PUSHL	$2
+	JMP	intrcommon
+TEXT	intr3(SB),$0
+	PUSHL	$0
+	PUSHL	$3
+	JMP	intrcommon
+TEXT	intr4(SB),$0
+	PUSHL	$0
+	PUSHL	$4
+	JMP	intrcommon
+TEXT	intr5(SB),$0
+	PUSHL	$0
+	PUSHL	$5
+	JMP	intrcommon
+TEXT	intr6(SB),$0
+	PUSHL	$0
+	PUSHL	$6
+	JMP	intrcommon
+TEXT	intr7(SB),$0
+	PUSHL	$0
+	PUSHL	$7
+	JMP	intrcommon
+TEXT	intr8(SB),$0
+	PUSHL	$8
+	JMP	intrscommon
+TEXT	intr9(SB),$0
+	PUSHL	$0
+	PUSHL	$9
+	JMP	intrcommon
+TEXT	intr10(SB),$0
+	PUSHL	$10
+	JMP	intrscommon
+TEXT	intr11(SB),$0
+	PUSHL	$11
+	JMP	intrscommon
+TEXT	intr12(SB),$0
+	PUSHL	$12
+	JMP	intrscommon
+TEXT	intr13(SB),$0
+	PUSHL	$13
+	JMP	intrscommon
+TEXT	intr14(SB),$0
+	PUSHL	$14
+	JMP	intrscommon
+TEXT	intr15(SB),$0
+	PUSHL	$0
+	PUSHL	$15
+	JMP	intrcommon
+TEXT	intr16(SB),$0
+	PUSHL	$0
+	PUSHL	$16
+	JMP	intrcommon
+TEXT	intr24(SB),$0
+	PUSHL	$0
+	PUSHL	$24
+	JMP	intrcommon
+TEXT	intr25(SB),$0
+	PUSHL	$0
+	PUSHL	$25
+	JMP	intrcommon
+TEXT	intr26(SB),$0
+	PUSHL	$0
+	PUSHL	$26
+	JMP	intrcommon
+TEXT	intr27(SB),$0
+	PUSHL	$0
+	PUSHL	$27
+	JMP	intrcommon
+TEXT	intr28(SB),$0
+	PUSHL	$0
+	PUSHL	$28
+	JMP	intrcommon
+TEXT	intr29(SB),$0
+	PUSHL	$0
+	PUSHL	$29
+	JMP	intrcommon
+TEXT	intr30(SB),$0
+	PUSHL	$0
+	PUSHL	$30
+	JMP	intrcommon
+TEXT	intr31(SB),$0
+	PUSHL	$0
+	PUSHL	$31
+	JMP	intrcommon
+TEXT	intr32(SB),$0
+	PUSHL	$0
+	PUSHL	$16
+	JMP	intrcommon
+TEXT	intr33(SB),$0
+	PUSHL	$0
+	PUSHL	$33
+	JMP	intrcommon
+TEXT	intr34(SB),$0
+	PUSHL	$0
+	PUSHL	$34
+	JMP	intrcommon
+TEXT	intr35(SB),$0
+	PUSHL	$0
+	PUSHL	$35
+	JMP	intrcommon
+TEXT	intr36(SB),$0
+	PUSHL	$0
+	PUSHL	$36
+	JMP	intrcommon
+TEXT	intr37(SB),$0
+	PUSHL	$0
+	PUSHL	$37
+	JMP	intrcommon
+TEXT	intr38(SB),$0
+	PUSHL	$0
+	PUSHL	$38
+	JMP	intrcommon
+TEXT	intr39(SB),$0
+	PUSHL	$0
+	PUSHL	$39
+	JMP	intrcommon
+TEXT	intr64(SB),$0
+	PUSHL	$0
+	PUSHL	$64
+	JMP	intrcommon
+TEXT	intrbad(SB),$0
+	PUSHL	$0
+	PUSHL	$0x1ff
+	JMP	intrcommon
+
+intrcommon:
+	PUSHL	DS
+	PUSHL	ES
+	PUSHL	FS
+	PUSHL	GS
+	PUSHAL
+	MOVL	$(KDSEL),AX
+	MOVW	AX,DS
+	MOVW	AX,ES
+	LEAL	0(SP),AX
+	PUSHL	AX
+	CALL	trap(SB)
+	POPL	AX
+	POPAL
+	POPL	GS
+	POPL	FS
+	POPL	ES
+	POPL	DS
+	ADDL	$8,SP	/* error code and trap type */
+	IRETL
+
+intrscommon:
+	PUSHL	DS
+	PUSHL	ES
+	PUSHL	FS
+	PUSHL	GS
+	PUSHAL
+	MOVL	$(KDSEL),AX
+	MOVW	AX,DS
+	MOVW	AX,ES
+	LEAL	0(SP),AX
+	PUSHL	AX
+	CALL	trap(SB)
+	POPL	AX
+	POPAL
+	POPL	GS
+	POPL	FS
+	POPL	ES
+	POPL	DS
+	ADDL	$8,SP	/* error code and trap type */
+	IRETL
+
+/*
+ *  interrupt level is interrupts on or off
+ */
+TEXT	spllo(SB),$0
+	PUSHFL
+	POPL	AX
+	STI
+	RET
+
+TEXT	splhi(SB),$0
+	PUSHFL
+	POPL	AX
+	CLI
+	RET
+
+TEXT	splx(SB),$0
+	MOVL	s+0(FP),AX
+	PUSHL	AX
+	POPFL
+	RET
+
+/*
+ *  do nothing whatsoever till interrupt happens
+ */
+TEXT	idle(SB),$0
+	HLT
+	RET
+
+/*
+ *  label consists of a stack pointer and a PC
+ */
+TEXT	gotolabel(SB),$0
+	MOVL	l+0(FP),AX
+	MOVL	0(AX),SP	/* restore sp */
+	MOVL	4(AX),AX	/* put return pc on the stack */
+	MOVL	AX,0(SP)
+	MOVL	$1,AX		/* return 1 */
+	RET
+
+TEXT	setlabel(SB),$0
+	MOVL	l+0(FP),AX
+	MOVL	SP,0(AX)	/* store sp */
+	MOVL	0(SP),BX	/* store return pc */
+	MOVL	BX,4(AX)
+	MOVL	$0,AX		/* return 0 */
+	RET
+
+/*
+ *  Used to get to the first process.
+ *  Set up an interrupt return frame and IRET to user level.
+ */
+TEXT	touser(SB),$0
+	PUSHL	$(UDSEL)		/* old ss */
+	PUSHL	$(USTKTOP)		/* old sp */
+	PUSHFL				/* old flags */
+	PUSHL	$(UESEL)		/* old cs */
+	PUSHL	$(UTZERO+32)		/* old pc */
+	MOVL	$(UDSEL),AX
+	MOVW	AX,DS
+	MOVW	AX,ES
+	MOVW	AX,GS
+	MOVW	AX,FS
+	IRETL
+
+/*
+ *  set configuration register
+ */
+TEXT	config(SB),$0
+	MOVL	l+0(FP),AX
+	MOVL	$0x3F3,DX
+	OUTB
+	OUTB
+	RET
--- /dev/null
+++ b/utils/8a/lex.c
@@ -1,0 +1,953 @@
+#include <ctype.h>
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = '8';
+	thestring = "386";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"AL",		LBREG,	D_AL,
+	"CL",		LBREG,	D_CL,
+	"DL",		LBREG,	D_DL,
+	"BL",		LBREG,	D_BL,
+	"AH",		LBREG,	D_AH,
+	"CH",		LBREG,	D_CH,
+	"DH",		LBREG,	D_DH,
+	"BH",		LBREG,	D_BH,
+
+	"AX",		LLREG,	D_AX,
+	"CX",		LLREG,	D_CX,
+	"DX",		LLREG,	D_DX,
+	"BX",		LLREG,	D_BX,
+/*	"SP",		LLREG,	D_SP,	*/
+	"BP",		LLREG,	D_BP,
+	"SI",		LLREG,	D_SI,
+	"DI",		LLREG,	D_DI,
+
+	"F0",		LFREG,	D_F0+0,
+	"F1",		LFREG,	D_F0+1,
+	"F2",		LFREG,	D_F0+2,
+	"F3",		LFREG,	D_F0+3,
+	"F4",		LFREG,	D_F0+4,
+	"F5",		LFREG,	D_F0+5,
+	"F6",		LFREG,	D_F0+6,
+	"F7",		LFREG,	D_F0+7,
+
+	"CS",		LSREG,	D_CS,
+	"SS",		LSREG,	D_SS,
+	"DS",		LSREG,	D_DS,
+	"ES",		LSREG,	D_ES,
+	"FS",		LSREG,	D_FS,
+	"GS",		LSREG,	D_GS,
+
+	"GDTR",		LBREG,	D_GDTR,
+	"IDTR",		LBREG,	D_IDTR,
+	"LDTR",		LBREG,	D_LDTR,
+	"MSW",		LBREG,	D_MSW,
+	"TASK",		LBREG,	D_TASK,
+
+	"CR0",		LBREG,	D_CR+0,
+	"CR1",		LBREG,	D_CR+1,
+	"CR2",		LBREG,	D_CR+2,
+	"CR3",		LBREG,	D_CR+3,
+	"CR4",		LBREG,	D_CR+4,
+	"CR5",		LBREG,	D_CR+5,
+	"CR6",		LBREG,	D_CR+6,
+	"CR7",		LBREG,	D_CR+7,
+
+	"DR0",		LBREG,	D_DR+0,
+	"DR1",		LBREG,	D_DR+1,
+	"DR2",		LBREG,	D_DR+2,
+	"DR3",		LBREG,	D_DR+3,
+	"DR4",		LBREG,	D_DR+4,
+	"DR5",		LBREG,	D_DR+5,
+	"DR6",		LBREG,	D_DR+6,
+	"DR7",		LBREG,	D_DR+7,
+
+	"TR0",		LBREG,	D_TR+0,
+	"TR1",		LBREG,	D_TR+1,
+	"TR2",		LBREG,	D_TR+2,
+	"TR3",		LBREG,	D_TR+3,
+	"TR4",		LBREG,	D_TR+4,
+	"TR5",		LBREG,	D_TR+5,
+	"TR6",		LBREG,	D_TR+6,
+	"TR7",		LBREG,	D_TR+7,
+
+	"AAA",		LTYPE0,	AAAA,
+	"AAD",		LTYPE0,	AAAD,
+	"AAM",		LTYPE0,	AAAM,
+	"AAS",		LTYPE0,	AAAS,
+	"ADCB",		LTYPE3,	AADCB,
+	"ADCL",		LTYPE3,	AADCL,
+	"ADCW",		LTYPE3,	AADCW,
+	"ADDB",		LTYPE3,	AADDB,
+	"ADDL",		LTYPE3,	AADDL,
+	"ADDW",		LTYPE3,	AADDW,
+	"ADJSP",	LTYPE2,	AADJSP,
+	"ANDB",		LTYPE3,	AANDB,
+	"ANDL",		LTYPE3,	AANDL,
+	"ANDW",		LTYPE3,	AANDW,
+	"ARPL",		LTYPE3,	AARPL,
+	"BOUNDL",	LTYPE3,	ABOUNDL,
+	"BOUNDW",	LTYPE3,	ABOUNDW,
+	"BSFL",		LTYPE3,	ABSFL,
+	"BSFW",		LTYPE3,	ABSFW,
+	"BSRL",		LTYPE3,	ABSRL,
+	"BSRW",		LTYPE3,	ABSRW,
+	"BTCL",		LTYPE3,	ABTCL,
+	"BTCW",		LTYPE3,	ABTCW,
+	"BTL",		LTYPE3,	ABTL,
+	"BTRL",		LTYPE3,	ABTRL,
+	"BTRW",		LTYPE3,	ABTRW,
+	"BTSL",		LTYPE3,	ABTSL,
+	"BTSW",		LTYPE3,	ABTSW,
+	"BTW",		LTYPE3,	ABTW,
+	"BYTE",		LTYPE2,	ABYTE,
+	"CALL",		LTYPEC,	ACALL,
+	"CLC",		LTYPE0,	ACLC,
+	"CLD",		LTYPE0,	ACLD,
+	"CLI",		LTYPE0,	ACLI,
+	"CLTS",		LTYPE0,	ACLTS,
+	"CMC",		LTYPE0,	ACMC,
+	"CMPB",		LTYPE4,	ACMPB,
+	"CMPL",		LTYPE4,	ACMPL,
+	"CMPW",		LTYPE4,	ACMPW,
+	"CMPSB",	LTYPE0,	ACMPSB,
+	"CMPSL",	LTYPE0,	ACMPSL,
+	"CMPSW",	LTYPE0,	ACMPSW,
+	"CMPXCHGB",	LTYPE3,	ACMPXCHGB,
+	"CMPXCHGL",	LTYPE3,	ACMPXCHGL,
+	"CMPXCHGW",	LTYPE3,	ACMPXCHGW,
+	"DAA",		LTYPE0,	ADAA,
+	"DAS",		LTYPE0,	ADAS,
+	"DATA",		LTYPED,	ADATA,
+	"DECB",		LTYPE1,	ADECB,
+	"DECL",		LTYPE1,	ADECL,
+	"DECW",		LTYPE1,	ADECW,
+	"DIVB",		LTYPE2,	ADIVB,
+	"DIVL",		LTYPE2,	ADIVL,
+	"DIVW",		LTYPE2,	ADIVW,
+	"END",		LTYPE0,	AEND,
+	"ENTER",	LTYPE2,	AENTER,
+	"GLOBL",	LTYPEG,	AGLOBL,
+	"HLT",		LTYPE0,	AHLT,
+	"IDIVB",	LTYPE2,	AIDIVB,
+	"IDIVL",	LTYPE2,	AIDIVL,
+	"IDIVW",	LTYPE2,	AIDIVW,
+	"IMULB",	LTYPEI,	AIMULB,
+	"IMULL",	LTYPEI,	AIMULL,
+	"IMULW",	LTYPEI,	AIMULW,
+	"INB",		LTYPE0,	AINB,
+	"INL",		LTYPE0,	AINL,
+	"INW",		LTYPE0,	AINW,
+	"INCB",		LTYPE1,	AINCB,
+	"INCL",		LTYPE1,	AINCL,
+	"INCW",		LTYPE1,	AINCW,
+	"INSB",		LTYPE0,	AINSB,
+	"INSL",		LTYPE0,	AINSL,
+	"INSW",		LTYPE0,	AINSW,
+	"INT",		LTYPE2,	AINT,
+	"INTO",		LTYPE0,	AINTO,
+	"IRETL",	LTYPE0,	AIRETL,
+	"IRETW",	LTYPE0,	AIRETW,
+
+	"JOS",		LTYPER,	AJOS,
+	"JO",		LTYPER,	AJOS,	/* alternate */
+	"JOC",		LTYPER,	AJOC,
+	"JNO",		LTYPER,	AJOC,	/* alternate */
+	"JCS",		LTYPER,	AJCS,
+	"JB",		LTYPER,	AJCS,	/* alternate */
+	"JC",		LTYPER,	AJCS,	/* alternate */
+	"JNAE",		LTYPER,	AJCS,	/* alternate */
+	"JLO",		LTYPER,	AJCS,	/* alternate */
+	"JCC",		LTYPER,	AJCC,
+	"JAE",		LTYPER,	AJCC,	/* alternate */
+	"JNB",		LTYPER,	AJCC,	/* alternate */
+	"JNC",		LTYPER,	AJCC,	/* alternate */
+	"JHS",		LTYPER,	AJCC,	/* alternate */
+	"JEQ",		LTYPER,	AJEQ,
+	"JE",		LTYPER,	AJEQ,	/* alternate */
+	"JZ",		LTYPER,	AJEQ,	/* alternate */
+	"JNE",		LTYPER,	AJNE,
+	"JNZ",		LTYPER,	AJNE,	/* alternate */
+	"JLS",		LTYPER,	AJLS,
+	"JBE",		LTYPER,	AJLS,	/* alternate */
+	"JNA",		LTYPER,	AJLS,	/* alternate */
+	"JHI",		LTYPER,	AJHI,
+	"JA",		LTYPER,	AJHI,	/* alternate */
+	"JNBE",		LTYPER,	AJHI,	/* alternate */
+	"JMI",		LTYPER,	AJMI,
+	"JS",		LTYPER,	AJMI,	/* alternate */
+	"JPL",		LTYPER,	AJPL,
+	"JNS",		LTYPER,	AJPL,	/* alternate */
+	"JPS",		LTYPER,	AJPS,
+	"JP",		LTYPER,	AJPS,	/* alternate */
+	"JPE",		LTYPER,	AJPS,	/* alternate */
+	"JPC",		LTYPER,	AJPC,
+	"JNP",		LTYPER,	AJPC,	/* alternate */
+	"JPO",		LTYPER,	AJPC,	/* alternate */
+	"JLT",		LTYPER,	AJLT,
+	"JL",		LTYPER,	AJLT,	/* alternate */
+	"JNGE",		LTYPER,	AJLT,	/* alternate */
+	"JGE",		LTYPER,	AJGE,
+	"JNL",		LTYPER,	AJGE,	/* alternate */
+	"JLE",		LTYPER,	AJLE,
+	"JNG",		LTYPER,	AJLE,	/* alternate */
+	"JGT",		LTYPER,	AJGT,
+	"JG",		LTYPER,	AJGT,	/* alternate */
+	"JNLE",		LTYPER,	AJGT,	/* alternate */
+
+	"JCXZ",		LTYPER,	AJCXZ,
+	"JMP",		LTYPEC,	AJMP,
+	"LAHF",		LTYPE0,	ALAHF,
+	"LARL",		LTYPE3,	ALARL,
+	"LARW",		LTYPE3,	ALARW,
+	"LEAL",		LTYPE3,	ALEAL,
+	"LEAW",		LTYPE3,	ALEAW,
+	"LEAVEL",	LTYPE0,	ALEAVEL,
+	"LEAVEW",	LTYPE0,	ALEAVEW,
+	"LOCK",		LTYPE0,	ALOCK,
+	"LODSB",	LTYPE0,	ALODSB,
+	"LODSL",	LTYPE0,	ALODSL,
+	"LODSW",	LTYPE0,	ALODSW,
+	"LONG",		LTYPE2,	ALONG,
+	"LOOP",		LTYPER,	ALOOP,
+	"LOOPEQ",	LTYPER,	ALOOPEQ,
+	"LOOPNE",	LTYPER,	ALOOPNE,
+	"LSLL",		LTYPE3,	ALSLL,
+	"LSLW",		LTYPE3,	ALSLW,
+	"MOVB",		LTYPE3,	AMOVB,
+	"MOVL",		LTYPEM,	AMOVL,
+	"MOVW",		LTYPEM,	AMOVW,
+	"MOVBLSX",	LTYPE3, AMOVBLSX,
+	"MOVBLZX",	LTYPE3, AMOVBLZX,
+	"MOVBWSX",	LTYPE3, AMOVBWSX,
+	"MOVBWZX",	LTYPE3, AMOVBWZX,
+	"MOVWLSX",	LTYPE3, AMOVWLSX,
+	"MOVWLZX",	LTYPE3, AMOVWLZX,
+	"MOVSB",	LTYPE0,	AMOVSB,
+	"MOVSL",	LTYPE0,	AMOVSL,
+	"MOVSW",	LTYPE0,	AMOVSW,
+	"MULB",		LTYPE2,	AMULB,
+	"MULL",		LTYPE2,	AMULL,
+	"MULW",		LTYPE2,	AMULW,
+	"NEGB",		LTYPE1,	ANEGB,
+	"NEGL",		LTYPE1,	ANEGL,
+	"NEGW",		LTYPE1,	ANEGW,
+	"NOP",		LTYPEN,	ANOP,
+	"NOTB",		LTYPE1,	ANOTB,
+	"NOTL",		LTYPE1,	ANOTL,
+	"NOTW",		LTYPE1,	ANOTW,
+	"ORB",		LTYPE3,	AORB,
+	"ORL",		LTYPE3,	AORL,
+	"ORW",		LTYPE3,	AORW,
+	"OUTB",		LTYPE0,	AOUTB,
+	"OUTL",		LTYPE0,	AOUTL,
+	"OUTW",		LTYPE0,	AOUTW,
+	"OUTSB",	LTYPE0,	AOUTSB,
+	"OUTSL",	LTYPE0,	AOUTSL,
+	"OUTSW",	LTYPE0,	AOUTSW,
+	"POPAL",	LTYPE0,	APOPAL,
+	"POPAW",	LTYPE0,	APOPAW,
+	"POPFL",	LTYPE0,	APOPFL,
+	"POPFW",	LTYPE0,	APOPFW,
+	"POPL",		LTYPE1,	APOPL,
+	"POPW",		LTYPE1,	APOPW,
+	"PUSHAL",	LTYPE0,	APUSHAL,
+	"PUSHAW",	LTYPE0,	APUSHAW,
+	"PUSHFL",	LTYPE0,	APUSHFL,
+	"PUSHFW",	LTYPE0,	APUSHFW,
+	"PUSHL",	LTYPE2,	APUSHL,
+	"PUSHW",	LTYPE2,	APUSHW,
+	"RCLB",		LTYPE3,	ARCLB,
+	"RCLL",		LTYPE3,	ARCLL,
+	"RCLW",		LTYPE3,	ARCLW,
+	"RCRB",		LTYPE3,	ARCRB,
+	"RCRL",		LTYPE3,	ARCRL,
+	"RCRW",		LTYPE3,	ARCRW,
+	"REP",		LTYPE0,	AREP,
+	"REPN",		LTYPE0,	AREPN,
+	"RET",		LTYPE0,	ARET,
+	"ROLB",		LTYPE3,	AROLB,
+	"ROLL",		LTYPE3,	AROLL,
+	"ROLW",		LTYPE3,	AROLW,
+	"RORB",		LTYPE3,	ARORB,
+	"RORL",		LTYPE3,	ARORL,
+	"RORW",		LTYPE3,	ARORW,
+	"SAHF",		LTYPE0,	ASAHF,
+	"SALB",		LTYPE3,	ASALB,
+	"SALL",		LTYPE3,	ASALL,
+	"SALW",		LTYPE3,	ASALW,
+	"SARB",		LTYPE3,	ASARB,
+	"SARL",		LTYPE3,	ASARL,
+	"SARW",		LTYPE3,	ASARW,
+	"SBBB",		LTYPE3,	ASBBB,
+	"SBBL",		LTYPE3,	ASBBL,
+	"SBBW",		LTYPE3,	ASBBW,
+	"SCASB",	LTYPE0,	ASCASB,
+	"SCASL",	LTYPE0,	ASCASL,
+	"SCASW",	LTYPE0,	ASCASW,
+	"SETCC",	LTYPE1,	ASETCC,
+	"SETCS",	LTYPE1,	ASETCS,
+	"SETEQ",	LTYPE1,	ASETEQ,
+	"SETGE",	LTYPE1,	ASETGE,
+	"SETGT",	LTYPE1,	ASETGT,
+	"SETHI",	LTYPE1,	ASETHI,
+	"SETLE",	LTYPE1,	ASETLE,
+	"SETLS",	LTYPE1,	ASETLS,
+	"SETLT",	LTYPE1,	ASETLT,
+	"SETMI",	LTYPE1,	ASETMI,
+	"SETNE",	LTYPE1,	ASETNE,
+	"SETOC",	LTYPE1,	ASETOC,
+	"SETOS",	LTYPE1,	ASETOS,
+	"SETPC",	LTYPE1,	ASETPC,
+	"SETPL",	LTYPE1,	ASETPL,
+	"SETPS",	LTYPE1,	ASETPS,
+	"CDQ",		LTYPE0,	ACDQ,
+	"CWD",		LTYPE0,	ACWD,
+	"SHLB",		LTYPE3,	ASHLB,
+	"SHLL",		LTYPES,	ASHLL,
+	"SHLW",		LTYPES,	ASHLW,
+	"SHRB",		LTYPE3,	ASHRB,
+	"SHRL",		LTYPES,	ASHRL,
+	"SHRW",		LTYPES,	ASHRW,
+	"STC",		LTYPE0,	ASTC,
+	"STD",		LTYPE0,	ASTD,
+	"STI",		LTYPE0,	ASTI,
+	"STOSB",	LTYPE0,	ASTOSB,
+	"STOSL",	LTYPE0,	ASTOSL,
+	"STOSW",	LTYPE0,	ASTOSW,
+	"SUBB",		LTYPE3,	ASUBB,
+	"SUBL",		LTYPE3,	ASUBL,
+	"SUBW",		LTYPE3,	ASUBW,
+	"SYSCALL",	LTYPE0,	ASYSCALL,
+	"TESTB",	LTYPE3,	ATESTB,
+	"TESTL",	LTYPE3,	ATESTL,
+	"TESTW",	LTYPE3,	ATESTW,
+	"TEXT",		LTYPET,	ATEXT,
+	"VERR",		LTYPE2,	AVERR,
+	"VERW",		LTYPE2,	AVERW,
+	"WAIT",		LTYPE0,	AWAIT,
+	"WORD",		LTYPE2,	AWORD,
+	"XCHGB",	LTYPE3,	AXCHGB,
+	"XCHGL",	LTYPE3,	AXCHGL,
+	"XCHGW",	LTYPE3,	AXCHGW,
+	"XLAT",		LTYPE2,	AXLAT,
+	"XORB",		LTYPE3,	AXORB,
+	"XORL",		LTYPE3,	AXORL,
+	"XORW",		LTYPE3,	AXORW,
+
+	"CMOVLCC",	LTYPE3,	ACMOVLCC,
+	"CMOVLCS",	LTYPE3,	ACMOVLCS,
+	"CMOVLEQ",	LTYPE3,	ACMOVLEQ,
+	"CMOVLGE",	LTYPE3,	ACMOVLGE,
+	"CMOVLGT",	LTYPE3,	ACMOVLGT,
+	"CMOVLHI",	LTYPE3,	ACMOVLHI,
+	"CMOVLLE",	LTYPE3,	ACMOVLLE,
+	"CMOVLLS",	LTYPE3,	ACMOVLLS,
+	"CMOVLLT",	LTYPE3,	ACMOVLLT,
+	"CMOVLMI",	LTYPE3,	ACMOVLMI,
+	"CMOVLNE",	LTYPE3,	ACMOVLNE,
+	"CMOVLOC",	LTYPE3,	ACMOVLOC,
+	"CMOVLOS",	LTYPE3,	ACMOVLOS,
+	"CMOVLPC",	LTYPE3,	ACMOVLPC,
+	"CMOVLPL",	LTYPE3,	ACMOVLPL,
+	"CMOVLPS",	LTYPE3,	ACMOVLPS,
+	"CMOVWCC",	LTYPE3,	ACMOVWCC,
+	"CMOVWCS",	LTYPE3,	ACMOVWCS,
+	"CMOVWEQ",	LTYPE3,	ACMOVWEQ,
+	"CMOVWGE",	LTYPE3,	ACMOVWGE,
+	"CMOVWGT",	LTYPE3,	ACMOVWGT,
+	"CMOVWHI",	LTYPE3,	ACMOVWHI,
+	"CMOVWLE",	LTYPE3,	ACMOVWLE,
+	"CMOVWLS",	LTYPE3,	ACMOVWLS,
+	"CMOVWLT",	LTYPE3,	ACMOVWLT,
+	"CMOVWMI",	LTYPE3,	ACMOVWMI,
+	"CMOVWNE",	LTYPE3,	ACMOVWNE,
+	"CMOVWOC",	LTYPE3,	ACMOVWOC,
+	"CMOVWOS",	LTYPE3,	ACMOVWOS,
+	"CMOVWPC",	LTYPE3,	ACMOVWPC,
+	"CMOVWPL",	LTYPE3,	ACMOVWPL,
+	"CMOVWPS",	LTYPE3,	ACMOVWPS,
+
+	"FMOVB",	LTYPE3, AFMOVB,
+	"FMOVBP",	LTYPE3, AFMOVBP,
+	"FMOVD",	LTYPE3, AFMOVD,
+	"FMOVDP",	LTYPE3, AFMOVDP,
+	"FMOVF",	LTYPE3, AFMOVF,
+	"FMOVFP",	LTYPE3, AFMOVFP,
+	"FMOVL",	LTYPE3, AFMOVL,
+	"FMOVLP",	LTYPE3, AFMOVLP,
+	"FMOVV",	LTYPE3, AFMOVV,
+	"FMOVVP",	LTYPE3, AFMOVVP,
+	"FMOVW",	LTYPE3, AFMOVW,
+	"FMOVWP",	LTYPE3, AFMOVWP,
+	"FMOVX",	LTYPE3, AFMOVX,
+	"FMOVXP",	LTYPE3, AFMOVXP,
+	"FCMOVCC",	LTYPE3, AFCMOVCC,
+	"FCMOVCS",	LTYPE3, AFCMOVCS,
+	"FCMOVEQ",	LTYPE3, AFCMOVEQ,
+	"FCMOVHI",	LTYPE3, AFCMOVHI,
+	"FCMOVLS",	LTYPE3, AFCMOVLS,
+	"FCMOVNE",	LTYPE3, AFCMOVNE,
+	"FCMOVNU",	LTYPE3, AFCMOVNU,
+	"FCMOVUN",	LTYPE3, AFCMOVUN,
+	"FCOMB",	LTYPE3, AFCOMB,
+	"FCOMBP",	LTYPE3, AFCOMBP,
+	"FCOMD",	LTYPE3, AFCOMD,
+	"FCOMDP",	LTYPE3, AFCOMDP,
+	"FCOMDPP",	LTYPE3, AFCOMDPP,
+	"FCOMF",	LTYPE3, AFCOMF,
+	"FCOMFP",	LTYPE3, AFCOMFP,
+	"FCOMI",	LTYPE3, AFCOMI,
+	"FCOMIP",	LTYPE3, AFCOMIP,
+	"FCOML",	LTYPE3, AFCOML,
+	"FCOMLP",	LTYPE3, AFCOMLP,
+	"FCOMW",	LTYPE3, AFCOMW,
+	"FCOMWP",	LTYPE3, AFCOMWP,
+	"FUCOM",	LTYPE3, AFUCOM,
+	"FUCOMI",	LTYPE3, AFUCOMI,
+	"FUCOMIP",	LTYPE3, AFUCOMIP,
+	"FUCOMP",	LTYPE3, AFUCOMP,
+	"FUCOMPP",	LTYPE3, AFUCOMPP,
+	"FADDW",	LTYPE3, AFADDW,
+	"FADDL",	LTYPE3, AFADDL,
+	"FADDF",	LTYPE3, AFADDF,
+	"FADDD",	LTYPE3, AFADDD,
+	"FADDDP",	LTYPE3, AFADDDP,
+	"FSUBDP",	LTYPE3, AFSUBDP,
+	"FSUBW",	LTYPE3, AFSUBW,
+	"FSUBL",	LTYPE3, AFSUBL,
+	"FSUBF",	LTYPE3, AFSUBF,
+	"FSUBD",	LTYPE3, AFSUBD,
+	"FSUBRDP",	LTYPE3, AFSUBRDP,
+	"FSUBRW",	LTYPE3, AFSUBRW,
+	"FSUBRL",	LTYPE3, AFSUBRL,
+	"FSUBRF",	LTYPE3, AFSUBRF,
+	"FSUBRD",	LTYPE3, AFSUBRD,
+	"FMULDP",	LTYPE3, AFMULDP,
+	"FMULW",	LTYPE3, AFMULW,
+	"FMULL",	LTYPE3, AFMULL,
+	"FMULF",	LTYPE3, AFMULF,
+	"FMULD",	LTYPE3, AFMULD,
+	"FDIVDP",	LTYPE3, AFDIVDP,
+	"FDIVW",	LTYPE3, AFDIVW,
+	"FDIVL",	LTYPE3, AFDIVL,
+	"FDIVF",	LTYPE3, AFDIVF,
+	"FDIVD",	LTYPE3, AFDIVD,
+	"FDIVRDP",	LTYPE3, AFDIVRDP,
+	"FDIVRW",	LTYPE3, AFDIVRW,
+	"FDIVRL",	LTYPE3, AFDIVRL,
+	"FDIVRF",	LTYPE3, AFDIVRF,
+	"FDIVRD",	LTYPE3, AFDIVRD,
+	"FXCHD",	LTYPE3, AFXCHD,
+	"FFREE",	LTYPE1, AFFREE,
+	"FLDCW",	LTYPE2, AFLDCW,
+	"FLDENV",	LTYPE1, AFLDENV,
+	"FRSTOR",	LTYPE2, AFRSTOR,
+	"FSAVE",	LTYPE1, AFSAVE,
+	"FSTCW",	LTYPE1, AFSTCW,
+	"FSTENV",	LTYPE1, AFSTENV,
+	"FSTSW",	LTYPE1, AFSTSW,
+	"F2XM1",	LTYPE0, AF2XM1,
+	"FABS",		LTYPE0, AFABS,
+	"FCHS",		LTYPE0, AFCHS,
+	"FCLEX",	LTYPE0, AFCLEX,
+	"FCOS",		LTYPE0, AFCOS,
+	"FDECSTP",	LTYPE0, AFDECSTP,
+	"FINCSTP",	LTYPE0, AFINCSTP,
+	"FINIT",	LTYPE0, AFINIT,
+	"FLD1",		LTYPE0, AFLD1,
+	"FLDL2E",	LTYPE0, AFLDL2E,
+	"FLDL2T",	LTYPE0, AFLDL2T,
+	"FLDLG2",	LTYPE0, AFLDLG2,
+	"FLDLN2",	LTYPE0, AFLDLN2,
+	"FLDPI",	LTYPE0, AFLDPI,
+	"FLDZ",		LTYPE0, AFLDZ,
+	"FNOP",		LTYPE0, AFNOP,
+	"FPATAN",	LTYPE0, AFPATAN,
+	"FPREM",	LTYPE0, AFPREM,
+	"FPREM1",	LTYPE0, AFPREM1,
+	"FPTAN",	LTYPE0, AFPTAN,
+	"FRNDINT",	LTYPE0, AFRNDINT,
+	"FSCALE",	LTYPE0, AFSCALE,
+	"FSIN",		LTYPE0, AFSIN,
+	"FSINCOS",	LTYPE0, AFSINCOS,
+	"FSQRT",	LTYPE0, AFSQRT,
+	"FTST",		LTYPE0, AFTST,
+	"FXAM",		LTYPE0, AFXAM,
+	"FXTRACT",	LTYPE0, AFXTRACT,
+	"FYL2X",	LTYPE0, AFYL2X,
+	"FYL2XP1",	LTYPE0, AFYL2XP1,
+
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+	nullgen.type = D_NONE;
+	nullgen.index = D_NONE;
+	nullgen.scale = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		if(s->type != LNAME)
+			yyerror("double initialization %s", itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+checkscale(int scale)
+{
+
+	switch(scale) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+		return;
+	}
+	yyerror("scale must be 1248: %d", scale);
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+void
+cclean(void)
+{
+	Gen2 g2;
+
+	g2.from = nullgen;
+	g2.to = nullgen;
+	outcode(AEND, &g2);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);		/* as(2) */
+	Bputc(&obuf, ANAME>>8);
+	Bputc(&obuf, t);		/* type */
+	Bputc(&obuf, s);		/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i, t;
+	char *n;
+	Ieee e;
+
+	t = 0;
+	if(a->index != D_NONE || a->scale != 0)
+		t |= T_INDEX;
+	if(a->offset != 0)
+		t |= T_OFFSET;
+	if(s != 0)
+		t |= T_SYM;
+
+	switch(a->type) {
+	default:
+		t |= T_TYPE;
+		break;
+	case D_FCONST:
+		t |= T_FCONST;
+		break;
+	case D_CONST2:
+		t |= T_OFFSET|T_OFFSET2;
+		break;
+	case D_SCONST:
+		t |= T_SCONST;
+		break;
+	case D_NONE:
+		break;
+	}
+	Bputc(&obuf, t);
+
+	if(t & T_INDEX) {	/* implies index, scale */
+		Bputc(&obuf, a->index);
+		Bputc(&obuf, a->scale);
+	}
+	if(t & T_OFFSET) {	/* implies offset */
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+	}
+	if(t & T_OFFSET2) {
+		l = a->offset2;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+	}
+	if(t & T_SYM)		/* implies sym */
+		Bputc(&obuf, s);
+	if(t & T_FCONST) {
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		l = e.h;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		return;
+	}
+	if(t & T_SCONST) {
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		return;
+	}
+	if(t & T_TYPE)
+		Bputc(&obuf, a->type);
+}
+
+void
+outcode(int a, Gen2 *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+
+jackpot:
+	sf = 0;
+	s = g2->from.sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g2->from.type;
+		if(t == D_ADDR)
+			t = g2->from.index;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->to.sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->to.type;
+		if(t == D_ADDR)
+			t = g2->to.index;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(&g2->from, sf);
+	zaddr(&g2->to, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, ANAME>>8);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, AHISTORY>>8);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/8a/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=8a
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../8c/8.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/8c/8.out.h
@@ -1,0 +1,505 @@
+#define	NSYM	50
+#define	NSNAME	8
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+#define NOSPLIT	(1<<2)
+
+enum	as
+{
+	AXXX,
+	AAAA,
+	AAAD,
+	AAAM,
+	AAAS,
+	AADCB,
+	AADCL,
+	AADCW,
+	AADDB,
+	AADDL,
+	AADDW,
+	AADJSP,
+	AANDB,
+	AANDL,
+	AANDW,
+	AARPL,
+	ABOUNDL,
+	ABOUNDW,
+	ABSFL,
+	ABSFW,
+	ABSRL,
+	ABSRW,
+	ABTL,
+	ABTW,
+	ABTCL,
+	ABTCW,
+	ABTRL,
+	ABTRW,
+	ABTSL,
+	ABTSW,
+	ABYTE,
+	ACALL,
+	ACLC,
+	ACLD,
+	ACLI,
+	ACLTS,
+	ACMC,
+	ACMPB,
+	ACMPL,
+	ACMPW,
+	ACMPSB,
+	ACMPSL,
+	ACMPSW,
+	ADAA,
+	ADAS,
+	ADATA,
+	ADECB,
+	ADECL,
+	ADECW,
+	ADIVB,
+	ADIVL,
+	ADIVW,
+	AENTER,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AHLT,
+	AIDIVB,
+	AIDIVL,
+	AIDIVW,
+	AIMULB,
+	AIMULL,
+	AIMULW,
+	AINB,
+	AINL,
+	AINW,
+	AINCB,
+	AINCL,
+	AINCW,
+	AINSB,
+	AINSL,
+	AINSW,
+	AINT,
+	AINTO,
+	AIRETL,
+	AIRETW,
+	AJCC,
+	AJCS,
+	AJCXZ,
+	AJEQ,
+	AJGE,
+	AJGT,
+	AJHI,
+	AJLE,
+	AJLS,
+	AJLT,
+	AJMI,
+	AJMP,
+	AJNE,
+	AJOC,
+	AJOS,
+	AJPC,
+	AJPL,
+	AJPS,
+	ALAHF,
+	ALARL,
+	ALARW,
+	ALEAL,
+	ALEAW,
+	ALEAVEL,
+	ALEAVEW,
+	ALOCK,
+	ALODSB,
+	ALODSL,
+	ALODSW,
+	ALONG,
+	ALOOP,
+	ALOOPEQ,
+	ALOOPNE,
+	ALSLL,
+	ALSLW,
+	AMOVB,
+	AMOVL,
+	AMOVW,
+	AMOVBLSX,
+	AMOVBLZX,
+	AMOVBWSX,
+	AMOVBWZX,
+	AMOVWLSX,
+	AMOVWLZX,
+	AMOVSB,
+	AMOVSL,
+	AMOVSW,
+	AMULB,
+	AMULL,
+	AMULW,
+	ANAME,
+	ANEGB,
+	ANEGL,
+	ANEGW,
+	ANOP,
+	ANOTB,
+	ANOTL,
+	ANOTW,
+	AORB,
+	AORL,
+	AORW,
+	AOUTB,
+	AOUTL,
+	AOUTW,
+	AOUTSB,
+	AOUTSL,
+	AOUTSW,
+	APOPAL,
+	APOPAW,
+	APOPFL,
+	APOPFW,
+	APOPL,
+	APOPW,
+	APUSHAL,
+	APUSHAW,
+	APUSHFL,
+	APUSHFW,
+	APUSHL,
+	APUSHW,
+	ARCLB,
+	ARCLL,
+	ARCLW,
+	ARCRB,
+	ARCRL,
+	ARCRW,
+	AREP,
+	AREPN,
+	ARET,
+	AROLB,
+	AROLL,
+	AROLW,
+	ARORB,
+	ARORL,
+	ARORW,
+	ASAHF,
+	ASALB,
+	ASALL,
+	ASALW,
+	ASARB,
+	ASARL,
+	ASARW,
+	ASBBB,
+	ASBBL,
+	ASBBW,
+	ASCASB,
+	ASCASL,
+	ASCASW,
+	ASETCC,
+	ASETCS,
+	ASETEQ,
+	ASETGE,
+	ASETGT,
+	ASETHI,
+	ASETLE,
+	ASETLS,
+	ASETLT,
+	ASETMI,
+	ASETNE,
+	ASETOC,
+	ASETOS,
+	ASETPC,
+	ASETPL,
+	ASETPS,
+	ACDQ,
+	ACWD,
+	ASHLB,
+	ASHLL,
+	ASHLW,
+	ASHRB,
+	ASHRL,
+	ASHRW,
+	ASTC,
+	ASTD,
+	ASTI,
+	ASTOSB,
+	ASTOSL,
+	ASTOSW,
+	ASUBB,
+	ASUBL,
+	ASUBW,
+	ASYSCALL,
+	ATESTB,
+	ATESTL,
+	ATESTW,
+	ATEXT,
+	AVERR,
+	AVERW,
+	AWAIT,
+	AWORD,
+	AXCHGB,
+	AXCHGL,
+	AXCHGW,
+	AXLAT,
+	AXORB,
+	AXORL,
+	AXORW,
+
+	AFMOVB,
+	AFMOVBP,
+	AFMOVD,
+	AFMOVDP,
+	AFMOVF,
+	AFMOVFP,
+	AFMOVL,
+	AFMOVLP,
+	AFMOVV,
+	AFMOVVP,
+	AFMOVW,
+	AFMOVWP,
+	AFMOVX,
+	AFMOVXP,
+
+	AFCOMB,
+	AFCOMBP,
+	AFCOMD,
+	AFCOMDP,
+	AFCOMDPP,
+	AFCOMF,
+	AFCOMFP,
+	AFCOML,
+	AFCOMLP,
+	AFCOMW,
+	AFCOMWP,
+	AFUCOM,
+	AFUCOMP,
+	AFUCOMPP,
+
+	AFADDDP,
+	AFADDW,
+	AFADDL,
+	AFADDF,
+	AFADDD,
+
+	AFMULDP,
+	AFMULW,
+	AFMULL,
+	AFMULF,
+	AFMULD,
+
+	AFSUBDP,
+	AFSUBW,
+	AFSUBL,
+	AFSUBF,
+	AFSUBD,
+
+	AFSUBRDP,
+	AFSUBRW,
+	AFSUBRL,
+	AFSUBRF,
+	AFSUBRD,
+
+	AFDIVDP,
+	AFDIVW,
+	AFDIVL,
+	AFDIVF,
+	AFDIVD,
+
+	AFDIVRDP,
+	AFDIVRW,
+	AFDIVRL,
+	AFDIVRF,
+	AFDIVRD,
+
+	AFXCHD,
+	AFFREE,
+
+	AFLDCW,
+	AFLDENV,
+	AFRSTOR,
+	AFSAVE,
+	AFSTCW,
+	AFSTENV,
+	AFSTSW,
+
+	AF2XM1,
+	AFABS,
+	AFCHS,
+	AFCLEX,
+	AFCOS,
+	AFDECSTP,
+	AFINCSTP,
+	AFINIT,
+	AFLD1,
+	AFLDL2E,
+	AFLDL2T,
+	AFLDLG2,
+	AFLDLN2,
+	AFLDPI,
+	AFLDZ,
+	AFNOP,
+	AFPATAN,
+	AFPREM,
+	AFPREM1,
+	AFPTAN,
+	AFRNDINT,
+	AFSCALE,
+	AFSIN,
+	AFSINCOS,
+	AFSQRT,
+	AFTST,
+	AFXAM,
+	AFXTRACT,
+	AFYL2X,
+	AFYL2XP1,
+
+	AEND,
+
+	ADYNT,
+	AINIT,
+
+	ASIGNAME,
+
+	AFCOMI,
+	AFCOMIP,
+	AFUCOMI,
+	AFUCOMIP,
+	ACMPXCHGB,
+	ACMPXCHGL,
+	ACMPXCHGW,
+
+	/* conditional move */
+	ACMOVLCC,
+	ACMOVLCS,
+	ACMOVLEQ,
+	ACMOVLGE,
+	ACMOVLGT,
+	ACMOVLHI,
+	ACMOVLLE,
+	ACMOVLLS,
+	ACMOVLLT,
+	ACMOVLMI,
+	ACMOVLNE,
+	ACMOVLOC,
+	ACMOVLOS,
+	ACMOVLPC,
+	ACMOVLPL,
+	ACMOVLPS,
+	ACMOVWCC,
+	ACMOVWCS,
+	ACMOVWEQ,
+	ACMOVWGE,
+	ACMOVWGT,
+	ACMOVWHI,
+	ACMOVWLE,
+	ACMOVWLS,
+	ACMOVWLT,
+	ACMOVWMI,
+	ACMOVWNE,
+	ACMOVWOC,
+	ACMOVWOS,
+	ACMOVWPC,
+	ACMOVWPL,
+	ACMOVWPS,
+
+	AFCMOVCC,
+	AFCMOVCS,
+	AFCMOVEQ,
+	AFCMOVHI,
+	AFCMOVLS,
+	AFCMOVNE,
+	AFCMOVNU,
+	AFCMOVUN,
+
+	/* add new operations here. nowhere else. here. */
+	ALAST
+};
+
+enum
+{
+	D_AL		= 0,
+	D_CL,
+	D_DL,
+	D_BL,
+
+	D_AH		= 4,
+	D_CH,
+	D_DH,
+	D_BH,
+
+	D_AX		= 8,
+	D_CX,
+	D_DX,
+	D_BX,
+	D_SP,
+	D_BP,
+	D_SI,
+	D_DI,
+
+	D_F0		= 16,
+	D_F7		= D_F0 + 7,
+
+	D_CS		= 24,
+	D_SS,
+	D_DS,
+	D_ES,
+	D_FS,
+	D_GS,
+
+	D_GDTR,		/* global descriptor table register */
+	D_IDTR,		/* interrupt descriptor table register */
+	D_LDTR,		/* local descriptor table register */
+	D_MSW,		/* machine status word */
+	D_TASK,		/* task register */
+
+	D_CR		= 35,
+	D_DR		= 43,
+	D_TR		= 51,
+
+	D_NONE		= 59,
+
+	D_BRANCH	= 60,
+	D_EXTERN	= 61,
+	D_STATIC	= 62,
+	D_AUTO		= 63,
+	D_PARAM		= 64,
+	D_CONST		= 65,
+	D_FCONST	= 66,
+	D_SCONST	= 67,
+	D_ADDR		= 68,
+
+	D_FILE,
+	D_FILE1,
+
+	D_INDIR,	/* additive */
+
+	D_CONST2 = D_INDIR+D_INDIR,
+
+	D_SIZE,	/* 8l internal */
+
+	T_TYPE		= 1<<0,
+	T_INDEX		= 1<<1,
+	T_OFFSET	= 1<<2,
+	T_FCONST	= 1<<3,
+	T_SYM		= 1<<4,
+	T_SCONST	= 1<<5,
+	T_OFFSET2	= 1<<6,
+	T_GOTYPE	= 1<<7,
+
+	REGARG		= -1,
+	REGRET		= D_AX,
+	FREGRET		= D_F0,
+	REGSP		= D_SP,
+	REGTMP		= D_DI,
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/8c/cgen.c
@@ -1,0 +1,1852 @@
+#include "gc.h"
+
+/* ,x/^(print|prtree)\(/i/\/\/ */
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r, *t;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o, hardleft;
+	long v, curs;
+	vlong c;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesuv[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+// Go's version does the following, but it's the wrong place: doesn't allow assignment
+//	if(o == OEXREG || nn != Z && nn->op == OEXREG) {
+//		gmove(n, nn);
+//		return;
+//	}
+
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		if(cond(o) && typesuv[l->type->etype])
+			break;
+
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gmove(&nod, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	hardleft = l->addable < INDEXED || l->complex >= FNX;
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case ONEG:
+	case OCOM:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		gopcode(o, n->type, Z, &nod);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OAS:
+		if(typefd[n->type->etype]) {
+			cgen(r, &fregnode0);
+			if(nn != Z)
+				gins(AFMOVD, &fregnode0, &fregnode0);
+			if(l->addable < INDEXED) {
+				reglcgen(&nod, l, Z);
+				gmove(&fregnode0, &nod);
+				regfree(&nod);
+			} else
+				gmove(&fregnode0, l);
+			if(nn != Z)
+				gmove(&fregnode0, nn);
+			return;
+		}
+		if(l->op == OBIT)
+			goto bitas;
+		if(!hardleft) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			if(l->op == OINDEX && r->op == OCONST) {
+				gmove(r, l);
+				break;
+			}
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gmove(&nod1, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(r->op == OCONST) {
+			if(r->vconst == 0) {
+				cgen(l, nn);
+				break;
+			}
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(o == OASHL && r->vconst == 1)
+				gopcode(OADD, n->type, &nod, &nod);
+			else
+				gopcode(o, n->type, r, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+
+		/*
+		 * get nod to be D_CX
+		 */
+		if(nodreg(&nod, nn, D_CX)) {
+			regsalloc(&nod1, n);
+			gmove(&nod, &nod1);
+			cgen(n, &nod);		/* probably a bug */
+			gmove(&nod, nn);
+			gmove(&nod1, &nod);
+			break;
+		}
+		reg[D_CX]++;
+		if(nn->op == OREGISTER && nn->reg == D_CX)
+			regalloc(&nod1, l, Z);
+		else
+			regalloc(&nod1, l, nn);
+		if(r->complex >= l->complex) {
+			cgen(r, &nod);
+			cgen(l, &nod1);
+		} else {
+			cgen(l, &nod1);
+			cgen(r, &nod);
+		}
+		gopcode(o, n->type, &nod, &nod1);
+		gmove(&nod1, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OAND:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(typefd[n->type->etype])
+			goto fop;
+		if(r->op == OCONST) {
+			if(r->vconst == 0 && o != OAND) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		if(n->op == OADD && l->op == OASHL && l->right->op == OCONST
+		&& (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) {
+			c = l->right->vconst;
+			if(c > 0 && c <= 3 && nareg(1) >= 4) {
+				if(l->left->complex >= r->complex) {
+					regalloc(&nod, l->left, nn);
+					cgen(l->left, &nod);
+					if(r->addable < INDEXED) {
+						regalloc(&nod1, r, Z);
+						cgen(r, &nod1);
+						genmuladd(&nod, &nod, 1 << c, &nod1);
+						regfree(&nod1);
+					}
+					else
+						genmuladd(&nod, &nod, 1 << c, r);
+				}
+				else {
+					regalloc(&nod, r, nn);
+					cgen(r, &nod);
+					regalloc(&nod1, l->left, Z);
+					cgen(l->left, &nod1);
+					genmuladd(&nod, &nod1, 1 << c, &nod);
+					regfree(&nod1);
+				}
+				gmove(&nod, nn);
+				regfree(&nod);
+				break;
+			}
+		}
+		if(r->addable >= INDEXED) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, n->type, r, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, n->type, &nod1, &nod);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			regalloc(&nod, l, Z);
+			cgen(l, &nod);
+			gopcode(o, n->type, &nod1, &nod);
+		}
+		gmove(&nod, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OLMOD:
+	case OMOD:
+	case OLMUL:
+	case OLDIV:
+	case OMUL:
+	case ODIV:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(typefd[n->type->etype])
+			goto fop;
+		if(r->op == OCONST) {
+			v = 0;
+			switch(o) {
+			case ODIV:
+			case OMOD:
+				c = r->vconst;
+				if(c < 0)
+					c = -c;
+				v = log2(c);
+				if(v < 0)
+					break;
+				/* fall thru */
+			case OMUL:
+			case OLMUL:
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				switch(o) {
+				case OMUL:
+				case OLMUL:
+					mulgen(n->type, r, &nod);
+					break;
+				case ODIV:
+					sdiv2(r->vconst, v, l, &nod);
+					break;
+				case OMOD:
+					smod2(r->vconst, v, l, &nod);
+					break;
+				}
+				gmove(&nod, nn);
+				regfree(&nod);
+				goto done;
+			case OLDIV:
+				c = r->vconst;
+				if((c & 0x80000000) == 0)
+					break;
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+				regalloc(&nod, l, nn);
+				zeroregm(&nod);
+				gins(ACMPL, &nod1, nodconst(c));
+				gins(ASBBL, nodconst(-1), &nod);
+				regfree(&nod1);
+				gmove(&nod, nn);
+				regfree(&nod);
+				goto done;
+			}
+		}
+
+		if(o == OMUL) {
+			if(l->addable >= INDEXED) {
+				t = l;
+				l = r;
+				r = t;
+				goto imula;
+			}
+			else if(r->addable >= INDEXED) {
+			imula:
+/* should favour AX */
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				gopcode(OMUL, n->type, r, &nod);
+			}
+			else {
+/* should favour AX */
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(OMUL, n->type, &nod1, &nod);
+				regfree(&nod1);
+			}
+			gmove(&nod, nn);
+			regfree(&nod);
+			goto done;
+		}
+
+		/*
+		 * get nod to be D_AX
+		 * get nod1 to be D_DX
+		 */
+		if(nodreg(&nod, nn, D_AX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod, &nod2);
+			v = reg[D_AX];
+			reg[D_AX] = 0;
+
+			if(isreg(l, D_AX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_AX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod);
+			reg[D_AX] = v;
+			break;
+		}
+		if(nodreg(&nod1, nn, D_DX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod1, &nod2);
+			v = reg[D_DX];
+			reg[D_DX] = 0;
+
+			if(isreg(l, D_DX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_DX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod1);
+			reg[D_DX] = v;
+			break;
+		}
+		reg[D_AX]++;
+
+		if(r->op == OCONST) {
+			switch(o) {
+			case ODIV:
+				reg[D_DX]++;
+				if(l->addable < INDEXED) {
+					regalloc(&nod2, l, Z);
+					cgen(l, &nod2);
+					l = &nod2;
+				}
+				sdivgen(l, r, &nod, &nod1);
+				gmove(&nod1, nn);
+				if(l == &nod2)
+					regfree(l);
+				goto freeaxdx;
+			case OLDIV:
+				reg[D_DX]++;
+				if(l->addable < INDEXED) {
+					regalloc(&nod2, l, Z);
+					cgen(l, &nod2);
+					l = &nod2;
+				}
+				udivgen(l, r, &nod, &nod1);
+				gmove(&nod1, nn);
+				if(l == &nod2)
+					regfree(l);
+				goto freeaxdx;
+			}
+		}
+
+		if(l->complex >= r->complex) {
+			cgen(l, &nod);
+			reg[D_DX]++;
+			if(o == ODIV || o == OMOD)
+				gins(ACDQ, Z, Z);
+			if(o == OLDIV || o == OLMOD)
+				zeroregm(&nod1);
+			if(r->addable < INDEXED || r->op == OCONST) {
+				regsalloc(&nod3, r);
+				cgen(r, &nod3);
+				gopcode(o, n->type, &nod3, Z);
+			} else
+				gopcode(o, n->type, r, Z);
+		} else {
+			regsalloc(&nod3, r);
+			cgen(r, &nod3);
+			cgen(l, &nod);
+			reg[D_DX]++;
+			if(o == ODIV || o == OMOD)
+				gins(ACDQ, Z, Z);
+			if(o == OLDIV || o == OLMOD)
+				zeroregm(&nod1);
+			gopcode(o, n->type, &nod3, Z);
+		}
+		if(o == OMOD || o == OLMOD)
+			gmove(&nod1, nn);
+		else
+			gmove(&nod, nn);
+	freeaxdx:
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+		if(r->op == OCONST)
+			goto asand;
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[n->type->etype])
+			goto asfop;
+
+		/*
+		 * get nod to be D_CX
+		 */
+		if(nodreg(&nod, nn, D_CX)) {
+			regsalloc(&nod1, n);
+			gmove(&nod, &nod1);
+			cgen(n, &nod);
+			if(nn != Z)
+				gmove(&nod, nn);
+			gmove(&nod1, &nod);
+			break;
+		}
+		reg[D_CX]++;
+
+		if(r->complex >= l->complex) {
+			cgen(r, &nod);
+			if(hardleft)
+				reglcgen(&nod1, l, Z);
+			else
+				nod1 = *l;
+		} else {
+			if(hardleft)
+				reglcgen(&nod1, l, Z);
+			else
+				nod1 = *l;
+			cgen(r, &nod);
+		}
+
+		gopcode(o, l->type, &nod, &nod1);
+		regfree(&nod);
+		if(nn != Z)
+			gmove(&nod1, nn);
+		if(hardleft)
+			regfree(&nod1);
+		break;
+
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+	asand:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[n->type->etype]||typefd[r->type->etype])
+			goto asfop;
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			if(r->op != OCONST) {
+				regalloc(&nod1, r, nn);
+				cgen(r, &nod1);
+				gopcode(o, l->type, &nod1, &nod);
+				regfree(&nod1);
+			} else
+				gopcode(o, l->type, r, &nod);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			gopcode(o, l->type, &nod1, &nod);
+			regfree(&nod1);
+		}
+		if(nn != Z)
+			gmove(&nod, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(typefd[n->type->etype]||typefd[r->type->etype])
+			goto asfop;
+		if(r->op == OCONST) {
+			v = 0;
+			switch(o) {
+			case OASDIV:
+			case OASMOD:
+				c = r->vconst;
+				if(c < 0)
+					c = -c;
+				v = log2(c);
+				if(v < 0)
+					break;
+				/* fall thru */
+			case OASMUL:
+			case OASLMUL:
+				if(hardleft)
+					reglcgen(&nod2, l, Z);
+				else
+					nod2 = *l;
+				regalloc(&nod, l, nn);
+				cgen(&nod2, &nod);
+				switch(o) {
+				case OASMUL:
+				case OASLMUL:
+					mulgen(n->type, r, &nod);
+					break;
+				case OASDIV:
+					sdiv2(r->vconst, v, l, &nod);
+					break;
+				case OASMOD:
+					smod2(r->vconst, v, l, &nod);
+					break;
+				}
+			havev:
+				gmove(&nod, &nod2);
+				if(nn != Z)
+					gmove(&nod, nn);
+				if(hardleft)
+					regfree(&nod2);
+				regfree(&nod);
+				goto done;
+			case OASLDIV:
+				c = r->vconst;
+				if((c & 0x80000000) == 0)
+					break;
+				if(hardleft)
+					reglcgen(&nod2, l, Z);
+				else
+					nod2 = *l;
+				regalloc(&nod1, l, nn);
+				cgen(&nod2, &nod1);
+				regalloc(&nod, l, nn);
+				zeroregm(&nod);
+				gins(ACMPL, &nod1, nodconst(c));
+				gins(ASBBL, nodconst(-1), &nod);
+				regfree(&nod1);
+				goto havev;
+			}
+		}
+
+		if(o == OASMUL) {
+/* should favour AX */
+			regalloc(&nod, l, nn);
+			if(r->complex >= FNX) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				r = &nod1;
+			}
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(r->addable < INDEXED) {
+				if(r->complex < FNX) {
+					regalloc(&nod1, r, Z);
+					cgen(r, &nod1);
+				}
+				gopcode(OASMUL, n->type, &nod1, &nod);
+				regfree(&nod1);
+			}
+			else
+				gopcode(OASMUL, n->type, r, &nod);
+			if(r == &nod1)
+				regfree(r);
+			gmove(&nod, &nod2);
+			if(nn != Z)
+				gmove(&nod, nn);
+			regfree(&nod);
+			if(hardleft)
+				regfree(&nod2);
+			goto done;
+		}
+
+		/*
+		 * get nod to be D_AX
+		 * get nod1 to be D_DX
+		 */
+		if(nodreg(&nod, nn, D_AX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod, &nod2);
+			v = reg[D_AX];
+			reg[D_AX] = 0;
+
+			if(isreg(l, D_AX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_AX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod);
+			reg[D_AX] = v;
+			break;
+		}
+		if(nodreg(&nod1, nn, D_DX)) {
+			regsalloc(&nod2, n);
+			gmove(&nod1, &nod2);
+			v = reg[D_DX];
+			reg[D_DX] = 0;
+
+			if(isreg(l, D_DX)) {
+				nod3 = *n;
+				nod3.left = &nod2;
+				cgen(&nod3, nn);
+			} else
+			if(isreg(r, D_DX)) {
+				nod3 = *n;
+				nod3.right = &nod2;
+				cgen(&nod3, nn);
+			} else
+				cgen(n, nn);
+
+			gmove(&nod2, &nod1);
+			reg[D_DX] = v;
+			break;
+		}
+		reg[D_AX]++;
+		reg[D_DX]++;
+
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(r->op == OCONST) {
+				switch(o) {
+				case OASDIV:
+					sdivgen(&nod2, r, &nod, &nod1);
+					goto divdone;
+				case OASLDIV:
+					udivgen(&nod2, r, &nod, &nod1);
+				divdone:
+					gmove(&nod1, &nod2);
+					if(nn != Z)
+						gmove(&nod1, nn);
+					goto freelxaxdx;
+				}
+			}
+			if(o == OASDIV || o == OASMOD)
+				gins(ACDQ, Z, Z);
+			if(o == OASLDIV || o == OASLMOD)
+				zeroregm(&nod1);
+			if(r->addable < INDEXED || r->op == OCONST ||
+			   !typeil[r->type->etype]) {
+				regalloc(&nod3, r, Z);
+				cgen(r, &nod3);
+				gopcode(o, l->type, &nod3, Z);
+				regfree(&nod3);
+			} else
+				gopcode(o, n->type, r, Z);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			if(hardleft)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			cgen(&nod2, &nod);
+			if(o == OASDIV || o == OASMOD)
+				gins(ACDQ, Z, Z);
+			if(o == OASLDIV || o == OASLMOD)
+				zeroregm(&nod1);
+			gopcode(o, l->type, &nod3, Z);
+			regfree(&nod3);
+		}
+		if(o == OASMOD || o == OASLMOD) {
+			gmove(&nod1, &nod2);
+			if(nn != Z)
+				gmove(&nod1, nn);
+		} else {
+			gmove(&nod, &nod2);
+			if(nn != Z)
+				gmove(&nod, nn);
+		}
+	freelxaxdx:
+		if(hardleft)
+			regfree(&nod2);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	fop:
+		if(l->complex >= r->complex) {
+			cgen(l, &fregnode0);
+			if(r->addable < INDEXED) {
+				cgen(r, &fregnode0);
+				fgopcode(o, &fregnode0, &fregnode1, 1, 0);
+			} else
+				fgopcode(o, r, &fregnode0, 0, 0);
+		} else {
+			cgen(r, &fregnode0);
+			if(l->addable < INDEXED) {
+				cgen(l, &fregnode0);
+				fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+			} else
+				fgopcode(o, l, &fregnode0, 0, 1);
+		}
+		gmove(&fregnode0, nn);
+		break;
+
+	asfop:
+		if(l->complex >= r->complex) {
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+			cgen(r, &fregnode0);
+		} else {
+			cgen(r, &fregnode0);
+			if(hardleft)
+				reglcgen(&nod, l, Z);
+			else
+				nod = *l;
+		}
+		if(!typefd[l->type->etype]) {
+			gmove(&nod, &fregnode0);
+			fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+		} else
+			fgopcode(o, &nod, &fregnode0, 0, 1);
+		if(nn != Z)
+			gins(AFMOVD, &fregnode0, &fregnode0);
+		gmove(&fregnode0, &nod);
+		if(nn != Z)
+			gmove(&fregnode0, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+
+		if(typefd[nod3.type->etype])
+			fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+		else {
+			Node onod;
+
+			/* incredible grot ... */
+			onod = nod3;
+			onod.op = o;
+			onod.complex = 2;
+			onod.addable = 0;
+			onod.type = tfield;
+			onod.left = &nod4;
+			onod.right = &nod3;
+			cgen(&onod, Z);
+		}
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		l = uncomma(l);
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gmove(&nod, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, nn);
+			nod.op = OREGISTER;
+			gopcode(OFUNC, n->type, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, n->type, Z, l);
+		if(REGARG>=0 && reg[REGARG])
+			reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gmove(&nod, nn);
+			regfree(&nod);
+		} else
+		if(typefd[n->type->etype])
+			gins(AFMOVDP, &fregnode0, &fregnode0);
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+			/* both null, gen l->nn */
+			cgen(l, nn);
+			break;
+		}
+		if(typev[l->type->etype]) {
+			cgen64(n, nn);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gmove(&nod, &nod1);
+		gmove(&nod1, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn == Z)
+			break;
+		warn(n, "non-interruptable temporary");
+		nod = *nodrat;
+		if(!r || r->op != OCONST) {
+			diag(n, "DOT and no offset");
+			break;
+		}
+		nod.xoffset += (long)r->vconst;
+		nod.type = n->type;
+		cgen(&nod, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(hardleft)
+			reglcgen(&nod, l, Z);
+		else
+			nod = *l;
+
+		if(typefd[n->type->etype])
+			goto fltinc;
+		gmove(&nod, nn);
+		gopcode(OADD, n->type, nodconst(v), &nod);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(hardleft)
+			reglcgen(&nod, l, Z);
+		else
+			nod = *l;
+		if(typefd[n->type->etype])
+			goto fltinc;
+		gopcode(OADD, n->type, nodconst(v), &nod);
+		if(nn != Z)
+			gmove(&nod, nn);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	fltinc:
+		gmove(&nod, &fregnode0);
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC))
+			gins(AFMOVD, &fregnode0, &fregnode0);
+		gins(AFLD1, Z, Z);
+		if(v < 0)
+			fgopcode(OSUB, &fregnode0, &fregnode1, 1, 0);
+		else
+			fgopcode(OADD, &fregnode0, &fregnode1, 1, 0);
+		if(nn != Z && (o == OPREINC || o == OPREDEC))
+			gins(AFMOVD, &fregnode0, &fregnode0);
+		gmove(&fregnode0, &nod);
+		if(hardleft)
+			regfree(&nod);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gmove(&nod, nn);
+			gopcode(OADD, tfield, nodconst(v), &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, tfield, nodconst(v), &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+done:
+	cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		gopcode(OADDR, n->type, n, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		if(typev[n->type->etype]) {
+			testv(n, true);
+			goto com;
+		}
+		o = ONE;
+		if(true)
+			o = OEQ;
+		if(typefd[n->type->etype]) {
+			if(n->addable < INDEXED) {
+				cgen(n, &fregnode0);
+				gins(AFLDZ, Z, Z);
+				fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+			} else {
+				gins(AFLDZ, Z, Z);
+				fgopcode(o, n, &fregnode0, 0, 1);
+			}
+			goto com;
+		}
+		/* bad, 13 is address of external that becomes constant */
+		if(n->addable >= INDEXED && n->addable != 13) {
+			gopcode(o, n->type, n, nodconst(0));
+			goto com;
+		}
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		gopcode(o, n->type, &nod, nodconst(0));
+		regfree(&nod);
+		goto com;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(typev[l->type->etype]) {
+			if(!true)
+				n->op = comrel[relindex(o)];
+			cgen64(n, Z);
+			goto com;
+		}
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gmove(&nod, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(typefd[l->type->etype]) {
+			if(l->complex >= r->complex) {
+				cgen(l, &fregnode0);
+				if(r->addable < INDEXED) {
+					cgen(r, &fregnode0);
+					o = invrel[relindex(o)];
+					fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+				} else
+					fgopcode(o, r, &fregnode0, 0, 1);
+			} else {
+				o = invrel[relindex(o)];
+				cgen(r, &fregnode0);
+				if(l->addable < INDEXED) {
+					cgen(l, &fregnode0);
+					o = invrel[relindex(o)];
+					fgopcode(o, &fregnode0, &fregnode1, 1, 1);
+				} else
+					fgopcode(o, l, &fregnode0, 0, 1);
+			}
+			goto com;
+		}
+		if(l->op == OCONST) {
+			o = invrel[relindex(o)];
+			/* bad, 13 is address of external that becomes constant */
+			if(r->addable < INDEXED || r->addable == 13) {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gopcode(o, l->type, &nod, l);
+				regfree(&nod);
+			} else
+				gopcode(o, l->type, r, l);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(r->addable < INDEXED) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(o, l->type, &nod, &nod1);
+				regfree(&nod1);
+			} else
+				gopcode(o, l->type, &nod, r);
+			regfree(&nod);
+			goto com;
+		}
+		regalloc(&nod, r, nn);
+		cgen(r, &nod);
+		if(l->addable < INDEXED || l->addable == 13) {
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			if(typechlp[l->type->etype])
+				gopcode(o, types[TINT], &nod1, &nod);
+			else
+				gopcode(o, l->type, &nod1, &nod);
+			regfree(&nod1);
+		} else
+			gopcode(o, l->type, l, &nod);
+		regfree(&nod);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gmove(nodconst(1L), nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gmove(nodconst(0L), nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *h, *l, *r;
+	Type *t;
+	int c, v, x;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			if(nn->op == OREGPAIR) {
+				loadpair(n, nn);
+				break;
+			}
+			else if(!vaddr(nn, 0)) {
+				t = nn->type;
+				nn->type = types[TLONG];
+				reglcgen(&nod1, nn, Z);
+				nn->type = t;
+
+				gmove(lo64(n), &nod1);
+				nod1.xoffset += SZ_LONG;
+				gmove(hi64(n), &nod1);
+				regfree(&nod1);
+			}
+			else {
+				gins(AMOVL, lo64(n), nn);
+				nn->xoffset += SZ_LONG;
+				gins(AMOVL, hi64(n), nn);
+				nn->xoffset -= SZ_LONG;
+				break;
+			}
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn == Z)
+			break;
+		warn(n, "non-interruptable temporary");
+		nod1 = *nodrat;
+		r = n->right;
+		if(!r || r->op != OCONST) {
+			diag(n, "DOT and no offset");
+			break;
+		}
+		nod1.xoffset += (long)r->vconst;
+		nod1.type = n->type;
+		sugen(&nod1, nn, w);
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && side(nn)) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			cgen(&nod2, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = nil;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+			nod0.right = l;
+
+			/* prtree(&nod0, "hand craft"); /* */
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		h = nn;
+		if(nn->op == OREGPAIR) {
+			regsalloc(&nod1, nn);
+			nn = &nod1;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		if(h->op == OREGPAIR)
+			loadpair(nn->left, h);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z) {
+		switch(n->op) {
+		case OASADD:
+		case OASSUB:
+		case OASAND:
+		case OASOR:
+		case OASXOR:
+
+		case OASMUL:
+		case OASLMUL:
+
+		case OASASHL:
+		case OASASHR:
+		case OASLSHR:
+			break;
+
+		case OPOSTINC:
+		case OPOSTDEC:
+		case OPREINC:
+		case OPREDEC:
+			break;
+
+		default:
+			return;
+		}
+	}
+
+	if(n->complex >= FNX && nn != nil && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gins(AMOVL, &nod1, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	x = 0;
+	v = w == 8;
+	if(v) {
+		c = cursafe;
+		if(n->left != Z && n->left->complex >= FNX
+		&& n->right != Z && n->right->complex >= FNX) {
+//			warn(n, "toughie");
+			regsalloc(&nod1, n->right);
+			cgen(n->right, &nod1);
+			nod2 = *n;
+			nod2.right = &nod1;
+			cgen(&nod2, nn);
+			cursafe = c;
+			return;
+		}
+		if(cgen64(n, nn)) {
+			cursafe = c;
+			return;
+		}
+		if(n->op == OCOM) {
+			n = n->left;
+			x = 1;
+		}
+	}
+
+	/* botch, need to save in .safe */
+	c = 0;
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TLONG];
+		if(v) {
+			regalloc(&nod0, n, Z);
+			if(!vaddr(n, 0)) {
+				reglcgen(&nod1, n, Z);
+				n->type = t;
+				n = &nod1;
+			}
+			else
+				n->type = t;
+		}
+		else {
+			nodreg(&nod1, n, D_SI);
+			if(reg[D_SI]) {
+				gins(APUSHL, &nod1, Z);
+				c |= 1;
+				reg[D_SI]++;
+			}
+			lcgen(n, &nod1);
+			n->type = t;
+		}
+
+		t = nn->type;
+		nn->type = types[TLONG];
+		if(v) {
+			if(!vaddr(nn, 0)) {
+				reglcgen(&nod2, nn, Z);
+				nn->type = t;
+				nn = &nod2;
+			}
+			else
+				nn->type = t;
+		}
+		else {
+			nodreg(&nod2, nn, D_DI);
+			if(reg[D_DI]) {
+				gins(APUSHL, &nod2, Z);
+				c |= 2;
+				reg[D_DI]++;
+			}
+			lcgen(nn, &nod2);
+			nn->type = t;
+		}
+	} else {
+		t = nn->type;
+		nn->type = types[TLONG];
+		if(v) {
+			regalloc(&nod0, nn, Z);
+			if(!vaddr(nn, 0)) {
+				reglcgen(&nod2, nn, Z);
+				nn->type = t;
+				nn = &nod2;
+			}
+			else
+				nn->type = t;
+		}
+		else {
+			nodreg(&nod2, nn, D_DI);
+			if(reg[D_DI]) {
+				gins(APUSHL, &nod2, Z);
+				c |= 2;
+				reg[D_DI]++;
+			}
+			lcgen(nn, &nod2);
+			nn->type = t;
+		}
+
+		t = n->type;
+		n->type = types[TLONG];
+		if(v) {
+			if(!vaddr(n, 0)) {
+				reglcgen(&nod1, n, Z);
+				n->type = t;
+				n = &nod1;
+			}
+			else
+				n->type = t;
+		}
+		else {
+			nodreg(&nod1, n, D_SI);
+			if(reg[D_SI]) {
+				gins(APUSHL, &nod1, Z);
+				c |= 1;
+				reg[D_SI]++;
+			}
+			lcgen(n, &nod1);
+			n->type = t;
+		}
+	}
+	if(v) {
+		gins(AMOVL, n, &nod0);
+		if(x)
+			gins(ANOTL, Z, &nod0);
+		gins(AMOVL, &nod0, nn);
+		n->xoffset += SZ_LONG;
+		nn->xoffset += SZ_LONG;
+		gins(AMOVL, n, &nod0);
+		if(x)
+			gins(ANOTL, Z, &nod0);
+		gins(AMOVL, &nod0, nn);
+		n->xoffset -= SZ_LONG;
+		nn->xoffset -= SZ_LONG;
+		if(nn == &nod2)
+			regfree(&nod2);
+		if(n == &nod1)
+			regfree(&nod1);
+		regfree(&nod0);
+		return;
+	}
+	nodreg(&nod3, n, D_CX);
+	if(reg[D_CX]) {
+		gins(APUSHL, &nod3, Z);
+		c |= 4;
+		reg[D_CX]++;
+	}
+	gins(AMOVL, nodconst(w/SZ_LONG), &nod3);
+	gins(ACLD, Z, Z);
+	gins(AREP, Z, Z);
+	gins(AMOVSL, Z, Z);
+	if(w & (SZ_LONG-1)) {
+		/* odd length of packed structure */
+		gins(AMOVL, nodconst(w & (SZ_LONG-1)), &nod3);
+		gins(AREP, Z, Z);
+		gins(AMOVSB, Z, Z);
+	}
+	if(c & 4) {
+		gins(APOPL, Z, &nod3);
+		reg[D_CX]--;
+	}
+	if(c & 2) {
+		gins(APOPL, Z, &nod2);
+		reg[nod2.reg]--;
+	}
+	if(c & 1) {
+		gins(APOPL, Z, &nod1);
+		reg[nod1.reg]--;
+	}
+}
--- /dev/null
+++ b/utils/8c/cgen64.c
@@ -1,0 +1,2639 @@
+#include "gc.h"
+
+void
+zeroregm(Node *n)
+{
+	gins(AMOVL, nodconst(0), n);
+}
+
+/* do we need to load the address of a vlong? */
+int
+vaddr(Node *n, int a)
+{
+	switch(n->op) {
+	case ONAME:
+		if(a)
+			return 1;
+		return !(n->class == CEXTERN || n->class == CGLOBL || n->class == CSTATIC);
+
+	case OCONST:
+	case OREGISTER:
+	case OINDREG:
+		return 1;
+	}
+	return 0;
+}
+
+long
+hi64v(Node *n)
+{
+	if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+		return (long)(n->vconst) & ~0L;
+	else
+		return (long)((uvlong)n->vconst>>32) & ~0L;
+}
+
+long
+lo64v(Node *n)
+{
+	if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+		return (long)((uvlong)n->vconst>>32) & ~0L;
+	else
+		return (long)(n->vconst) & ~0L;
+}
+
+Node *
+hi64(Node *n)
+{
+	return nodconst(hi64v(n));
+}
+
+Node *
+lo64(Node *n)
+{
+	return nodconst(lo64v(n));
+}
+
+static Node *
+anonreg(void)
+{
+	Node *n;
+
+	n = new(OREGISTER, Z, Z);
+	n->reg = D_NONE;
+	n->type = types[TLONG];
+	return n;
+}
+
+static Node *
+regpair(Node *n, Node *t)
+{
+	Node *r;
+
+	if(n != Z && n->op == OREGPAIR)
+		return n;
+	r = new(OREGPAIR, anonreg(), anonreg());
+	if(n != Z)
+		r->type = n->type;
+	else
+		r->type = t->type;
+	return r;
+}
+
+static void
+evacaxdx(Node *r)
+{
+	Node nod1, nod2;
+
+	if(r->reg == D_AX || r->reg == D_DX) {
+		reg[D_AX]++;
+		reg[D_DX]++;
+		/*
+		 * this is just an optim that should
+		 * check for spill
+		 */
+		r->type = types[TULONG];
+		regalloc(&nod1, r, Z);
+		nodreg(&nod2, Z, r->reg);
+		gins(AMOVL, &nod2, &nod1);
+		regfree(r);
+		r->reg = nod1.reg;
+		reg[D_AX]--;
+		reg[D_DX]--;
+	}
+}
+
+/* lazy instantiation of register pair */
+static int
+instpair(Node *n, Node *l)
+{
+	int r;
+
+	r = 0;
+	if(n->left->reg == D_NONE) {
+		if(l != Z) {
+			n->left->reg = l->reg;
+			r = 1;
+		}
+		else
+			regalloc(n->left, n->left, Z);
+	}
+	if(n->right->reg == D_NONE)
+		regalloc(n->right, n->right, Z);
+	return r;
+}
+
+static void
+zapreg(Node *n)
+{
+	if(n->reg != D_NONE) {
+		regfree(n);
+		n->reg = D_NONE;
+	}
+}
+
+static void
+freepair(Node *n)
+{
+	regfree(n->left);
+	regfree(n->right);
+}
+
+/* n is not OREGPAIR, nn is */
+void
+loadpair(Node *n, Node *nn)
+{
+	Node nod;
+
+	instpair(nn, Z);
+	if(n->op == OCONST) {
+		gins(AMOVL, lo64(n), nn->left);
+		n->xoffset += SZ_LONG;
+		gins(AMOVL, hi64(n), nn->right);
+		n->xoffset -= SZ_LONG;
+		return;
+	}
+	if(!vaddr(n, 0)) {
+		/* steal the right register for the laddr */
+		nod = regnode;
+		nod.reg = nn->right->reg;
+		lcgen(n, &nod);
+		n = &nod;
+		regind(n, n);
+		n->xoffset = 0;
+	}
+	gins(AMOVL, n, nn->left);
+	n->xoffset += SZ_LONG;
+	gins(AMOVL, n, nn->right);
+	n->xoffset -= SZ_LONG;
+}
+
+/* n is OREGPAIR, nn is not */
+static void
+storepair(Node *n, Node *nn, int f)
+{
+	Node nod;
+
+	if(!vaddr(nn, 0)) {
+		reglcgen(&nod, nn, Z);
+		nn = &nod;
+	}
+	gins(AMOVL, n->left, nn);
+	nn->xoffset += SZ_LONG;
+	gins(AMOVL, n->right, nn);
+	nn->xoffset -= SZ_LONG;
+	if(nn == &nod)
+		regfree(&nod);
+	if(f)
+		freepair(n);
+}
+
+enum
+{
+/* 4 only, see WW */
+	WNONE	= 0,
+	WCONST,
+	WADDR,
+	WHARD,
+};
+
+static int
+whatof(Node *n, int a)
+{
+	if(n->op == OCONST)
+		return WCONST;
+	return !vaddr(n, a) ? WHARD : WADDR;
+}
+
+/* can upgrade an extern to addr for AND */
+static int
+reduxv(Node *n)
+{
+	return lo64v(n) == 0 || hi64v(n) == 0;
+}
+
+int
+cond(int op)
+{
+	switch(op) {
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * for a func operand call it and then return
+ * the safe node
+ */
+static Node *
+vfunc(Node *n, Node *nn)
+{
+	Node *t;
+
+	if(n->op != OFUNC)
+		return n;
+	t = new(0, Z, Z);
+	if(nn == Z || nn == nodret)
+		nn = n;
+	regsalloc(t, nn);
+	sugen(n, t, 8);
+	return t;
+}
+
+/* try to steal a reg */
+static int
+getreg(Node **np, Node *t, int r)
+{
+	Node *n, *p;
+
+	n = *np;
+	if(n->reg == r) {
+		p = new(0, Z, Z);
+		regalloc(p, n, Z);
+		gins(AMOVL, n, p);
+		*t = *n;
+		*np = p;
+		return 1;
+	}
+	return 0;
+}
+
+static Node *
+snarfreg(Node *n, Node *t, int r, Node *d, Node *c)
+{
+	if(n == Z || n->op != OREGPAIR || (!getreg(&n->left, t, r) && !getreg(&n->right, t, r))) {
+		if(nodreg(t, Z, r)) {
+			regalloc(c, d, Z);
+			gins(AMOVL, t, c);
+			reg[r]++;
+			return c;
+		}
+		reg[r]++;
+	}
+	return Z;
+}
+
+enum
+{
+	Vstart	= OEND,
+
+	Vgo,
+	Vamv,
+	Vmv,
+	Vzero,
+	Vop,
+	Vopx,
+	Vins,
+	Vins0,
+	Vinsl,
+	Vinsr,
+	Vinsla,
+	Vinsra,
+	Vinsx,
+	Vmul,
+	Vshll,
+	VT,
+	VF,
+	V_l_lo_f,
+	V_l_hi_f,
+	V_l_lo_t,
+	V_l_hi_t,
+	V_l_lo_u,
+	V_l_hi_u,
+	V_r_lo_f,
+	V_r_hi_f,
+	V_r_lo_t,
+	V_r_hi_t,
+	V_r_lo_u,
+	V_r_hi_u,
+	Vspazz,
+	Vend,
+
+	V_T0,
+	V_T1,
+	V_F0,
+	V_F1,
+
+	V_a0,
+	V_a1,
+	V_f0,
+	V_f1,
+
+	V_p0,
+	V_p1,
+	V_p2,
+	V_p3,
+	V_p4,
+
+	V_s0,
+	V_s1,
+	V_s2,
+	V_s3,
+	V_s4,
+
+	C00,
+	C01,
+	C31,
+	C32,
+
+	O_l_lo,
+	O_l_hi,
+	O_r_lo,
+	O_r_hi,
+	O_t_lo,
+	O_t_hi,
+	O_l,
+	O_r,
+	O_l_rp,
+	O_r_rp,
+	O_t_rp,
+	O_r0,
+	O_r1,
+	O_Zop,
+
+	O_a0,
+	O_a1,
+
+	V_C0,
+	V_C1,
+
+	V_S0,
+	V_S1,
+
+	VOPS	= 5,
+	VLEN	= 5,
+	VARGS	= 2,
+
+	S00	= 0,
+	Sc0,
+	Sc1,
+	Sc2,
+	Sac3,
+	Sac4,
+	S10,
+
+	SAgen	= 0,
+	SAclo,
+	SAc32,
+	SAchi,
+	SAdgen,
+	SAdclo,
+	SAdc32,
+	SAdchi,
+
+	B0c	= 0,
+	Bca,
+	Bac,
+
+	T0i	= 0,
+	Tii,
+
+	Bop0	= 0,
+	Bop1,
+};
+
+/*
+ * _testv:
+ * 	CMPL	lo,$0
+ * 	JNE	true
+ * 	CMPL	hi,$0
+ * 	JNE	true
+ * 	GOTO	false
+ * false:
+ * 	GOTO	code
+ * true:
+ * 	GOTO	patchme
+ * code:
+ */
+
+static uchar	testi[][VLEN] =
+{
+	{Vop, ONE, O_l_lo, C00},
+	{V_s0, Vop, ONE, O_l_hi, C00},
+	{V_s1, Vgo, V_s2, Vgo, V_s3},
+	{VF, V_p0, V_p1, VT, V_p2},
+	{Vgo, V_p3},
+	{VT, V_p0, V_p1, VF, V_p2},
+	{Vend},
+};
+
+/* shift left general case */
+static uchar	shll00[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vinsl, ASHLL, O_r, O_l_rp},
+	{Vins, ASHLL, O_r, O_l_lo, Vgo},
+	{V_p0, V_s0},
+	{Vins, ASHLL, O_r, O_l_lo},
+	{Vins, AMOVL, O_l_lo, O_l_hi},
+	{Vzero, O_l_lo, V_p0, Vend},
+};
+
+/* shift left rp, const < 32 */
+static uchar	shllc0[][VLEN] =
+{
+	{Vinsl, ASHLL, O_r, O_l_rp},
+	{Vshll, O_r, O_l_lo, Vend},
+};
+
+/* shift left rp, const == 32 */
+static uchar	shllc1[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_l_hi},
+	{Vzero, O_l_lo, Vend},
+};
+
+/* shift left rp, const > 32 */
+static uchar	shllc2[][VLEN] =
+{
+	{Vshll, O_r, O_l_lo},
+	{Vins, AMOVL, O_l_lo, O_l_hi},
+	{Vzero, O_l_lo, Vend},
+};
+
+/* shift left addr, const == 32 */
+static uchar	shllac3[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{Vzero, O_t_lo, Vend},
+};
+
+/* shift left addr, const > 32 */
+static uchar	shllac4[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{Vshll, O_r, O_t_hi},
+	{Vzero, O_t_lo, Vend},
+};
+
+/* shift left of constant */
+static uchar	shll10[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsl, ASHLL, O_r, O_t_rp},
+	{Vins, ASHLL, O_r, O_t_lo, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{V_l_lo_t, Vins, ASHLL, O_r, O_t_hi},
+	{Vzero, O_t_lo, V_p0, Vend},
+};
+
+static uchar	(*shlltab[])[VLEN] =
+{
+	shll00,
+	shllc0,
+	shllc1,
+	shllc2,
+	shllac3,
+	shllac4,
+	shll10,
+};
+
+/* shift right general case */
+static uchar	shrl00[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vinsr, ASHRL, O_r, O_l_rp},
+	{Vins, O_a0, O_r, O_l_hi, Vgo},
+	{V_p0, V_s0},
+	{Vins, O_a0, O_r, O_l_hi},
+	{Vins, AMOVL, O_l_hi, O_l_lo},
+	{V_T1, Vzero, O_l_hi},
+	{V_F1, Vins, ASARL, C31, O_l_hi},
+	{V_p0, Vend},
+};
+
+/* shift right rp, const < 32 */
+static uchar	shrlc0[][VLEN] =
+{
+	{Vinsr, ASHRL, O_r, O_l_rp},
+	{Vins, O_a0, O_r, O_l_hi, Vend},
+};
+
+/* shift right rp, const == 32 */
+static uchar	shrlc1[][VLEN] =
+{
+	{Vins, AMOVL, O_l_hi, O_l_lo},
+	{V_T1, Vzero, O_l_hi},
+	{V_F1, Vins, ASARL, C31, O_l_hi},
+	{Vend},
+};
+
+/* shift right rp, const > 32 */
+static uchar	shrlc2[][VLEN] =
+{
+	{Vins, O_a0, O_r, O_l_hi},
+	{Vins, AMOVL, O_l_hi, O_l_lo},
+	{V_T1, Vzero, O_l_hi},
+	{V_F1, Vins, ASARL, C31, O_l_hi},
+	{Vend},
+};
+
+/* shift right addr, const == 32 */
+static uchar	shrlac3[][VLEN] =
+{
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{V_T1, Vzero, O_t_hi},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{Vend},
+};
+
+/* shift right addr, const > 32 */
+static uchar	shrlac4[][VLEN] =
+{
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{Vins, O_a0, O_r, O_t_lo},
+	{V_T1, Vzero, O_t_hi},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{Vend},
+};
+
+/* shift right of constant */
+static uchar	shrl10[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsr, ASHRL, O_r, O_t_rp},
+	{Vins, O_a0, O_r, O_t_hi, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{V_l_hi_t, Vins, O_a0, O_r, O_t_lo},
+	{V_l_hi_u, V_S1},
+	{V_T1, Vzero, O_t_hi, V_p0},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{Vend},
+};
+
+static uchar	(*shrltab[])[VLEN] =
+{
+	shrl00,
+	shrlc0,
+	shrlc1,
+	shrlc2,
+	shrlac3,
+	shrlac4,
+	shrl10,
+};
+
+/* shift asop left general case */
+static uchar	asshllgen[][VLEN] =
+{
+	{V_a0, V_a1},
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_r0},
+	{Vins, AMOVL, O_l_hi, O_r1},
+	{Vinsla, ASHLL, O_r, O_r0},
+	{Vins, ASHLL, O_r, O_r0},
+	{Vins, AMOVL, O_r1, O_l_hi},
+	{Vins, AMOVL, O_r0, O_l_lo, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vzero, O_l_lo},
+	{Vins, ASHLL, O_r, O_r0},
+	{Vins, AMOVL, O_r0, O_l_hi, V_p0},
+	{V_f0, V_f1, Vend},
+};
+
+/* shift asop left, const < 32 */
+static uchar	asshllclo[][VLEN] =
+{
+	{V_a0, V_a1},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vins, AMOVL, O_l_hi, O_r1},
+	{Vinsla, ASHLL, O_r, O_r0},
+	{Vshll, O_r, O_r0},
+	{Vins, AMOVL, O_r1, O_l_hi},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{V_f0, V_f1, Vend},
+};
+
+/* shift asop left, const == 32 */
+static uchar	asshllc32[][VLEN] =
+{
+	{V_a0},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vzero, O_l_lo},
+	{Vins, AMOVL, O_r0, O_l_hi},
+	{V_f0, Vend},
+};
+
+/* shift asop left, const > 32 */
+static uchar	asshllchi[][VLEN] =
+{
+	{V_a0},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vzero, O_l_lo},
+	{Vshll, O_r, O_r0},
+	{Vins, AMOVL, O_r0, O_l_hi},
+	{V_f0, Vend},
+};
+
+/* shift asop dest left general case */
+static uchar	asdshllgen[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsl, ASHLL, O_r, O_t_rp},
+	{Vins, ASHLL, O_r, O_t_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{Vzero, O_l_lo},
+	{Vins, ASHLL, O_r, O_t_hi},
+	{Vzero, O_t_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
+	{Vend},
+};
+
+/* shift asop dest left, const < 32 */
+static uchar	asdshllclo[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsl, ASHLL, O_r, O_t_rp},
+	{Vshll, O_r, O_t_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vend},
+};
+
+/* shift asop dest left, const == 32 */
+static uchar	asdshllc32[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{Vzero, O_t_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vend},
+};
+
+/* shift asop dest, const > 32 */
+static uchar	asdshllchi[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_hi},
+	{Vzero, O_t_lo},
+	{Vshll, O_r, O_t_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vend},
+};
+
+static uchar	(*asshlltab[])[VLEN] =
+{
+	asshllgen,
+	asshllclo,
+	asshllc32,
+	asshllchi,
+	asdshllgen,
+	asdshllclo,
+	asdshllc32,
+	asdshllchi,
+};
+
+/* shift asop right general case */
+static uchar	asshrlgen[][VLEN] =
+{
+	{V_a0, V_a1},
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_r0},
+	{Vins, AMOVL, O_l_hi, O_r1},
+	{Vinsra, ASHRL, O_r, O_r0},
+	{Vinsx, Bop0, O_r, O_r1},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{Vins, AMOVL, O_r1, O_l_hi, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_hi, O_r0},
+	{Vinsx, Bop0, O_r, O_r0},
+	{V_T1, Vzero, O_l_hi},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{V_F1, Vins, ASARL, C31, O_r0},
+	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
+	{V_p0, V_f0, V_f1, Vend},
+};
+
+/* shift asop right, const < 32 */
+static uchar	asshrlclo[][VLEN] =
+{
+	{V_a0, V_a1},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vins, AMOVL, O_l_hi, O_r1},
+	{Vinsra, ASHRL, O_r, O_r0},
+	{Vinsx, Bop0, O_r, O_r1},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{Vins, AMOVL, O_r1, O_l_hi},
+	{V_f0, V_f1, Vend},
+};
+
+/* shift asop right, const == 32 */
+static uchar	asshrlc32[][VLEN] =
+{
+	{V_a0},
+	{Vins, AMOVL, O_l_hi, O_r0},
+	{V_T1, Vzero, O_l_hi},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{V_F1, Vins, ASARL, C31, O_r0},
+	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
+	{V_f0, Vend},
+};
+
+/* shift asop right, const > 32 */
+static uchar	asshrlchi[][VLEN] =
+{
+	{V_a0},
+	{Vins, AMOVL, O_l_hi, O_r0},
+	{V_T1, Vzero, O_l_hi},
+	{Vinsx, Bop0, O_r, O_r0},
+	{Vins, AMOVL, O_r0, O_l_lo},
+	{V_F1, Vins, ASARL, C31, O_r0},
+	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
+	{V_f0, Vend},
+};
+
+/* shift asop dest right general case */
+static uchar	asdshrlgen[][VLEN] =
+{
+	{Vop, OGE, O_r, C32},
+	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsr, ASHRL, O_r, O_t_rp},
+	{Vinsx, Bop0, O_r, O_t_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi, Vgo},
+	{V_p0, V_s0},
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{V_T1, Vzero, O_t_hi},
+	{Vinsx, Bop0, O_r, O_t_lo},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
+	{Vend},
+};
+
+/* shift asop dest right, const < 32 */
+static uchar	asdshrlclo[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsr, ASHRL, O_r, O_t_rp},
+	{Vinsx, Bop0, O_r, O_t_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vend},
+};
+
+/* shift asop dest right, const == 32 */
+static uchar	asdshrlc32[][VLEN] =
+{
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{V_T1, Vzero, O_t_hi},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vend},
+};
+
+/* shift asop dest, const > 32 */
+static uchar	asdshrlchi[][VLEN] =
+{
+	{Vins, AMOVL, O_l_hi, O_t_lo},
+	{V_T1, Vzero, O_t_hi},
+	{Vinsx, Bop0, O_r, O_t_lo},
+	{V_T1, Vins, AMOVL, O_t_hi, O_l_hi},
+	{V_T1, Vins, AMOVL, O_t_lo, O_l_lo},
+	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
+	{V_F1, Vins, ASARL, C31, O_t_hi},
+	{V_F1, Vins, AMOVL, O_t_lo, O_l_lo},
+	{V_F1, Vins, AMOVL, O_t_hi, O_l_hi},
+	{Vend},
+};
+
+static uchar	(*asshrltab[])[VLEN] =
+{
+	asshrlgen,
+	asshrlclo,
+	asshrlc32,
+	asshrlchi,
+	asdshrlgen,
+	asdshrlclo,
+	asdshrlc32,
+	asdshrlchi,
+};
+
+static uchar	shrlargs[]	= { ASHRL, 1 };
+static uchar	sarlargs[]	= { ASARL, 0 };
+
+/* ++ -- */
+static uchar	incdec[][VLEN] =
+{
+	{Vinsx, Bop0, C01, O_l_lo},
+	{Vinsx, Bop1, C00, O_l_hi, Vend},
+};
+
+/* ++ -- *p */
+static uchar	incdecpre[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsx, Bop0, C01, O_t_lo},
+	{Vinsx, Bop1, C00, O_t_hi},
+	{Vins, AMOVL, O_t_lo, O_l_lo},
+	{Vins, AMOVL, O_t_hi, O_l_hi, Vend},
+};
+
+/* *p ++ -- */
+static uchar	incdecpost[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsx, Bop0, C01, O_l_lo},
+	{Vinsx, Bop1, C00, O_l_hi, Vend},
+};
+
+/* binop rp, rp */
+static uchar	binop00[][VLEN] =
+{
+	{Vinsx, Bop0, O_r_lo, O_l_lo},
+	{Vinsx, Bop1, O_r_hi, O_l_hi, Vend},
+	{Vend},
+};
+
+/* binop rp, addr */
+static uchar	binoptmp[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_r_lo, O_r0},
+	{Vinsx, Bop0, O_r0, O_l_lo},
+	{Vins, AMOVL, O_r_hi, O_r0},
+	{Vinsx, Bop1, O_r0, O_l_hi},
+	{V_f0, Vend},
+};
+
+/* binop t = *a op *b */
+static uchar	binop11[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vinsx, Bop0, O_r_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsx, Bop1, O_r_hi, O_t_hi, Vend},
+};
+
+/* binop t = rp +- c */
+static uchar	add0c[][VLEN] =
+{
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
+	{V_r_lo_f, Vamv, Bop0, Bop1},
+	{Vinsx, Bop1, O_r_hi, O_l_hi},
+	{Vend},
+};
+
+/* binop t = rp & c */
+static uchar	and0c[][VLEN] =
+{
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
+	{V_r_lo_f, Vins, AMOVL, C00, O_l_lo},
+	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
+	{V_r_hi_f, Vins, AMOVL, C00, O_l_hi},
+	{Vend},
+};
+
+/* binop t = rp | c */
+static uchar	or0c[][VLEN] =
+{
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
+	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
+	{Vend},
+};
+
+/* binop t = c - rp */
+static uchar	sub10[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_l_lo, O_r0},
+	{Vinsx, Bop0, O_r_lo, O_r0},
+	{Vins, AMOVL, O_l_hi, O_r_lo},
+	{Vinsx, Bop1, O_r_hi, O_r_lo},
+	{Vspazz, V_f0, Vend},
+};
+
+/* binop t = c + *b */
+static uchar	addca[][VLEN] =
+{
+	{Vins, AMOVL, O_r_lo, O_t_lo},
+	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
+	{V_l_lo_f, Vamv, Bop0, Bop1},
+	{Vins, AMOVL, O_r_hi, O_t_hi},
+	{Vinsx, Bop1, O_l_hi, O_t_hi},
+	{Vend},
+};
+
+/* binop t = c & *b */
+static uchar	andca[][VLEN] =
+{
+	{V_l_lo_t, Vins, AMOVL, O_r_lo, O_t_lo},
+	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
+	{V_l_lo_f, Vzero, O_t_lo},
+	{V_l_hi_t, Vins, AMOVL, O_r_hi, O_t_hi},
+	{V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
+	{V_l_hi_f, Vzero, O_t_hi},
+	{Vend},
+};
+
+/* binop t = c | *b */
+static uchar	orca[][VLEN] =
+{
+	{Vins, AMOVL, O_r_lo, O_t_lo},
+	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_r_hi, O_t_hi},
+	{V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
+	{Vend},
+};
+
+/* binop t = c - *b */
+static uchar	subca[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsx, Bop0, O_r_lo, O_t_lo},
+	{Vinsx, Bop1, O_r_hi, O_t_hi},
+	{Vend},
+};
+
+/* binop t = *a +- c */
+static uchar	addac[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
+	{V_r_lo_f, Vamv, Bop0, Bop1},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{Vinsx, Bop1, O_r_hi, O_t_hi},
+	{Vend},
+};
+
+/* binop t = *a | c */
+static uchar	orac[][VLEN] =
+{
+	{Vins, AMOVL, O_l_lo, O_t_lo},
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
+	{Vins, AMOVL, O_l_hi, O_t_hi},
+	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_t_hi},
+	{Vend},
+};
+
+/* binop t = *a & c */
+static uchar	andac[][VLEN] =
+{
+	{V_r_lo_t, Vins, AMOVL, O_l_lo, O_t_lo},
+	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
+	{V_r_lo_f, Vzero, O_t_lo},
+	{V_r_hi_t, Vins, AMOVL, O_l_hi, O_t_hi},
+	{V_r_hi_t, Vinsx, Bop0, O_r_hi, O_t_hi},
+	{V_r_hi_f, Vzero, O_t_hi},
+	{Vend},
+};
+
+static uchar	ADDargs[]	= { AADDL, AADCL };
+static uchar	ANDargs[]	= { AANDL, AANDL };
+static uchar	ORargs[]	= { AORL, AORL };
+static uchar	SUBargs[]	= { ASUBL, ASBBL };
+static uchar	XORargs[]	= { AXORL, AXORL };
+
+static uchar	(*ADDtab[])[VLEN] =
+{
+	add0c, addca, addac,
+};
+
+static uchar	(*ANDtab[])[VLEN] =
+{
+	and0c, andca, andac,
+};
+
+static uchar	(*ORtab[])[VLEN] =
+{
+	or0c, orca, orac,
+};
+
+static uchar	(*SUBtab[])[VLEN] =
+{
+	add0c, subca, addac,
+};
+
+/* mul of const32 */
+static uchar	mulc32[][VLEN] =
+{
+	{V_a0, Vop, ONE, O_l_hi, C00},
+	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
+	{Vins, AMULL, O_r0, O_Zop},
+	{Vgo, V_p0, V_s0},
+	{Vins, AMOVL, O_l_hi, O_r0},
+	{Vmul, O_r_lo, O_r0},
+	{Vins, AMOVL, O_r_lo, O_l_hi},
+	{Vins, AMULL, O_l_hi, O_Zop},
+	{Vins, AADDL, O_r0, O_l_hi},
+	{V_f0, V_p0, Vend},
+};
+
+/* mul of const64 */
+static uchar	mulc64[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_r_hi, O_r0},
+	{Vop, OOR, O_l_hi, O_r0},
+	{Vop, ONE, O_r0, C00},
+	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
+	{Vins, AMULL, O_r0, O_Zop},
+	{Vgo, V_p0, V_s0},
+	{Vmul, O_r_lo, O_l_hi},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vmul, O_r_hi, O_r0},
+	{Vins, AADDL, O_l_hi, O_r0},
+	{Vins, AMOVL, O_r_lo, O_l_hi},
+	{Vins, AMULL, O_l_hi, O_Zop},
+	{Vins, AADDL, O_r0, O_l_hi},
+	{V_f0, V_p0, Vend},
+};
+
+/* mul general */
+static uchar	mull[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_r_hi, O_r0},
+	{Vop, OOR, O_l_hi, O_r0},
+	{Vop, ONE, O_r0, C00},
+	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
+	{Vins, AMULL, O_r0, O_Zop},
+	{Vgo, V_p0, V_s0},
+	{Vins, AIMULL, O_r_lo, O_l_hi},
+	{Vins, AMOVL, O_l_lo, O_r0},
+	{Vins, AIMULL, O_r_hi, O_r0},
+	{Vins, AADDL, O_l_hi, O_r0},
+	{Vins, AMOVL, O_r_lo, O_l_hi},
+	{Vins, AMULL, O_l_hi, O_Zop},
+	{Vins, AADDL, O_r0, O_l_hi},
+	{V_f0, V_p0, Vend},
+};
+
+/* cast rp l to rp t */
+static uchar	castrp[][VLEN] =
+{
+	{Vmv, O_l, O_t_lo},
+	{VT, Vins, AMOVL, O_t_lo, O_t_hi},
+	{VT, Vins, ASARL, C31, O_t_hi},
+	{VF, Vzero, O_t_hi},
+	{Vend},
+};
+
+/* cast rp l to addr t */
+static uchar	castrpa[][VLEN] =
+{
+	{VT, V_a0, Vmv, O_l, O_r0},
+	{VT, Vins, AMOVL, O_r0, O_t_lo},
+	{VT, Vins, ASARL, C31, O_r0},
+	{VT, Vins, AMOVL, O_r0, O_t_hi},
+	{VT, V_f0},
+	{VF, Vmv, O_l, O_t_lo},
+	{VF, Vzero, O_t_hi},
+	{Vend},
+};
+
+static uchar	netab0i[][VLEN] =
+{
+	{Vop, ONE, O_l_lo, O_r_lo},
+	{V_s0, Vop, ONE, O_l_hi, O_r_hi},
+	{V_s1, Vgo, V_s2, Vgo, V_s3},
+	{VF, V_p0, V_p1, VT, V_p2},
+	{Vgo, V_p3},
+	{VT, V_p0, V_p1, VF, V_p2},
+	{Vend},
+};
+
+static uchar	netabii[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_l_lo, O_r0},
+	{Vop, ONE, O_r0, O_r_lo},
+	{V_s0, Vins, AMOVL, O_l_hi, O_r0},
+	{Vop, ONE, O_r0, O_r_hi},
+	{V_s1, Vgo, V_s2, Vgo, V_s3},
+	{VF, V_p0, V_p1, VT, V_p2},
+	{Vgo, V_p3},
+	{VT, V_p0, V_p1, VF, V_p2},
+	{V_f0, Vend},
+};
+
+static uchar	cmptab0i[][VLEN] =
+{
+	{Vopx, Bop0, O_l_hi, O_r_hi},
+	{V_s0, Vins0, AJNE},
+	{V_s1, Vopx, Bop1, O_l_lo, O_r_lo},
+	{V_s2, Vgo, V_s3, Vgo, V_s4},
+	{VT, V_p1, V_p3},
+	{VF, V_p0, V_p2},
+	{Vgo, V_p4},
+	{VT, V_p0, V_p2},
+	{VF, V_p1, V_p3},
+	{Vend},
+};
+
+static uchar	cmptabii[][VLEN] =
+{
+	{V_a0, Vins, AMOVL, O_l_hi, O_r0},
+	{Vopx, Bop0, O_r0, O_r_hi},
+	{V_s0, Vins0, AJNE},
+	{V_s1, Vins, AMOVL, O_l_lo, O_r0},
+	{Vopx, Bop1, O_r0, O_r_lo},
+	{V_s2, Vgo, V_s3, Vgo, V_s4},
+	{VT, V_p1, V_p3},
+	{VF, V_p0, V_p2},
+	{Vgo, V_p4},
+	{VT, V_p0, V_p2},
+	{VF, V_p1, V_p3},
+	{V_f0, Vend},
+};
+
+static uchar	(*NEtab[])[VLEN] =
+{
+	netab0i, netabii,
+};
+
+static uchar	(*cmptab[])[VLEN] =
+{
+	cmptab0i, cmptabii,
+};
+
+static uchar	GEargs[]	= { OGT, OHS };
+static uchar	GTargs[]	= { OGT, OHI };
+static uchar	HIargs[]	= { OHI, OHI };
+static uchar	HSargs[]	= { OHI, OHS };
+
+/* Big Generator */
+static void
+biggen(Node *l, Node *r, Node *t, int true, uchar code[][VLEN], uchar *a)
+{
+	int i, j, g, oc, op, lo, ro, to, xo, *xp;
+	Type *lt;
+	Prog *pr[VOPS];
+	Node *ot, *tl, *tr, tmps[2];
+	uchar *c, (*cp)[VLEN], args[VARGS];
+
+	if(a != nil)
+		memmove(args, a, VARGS);
+//print("biggen %d %d %d\n", args[0], args[1], args[2]);
+//if(l) prtree(l, "l");
+//if(r) prtree(r, "r");
+//if(t) prtree(t, "t");
+	lo = ro = to = 0;
+	cp = code;
+
+	for (;;) {
+		c = *cp++;
+		g = 1;
+		i = 0;
+//print("code %d %d %d %d %d\n", c[0], c[1], c[2], c[3], c[4]);
+		for(;;) {
+			switch(op = c[i]) {
+			case Vgo:
+				if(g)
+					gbranch(OGOTO);
+				i++;
+				break;
+
+			case Vamv:
+				i += 3;
+				if(i > VLEN) {
+					diag(l, "bad Vop");
+					return;
+				}
+				if(g)
+					args[c[i - 1]] = args[c[i - 2]];
+				break;
+
+			case Vzero:
+				i += 2;
+				if(i > VLEN) {
+					diag(l, "bad Vop");
+					return;
+				}
+				j = i - 1;
+				goto op;
+
+			case Vspazz:	// nasty hack to save a reg in SUB
+//print("spazz\n");
+				if(g) {
+//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
+					ot = r->right;
+					r->right = r->left;
+					tl = new(0, Z, Z);
+					*tl = tmps[0];
+					r->left = tl;
+					tmps[0] = *ot;
+//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
+				}
+				i++;
+				break;
+
+			case Vmv:
+			case Vmul:
+			case Vshll:
+				i += 3;
+				if(i > VLEN) {
+					diag(l, "bad Vop");
+					return;
+				}
+				j = i - 2;
+				goto op;
+
+			case Vins0:
+				i += 2;
+				if(i > VLEN) {
+					diag(l, "bad Vop");
+					return;
+				}
+				gins(c[i - 1], Z, Z);
+				break;
+
+			case Vop:
+			case Vopx:
+			case Vins:
+			case Vinsl:
+			case Vinsr:
+			case Vinsla:
+			case Vinsra:
+			case Vinsx:
+				i += 4;
+				if(i > VLEN) {
+					diag(l, "bad Vop");
+					return;
+				}
+				j = i - 2;
+				goto op;
+
+			op:
+				if(!g)
+					break;
+				tl = Z;
+				tr = Z;
+				for(; j < i; j++) {
+					switch(c[j]) {
+					case C00:
+						ot = nodconst(0);
+						break;
+					case C01:
+						ot = nodconst(1);
+						break;
+					case C31:
+						ot = nodconst(31);
+						break;
+					case C32:
+						ot = nodconst(32);
+						break;
+
+					case O_l:
+					case O_l_lo:
+						ot = l; xp = &lo; xo = 0;
+						goto op0;
+					case O_l_hi:
+						ot = l; xp = &lo; xo = SZ_LONG;
+						goto op0;
+					case O_r:
+					case O_r_lo:
+						ot = r; xp = &ro; xo = 0;
+						goto op0;
+					case O_r_hi:
+						ot = r; xp = &ro; xo = SZ_LONG;
+						goto op0;
+					case O_t_lo:
+						ot = t; xp = &to; xo = 0;
+						goto op0;
+					case O_t_hi:
+						ot = t; xp = &to; xo = SZ_LONG;
+						goto op0;
+					case O_l_rp:
+						ot = l;
+						break;
+					case O_r_rp:
+						ot = r;
+						break;
+					case O_t_rp:
+						ot = t;
+						break;
+					case O_r0:
+					case O_r1:
+						ot = &tmps[c[j] - O_r0];
+						break;
+					case O_Zop:
+						ot = Z;
+						break;
+
+					op0:
+						switch(ot->op) {
+						case OCONST:
+							if(xo)
+								ot = hi64(ot);
+							else
+								ot = lo64(ot);
+							break;
+						case OREGPAIR:
+							if(xo)
+								ot = ot->right;
+							else
+								ot = ot->left;
+							break;
+						case OREGISTER:
+							break;
+						default:
+							if(xo != *xp) {
+								ot->xoffset += xo - *xp;
+								*xp = xo;
+							}
+						}
+						break;
+					
+					default:
+						diag(l, "bad V_lop");
+						return;
+					}
+					if(tl == nil)
+						tl = ot;
+					else
+						tr = ot;
+				}
+				if(op == Vzero) {
+					zeroregm(tl);
+					break;
+				}
+				oc = c[i - 3];
+				if(op == Vinsx || op == Vopx) {
+//print("%d -> %d\n", oc, args[oc]);
+					oc = args[oc];
+				}
+				else {
+					switch(oc) {
+					case O_a0:
+					case O_a1:
+						oc = args[oc - O_a0];
+						break;
+					}
+				}
+				switch(op) {
+				case Vmul:
+					mulgen(tr->type, tl, tr);
+					break;
+				case Vmv:
+					gmove(tl, tr);
+					break;
+				case Vshll:
+					shiftit(tr->type, tl, tr);
+					break;
+				case Vop:
+				case Vopx:
+					gopcode(oc, types[TULONG], tl, tr);
+					break;
+				case Vins:
+				case Vinsx:
+					gins(oc, tl, tr);
+					break;
+				case Vinsl:
+					gins(oc, tl, tr->right);
+					p->from.index = tr->left->reg;
+					break;
+				case Vinsr:
+					gins(oc, tl, tr->left);
+					p->from.index = tr->right->reg;
+					break;
+				case Vinsla:
+					gins(oc, tl, tr + 1);
+					p->from.index = tr->reg;
+					break;
+				case Vinsra:
+					gins(oc, tl, tr);
+					p->from.index = (tr + 1)->reg;
+					break;
+				}
+				break;
+
+			case VT:
+				g = true;
+				i++;
+				break;
+			case VF:
+				g = !true;
+				i++;
+				break;
+
+			case V_T0: case V_T1:
+				g = args[op - V_T0];
+				i++;
+				break;
+
+			case V_F0: case V_F1:
+				g = !args[op - V_F0];
+				i++;
+				break;
+
+			case V_C0: case V_C1:
+				if(g)
+					args[op - V_C0] = 0;
+				i++;
+				break;
+
+			case V_S0: case V_S1:
+				if(g)
+					args[op - V_S0] = 1;
+				i++;
+				break;
+
+			case V_l_lo_f:
+				g = lo64v(l) == 0;
+				i++;
+				break;
+			case V_l_hi_f:
+				g = hi64v(l) == 0;
+				i++;
+				break;
+			case V_l_lo_t:
+				g = lo64v(l) != 0;
+				i++;
+				break;
+			case V_l_hi_t:
+				g = hi64v(l) != 0;
+				i++;
+				break;
+			case V_l_lo_u:
+				g = lo64v(l) >= 0;
+				i++;
+				break;
+			case V_l_hi_u:
+				g = hi64v(l) >= 0;
+				i++;
+				break;
+			case V_r_lo_f:
+				g = lo64v(r) == 0;
+				i++;
+				break;
+			case V_r_hi_f:
+				g = hi64v(r) == 0;
+				i++;
+				break;
+			case V_r_lo_t:
+				g = lo64v(r) != 0;
+				i++;
+				break;
+			case V_r_hi_t:
+				g = hi64v(r) != 0;
+				i++;
+				break;
+			case V_r_lo_u:
+				g = lo64v(r) >= 0;
+				i++;
+				break;
+			case V_r_hi_u:
+				g = hi64v(r) >= 0;
+				i++;
+				break;
+
+			case Vend:
+				goto out;
+
+			case V_a0: case V_a1:
+				if(g) {
+					lt = l->type;
+					l->type = types[TULONG];
+					regalloc(&tmps[op - V_a0], l, Z);
+					l->type = lt;
+				}
+				i++;
+				break;
+
+			case V_f0: case V_f1:
+				if(g)
+					regfree(&tmps[op - V_f0]);
+				i++;
+				break;
+
+			case V_p0: case V_p1: case V_p2: case V_p3: case V_p4:
+				if(g)
+					patch(pr[op - V_p0], pc);
+				i++;
+				break;
+
+			case V_s0: case V_s1: case V_s2: case V_s3: case V_s4:
+				if(g)
+					pr[op - V_s0] = p;
+				i++;
+				break;
+
+			default:
+				diag(l, "bad biggen: %d", op);
+				return;
+			}
+			if(i == VLEN || c[i] == 0)
+				break;
+		}
+	}
+out:
+	if(lo)
+		l->xoffset -= lo;
+	if(ro)
+		r->xoffset -= ro;
+	if(to)
+		t->xoffset -= to;
+}
+
+int
+cgen64(Node *n, Node *nn)
+{
+	Type *dt;
+	uchar *args, (*cp)[VLEN], (**optab)[VLEN];
+	int li, ri, lri, dr, si, m, op, sh, cmp, true;
+	Node *c, *d, *l, *r, *t, *s, nod1, nod2, nod3, nod4, nod5;
+
+	if(debug['g']) {
+		prtree(nn, "cgen64 lhs");
+		prtree(n, "cgen64");
+		print("AX = %d\n", reg[D_AX]);
+	}
+	cmp = 0;
+	sh = 0;
+
+	switch(n->op) {
+	case ONEG:
+		d = regpair(nn, n);
+		sugen(n->left, d, 8);
+		gins(ANOTL, Z, d->right);
+		gins(ANEGL, Z, d->left);
+		gins(ASBBL, nodconst(-1), d->right);
+		break;
+
+	case OCOM:
+		if(!vaddr(n->left, 0) || !vaddr(nn, 0))
+			d = regpair(nn, n);
+		else
+			return 0;
+		sugen(n->left, d, 8);
+		gins(ANOTL, Z, d->left);
+		gins(ANOTL, Z, d->right);
+		break;
+
+	case OADD:
+		optab = ADDtab;
+		args = ADDargs;
+		goto twoop;
+	case OAND:
+		optab = ANDtab;
+		args = ANDargs;
+		goto twoop;
+	case OOR:
+		optab = ORtab;
+		args = ORargs;
+		goto twoop;
+	case OSUB:
+		optab = SUBtab;
+		args = SUBargs;
+		goto twoop;
+	case OXOR:
+		optab = ORtab;
+		args = XORargs;
+		goto twoop;
+	case OASHL:
+		sh = 1;
+		args = nil;
+		optab = shlltab;
+		goto twoop;
+	case OLSHR:
+		sh = 1;
+		args = shrlargs;
+		optab = shrltab;
+		goto twoop;
+	case OASHR:
+		sh = 1;
+		args = sarlargs;
+		optab = shrltab;
+		goto twoop;
+	case OEQ:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case ONE:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OLE:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OLT:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OGE:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OGT:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OHI:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OHS:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OLO:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+	case OLS:
+		cmp = 1;
+		args = nil;
+		optab = nil;
+		goto twoop;
+
+twoop:
+		dr = nn != Z && nn->op == OREGPAIR;
+		l = vfunc(n->left, nn);
+		if(sh)
+			r = n->right;
+		else
+			r = vfunc(n->right, nn);
+
+		li = l->op == ONAME || l->op == OINDREG || l->op == OCONST;
+		ri = r->op == ONAME || r->op == OINDREG || r->op == OCONST;
+
+#define	IMM(l, r)	((l) | ((r) << 1))
+
+		lri = IMM(li, ri);
+
+		/* find out what is so easy about some operands */
+		if(li)
+			li = whatof(l, sh | cmp);
+		if(ri)
+			ri = whatof(r, cmp);
+
+		if(sh)
+			goto shift;
+
+		if(cmp)
+			goto cmp;
+
+		/* evaluate hard subexps, stealing nn if possible. */
+		switch(lri) {
+		case IMM(0, 0):
+		bin00:
+			if(l->complex > r->complex) {
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, n);
+				sugen(l, t, 8);
+				l = t;
+				t = regpair(Z, n);
+				sugen(r, t, 8);
+				r = t;
+			}
+			else {
+				t = regpair(Z, n);
+				sugen(r, t, 8);
+				r = t;
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, n);
+				sugen(l, t, 8);
+				l = t;
+			}
+			break;
+		case IMM(0, 1):
+			if(dr)
+				t = nn;
+			else
+				t = regpair(Z, n);
+			sugen(l, t, 8);
+			l = t;
+			break;
+		case IMM(1, 0):
+			if(n->op == OSUB && l->op == OCONST && hi64v(l) == 0) {
+				lri = IMM(0, 0);
+				goto bin00;
+			}
+			if(dr)
+				t = nn;
+			else
+				t = regpair(Z, n);
+			sugen(r, t, 8);
+			r = t;
+			break;
+		case IMM(1, 1):
+			break;
+		}
+
+#define	WW(l, r)	((l) | ((r) << 2))
+		d = Z;
+		dt = nn->type;
+		nn->type = types[TLONG];
+
+		switch(lri) {
+		case IMM(0, 0):
+			biggen(l, r, Z, 0, binop00, args);
+			break;
+		case IMM(0, 1):
+			switch(ri) {
+			case WNONE:
+				diag(r, "bad whatof\n");
+				break;
+			case WCONST:
+				biggen(l, r, Z, 0, optab[B0c], args);
+				break;
+			case WHARD:
+				reglcgen(&nod2, r, Z);
+				r = &nod2;
+				/* fall thru */
+			case WADDR:
+				biggen(l, r, Z, 0, binoptmp, args);
+				if(ri == WHARD)
+					regfree(r);
+				break;
+			}
+			break;
+		case IMM(1, 0):
+			if(n->op == OSUB) {
+				switch(li) {
+				case WNONE:
+					diag(l, "bad whatof\n");
+					break;
+				case WHARD:
+					reglcgen(&nod2, l, Z);
+					l = &nod2;
+					/* fall thru */
+				case WADDR:
+				case WCONST:
+					biggen(l, r, Z, 0, sub10, args);
+					break;
+				}
+				if(li == WHARD)
+					regfree(l);
+			}
+			else {
+				switch(li) {
+				case WNONE:
+					diag(l, "bad whatof\n");
+					break;
+				case WCONST:
+					biggen(r, l, Z, 0, optab[B0c], args);
+					break;
+				case WHARD:
+					reglcgen(&nod2, l, Z);
+					l = &nod2;
+					/* fall thru */
+				case WADDR:
+					biggen(r, l, Z, 0, binoptmp, args);
+					if(li == WHARD)
+						regfree(l);
+					break;
+				}
+			}
+			break;
+		case IMM(1, 1):
+			switch(WW(li, ri)) {
+			case WW(WCONST, WHARD):
+				if(r->op == ONAME && n->op == OAND && reduxv(l))
+					ri = WADDR;
+				break;
+			case WW(WHARD, WCONST):
+				if(l->op == ONAME && n->op == OAND && reduxv(r))
+					li = WADDR;
+				break;
+			}
+			if(li == WHARD) {
+				reglcgen(&nod3, l, Z);
+				l = &nod3;
+			}
+			if(ri == WHARD) {
+				reglcgen(&nod2, r, Z);
+				r = &nod2;
+			}
+			d = regpair(nn, n);
+			instpair(d, Z);
+			switch(WW(li, ri)) {
+			case WW(WCONST, WADDR):
+			case WW(WCONST, WHARD):
+				biggen(l, r, d, 0, optab[Bca], args);
+				break;
+
+			case WW(WADDR, WCONST):
+			case WW(WHARD, WCONST):
+				biggen(l, r, d, 0, optab[Bac], args);
+				break;
+
+			case WW(WADDR, WADDR):
+			case WW(WADDR, WHARD):
+			case WW(WHARD, WADDR):
+			case WW(WHARD, WHARD):
+				biggen(l, r, d, 0, binop11, args);
+				break;
+
+			default:
+				diag(r, "bad whatof pair %d %d\n", li, ri);
+				break;
+			}
+			if(li == WHARD)
+				regfree(l);
+			if(ri == WHARD)
+				regfree(r);
+			break;
+		}
+
+		nn->type = dt;
+
+		if(d != Z)
+			goto finished;
+
+		switch(lri) {
+		case IMM(0, 0):
+			freepair(r);
+			/* fall thru */;
+		case IMM(0, 1):
+			if(!dr)
+				storepair(l, nn, 1);
+			break;
+		case IMM(1, 0):
+			if(!dr)
+				storepair(r, nn, 1);
+			break;
+		case IMM(1, 1):
+			break;
+		}
+		return 1;
+
+	shift:
+		c = Z;
+
+		/* evaluate hard subexps, stealing nn if possible. */
+		/* must also secure CX.  not as many optims as binop. */
+		switch(lri) {
+		case IMM(0, 0):
+		imm00:
+			if(l->complex + 1 > r->complex) {
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, l);
+				sugen(l, t, 8);
+				l = t;
+				t = &nod1;
+				c = snarfreg(l, t, D_CX, r, &nod2);
+				cgen(r, t);
+				r = t;
+			}
+			else {
+				t = &nod1;
+				c = snarfreg(nn, t, D_CX, r, &nod2);
+				cgen(r, t);
+				r = t;
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, l);
+				sugen(l, t, 8);
+				l = t;
+			}
+			break;
+		case IMM(0, 1):
+		imm01:
+			if(ri != WCONST) {
+				lri = IMM(0, 0);
+				goto imm00;
+			}
+			if(dr)
+				t = nn;
+			else
+				t = regpair(Z, n);
+			sugen(l, t, 8);
+			l = t;
+			break;
+		case IMM(1, 0):
+		imm10:
+			if(li != WCONST) {
+				lri = IMM(0, 0);
+				goto imm00;
+			}
+			t = &nod1;
+			c = snarfreg(nn, t, D_CX, r, &nod2);
+			cgen(r, t);
+			r = t;
+			break;
+		case IMM(1, 1):
+			if(ri != WCONST) {
+				lri = IMM(1, 0);
+				goto imm10;
+			}
+			if(li == WHARD) {
+				lri = IMM(0, 1);
+				goto imm01;
+			}
+			break;
+		}
+
+		d = Z;
+
+		switch(lri) {
+		case IMM(0, 0):
+			biggen(l, r, Z, 0, optab[S00], args);
+			break;
+		case IMM(0, 1):
+			switch(ri) {
+			case WNONE:
+			case WADDR:
+			case WHARD:
+				diag(r, "bad whatof\n");
+				break;
+			case WCONST:
+				m = r->vconst & 63;
+				s = nodconst(m);
+				if(m < 32)
+					cp = optab[Sc0];
+				else if(m == 32)
+					cp = optab[Sc1];
+				else
+					cp = optab[Sc2];
+				biggen(l, s, Z, 0, cp, args);
+				break;
+			}
+			break;
+		case IMM(1, 0):
+			/* left is const */
+			d = regpair(nn, n);
+			instpair(d, Z);
+			biggen(l, r, d, 0, optab[S10], args);
+			regfree(r);
+			break;
+		case IMM(1, 1):
+			d = regpair(nn, n);
+			instpair(d, Z);
+			switch(WW(li, ri)) {
+			case WW(WADDR, WCONST):
+				m = r->vconst & 63;
+				s = nodconst(m);
+				if(m < 32) {
+					loadpair(l, d);
+					l = d;
+					cp = optab[Sc0];
+				}
+				else if(m == 32)
+					cp = optab[Sac3];
+				else
+					cp = optab[Sac4];
+				biggen(l, s, d, 0, cp, args);
+				break;
+
+			default:
+				diag(r, "bad whatof pair %d %d\n", li, ri);
+				break;
+			}
+			break;
+		}
+
+		if(c != Z) {
+			gins(AMOVL, c, r);
+			regfree(c);
+		}
+
+		if(d != Z)
+			goto finished;
+
+		switch(lri) {
+		case IMM(0, 0):
+			regfree(r);
+			/* fall thru */
+		case IMM(0, 1):
+			if(!dr)
+				storepair(l, nn, 1);
+			break;
+		case IMM(1, 0):
+			regfree(r);
+			break;
+		case IMM(1, 1):
+			break;
+		}
+		return 1;
+
+	cmp:
+		op = n->op;
+		/* evaluate hard subexps */
+		switch(lri) {
+		case IMM(0, 0):
+			if(l->complex > r->complex) {
+				t = regpair(Z, l);
+				sugen(l, t, 8);
+				l = t;
+				t = regpair(Z, r);
+				sugen(r, t, 8);
+				r = t;
+			}
+			else {
+				t = regpair(Z, r);
+				sugen(r, t, 8);
+				r = t;
+				t = regpair(Z, l);
+				sugen(l, t, 8);
+				l = t;
+			}
+			break;
+		case IMM(1, 0):
+			t = r;
+			r = l;
+			l = t;
+			ri = li;
+			op = invrel[relindex(op)];
+			/* fall thru */
+		case IMM(0, 1):
+			t = regpair(Z, l);
+			sugen(l, t, 8);
+			l = t;
+			break;
+		case IMM(1, 1):
+			break;
+		}
+
+		true = 1;
+		optab = cmptab;
+		switch(op) {
+		case OEQ:
+			optab = NEtab;
+			true = 0;
+			break;
+		case ONE:
+			optab = NEtab;
+			break;
+		case OLE:
+			args = GTargs;
+			true = 0;
+			break;
+		case OGT:
+			args = GTargs;
+			break;
+		case OLS:
+			args = HIargs;
+			true = 0;
+			break;
+		case OHI:
+			args = HIargs;
+			break;
+		case OLT:
+			args = GEargs;
+			true = 0;
+			break;
+		case OGE:
+			args = GEargs;
+			break;
+		case OLO:
+			args = HSargs;
+			true = 0;
+			break;
+		case OHS:
+			args = HSargs;
+			break;
+		default:
+			diag(n, "bad cmp\n");
+			SET(optab);
+		}
+
+		switch(lri) {
+		case IMM(0, 0):
+			biggen(l, r, Z, true, optab[T0i], args);
+			break;
+		case IMM(0, 1):
+		case IMM(1, 0):
+			switch(ri) {
+			case WNONE:
+				diag(l, "bad whatof\n");
+				break;
+			case WCONST:
+				biggen(l, r, Z, true, optab[T0i], args);
+				break;
+			case WHARD:
+				reglcgen(&nod2, r, Z);
+				r = &nod2;
+				/* fall thru */
+			case WADDR:
+				biggen(l, r, Z, true, optab[T0i], args);
+				if(ri == WHARD)
+					regfree(r);
+				break;
+			}
+			break;
+		case IMM(1, 1):
+			if(li == WHARD) {
+				reglcgen(&nod3, l, Z);
+				l = &nod3;
+			}
+			if(ri == WHARD) {
+				reglcgen(&nod2, r, Z);
+				r = &nod2;
+			}
+			biggen(l, r, Z, true, optab[Tii], args);
+			if(li == WHARD)
+				regfree(l);
+			if(ri == WHARD)
+				regfree(r);
+			break;
+		}
+
+		switch(lri) {
+		case IMM(0, 0):
+			freepair(r);
+			/* fall thru */;
+		case IMM(0, 1):
+		case IMM(1, 0):
+			freepair(l);
+			break;
+		case IMM(1, 1):
+			break;
+		}
+		return 1;
+
+	case OASMUL:
+	case OASLMUL:
+		m = 0;
+		goto mulop;
+
+	case OMUL:
+	case OLMUL:
+		m = 1;
+		goto mulop;
+
+	mulop:
+		dr = nn != Z && nn->op == OREGPAIR;
+		l = vfunc(n->left, nn);
+		r = vfunc(n->right, nn);
+		if(r->op != OCONST) {
+			if(l->complex > r->complex) {
+				if(m) {
+					t = l;
+					l = r;
+					r = t;
+				}
+				else if(!vaddr(l, 1)) {
+					reglcgen(&nod5, l, Z);
+					l = &nod5;
+					evacaxdx(l);
+				}
+			}
+			t = regpair(Z, n);
+			sugen(r, t, 8);
+			r = t;
+			evacaxdx(r->left);
+			evacaxdx(r->right);
+			if(l->complex <= r->complex && !m && !vaddr(l, 1)) {
+				reglcgen(&nod5, l, Z);
+				l = &nod5;
+				evacaxdx(l);
+			}
+		}
+		if(dr)
+			t = nn;
+		else
+			t = regpair(Z, n);
+		c = Z;
+		d = Z;
+		if(!nodreg(&nod1, t->left, D_AX)) {
+			if(t->left->reg != D_AX){
+				t->left->reg = D_AX;
+				reg[D_AX]++;
+			}else if(reg[D_AX] == 0)
+				fatal(Z, "vlong mul AX botch");
+		}
+		if(!nodreg(&nod2, t->right, D_DX)) {
+			if(t->right->reg != D_DX){
+				t->right->reg = D_DX;
+				reg[D_DX]++;
+			}else if(reg[D_DX] == 0)
+				fatal(Z, "vlong mul DX botch");
+		}
+		if(m)
+			sugen(l, t, 8);
+		else
+			loadpair(l, t);
+		if(t->left->reg != D_AX) {
+			c = &nod3;
+			regsalloc(c, t->left);
+			gmove(&nod1, c);
+			gmove(t->left, &nod1);
+			zapreg(t->left);
+		}
+		if(t->right->reg != D_DX) {
+			d = &nod4;
+			regsalloc(d, t->right);
+			gmove(&nod2, d);
+			gmove(t->right, &nod2);
+			zapreg(t->right);
+		}
+		if(c != Z || d != Z) {
+			s = regpair(Z, n);
+			s->left = &nod1;
+			s->right = &nod2;
+		}
+		else
+			s = t;
+		if(r->op == OCONST) {
+			if(hi64v(r) == 0)
+				biggen(s, r, Z, 0, mulc32, nil);
+			else
+				biggen(s, r, Z, 0, mulc64, nil);
+		}
+		else
+			biggen(s, r, Z, 0, mull, nil);
+		instpair(t, Z);
+		if(c != Z) {
+			gmove(&nod1, t->left);
+			gmove(&nod3, &nod1);
+		}
+		if(d != Z) {
+			gmove(&nod2, t->right);
+			gmove(&nod4, &nod2);
+		}
+		if(r->op == OREGPAIR)
+			freepair(r);
+		if(!m)
+			storepair(t, l, 0);
+		if(l == &nod5)
+			regfree(l);
+		if(!dr) {
+			if(nn != Z)
+				storepair(t, nn, 1);
+			else
+				freepair(t);
+		}
+		return 1;
+
+	case OASADD:
+		args = ADDargs;
+		goto vasop;
+	case OASAND:
+		args = ANDargs;
+		goto vasop;
+	case OASOR:
+		args = ORargs;
+		goto vasop;
+	case OASSUB:
+		args = SUBargs;
+		goto vasop;
+	case OASXOR:
+		args = XORargs;
+		goto vasop;
+
+	vasop:
+		l = n->left;
+		r = n->right;
+		dr = nn != Z && nn->op == OREGPAIR;
+		m = 0;
+		if(l->complex > r->complex) {
+			if(!vaddr(l, 1)) {
+				reglcgen(&nod1, l, Z);
+				l = &nod1;
+			}
+			if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, r);
+				sugen(r, t, 8);
+				r = t;
+				m = 1;
+			}
+		}
+		else {
+			if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
+				if(dr)
+					t = nn;
+				else
+					t = regpair(Z, r);
+				sugen(r, t, 8);
+				r = t;
+				m = 1;
+			}
+			if(!vaddr(l, 1)) {
+				reglcgen(&nod1, l, Z);
+				l = &nod1;
+			}
+		}
+		if(nn != Z) {
+			if(n->op == OASSUB)
+				biggen(l, r, Z, 0, sub10, args);
+			else
+				biggen(r, l, Z, 0, binoptmp, args);
+			storepair(r, l, 0);
+		}
+		else {
+			if(m)
+				biggen(l, r, Z, 0, binop00, args);
+			else
+				biggen(l, r, Z, 0, binoptmp, args);
+		}
+		if(l == &nod1)
+			regfree(&nod1);
+		if(m) {
+			if(nn == Z)
+				freepair(r);
+			else if(!dr)
+				storepair(r, nn, 1);
+		}
+		return 1;
+
+	case OASASHL:
+		args = nil;
+		optab = asshlltab;
+		goto assh;
+	case OASLSHR:
+		args = shrlargs;
+		optab = asshrltab;
+		goto assh;
+	case OASASHR:
+		args = sarlargs;
+		optab = asshrltab;
+		goto assh;
+
+	assh:
+		c = Z;
+		l = n->left;
+		r = n->right;
+		if(r->op == OCONST) {
+			m = r->vconst & 63;
+			if(m < 32)
+				m = SAclo;
+			else if(m == 32)
+				m = SAc32;
+			else
+				m = SAchi;
+		}
+		else
+			m = SAgen;
+		if(l->complex > r->complex) {
+			if(!vaddr(l, 0)) {
+				reglcgen(&nod1, l, Z);
+				l = &nod1;
+			}
+			if(m == SAgen) {
+				t = &nod2;
+				if(l->reg == D_CX) {
+					regalloc(t, r, Z);
+					gmove(l, t);
+					l->reg = t->reg;
+					t->reg = D_CX;
+				}
+				else
+					c = snarfreg(nn, t, D_CX, r, &nod3);
+				cgen(r, t);
+				r = t;
+			}
+		}
+		else {
+			if(m == SAgen) {
+				t = &nod2;
+				c = snarfreg(nn, t, D_CX, r, &nod3);
+				cgen(r, t);
+				r = t;
+			}
+			if(!vaddr(l, 0)) {
+				reglcgen(&nod1, l, Z);
+				l = &nod1;
+			}
+		}
+
+		if(nn != Z) {
+			m += SAdgen - SAgen;
+			d = regpair(nn, n);
+			instpair(d, Z);
+			biggen(l, r, d, 0, optab[m], args);
+			if(l == &nod1) {
+				regfree(&nod1);
+				l = Z;
+			}
+			if(r == &nod2 && c == Z) {
+				regfree(&nod2);
+				r = Z;
+			}
+			if(d != nn)
+				storepair(d, nn, 1);
+		}
+		else
+			biggen(l, r, Z, 0, optab[m], args);
+
+		if(c != Z) {
+			gins(AMOVL, c, r);
+			regfree(c);
+		}
+		if(l == &nod1)
+			regfree(&nod1);
+		if(r == &nod2)
+			regfree(&nod2);
+		return 1;
+
+	case OPOSTINC:
+		args = ADDargs;
+		cp = incdecpost;
+		goto vinc;
+	case OPOSTDEC:
+		args = SUBargs;
+		cp = incdecpost;
+		goto vinc;
+	case OPREINC:
+		args = ADDargs;
+		cp = incdecpre;
+		goto vinc;
+	case OPREDEC:
+		args = SUBargs;
+		cp = incdecpre;
+		goto vinc;
+
+	vinc:
+		l = n->left;
+		if(!vaddr(l, 1)) {
+			reglcgen(&nod1, l, Z);
+			l = &nod1;
+		}
+		
+		if(nn != Z) {
+			d = regpair(nn, n);
+			instpair(d, Z);
+			biggen(l, Z, d, 0, cp, args);
+			if(l == &nod1) {
+				regfree(&nod1);
+				l = Z;
+			}
+			if(d != nn)
+				storepair(d, nn, 1);
+		}
+		else
+			biggen(l, Z, Z, 0, incdec, args);
+
+		if(l == &nod1)
+			regfree(&nod1);
+		return 1;
+
+	case OCAST:
+		l = n->left;
+		if(typev[l->type->etype]) {
+			if(!vaddr(l, 1)) {
+				if(l->complex + 1 > nn->complex) {
+					d = regpair(Z, l);
+					sugen(l, d, 8);
+					if(!vaddr(nn, 1)) {
+						reglcgen(&nod1, nn, Z);
+						r = &nod1;
+					}
+					else
+						r = nn;
+				}
+				else {
+					if(!vaddr(nn, 1)) {
+						reglcgen(&nod1, nn, Z);
+						r = &nod1;
+					}
+					else
+						r = nn;
+					d = regpair(Z, l);
+					sugen(l, d, 8);
+				}
+//				d->left->type = r->type;
+				d->left->type = types[TLONG];
+				gmove(d->left, r);
+				freepair(d);
+			}
+			else {
+				if(nn->op != OREGISTER && !vaddr(nn, 1)) {
+					reglcgen(&nod1, nn, Z);
+					r = &nod1;
+				}
+				else
+					r = nn;
+//				l->type = r->type;
+				l->type = types[TLONG];
+				gmove(l, r);
+			}
+			if(r != nn)
+				regfree(r);
+		}
+		else {
+			if(typeu[l->type->etype] || cond(l->op))
+				si = TUNSIGNED;
+			else
+				si = TSIGNED;
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			if(nn->op == OREGPAIR) {
+				m = instpair(nn, &nod1);
+				biggen(&nod1, Z, nn, si == TSIGNED, castrp, nil);
+			}
+			else {
+				m = 0;
+				if(!vaddr(nn, si != TSIGNED)) {
+					dt = nn->type;
+					nn->type = types[TLONG];
+					reglcgen(&nod2, nn, Z);
+					nn->type = dt;
+					nn = &nod2;
+				}
+				dt = nn->type;
+				nn->type = types[TLONG];
+				biggen(&nod1, Z, nn, si == TSIGNED, castrpa, nil);
+				nn->type = dt;
+				if(nn == &nod2)
+					regfree(&nod2);
+			}
+			if(!m)
+				regfree(&nod1);
+		}
+		return 1;
+
+	default:
+		if(n->op == OREGPAIR) {
+			storepair(n, nn, 1);
+			return 1;
+		}
+		if(nn->op == OREGPAIR) {
+			loadpair(n, nn);
+			return 1;
+		}
+		return 0;
+	}
+finished:
+	if(d != nn)
+		storepair(d, nn, 1);
+	return 1;
+}
+
+void
+testv(Node *n, int true)
+{
+	Type *t;
+	Node *nn, nod, *b;
+
+	if(machcap(Z)) {
+		b = &nod;
+		b->op = true ? ONE : OEQ;
+		b->left = n;
+		b->right = new(0, Z, Z);
+		*b->right = *nodconst(0);
+		b->right->type = n->type;
+		b->type = types[TLONG];
+		cgen64(b, Z);
+		return;
+	}
+
+	switch(n->op) {
+	case OINDREG:
+	case ONAME:
+		biggen(n, Z, Z, true, testi, nil);
+		break;
+
+	default:
+		n = vfunc(n, n);
+		if(n->addable >= INDEXED) {
+			t = n->type;
+			n->type = types[TLONG];
+			reglcgen(&nod, n, Z);
+			n->type = t;
+			n = &nod;
+			biggen(n, Z, Z, true, testi, nil);
+			if(n == &nod)
+				regfree(n);
+		}
+		else {
+			nn = regpair(Z, n);
+			sugen(n, nn, 8);
+			biggen(nn, Z, Z, true, testi, nil);
+			freepair(nn);
+		}
+	}
+}
--- /dev/null
+++ b/utils/8c/div.c
@@ -1,0 +1,206 @@
+#include "gc.h"
+
+/*
+ * Based on: Granlund, T.; Montgomery, P.L.
+ * "Division by Invariant Integers using Multiplication".
+ * SIGPLAN Notices, Vol. 29, June 1994, page 61.
+ */
+
+#define	TN(n)	((uvlong)1 << (n))
+#define	T31	TN(31)
+#define	T32	TN(32)
+
+int
+multiplier(ulong d, int p, uvlong *mp)
+{
+	int l;
+	uvlong mlo, mhi, tlo, thi;
+
+	l = topbit(d - 1) + 1;
+	mlo = (((TN(l) - d) << 32) / d) + T32;
+	if(l + p == 64)
+		mhi = (((TN(l) + 1 - d) << 32) / d) + T32;
+	else
+		mhi = (TN(32 + l) + TN(32 + l - p)) / d;
+	/*assert(mlo < mhi);*/
+	while(l > 0) {
+		tlo = mlo >> 1;
+		thi = mhi >> 1;
+		if(tlo == thi)
+			break;
+		mlo = tlo;
+		mhi = thi;
+		l--;
+	}
+	*mp = mhi;
+	return l;
+}
+
+int
+sdiv(ulong d, ulong *mp, int *sp)
+{
+	int s;
+	uvlong m;
+
+	s = multiplier(d, 32 - 1, &m);
+	*mp = m;
+	*sp = s;
+	if(m >= T31)
+		return 1;
+	else
+		return 0;
+}
+
+int
+udiv(ulong d, ulong *mp, int *sp, int *pp)
+{
+	int p, s;
+	uvlong m;
+
+	s = multiplier(d, 32, &m);
+	p = 0;
+	if(m >= T32) {
+		while((d & 1) == 0) {
+			d >>= 1;
+			p++;
+		}
+		s = multiplier(d, 32 - p, &m);
+	}
+	*mp = m;
+	*pp = p;
+	if(m >= T32) {
+		/*assert(p == 0);*/
+		*sp = s - 1;
+		return 1;
+	}
+	else {
+		*sp = s;
+		return 0;
+	}
+}
+
+void
+sdivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+	int a, s;
+	ulong m;
+	vlong c;
+
+	c = r->vconst;
+	if(c < 0)
+		c = -c;
+	a = sdiv(c, &m, &s);
+//print("a=%d i=%ld s=%d m=%lux\n", a, (long)r->vconst, s, m);
+	gins(AMOVL, nodconst(m), ax);
+	gins(AIMULL, l, Z);
+	gins(AMOVL, l, ax);
+	if(a)
+		gins(AADDL, ax, dx);
+	gins(ASHRL, nodconst(31), ax);
+	gins(ASARL, nodconst(s), dx);
+	gins(AADDL, ax, dx);
+	if(r->vconst < 0)
+		gins(ANEGL, Z, dx);
+}
+
+void
+udivgen(Node *l, Node *r, Node *ax, Node *dx)
+{
+	int a, s, t;
+	ulong m;
+	Node nod;
+
+	a = udiv(r->vconst, &m, &s, &t);
+//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (long)r->vconst, t, s, m);
+	if(t != 0) {
+		gins(AMOVL, l, ax);
+		gins(ASHRL, nodconst(t), ax);
+		gins(AMOVL, nodconst(m), dx);
+		gins(AMULL, dx, Z);
+	}
+	else if(a) {
+		if(l->op != OREGISTER) {
+			regalloc(&nod, l, Z);
+			gins(AMOVL, l, &nod);
+			l = &nod;
+		}
+		gins(AMOVL, nodconst(m), ax);
+		gins(AMULL, l, Z);
+		gins(AADDL, l, dx);
+		gins(ARCRL, nodconst(1), dx);
+		if(l == &nod)
+			regfree(l);
+	}
+	else {
+		gins(AMOVL, nodconst(m), ax);
+		gins(AMULL, l, Z);
+	}
+	if(s != 0)
+		gins(ASHRL, nodconst(s), dx);
+}
+
+void
+sext(Node *d, Node *s, Node *l)
+{
+	if(s->reg == D_AX && !nodreg(d, Z, D_DX)) {
+		reg[D_DX]++;
+		gins(ACDQ, Z, Z);
+	}
+	else {
+		regalloc(d, l, Z);
+		gins(AMOVL, s, d);
+		gins(ASARL, nodconst(31), d);
+	}
+}
+
+void
+sdiv2(long c, int v, Node *l, Node *n)
+{
+	Node nod;
+
+	if(v > 0) {
+		if(v > 1) {
+			sext(&nod, n, l);
+			gins(AANDL, nodconst((1 << v) - 1), &nod);
+			gins(AADDL, &nod, n);
+			regfree(&nod);
+		}
+		else {
+			gins(ACMPL, n, nodconst(0x80000000));
+			gins(ASBBL, nodconst(-1), n);
+		}
+		gins(ASARL, nodconst(v), n);
+	}
+	if(c < 0)
+		gins(ANEGL, Z, n);
+}
+
+void
+smod2(long c, int v, Node *l, Node *n)
+{
+	Node nod;
+
+	if(c == 1) {
+		zeroregm(n);
+		return;
+	}
+
+	sext(&nod, n, l);
+	if(v == 0) {
+		zeroregm(n);
+		gins(AXORL, &nod, n);
+		gins(ASUBL, &nod, n);
+	}
+	else if(v > 1) {
+		gins(AANDL, nodconst((1 << v) - 1), &nod);
+		gins(AADDL, &nod, n);
+		gins(AANDL, nodconst((1 << v) - 1), n);
+		gins(ASUBL, &nod, n);
+	}
+	else {
+		gins(AANDL, nodconst(1), n);
+		gins(AXORL, &nod, n);
+		gins(ASUBL, &nod, n);
+	}
+	regfree(&nod);
+}
--- /dev/null
+++ b/utils/8c/enam.c
@@ -1,0 +1,384 @@
+char*	anames[] =
+{
+	"XXX",
+	"AAA",
+	"AAD",
+	"AAM",
+	"AAS",
+	"ADCB",
+	"ADCL",
+	"ADCW",
+	"ADDB",
+	"ADDL",
+	"ADDW",
+	"ADJSP",
+	"ANDB",
+	"ANDL",
+	"ANDW",
+	"ARPL",
+	"BOUNDL",
+	"BOUNDW",
+	"BSFL",
+	"BSFW",
+	"BSRL",
+	"BSRW",
+	"BTL",
+	"BTW",
+	"BTCL",
+	"BTCW",
+	"BTRL",
+	"BTRW",
+	"BTSL",
+	"BTSW",
+	"BYTE",
+	"CALL",
+	"CLC",
+	"CLD",
+	"CLI",
+	"CLTS",
+	"CMC",
+	"CMPB",
+	"CMPL",
+	"CMPW",
+	"CMPSB",
+	"CMPSL",
+	"CMPSW",
+	"DAA",
+	"DAS",
+	"DATA",
+	"DECB",
+	"DECL",
+	"DECW",
+	"DIVB",
+	"DIVL",
+	"DIVW",
+	"ENTER",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"HLT",
+	"IDIVB",
+	"IDIVL",
+	"IDIVW",
+	"IMULB",
+	"IMULL",
+	"IMULW",
+	"INB",
+	"INL",
+	"INW",
+	"INCB",
+	"INCL",
+	"INCW",
+	"INSB",
+	"INSL",
+	"INSW",
+	"INT",
+	"INTO",
+	"IRETL",
+	"IRETW",
+	"JCC",
+	"JCS",
+	"JCXZ",
+	"JEQ",
+	"JGE",
+	"JGT",
+	"JHI",
+	"JLE",
+	"JLS",
+	"JLT",
+	"JMI",
+	"JMP",
+	"JNE",
+	"JOC",
+	"JOS",
+	"JPC",
+	"JPL",
+	"JPS",
+	"LAHF",
+	"LARL",
+	"LARW",
+	"LEAL",
+	"LEAW",
+	"LEAVEL",
+	"LEAVEW",
+	"LOCK",
+	"LODSB",
+	"LODSL",
+	"LODSW",
+	"LONG",
+	"LOOP",
+	"LOOPEQ",
+	"LOOPNE",
+	"LSLL",
+	"LSLW",
+	"MOVB",
+	"MOVL",
+	"MOVW",
+	"MOVBLSX",
+	"MOVBLZX",
+	"MOVBWSX",
+	"MOVBWZX",
+	"MOVWLSX",
+	"MOVWLZX",
+	"MOVSB",
+	"MOVSL",
+	"MOVSW",
+	"MULB",
+	"MULL",
+	"MULW",
+	"NAME",
+	"NEGB",
+	"NEGL",
+	"NEGW",
+	"NOP",
+	"NOTB",
+	"NOTL",
+	"NOTW",
+	"ORB",
+	"ORL",
+	"ORW",
+	"OUTB",
+	"OUTL",
+	"OUTW",
+	"OUTSB",
+	"OUTSL",
+	"OUTSW",
+	"POPAL",
+	"POPAW",
+	"POPFL",
+	"POPFW",
+	"POPL",
+	"POPW",
+	"PUSHAL",
+	"PUSHAW",
+	"PUSHFL",
+	"PUSHFW",
+	"PUSHL",
+	"PUSHW",
+	"RCLB",
+	"RCLL",
+	"RCLW",
+	"RCRB",
+	"RCRL",
+	"RCRW",
+	"REP",
+	"REPN",
+	"RET",
+	"ROLB",
+	"ROLL",
+	"ROLW",
+	"RORB",
+	"RORL",
+	"RORW",
+	"SAHF",
+	"SALB",
+	"SALL",
+	"SALW",
+	"SARB",
+	"SARL",
+	"SARW",
+	"SBBB",
+	"SBBL",
+	"SBBW",
+	"SCASB",
+	"SCASL",
+	"SCASW",
+	"SETCC",
+	"SETCS",
+	"SETEQ",
+	"SETGE",
+	"SETGT",
+	"SETHI",
+	"SETLE",
+	"SETLS",
+	"SETLT",
+	"SETMI",
+	"SETNE",
+	"SETOC",
+	"SETOS",
+	"SETPC",
+	"SETPL",
+	"SETPS",
+	"CDQ",
+	"CWD",
+	"SHLB",
+	"SHLL",
+	"SHLW",
+	"SHRB",
+	"SHRL",
+	"SHRW",
+	"STC",
+	"STD",
+	"STI",
+	"STOSB",
+	"STOSL",
+	"STOSW",
+	"SUBB",
+	"SUBL",
+	"SUBW",
+	"SYSCALL",
+	"TESTB",
+	"TESTL",
+	"TESTW",
+	"TEXT",
+	"VERR",
+	"VERW",
+	"WAIT",
+	"WORD",
+	"XCHGB",
+	"XCHGL",
+	"XCHGW",
+	"XLAT",
+	"XORB",
+	"XORL",
+	"XORW",
+	"FMOVB",
+	"FMOVBP",
+	"FMOVD",
+	"FMOVDP",
+	"FMOVF",
+	"FMOVFP",
+	"FMOVL",
+	"FMOVLP",
+	"FMOVV",
+	"FMOVVP",
+	"FMOVW",
+	"FMOVWP",
+	"FMOVX",
+	"FMOVXP",
+	"FCOMB",
+	"FCOMBP",
+	"FCOMD",
+	"FCOMDP",
+	"FCOMDPP",
+	"FCOMF",
+	"FCOMFP",
+	"FCOML",
+	"FCOMLP",
+	"FCOMW",
+	"FCOMWP",
+	"FUCOM",
+	"FUCOMP",
+	"FUCOMPP",
+	"FADDDP",
+	"FADDW",
+	"FADDL",
+	"FADDF",
+	"FADDD",
+	"FMULDP",
+	"FMULW",
+	"FMULL",
+	"FMULF",
+	"FMULD",
+	"FSUBDP",
+	"FSUBW",
+	"FSUBL",
+	"FSUBF",
+	"FSUBD",
+	"FSUBRDP",
+	"FSUBRW",
+	"FSUBRL",
+	"FSUBRF",
+	"FSUBRD",
+	"FDIVDP",
+	"FDIVW",
+	"FDIVL",
+	"FDIVF",
+	"FDIVD",
+	"FDIVRDP",
+	"FDIVRW",
+	"FDIVRL",
+	"FDIVRF",
+	"FDIVRD",
+	"FXCHD",
+	"FFREE",
+	"FLDCW",
+	"FLDENV",
+	"FRSTOR",
+	"FSAVE",
+	"FSTCW",
+	"FSTENV",
+	"FSTSW",
+	"F2XM1",
+	"FABS",
+	"FCHS",
+	"FCLEX",
+	"FCOS",
+	"FDECSTP",
+	"FINCSTP",
+	"FINIT",
+	"FLD1",
+	"FLDL2E",
+	"FLDL2T",
+	"FLDLG2",
+	"FLDLN2",
+	"FLDPI",
+	"FLDZ",
+	"FNOP",
+	"FPATAN",
+	"FPREM",
+	"FPREM1",
+	"FPTAN",
+	"FRNDINT",
+	"FSCALE",
+	"FSIN",
+	"FSINCOS",
+	"FSQRT",
+	"FTST",
+	"FXAM",
+	"FXTRACT",
+	"FYL2X",
+	"FYL2XP1",
+	"END",
+	"DYNT",
+	"INIT",
+	"SIGNAME",
+	"FCOMI",
+	"FCOMIP",
+	"FUCOMI",
+	"FUCOMIP",
+	"CMPXCHGB",
+	"CMPXCHGL",
+	"CMPXCHGW",
+	"CMOVLCC",
+	"CMOVLCS",
+	"CMOVLEQ",
+	"CMOVLGE",
+	"CMOVLGT",
+	"CMOVLHI",
+	"CMOVLLE",
+	"CMOVLLS",
+	"CMOVLLT",
+	"CMOVLMI",
+	"CMOVLNE",
+	"CMOVLOC",
+	"CMOVLOS",
+	"CMOVLPC",
+	"CMOVLPL",
+	"CMOVLPS",
+	"CMOVWCC",
+	"CMOVWCS",
+	"CMOVWEQ",
+	"CMOVWGE",
+	"CMOVWGT",
+	"CMOVWHI",
+	"CMOVWLE",
+	"CMOVWLS",
+	"CMOVWLT",
+	"CMOVWMI",
+	"CMOVWNE",
+	"CMOVWOC",
+	"CMOVWOS",
+	"CMOVWPC",
+	"CMOVWPL",
+	"CMOVWPS",
+	"FCMOVCC",
+	"FCMOVCS",
+	"FCMOVEQ",
+	"FCMOVHI",
+	"FCMOVLS",
+	"FCMOVNE",
+	"FCMOVNU",
+	"FCMOVUN",
+	"LAST",
+};
--- /dev/null
+++ b/utils/8c/gc.h
@@ -1,0 +1,370 @@
+#include	"../cc/cc.h"
+#include	"../8c/8.out.h"
+
+/*
+ * 8c/386
+ * Intel 386
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+typedef	struct	Renv	Renv;
+
+EXTERN	struct
+{
+	Node*	regtree;
+	Node*	basetree;
+	short	scale;
+	short	reg;
+	short	ptr;
+} idx;
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	char	sval[NSNAME];
+
+	Sym*	sym;
+	uchar	type;
+	uchar	index;
+	uchar	etype;
+	uchar	scale;	/* doubles as width in DATA op */
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	short	as;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+struct	Renv
+{
+	int	safe;
+	Node	base;
+	Node*	saved;
+	Node*	scope;
+};
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	Node	fregnode0;
+EXTERN	Node	fregnode1;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	int	reg[D_NONE];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	5
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+EXTERN	int	suppress;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	usedset(Node*, int);
+void	xcom(Node*);
+void	indx(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	zeroregm(Node*);
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+int	needreg(Node*, int);
+
+/*
+ * cgen64.c
+ */
+int	vaddr(Node*, int);
+void	loadpair(Node*, Node*);
+int	cgen64(Node*, Node*);
+void	testv(Node*, int);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+int	nareg(int);
+Node*	nodfconst(double);
+int	nodreg(Node*, Node*, int);
+int	isreg(Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	naddr(Node*, Adr*);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	fgopcode(int, Node*, Node*, int, int);
+void	gopcode(int, Type*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Rconv(Fmt*);
+int	Xconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Reg*, Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+#define	D_HI	D_NONE
+#define	D_LO	D_NONE
+
+/*
+ * com64
+ */
+int	cond(int);
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+long	lo64v(Node*);
+long	hi64v(Node*);
+Node*	lo64(Node*);
+Node*	hi64(Node*);
+
+/*
+ * div/mul
+ */
+void	sdivgen(Node*, Node*, Node*, Node*);
+void	udivgen(Node*, Node*, Node*, Node*);
+void	sdiv2(long, int, Node*, Node*);
+void	smod2(long, int, Node*, Node*);
+void	mulgen(Type*, Node*, Node*);
+void	genmuladd(Node*, Node*, int, Node*);
+void	shiftit(Type*, Node*, Node*);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"S"	char*
+
+/* wrecklessly steal a field */
+
+#define	rplink	label
--- /dev/null
+++ b/utils/8c/list.c
@@ -1,0 +1,283 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			snprint(ss, sizeof(ss), "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+
+	p = va_arg(fp->args, Prog*);
+	if(p->as == ADATA)
+		snprint(str, sizeof(str), "	%A	%D/%d,%D",
+			p->as, &p->from, p->from.scale, &p->to);
+	else if(p->as == ATEXT)
+		snprint(str, sizeof(str), "	%A	%D,%d,%D",
+			p->as, &p->from, p->from.scale, &p->to);
+	else
+		snprint(str, sizeof(str), "	%A	%D,%D",
+			p->as, &p->from, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	int i;
+
+	i = va_arg(fp->args, int);
+	return fmtstrcpy(fp, anames[i]);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[40], s[20];
+	Adr *a;
+	int i;
+
+	a = va_arg(fp->args, Adr*);
+	i = a->type;
+	if(i >= D_INDIR) {
+		if(a->offset)
+			snprint(str, sizeof(str), "%ld(%R)", a->offset, i-D_INDIR);
+		else
+			snprint(str, sizeof(str), "(%R)", i-D_INDIR);
+		goto brk;
+	}
+	switch(i) {
+
+	default:
+		if(a->offset)
+			snprint(str, sizeof(str), "$%ld,%R", a->offset, i);
+		else
+			snprint(str, sizeof(str), "%R", i);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		break;
+
+	case D_BRANCH:
+		snprint(str, sizeof(str), "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_EXTERN:
+		snprint(str, sizeof(str), "%s+%ld(SB)", a->sym->name, a->offset);
+		break;
+
+	case D_STATIC:
+		snprint(str, sizeof(str), "%s<>+%ld(SB)", a->sym->name,
+			a->offset);
+		break;
+
+	case D_AUTO:
+		snprint(str, sizeof(str), "%s+%ld(SP)", a->sym->name, a->offset);
+		break;
+
+	case D_PARAM:
+		if(a->sym)
+			snprint(str, sizeof(str), "%s+%ld(FP)", a->sym->name, a->offset);
+		else
+			snprint(str, sizeof(str), "%ld(FP)", a->offset);
+		break;
+
+	case D_CONST:
+		snprint(str, sizeof(str), "$%ld", a->offset);
+		break;
+
+	case D_FCONST:
+		snprint(str, sizeof(str), "$(%.17e)", a->dval);
+		break;
+
+	case D_SCONST:
+		snprint(str, sizeof(str), "$\"%S\"", a->sval);
+		break;
+
+	case D_ADDR:
+		a->type = a->index;
+		a->index = D_NONE;
+		snprint(str, sizeof(str), "$%D", a);
+		a->index = a->type;
+		a->type = D_ADDR;
+		goto conv;
+	}
+brk:
+	if(a->index != D_NONE) {
+		fmtstrcpy(fp, str);
+		snprint(s, sizeof(s), "(%R*%d)", (int)a->index, (int)a->scale);
+		return fmtstrcpy(fp, s);
+	}
+conv:
+	return fmtstrcpy(fp, str);
+}
+
+char*	regstr[] =
+{
+	"AL",	/*[D_AL]*/	
+	"CL",
+	"DL",
+	"BL",
+	"AH",
+	"CH",
+	"DH",
+	"BH",
+
+	"AX",	/*[D_AX]*/
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+
+	"F0",	/*[D_F0]*/
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+
+	"CS",	/*[D_CS]*/
+	"SS",
+	"DS",
+	"ES",
+	"FS",
+	"GS",
+
+	"GDTR",	/*[D_GDTR]*/
+	"IDTR",	/*[D_IDTR]*/
+	"LDTR",	/*[D_LDTR]*/
+	"MSW",	/*[D_MSW] */
+	"TASK",	/*[D_TASK]*/
+
+	"CR0",	/*[D_CR]*/
+	"CR1",
+	"CR2",
+	"CR3",
+	"CR4",
+	"CR5",
+	"CR6",
+	"CR7",
+
+	"DR0",	/*[D_DR]*/
+	"DR1",
+	"DR2",
+	"DR3",
+	"DR4",
+	"DR5",
+	"DR6",
+	"DR7",
+
+	"TR0",	/*[D_TR]*/
+	"TR1",
+	"TR2",
+	"TR3",
+	"TR4",
+	"TR5",
+	"TR6",
+	"TR7",
+
+	"NONE",	/*[D_NONE]*/
+};
+
+int
+Rconv(Fmt *fp)
+{
+	char str[20];
+	int r;
+
+	r = va_arg(fp->args, int);
+	if(r >= D_AL && r <= D_NONE)
+		snprint(str, sizeof(str), "%s", regstr[r-D_AL]);
+	else
+		snprint(str, sizeof(str), "gok(%d)", r);
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[30], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(double); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		default:
+			if(c < 040 || c >= 0177)
+				break;	/* not portable */
+			p[-1] = c;
+			continue;
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/8c/machcap.c
@@ -1,0 +1,91 @@
+#include "gc.h"
+
+int
+machcap(Node *n)
+{
+//	return 0;
+
+	if(n == Z)
+		return 1;	/* test */
+
+	switch(n->op) {
+	case OMUL:
+	case OLMUL:
+	case OASMUL:
+	case OASLMUL:
+		if(typechl[n->type->etype])
+			return 1;
+		if(typev[n->type->etype]) {
+//		if(typev[n->type->etype] && n->right->op == OCONST) {
+//			if(hi64v(n->right) == 0)
+				return 1;
+		}
+		break;
+
+	case OCOM:
+	case ONEG:
+	case OADD:
+	case OAND:
+	case OOR:
+	case OSUB:
+	case OXOR:
+	case OASHL:
+	case OLSHR:
+	case OASHR:
+		if(typechlv[n->left->type->etype])
+			return 1;
+		break;
+
+	case OCAST:
+		if(typev[n->type->etype]) {
+			if(typechlp[n->left->type->etype])
+				return 1;
+		}
+		else if(!typefd[n->type->etype]) {
+			if(typev[n->left->type->etype])
+				return 1;
+		}
+		break;
+
+	case OCOND:
+	case OCOMMA:
+	case OLIST:
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+	case ODOT:
+		return 1;
+
+	case OASADD:
+	case OASSUB:
+	case OASAND:
+	case OASOR:
+	case OASXOR:
+		return 1;
+
+	case OASASHL:
+	case OASASHR:
+	case OASLSHR:
+		return 1;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+	case OPREINC:
+	case OPREDEC:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OGT:
+	case OLT:
+	case OGE:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+//print("%O\n", n->op);
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/8c/mkenam
@@ -1,0 +1,15 @@
+ed - ../8c/8.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/8c/mkfile
@@ -1,0 +1,43 @@
+<../../mkconfig
+
+TARG=8c
+
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+	reg.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	machcap.$O\
+	cgen64.$O\
+	div.$O\
+	mul.$O\
+
+HFILES=\
+	gc.h\
+	8.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	8.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/8c/mul.c
@@ -1,0 +1,428 @@
+#include "gc.h"
+
+typedef struct	Malg	Malg;
+typedef struct	Mparam	Mparam;
+
+struct	Malg
+{
+	char	vals[10];
+};
+
+struct	Mparam
+{
+	ulong	value;
+	char	alg;
+	char	neg;
+	char	shift;
+	char	arg;
+	char	off;
+};
+
+static	Mparam	multab[32];
+static	int	mulptr;
+
+static	Malg	malgs[]	=
+{
+	{0, 100},
+	{-1, 1, 100},
+	{-9, -5, -3, 3, 5, 9, 100},
+	{6, 10, 12, 18, 20, 24, 36, 40, 72, 100},
+	{-8, -4, -2, 2, 4, 8, 100},
+};
+
+/*
+ * return position of lowest 1
+ */
+int
+lowbit(ulong v)
+{
+	int s, i;
+	ulong m;
+
+	s = 0;
+	m = 0xFFFFFFFFUL;
+	for(i = 16; i > 0; i >>= 1) {
+		m >>= i;
+		if((v & m) == 0) {
+			v >>= i;
+			s += i;
+		}
+	}
+	return s;
+}
+
+void
+genmuladd(Node *d, Node *s, int m, Node *a)
+{
+	Node nod;
+
+	nod.op = OINDEX;
+	nod.left = a;
+	nod.right = s;
+	nod.scale = m;
+	nod.type = types[TIND];
+	nod.xoffset = 0;
+	xcom(&nod);
+	gopcode(OADDR, d->type, &nod, d);
+}
+
+void
+mulparam(ulong m, Mparam *mp)
+{
+	int c, i, j, n, o, q, s;
+	int bc, bi, bn, bo, bq, bs, bt;
+	char *p;
+	long u;
+	ulong t;
+
+	bc = bq = 10;
+	bi = bn = bo = bs = bt = 0;
+	for(i = 0; i < nelem(malgs); i++) {
+		for(p = malgs[i].vals, j = 0; (o = p[j]) < 100; j++)
+		for(s = 0; s < 2; s++) {
+			c = 10;
+			q = 10;
+			u = m - o;
+			if(u == 0)
+				continue;
+			if(s) {
+				o = -o;
+				if(o > 0)
+					continue;
+				u = -u;
+			}
+			n = lowbit(u);
+			t = (ulong)u >> n;
+			switch(i) {
+			case 0:
+				if(t == 1) {
+					c = s + 1;
+					q = 0;
+					break;
+				}
+				switch(t) {
+				case 3:
+				case 5:
+				case 9:
+					c = s + 1;
+					if(n)
+						c++;
+					q = 0;
+					break;
+				}
+				if(s)
+					break;
+				switch(t) {
+				case 15:
+				case 25:
+				case 27:
+				case 45:
+				case 81:
+					c = 2;
+					if(n)
+						c++;
+					q = 1;
+					break;
+				}
+				break;
+			case 1:
+				if(t == 1) {
+					c = 3;
+					q = 3;
+					break;
+				}
+				switch(t) {
+				case 3:
+				case 5:
+				case 9:
+					c = 3;
+					q = 2;
+					break;
+				}
+				break;
+			case 2:
+				if(t == 1) {
+					c = 3;
+					q = 2;
+					break;
+				}
+				break;
+			case 3:
+				if(s)
+					break;
+				if(t == 1) {
+					c = 3;
+					q = 1;
+					break;
+				}
+				break;
+			case 4:
+				if(t == 1) {
+					c = 3;
+					q = 0;
+					break;
+				}
+				break;
+			}
+			if(c < bc || (c == bc && q > bq)) {
+				bc = c;
+				bi = i;
+				bn = n;
+				bo = o;
+				bq = q;
+				bs = s;
+				bt = t;
+			}
+		}
+	}
+	mp->value = m;
+	if(bc <= 3) {
+		mp->alg = bi;
+		mp->shift = bn;
+		mp->off = bo;
+		mp->neg = bs;
+		mp->arg = bt;
+	}
+	else
+		mp->alg = -1;
+}
+
+int
+m0(int a)
+{
+	switch(a) {
+	case -2:
+	case 2:
+		return 2;
+	case -3:
+	case 3:
+		return 2;
+	case -4:
+	case 4:
+		return 4;
+	case -5:
+	case 5:
+		return 4;
+	case 6:
+		return 2;
+	case -8:
+	case 8:
+		return 8;
+	case -9:
+	case 9:
+		return 8;
+	case 10:
+		return 4;
+	case 12:
+		return 2;
+	case 15:
+		return 2;
+	case 18:
+		return 8;
+	case 20:
+		return 4;
+	case 24:
+		return 2;
+	case 25:
+		return 4;
+	case 27:
+		return 2;
+	case 36:
+		return 8;
+	case 40:
+		return 4;
+	case 45:
+		return 4;
+	case 72:
+		return 8;
+	case 81:
+		return 8;
+	}
+	diag(Z, "bad m0");
+	return 0;
+}
+
+int
+m1(int a)
+{
+	switch(a) {
+	case 15:
+		return 4;
+	case 25:
+		return 4;
+	case 27:
+		return 8;
+	case 45:
+		return 8;
+	case 81:
+		return 8;
+	}
+	diag(Z, "bad m1");
+	return 0;
+}
+
+int
+m2(int a)
+{
+	switch(a) {
+	case 6:
+		return 2;
+	case 10:
+		return 2;
+	case 12:
+		return 4;
+	case 18:
+		return 2;
+	case 20:
+		return 4;
+	case 24:
+		return 8;
+	case 36:
+		return 4;
+	case 40:
+		return 8;
+	case 72:
+		return 8;
+	}
+	diag(Z, "bad m2");
+	return 0;
+}
+
+void
+shiftit(Type *t, Node *s, Node *d)
+{
+	long c;
+
+	c = (long)s->vconst & 31;
+	switch(c) {
+	case 0:
+		break;
+	case 1:
+		gopcode(OADD, t, d, d);
+		break;
+	default:
+		gopcode(OASHL, t, s, d);
+	}
+}
+
+static int
+mulgen1(ulong v, Node *n)
+{
+	int i, o;
+	Mparam *p;
+	Node nod, nods;
+
+	for(i = 0; i < nelem(multab); i++) {
+		p = &multab[i];
+		if(p->value == v)
+			goto found;
+	}
+
+	p = &multab[mulptr];
+	if(++mulptr == nelem(multab))
+		mulptr = 0;
+
+	mulparam(v, p);
+
+found:
+//	print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off);
+	if(p->alg < 0)
+		return 0;
+
+	nods = *nodconst(p->shift);
+
+	o = OADD;
+	if(p->alg > 0) {
+		regalloc(&nod, n, Z);
+		if(p->off < 0)
+			o = OSUB;
+	}
+
+	switch(p->alg) {
+	case 0:
+		switch(p->arg) {
+		case 1:
+			shiftit(n->type, &nods, n);
+			break;
+		case 15:
+		case 25:
+		case 27:
+		case 45:
+		case 81:
+			genmuladd(n, n, m1(p->arg), n);
+			/* fall thru */
+		case 3:
+		case 5:
+		case 9:
+			genmuladd(n, n, m0(p->arg), n);
+			shiftit(n->type, &nods, n);
+			break;
+		default:
+			goto bad;
+		}
+		if(p->neg == 1)
+			gins(ANEGL, Z, n);
+		break;
+	case 1:
+		switch(p->arg) {
+		case 1:
+			gmove(n, &nod);
+			shiftit(n->type, &nods, &nod);
+			break;
+		case 3:
+		case 5:
+		case 9:
+			genmuladd(&nod, n, m0(p->arg), n);
+			shiftit(n->type, &nods, &nod);
+			break;
+		default:
+			goto bad;
+		}
+		if(p->neg)
+			gopcode(o, n->type, &nod, n);
+		else {
+			gopcode(o, n->type, n, &nod);
+			gmove(&nod, n);
+		}
+		break;
+	case 2:
+		genmuladd(&nod, n, m0(p->off), n);
+		shiftit(n->type, &nods, n);
+		goto comop;
+	case 3:
+		genmuladd(&nod, n, m0(p->off), n);
+		shiftit(n->type, &nods, n);
+		genmuladd(n, &nod, m2(p->off), n);
+		break;
+	case 4:
+		genmuladd(&nod, n, m0(p->off), nodconst(0));
+		shiftit(n->type, &nods, n);
+		goto comop;
+	default:
+		diag(Z, "bad mul alg");
+		break;
+	comop:
+		if(p->neg) {
+			gopcode(o, n->type, n, &nod);
+			gmove(&nod, n);
+		}
+		else
+			gopcode(o, n->type, &nod, n);
+	}
+
+	if(p->alg > 0)
+		regfree(&nod);
+
+	return 1;
+
+bad:
+	diag(Z, "mulgen botch");
+	return 1;
+}
+
+void
+mulgen(Type *t, Node *r, Node *n)
+{
+	if(!mulgen1(r->vconst, n))
+		gopcode(OMUL, t, r, n);
+}
--- /dev/null
+++ b/utils/8c/peep.c
@@ -1,0 +1,771 @@
+#include "gc.h"
+
+static int
+needc(Prog *p)
+{
+	while(p != P) {
+		switch(p->as) {
+		case AADCL:
+		case ASBBL:
+		case ARCRL:
+			return 1;
+		case AADDL:
+		case ASUBL:
+		case AJMP:
+		case ARET:
+		case ACALL:
+			return 0;
+		default:
+			if(p->to.type == D_BRANCH)
+				return 0;
+		}
+		p = p->link;
+	}
+	return 0;
+}
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+
+	/*
+	 * complete R structure
+	 */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+	pc = 0;	/* speculating it won't kill */
+
+loop1:
+
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		case AMOVL:
+			if(regtyp(&p->to))
+			if(regtyp(&p->from)) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				}
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			break;
+
+		case AMOVBLSX:
+		case AMOVBLZX:
+		case AMOVWLSX:
+		case AMOVWLZX:
+			if(regtyp(&p->to)) {
+				r1 = uniqs(r);
+				if(r1 != R) {
+					p1 = r1->prog;
+					if(p->as == p1->as && p->to.type == p1->from.type)
+						p1->as = AMOVL;
+				}
+			}
+			break;
+		case AADDL:
+		case AADDW:
+			if(p->from.type != D_CONST || needc(p->link))
+				break;
+			if(p->from.offset == -1){
+				if(p->as == AADDL)
+					p->as = ADECL;
+				else
+					p->as = ADECW;
+				p->from = zprog.from;
+			}
+			else if(p->from.offset == 1){
+				if(p->as == AADDL)
+					p->as = AINCL;
+				else
+					p->as = AINCW;
+				p->from = zprog.from;
+			}
+			break;
+		case ASUBL:
+		case ASUBW:
+			if(p->from.type != D_CONST || needc(p->link))
+				break;
+			if(p->from.offset == -1) {
+				if(p->as == ASUBL)
+					p->as = AINCL;
+				else
+					p->as = AINCW;
+				p->from = zprog.from;
+			}
+			else if(p->from.offset == 1){
+				if(p->as == ASUBL)
+					p->as = ADECL;
+				else
+					p->as = ADECW;
+				p->from = zprog.from;
+			}
+			break;
+		}
+	}
+	if(t)
+		goto loop1;
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+	int t;
+
+	t = a->type;
+	if(t >= D_AX && t <= D_DI)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ACALL:
+			return 0;
+
+		case AIMULL:
+		case AIMULW:
+			if(p->to.type != D_NONE)
+				break;
+
+		case ADIVB:
+		case ADIVL:
+		case ADIVW:
+		case AIDIVB:
+		case AIDIVL:
+		case AIDIVW:
+		case AIMULB:
+		case AMULB:
+		case AMULL:
+		case AMULW:
+
+		case AROLB:
+		case AROLL:
+		case AROLW:
+		case ARORB:
+		case ARORL:
+		case ARORW:
+		case ASALB:
+		case ASALL:
+		case ASALW:
+		case ASARB:
+		case ASARL:
+		case ASARW:
+		case ASHLB:
+		case ASHLL:
+		case ASHLW:
+		case ASHRB:
+		case ASHRL:
+		case ASHRW:
+
+		case AREP:
+		case AREPN:
+
+		case ACWD:
+		case ACDQ:
+
+		case ASTOSB:
+		case ASTOSL:
+		case AMOVSB:
+		case AMOVSL:
+		case AFSTSW:
+			return 0;
+
+		case AMOVL:
+			if(p->to.type == v1->type)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->type;
+	v1->type = v2->type;
+	v2->type = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %D rar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %D set; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %D used+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %D used and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub %D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %D used+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %D set and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print("unknown op %A\n", p->as);
+		return 2;
+
+	case ANEGB:
+	case ANEGW:
+	case ANEGL:
+	case ANOTB:
+	case ANOTW:
+	case ANOTL:
+		if(copyas(&p->to, v))
+			return 2;
+		break;
+
+	case ALEAL:	/* lhs addr, rhs store */
+		if(copyas(&p->from, v))
+			return 2;
+
+
+	case ANOP:	/* rhs store */
+	case AMOVL:
+	case AMOVBLSX:
+	case AMOVBLZX:
+	case AMOVWLSX:
+	case AMOVWLZX:
+		if(copyas(&p->to, v)) {
+			if(s != A)
+				return copysub(&p->from, v, s, 1);
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		goto caseread;
+
+	case AROLB:
+	case AROLL:
+	case AROLW:
+	case ARORB:
+	case ARORL:
+	case ARORW:
+	case ASALB:
+	case ASALL:
+	case ASALW:
+	case ASARB:
+	case ASARL:
+	case ASARW:
+	case ASHLB:
+	case ASHLL:
+	case ASHLW:
+	case ASHRB:
+	case ASHRL:
+	case ASHRW:
+		if(copyas(&p->to, v))
+			return 2;
+		if(copyas(&p->from, v))
+			if(p->from.type == D_CX)
+				return 2;
+		goto caseread;
+
+	case AADDB:	/* rhs rar */
+	case AADDL:
+	case AADDW:
+	case AANDB:
+	case AANDL:
+	case AANDW:
+	case ADECL:
+	case ADECW:
+	case AINCL:
+	case AINCW:
+	case ASUBB:
+	case ASUBL:
+	case ASUBW:
+	case AORB:
+	case AORL:
+	case AORW:
+	case AXORB:
+	case AXORL:
+	case AXORW:
+	case AMOVB:
+	case AMOVW:
+
+	case AFMOVB:
+	case AFMOVBP:
+	case AFMOVD:
+	case AFMOVDP:
+	case AFMOVF:
+	case AFMOVFP:
+	case AFMOVL:
+	case AFMOVLP:
+	case AFMOVV:
+	case AFMOVVP:
+	case AFMOVW:
+	case AFMOVWP:
+	case AFMOVX:
+	case AFMOVXP:
+	case AFADDDP:
+	case AFADDW:
+	case AFADDL:
+	case AFADDF:
+	case AFADDD:
+	case AFMULDP:
+	case AFMULW:
+	case AFMULL:
+	case AFMULF:
+	case AFMULD:
+	case AFSUBDP:
+	case AFSUBW:
+	case AFSUBL:
+	case AFSUBF:
+	case AFSUBD:
+	case AFSUBRDP:
+	case AFSUBRW:
+	case AFSUBRL:
+	case AFSUBRF:
+	case AFSUBRD:
+	case AFDIVDP:
+	case AFDIVW:
+	case AFDIVL:
+	case AFDIVF:
+	case AFDIVD:
+	case AFDIVRDP:
+	case AFDIVRW:
+	case AFDIVRL:
+	case AFDIVRF:
+	case AFDIVRD:
+		if(copyas(&p->to, v))
+			return 2;
+		goto caseread;
+
+	case ACMPL:	/* read only */
+	case ACMPW:
+	case ACMPB:
+
+	case AFCOMB:
+	case AFCOMBP:
+	case AFCOMD:
+	case AFCOMDP:
+	case AFCOMDPP:
+	case AFCOMF:
+	case AFCOMFP:
+	case AFCOML:
+	case AFCOMLP:
+	case AFCOMW:
+	case AFCOMWP:
+	case AFUCOM:
+	case AFUCOMP:
+	case AFUCOMPP:
+	caseread:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub(&p->to, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		break;
+
+	case AJGE:	/* no reference */
+	case AJNE:
+	case AJLE:
+	case AJEQ:
+	case AJHI:
+	case AJLS:
+	case AJMI:
+	case AJPL:
+	case AJGT:
+	case AJLT:
+	case AJCC:
+	case AJCS:
+
+	case AADJSP:
+	case AFLDZ:
+	case AWAIT:
+		break;
+
+	case AIMULL:
+	case AIMULW:
+		if(p->to.type != D_NONE) {
+			if(copyas(&p->to, v))
+				return 2;
+			goto caseread;
+		}
+
+	case ADIVB:
+	case ADIVL:
+	case ADIVW:
+	case AIDIVB:
+	case AIDIVL:
+	case AIDIVW:
+	case AIMULB:
+	case AMULB:
+	case AMULL:
+	case AMULW:
+
+	case ACWD:
+	case ACDQ:
+		if(v->type == D_AX || v->type == D_DX)
+			return 2;
+		goto caseread;
+
+	case AREP:
+	case AREPN:
+		if(v->type == D_CX)
+			return 2;
+		goto caseread;
+
+	case AMOVSB:
+	case AMOVSL:
+		if(v->type == D_DI || v->type == D_SI)
+			return 2;
+		goto caseread;
+
+	case ASTOSB:
+	case ASTOSL:
+		if(v->type == D_AX || v->type == D_DI)
+			return 2;
+		goto caseread;
+
+	case AFSTSW:
+		if(v->type == D_AX)
+			return 2;
+		goto caseread;
+
+	case AJMP:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == REGRET)
+			return 2;
+		if(s != A)
+			return 1;
+		return 3;
+
+	case ACALL:	/* funny */
+		if(REGARG>=0 && v->type == REGARG)
+			return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+	}
+	return 0;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+	if(a->type != v->type)
+		return 0;
+	if(regtyp(v))
+		return 1;
+	if(v->type == D_AUTO || v->type == D_PARAM)
+		if(v->offset == a->offset)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(regtyp(v)) {
+		if(a->type-D_INDIR == v->type)
+			return 1;
+		if(a->index == v->type)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+	int t;
+
+	if(copyas(a, v)) {
+		t = s->type;
+		if(t >= D_AX && t <= D_DI) {
+			if(f)
+				a->type = t;
+		}
+		return 0;
+	}
+	if(regtyp(v)) {
+		t = v->type;
+		if(a->type == t+D_INDIR) {
+			if(s->type == D_BP && a->index != D_NONE)
+				return 1;	/* can't use BP-base with index */
+			if(f)
+				a->type = s->type+D_INDIR;
+//			return 0;
+		}
+		if(a->index == t) {
+			if(f)
+				a->index = s->type;
+			return 0;
+		}
+		return 0;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/8c/reg.c
@@ -1,0 +1,1264 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = RtoB(D_SP) | RtoB(D_AX);
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AJMP:
+		case AIRETL:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		bit = mkvar(r, &p->from, p->as==AMOVL);
+		if(bany(&bit))
+		switch(p->as) {
+		/*
+		 * funny
+		 */
+		case ALEAL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * left side read
+		 */
+		default:
+			for(z=0; z<BITS; z++)
+				r->use1.b[z] |= bit.b[z];
+			break;
+		}
+
+		bit = mkvar(r, &p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown op: %A", p->as);
+			break;
+
+		/*
+		 * right side read
+		 */
+		case ACMPB:
+		case ACMPL:
+		case ACMPW:
+			for(z=0; z<BITS; z++)
+				r->use2.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVL:
+		case AMOVB:
+		case AMOVW:
+		case AMOVBLSX:
+		case AMOVBLZX:
+		case AMOVWLSX:
+		case AMOVWLZX:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * right side read+write
+		 */
+		case AADDB:
+		case AADDL:
+		case AADDW:
+		case AANDB:
+		case AANDL:
+		case AANDW:
+		case ASUBB:
+		case ASUBL:
+		case ASUBW:
+		case AORB:
+		case AORL:
+		case AORW:
+		case AXORB:
+		case AXORL:
+		case AXORW:
+		case ASALB:
+		case ASALL:
+		case ASALW:
+		case ASARB:
+		case ASARL:
+		case ASARW:
+		case AROLB:
+		case AROLL:
+		case AROLW:
+		case ARORB:
+		case ARORL:
+		case ARORW:
+		case ASHLB:
+		case ASHLL:
+		case ASHLW:
+		case ASHRB:
+		case ASHRL:
+		case ASHRW:
+		case AIMULL:
+		case AIMULW:
+		case ANEGL:
+		case ANOTL:
+		case AADCL:
+		case ASBBL:
+			for(z=0; z<BITS; z++) {
+				r->set.b[z] |= bit.b[z];
+				r->use2.b[z] |= bit.b[z];
+			}
+			break;
+
+		/*
+		 * funny
+		 */
+		case AFMOVDP:
+		case AFMOVFP:
+		case AFMOVLP:
+		case AFMOVVP:
+		case AFMOVWP:
+		case ACALL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+
+		switch(p->as) {
+		case AIMULL:
+		case AIMULW:
+			if(p->to.type != D_NONE)
+				break;
+
+		case AIDIVB:
+		case AIDIVL:
+		case AIDIVW:
+		case AIMULB:
+		case ADIVB:
+		case ADIVL:
+		case ADIVW:
+		case AMULB:
+		case AMULL:
+		case AMULW:
+
+		case ACWD:
+		case ACDQ:
+			r->regu |= RtoB(D_AX) | RtoB(D_DX);
+			break;
+
+		case AREP:
+		case AREPN:
+		case ALOOP:
+		case ALOOPEQ:
+		case ALOOPNE:
+			r->regu |= RtoB(D_CX);
+			break;
+
+		case AMOVSB:
+		case AMOVSL:
+		case AMOVSW:
+		case ACMPSB:
+		case ACMPSL:
+		case ACMPSW:
+			r->regu |= RtoB(D_SI) | RtoB(D_DI);
+			break;
+
+		case ASTOSB:
+		case ASTOSL:
+		case ASTOSW:
+		case ASCASB:
+		case ASCASL:
+		case ASCASW:
+			r->regu |= RtoB(D_AX) | RtoB(D_DI);
+			break;
+
+		case AINSB:
+		case AINSL:
+		case AINSW:
+		case AOUTSB:
+		case AOUTSL:
+		case AOUTSW:
+			r->regu |= RtoB(D_DI) | RtoB(D_DX);
+			break;
+
+		case AFSTSW:
+		case ASAHF:
+			r->regu |= RtoB(D_AX);
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+	if(debug['R'] && debug['v']) {
+		print("\nlooping structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->use1.b[z] |
+					   r->use2.b[z] |
+					   r->set.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+	if(debug['R'] && debug['v'])
+		print("\nprop structure:\n");
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		if(debug['R'] && debug['v']) {
+			print("%P\t", r->prog);
+			if(bany(&r->set))
+				print("s:%B ", r->set);
+			if(bany(&r->refahead))
+				print("ra:%B ", r->refahead);
+			if(bany(&r->calahead))
+				print("ca:%B ", r->calahead);
+			print("\n");
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L$%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			print("%L$%d %R: %B\n",
+				rgp->enter->prog->lineno,
+				rgp->cost,
+				rgp->regno,
+				bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = v->name;
+
+	p1->as = AMOVL;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVW;
+
+	p1->from.type = rn;
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = rn;
+		if(v->etype == TUCHAR)
+			p1->as = AMOVB;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVW;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+ulong
+doregbits(int r)
+{
+	ulong b;
+
+	b = 0;
+	if(r >= D_INDIR)
+		r -= D_INDIR;
+	if(r >= D_AX && r <= D_DI)
+		b |= RtoB(r);
+	else
+	if(r >= D_AL && r <= D_BL)
+		b |= RtoB(r-D_AL+D_AX);
+	else
+	if(r >= D_AH && r <= D_BH)
+		b |= RtoB(r-D_AH+D_AX);
+	return b;
+}
+
+Bits
+mkvar(Reg *r, Adr *a, int isro)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	/*
+	 * mark registers used
+	 */
+	t = a->type;
+	r->regu |= doregbits(t);
+	r->regu |= doregbits(a->index);
+	et = a->etype;
+
+	switch(t) {
+	default:
+		goto none;
+	case D_INDIR+D_GS:
+		if(!isro || 1)
+			goto none;
+		n = t;
+		{static Sym er; a->sym = &er;}
+		a->sym->name = "$extreg";
+		break;
+	case D_ADDR:
+		a->type = a->index;
+		bit = mkvar(r, a, 0);
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+		a->type = t;
+		goto none;
+	case D_EXTERN:
+	case D_STATIC:
+	case D_PARAM:
+	case D_AUTO:
+		n = t;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		goto none;
+	if(s->name[0] == '.')
+		goto none;
+	o = a->offset;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->name = n;
+	v->etype = et;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ACALL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(p->as == AFMOVL || p->as == AFMOVW)
+				if(BtoR(bb) != D_F0)
+					change = -CINF;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(p->as == AFMOVL || p->as == AFMOVW)
+				if(BtoR(bb) != D_F0)
+					change = -CINF;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(p->as == AFMOVL || p->as == AFMOVW)
+				if(BtoR(bb) != D_F0)
+					change = -CINF;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+regset(Reg *r, ulong bb)
+{
+	ulong b, set;
+	Adr v;
+	int c;
+
+	set = 0;
+	v = zprog.from;
+	while(b = bb & ~(bb-1)) {
+		v.type = BtoR(b);
+		c = copyu(r->prog, &v, A);
+		if(c == 3)
+			set |= b;
+		bb &= ~b;
+	}
+	return set;
+}
+
+ulong
+reguse(Reg *r, ulong bb)
+{
+	ulong b, set;
+	Adr v;
+	int c;
+
+	set = 0;
+	v = zprog.from;
+	while(b = bb & ~(bb-1)) {
+		v.type = BtoR(b);
+		c = copyu(r->prog, &v, A);
+		if(c == 1 || c == 2 || c == 4)
+			set |= b;
+		bb &= ~b;
+	}
+	return set;
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg, x;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+
+	bb = vreg;
+	for(; r; r=r->s1) {
+		x = r->regu & ~bb;
+		if(x) {
+			vreg |= reguse(r, x);
+			bb |= regset(r, x);
+		}
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->offset = 0;
+	a->type = rn;
+}
+
+long
+RtoB(int r)
+{
+
+	if(r < D_AX || r > D_DI)
+		return 0;
+	return 1L << (r-D_AX);
+}
+
+int
+BtoR(long b)
+{
+
+	b &= 0xffL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + D_AX;
+}
--- /dev/null
+++ b/utils/8c/sgen.c
@@ -1,0 +1,440 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = FREGRET;
+	}
+	if((n&3) == 3)
+	if(thisfn && thisfn->link && typefd[thisfn->link->etype])
+		gins(AFLDZ, Z, Z);
+}
+
+/* welcome to commute */
+static void
+commute(Node *n)
+{
+	Node *l, *r;
+
+	l = n->left;
+	r = n->right;
+	if(r->complex > l->complex) {
+		n->left = r;
+		n->right = l;
+	}
+}
+
+void
+indexshift(Node *n)
+{
+	int g;
+
+	if(!typechlp[n->type->etype])
+		return;
+	simplifyshift(n);
+	if(n->op == OASHL && n->right->op == OCONST){
+		g = vconst(n->right);
+		if(g >= 0 && g < 4)
+			n->addable = 7;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		NAME ==> 10/11		name+value(SB/SP)
+ *		REGISTER ==> 12		register
+ *		CONST ==> 20		$value
+ *		*(20) ==> 21		value
+ *		&(10) ==> 13		$name+value(SB)
+ *		&(11) ==> 1		$name+value(SP)
+ *		(13) + (20) ==> 13	fold constants
+ *		(1) + (20) ==> 1	fold constants
+ *		*(13) ==> 10		back to name
+ *		*(1) ==> 11		back to name
+ *
+ *		(20) * (X) ==> 7	multiplier in indexing
+ *		(X,7) + (13,1) ==> 8	adder in indexing (addresses)
+ *		(8) ==> &9(OINDEX)	index, almost addressable
+ *
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int g;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->complex = 0;
+	n->addable = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		break;
+
+	case ONAME:
+		n->addable = 10;
+		if(n->class == CPARAM || n->class == CAUTO)
+			n->addable = 11;
+		break;
+
+	case OEXREG:
+		n->addable = 12;
+		break;
+
+	case OREGISTER:
+		n->addable = 12;
+		break;
+
+	case OINDREG:
+		n->addable = 12;
+		break;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 13;
+		else
+		if(l->addable == 11)
+			n->addable = 1;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(n->type->etype != TIND)
+			break;
+
+		switch(r->addable) {
+		case 20:
+			switch(l->addable) {
+			case 1:
+			case 13:
+			commadd:
+				l->type = n->type;
+				*n = *l;
+				l = new(0, Z, Z);
+				*l = *(n->left);
+				l->xoffset += r->vconst;
+				n->left = l;
+				r = n->right;
+				goto brk;
+			}
+			break;
+
+		case 1:
+		case 13:
+		case 10:
+		case 11:
+			/* l is the base, r is the index */
+			if(l->addable != 20)
+				n->addable = 8;
+			break;
+		}
+		switch(l->addable) {
+		case 20:
+			switch(r->addable) {
+			case 13:
+			case 1:
+				r = n->left;
+				l = n->right;
+				n->left = l;
+				n->right = r;
+				goto commadd;
+			}
+			break;
+
+		case 13:
+		case 1:
+		case 10:
+		case 11:
+			/* r is the base, l is the index */
+			if(r->addable != 20)
+				n->addable = 8;
+			break;
+		}
+		if(n->addable == 8 && !side(n)) {
+			indx(n);
+			l = new1(OINDEX, idx.basetree, idx.regtree);
+			l->scale = idx.scale;
+			l->addable = 9;
+			l->complex = l->right->complex;
+			l->type = l->left->type;
+			n->op = OADDR;
+			n->left = l;
+			n->right = Z;
+			n->addable = 8;
+			break;
+		}
+		break;
+
+	case OINDEX:
+		xcom(l);
+		xcom(r);
+		n->addable = 9;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->op == OADDR) {
+			l = l->left;
+			l->type = n->type;
+			*n = *l;
+			return;
+		}
+		switch(l->addable) {
+		case 20:
+			n->addable = 21;
+			break;
+		case 1:
+			n->addable = 11;
+			break;
+		case 13:
+			n->addable = 10;
+			break;
+		}
+		break;
+
+	case OASHL:
+		xcom(l);
+		xcom(r);
+		indexshift(n);
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		g = vlog(l);
+		if(g >= 0) {
+			n->left = r;
+			n->right = l;
+			l = r;
+			r = n->right;
+		}
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASHL;
+			r->vconst = g;
+			r->type = types[TINT];
+			indexshift(n);
+			break;
+		}
+commute(n);
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASLSHR;
+			r->vconst = g;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OLSHR;
+			r->vconst = g;
+			r->type = types[TINT];
+			indexshift(n);
+			break;
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+		xcom(l);
+		xcom(r);
+		g = vlog(r);
+		if(g >= 0) {
+			n->op = OASASHL;
+			r->vconst = g;
+		}
+		break;
+
+	case OLSHR:
+	case OASHR:
+		xcom(l);
+		xcom(r);
+		indexshift(n);
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+brk:
+	if(n->addable >= 10)
+		return;
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OLMOD:
+	case OMOD:
+	case OLMUL:
+	case OLDIV:
+	case OMUL:
+	case ODIV:
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(r->complex >= l->complex) {
+			n->complex = l->complex + 3;
+			if(r->complex > n->complex)
+				n->complex = r->complex;
+		} else {
+			n->complex = r->complex + 3;
+			if(l->complex > n->complex)
+				n->complex = l->complex;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+		if(r->complex >= l->complex) {
+			n->complex = l->complex + 2;
+			if(r->complex > n->complex)
+				n->complex = r->complex;
+		} else {
+			n->complex = r->complex + 2;
+			if(l->complex > n->complex)
+				n->complex = l->complex;
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * compare operators, make const on left
+		 */
+		if(r->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+	}
+}
+
+void
+indx(Node *n)
+{
+	Node *l, *r;
+
+	if(debug['x'])
+		prtree(n, "indx");
+
+	l = n->left;
+	r = n->right;
+	if(l->addable == 1 || l->addable == 13 || r->complex > l->complex) {
+		n->right = l;
+		n->left = r;
+		l = r;
+		r = n->right;
+	}
+	if(l->addable != 7) {
+		idx.regtree = l;
+		idx.scale = 1;
+	} else
+	if(l->right->addable == 20) {
+		idx.regtree = l->left;
+		idx.scale = 1 << l->right->vconst;
+	} else
+	if(l->left->addable == 20) {
+		idx.regtree = l->right;
+		idx.scale = 1 << l->left->vconst;
+	} else
+		diag(n, "bad index");
+
+	idx.basetree = r;
+	if(debug['x']) {
+		print("scale = %d\n", idx.scale);
+		prtree(idx.regtree, "index");
+		prtree(idx.basetree, "base");
+	}
+}
--- /dev/null
+++ b/utils/8c/swt.c
@@ -1,0 +1,502 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gopcode(OEQ, n->type, n, nodconst(q->val));
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(debug['K'])
+		print("case > %.8llux\n", r->val);
+	gopcode(OGT, n->type, n, nodconst(r->val));
+	sp = p;
+	gbranch(OGOTO);
+	p->as = AJEQ;
+	patch(p, r->label);
+	swit1(q, i, def, n);
+
+	if(debug['K'])
+		print("case < %.8llux\n", r->val);
+	patch(sp, pc);
+	swit1(r+1, nc-i-1, def, n);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gmove(n2, n3);
+		gmove(n3, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, types[TLONG], nodconst(v), n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, types[TLONG], nodconst(sh), n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, types[TLONG], nodconst(sh), n1);
+			else
+				gopcode(OASHR, types[TLONG], nodconst(sh), n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod;
+	int sh;
+
+	regalloc(&nod, b->left, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, types[TLONG], nodconst(v), n1);
+	gmove(n1, &nod);
+	if(nn != Z)
+		gmove(n1, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, types[TLONG], nodconst(sh), &nod);
+	v <<= sh;
+	gopcode(OAND, types[TLONG], nodconst(~v), n3);
+	gopcode(OOR, types[TLONG], n3, &nod);
+	gmove(&nod, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->from.scale = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+	if(a->op == OCONST && typev[a->type->etype]) {
+		gpseudo(ADATA, s, lo64(a));
+		p->from.offset += o;
+		p->from.scale = 4;
+		gpseudo(ADATA, s, hi64(a));
+		p->from.offset += o + 4;
+		p->from.scale = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->from.scale = w;
+	switch(p->to.type) {
+	default:
+		p->to.index = p->to.type;
+		p->to.type = D_ADDR;
+	case D_CONST:
+	case D_FCONST:
+	case D_ADDR:
+		break;
+	}
+}
+
+void	zname(Biobuf*, Sym*, int);
+void	zaddr(Biobuf*, Adr*, int);
+void	outhist(Biobuf*);
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int f, sf, st, t, sym;
+	Biobuf b;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	f = open(outfile, OWRITE);
+	if(f < 0) {
+		diag(Z, "cannot open %s", outfile);
+		return;
+	}
+	Binit(&b, f, OWRITE);
+	Bseek(&b, 0L, 2);
+	outhist(&b);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.type;
+			if(t == D_ADDR)
+				t = p->from.index;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&b, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.type;
+			if(t == D_ADDR)
+				t = p->to.index;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&b, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		Bputc(&b, p->as);
+		Bputc(&b, p->as>>8);
+		Bputc(&b, p->lineno);
+		Bputc(&b, p->lineno>>8);
+		Bputc(&b, p->lineno>>16);
+		Bputc(&b, p->lineno>>24);
+		zaddr(&b, &p->from, sf);
+		zaddr(&b, &p->to, st);
+	}
+	Bflush(&b);
+	close(f);
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, ANAME>>8);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		Bputc(b, pg.as);
+		Bputc(b, pg.as>>8);
+		Bputc(b, pg.lineno);
+		Bputc(b, pg.lineno>>8);
+		Bputc(b, pg.lineno>>16);
+		Bputc(b, pg.lineno>>24);
+		zaddr(b, &pg.from, 0);
+		zaddr(b, &pg.to, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n;
+	ulong sig;
+
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		Bputc(b, ASIGNAME);
+		Bputc(b, ASIGNAME>>8);
+		Bputc(b, sig);
+		Bputc(b, sig>>8);
+		Bputc(b, sig>>16);
+		Bputc(b, sig>>24);
+		s->sig = SIGDONE;
+	}
+	else{
+		Bputc(b, ANAME);	/* as */
+		Bputc(b, ANAME>>8);	/* as */
+	}
+	Bputc(b, t);			/* type */
+	Bputc(b, s->sym);		/* sym */
+	n = s->name;
+	while(*n) {
+		Bputc(b, *n);
+		n++;
+	}
+	Bputc(b, 0);
+}
+
+void
+zaddr(Biobuf *b, Adr *a, int s)
+{
+	long l;
+	int i, t;
+	char *n;
+	Ieee e;
+
+	t = 0;
+	if(a->index != D_NONE || a->scale != 0)
+		t |= T_INDEX;
+	if(s != 0)
+		t |= T_SYM;
+
+	switch(a->type) {
+	default:
+		t |= T_TYPE;
+	case D_NONE:
+		if(a->offset != 0)
+			t |= T_OFFSET;
+		break;
+	case D_FCONST:
+		t |= T_FCONST;
+		break;
+	case D_SCONST:
+		t |= T_SCONST;
+		break;
+	}
+	Bputc(b, t);
+
+	if(t & T_INDEX) {	/* implies index, scale */
+		Bputc(b, a->index);
+		Bputc(b, a->scale);
+	}
+	if(t & T_OFFSET) {	/* implies offset */
+		l = a->offset;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+	}
+	if(t & T_SYM)		/* implies sym */
+		Bputc(b, s);
+	if(t & T_FCONST) {
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		l = e.h;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		return;
+	}
+	if(t & T_SCONST) {
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(b, *n);
+			n++;
+		}
+		return;
+	}
+	if(t & T_TYPE)
+		Bputc(b, a->type);
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_LONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/8c/txt.c
@@ -1,0 +1,1446 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+	int i;
+	Type *t;
+
+	thechar = '8';
+	thestring = "386";
+	exregoffset = 0;
+	exfregoffset = 0;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	typeswitch = typechlv;
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.from.type = D_NONE;
+	zprog.from.index = D_NONE;
+	zprog.from.scale = 0;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMP;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	fregnode0 = regnode;
+	fregnode0.reg = D_F0;
+	fregnode0.type = types[TDOUBLE];
+
+	fregnode1 = fregnode0;
+	fregnode1.reg = D_F0+1;
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	for(i=0; i<nelem(reg); i++) {
+		reg[i] = 1;
+		if(i >= D_AX && i <= D_DI && i != D_SP)
+			reg[i] = 0;
+	}
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	reg[D_SP]--;
+	for(i=D_AX; i<=D_DI; i++)
+		if(reg[i])
+			diag(Z, "reg %R left allocated", i);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+int
+nareg(int notbp)
+{
+	int i, n;
+
+	n = 0;
+	for(i=D_AX; i<=D_DI; i++)
+		if(reg[i] == 0)
+			n++;
+	if(notbp && reg[D_BP] == 0)
+		n--;
+	return n;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesu[n->type->etype] || typev[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG>=0 && curarg == 0 && typeilp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gmove(n, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gmove(tn1, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+int
+isreg(Node *n, int r)
+{
+
+	if(n->op == OREGISTER)
+		if(n->reg == r)
+			return 1;
+	return 0;
+}
+
+int
+nodreg(Node *n, Node *nn, int r)
+{
+
+	*n = regnode;
+	n->reg = r;
+	if(reg[r] == 0)
+		return 0;
+	if(nn != Z) {
+		n->type = nn->type;
+		n->lineno = nn->lineno;
+		if(nn->op == OREGISTER)
+		if(nn->reg == r)
+			return 0;
+	}
+	return 1;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= D_AX && i <= D_DI)
+				goto out;
+		}
+		for(i=D_AX; i<=D_DI; i++)
+			if(reg[i] == 0)
+				goto out;
+		diag(tn, "out of fixed registers");
+abort();
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+		i = D_F0;
+		goto out;
+
+	case TVLONG:
+	case TUVLONG:
+		n->op = OREGPAIR;
+		n->complex = 0; /* already in registers */
+		n->addable = 11;
+		n->type = tn->type;
+		n->lineno = nearln;
+		n->left = alloc(sizeof(Node));
+		n->right = alloc(sizeof(Node));
+		if(o != Z && o->op == OREGPAIR) {
+			regalloc(n->left, &regnode, o->left);
+			regalloc(n->right, &regnode, o->right);
+		} else {
+			regalloc(n->left, &regnode, Z);
+			regalloc(n->right, &regnode, Z);
+		}
+		n->right->type = types[TULONG];
+		if(tn->type->etype == TUVLONG)
+			n->left->type = types[TULONG];
+		return;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	i = 0;
+out:
+	if(i)
+		reg[i]++;
+	nodreg(n, tn, i);
+//print("+ %R %d\n", i, reg[i]);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	if(n->op == OREGPAIR) {
+		regfree(n->left);
+		regfree(n->right);
+		return;
+	}
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+//print("- %R %d\n", i, reg[i]);
+	return;
+err:
+	diag(n, "error in regfree: %R", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	USED(nn);
+
+	if(REGARG < 0) {
+		diag(n, "regaalloc1");
+		return;
+	}
+/* not reached 
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+*/
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O %D", n->op, a);
+//prtree(n, "naddr");
+		break;
+
+	case OREGISTER:
+		a->type = n->reg;
+		a->sym = S;
+		break;
+
+	case OEXREG:
+		a->type = D_INDIR + D_GS;
+		a->offset = n->reg - 1;
+		a->etype = n->etype;
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type >= D_AX && a->type <= D_DI)
+			a->type += D_INDIR;
+		else
+		if(a->type == D_CONST)
+			a->type = D_NONE+D_INDIR;
+		else
+		if(a->type == D_ADDR) {
+			a->type = a->index;
+			a->index = D_NONE;
+		} else
+			goto bad;
+		break;
+
+	case OINDEX:
+		a->type = idx.ptr;
+		if(n->left->op == OADDR || n->left->op == OCONST)
+			naddr(n->left, a);
+		if(a->type >= D_AX && a->type <= D_DI)
+			a->type += D_INDIR;
+		else
+		if(a->type == D_CONST)
+			a->type = D_NONE+D_INDIR;
+		else
+		if(a->type == D_ADDR) {
+			a->type = a->index;
+			a->index = D_NONE;
+		} else
+			goto bad;
+		a->index = idx.reg;
+		a->scale = n->scale;
+		a->offset += n->xoffset;
+		break;
+
+	case OINDREG:
+		a->type = n->reg+D_INDIR;
+		a->sym = S;
+		a->offset = n->xoffset;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->type = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->type = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->type = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+			break;
+		}
+		a->sym = S;
+		a->type = D_CONST;
+		a->offset = n->vconst;
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type >= D_INDIR) {
+			a->type -= D_INDIR;
+			break;
+		}
+		if(a->type == D_EXTERN || a->type == D_STATIC ||
+		   a->type == D_AUTO || a->type == D_PARAM)
+			if(a->index == D_NONE) {
+				a->index = a->type;
+				a->type = D_ADDR;
+				break;
+			}
+		goto bad;
+
+	case OADD:
+		if(n->right->op == OCONST) {
+			v = n->right->vconst;
+			naddr(n->left, a);
+		} else
+		if(n->left->op == OCONST) {
+			v = n->left->vconst;
+			naddr(n->right, a);
+		} else
+			goto bad;
+		a->offset += v;
+		break;
+
+	}
+}
+
+#define	CASE(a,b)	((a<<8)|(b<<0))
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod, nod1;
+	Prog *p1;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+	if(debug['M'])
+		print("gop: %O %O[%s],%O[%s]\n", OAS,
+			f->op, tnames[ft], t->op, tnames[tt]);
+	if(typefd[ft] && f->op == OCONST) {
+		if(f->fconst == 0)
+			gins(AFLDZ, Z, Z);
+		else
+		if(f->fconst == 1)
+			gins(AFLD1, Z, Z);
+		else
+			gins(AFMOVD, f, &fregnode0);
+		gmove(&fregnode0, t);
+		return;
+	}
+/*
+ * load
+ */
+	if(f->op == ONAME || f->op == OINDREG ||
+	   f->op == OIND || f->op == OINDEX)
+	switch(ft) {
+	case TCHAR:
+		a = AMOVBLSX;
+		goto ld;
+	case TUCHAR:
+		a = AMOVBLZX;
+		goto ld;
+	case TSHORT:
+		if(typefd[tt]) {
+			gins(AFMOVW, f, &fregnode0);
+			gmove(&fregnode0, t);
+			return;
+		}
+		a = AMOVWLSX;
+		goto ld;
+	case TUSHORT:
+		a = AMOVWLZX;
+		goto ld;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(typefd[tt]) {
+			gins(AFMOVL, f, &fregnode0);
+			gmove(&fregnode0, t);
+			return;
+		}
+		a = AMOVL;
+
+	ld:
+		regalloc(&nod, f, t);
+		nod.type = types[TLONG];
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+
+	case TFLOAT:
+		gins(AFMOVF, f, t);
+		return;
+	case TDOUBLE:
+		gins(AFMOVD, f, t);
+		return;
+	}
+
+/*
+ * store
+ */
+	if(t->op == ONAME || t->op == OINDREG ||
+	   t->op == OIND || t->op == OINDEX)
+	switch(tt) {
+	case TCHAR:
+	case TUCHAR:
+		a = AMOVB;	goto st;
+	case TSHORT:
+	case TUSHORT:
+		a = AMOVW;	goto st;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		a = AMOVL;	goto st;
+
+	st:
+		if(f->op == OCONST) {
+			gins(a, f, t);
+			return;
+		}
+		regalloc(&nod, t, f);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+
+	case TFLOAT:
+		gins(AFMOVFP, f, t);
+		return;
+	case TDOUBLE:
+		gins(AFMOVDP, f, t);
+		return;
+	}
+
+/*
+ * convert
+ */
+	switch(CASE(ft,tt)) {
+	default:
+/*
+ * integer to integer
+ ********
+		a = AGOK;	break;
+
+	case CASE(	TCHAR,	TCHAR):
+	case CASE(	TUCHAR,	TCHAR):
+	case CASE(	TSHORT,	TCHAR):
+	case CASE(	TUSHORT,TCHAR):
+	case CASE(	TINT,	TCHAR):
+	case CASE(	TUINT,	TCHAR):
+	case CASE(	TLONG,	TCHAR):
+	case CASE(	TULONG,	TCHAR):
+	case CASE(	TIND,	TCHAR):
+
+	case CASE(	TCHAR,	TUCHAR):
+	case CASE(	TUCHAR,	TUCHAR):
+	case CASE(	TSHORT,	TUCHAR):
+	case CASE(	TUSHORT,TUCHAR):
+	case CASE(	TINT,	TUCHAR):
+	case CASE(	TUINT,	TUCHAR):
+	case CASE(	TLONG,	TUCHAR):
+	case CASE(	TULONG,	TUCHAR):
+	case CASE(	TIND,	TUCHAR):
+
+	case CASE(	TSHORT,	TSHORT):
+	case CASE(	TUSHORT,TSHORT):
+	case CASE(	TINT,	TSHORT):
+	case CASE(	TUINT,	TSHORT):
+	case CASE(	TLONG,	TSHORT):
+	case CASE(	TULONG,	TSHORT):
+	case CASE(	TIND,	TSHORT):
+
+	case CASE(	TSHORT,	TUSHORT):
+	case CASE(	TUSHORT,TUSHORT):
+	case CASE(	TINT,	TUSHORT):
+	case CASE(	TUINT,	TUSHORT):
+	case CASE(	TLONG,	TUSHORT):
+	case CASE(	TULONG,	TUSHORT):
+	case CASE(	TIND,	TUSHORT):
+
+	case CASE(	TINT,	TINT):
+	case CASE(	TUINT,	TINT):
+	case CASE(	TLONG,	TINT):
+	case CASE(	TULONG,	TINT):
+	case CASE(	TIND,	TINT):
+
+	case CASE(	TINT,	TUINT):
+	case CASE(	TUINT,	TUINT):
+	case CASE(	TLONG,	TUINT):
+	case CASE(	TULONG,	TUINT):
+	case CASE(	TIND,	TUINT):
+
+	case CASE(	TINT,	TLONG):
+	case CASE(	TUINT,	TLONG):
+	case CASE(	TLONG,	TLONG):
+	case CASE(	TULONG,	TLONG):
+	case CASE(	TIND,	TLONG):
+
+	case CASE(	TINT,	TULONG):
+	case CASE(	TUINT,	TULONG):
+	case CASE(	TLONG,	TULONG):
+	case CASE(	TULONG,	TULONG):
+	case CASE(	TIND,	TULONG):
+
+	case CASE(	TINT,	TIND):
+	case CASE(	TUINT,	TIND):
+	case CASE(	TLONG,	TIND):
+	case CASE(	TULONG,	TIND):
+	case CASE(	TIND,	TIND):
+ *****/
+		a = AMOVL;
+		break;
+
+	case CASE(	TSHORT,	TINT):
+	case CASE(	TSHORT,	TUINT):
+	case CASE(	TSHORT,	TLONG):
+	case CASE(	TSHORT,	TULONG):
+	case CASE(	TSHORT,	TIND):
+		a = AMOVWLSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			if(f->vconst & 0x8000)
+				f->vconst |= 0xffff0000;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TUSHORT,TINT):
+	case CASE(	TUSHORT,TUINT):
+	case CASE(	TUSHORT,TLONG):
+	case CASE(	TUSHORT,TULONG):
+	case CASE(	TUSHORT,TIND):
+		a = AMOVWLZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xffff;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TCHAR,	TSHORT):
+	case CASE(	TCHAR,	TUSHORT):
+	case CASE(	TCHAR,	TINT):
+	case CASE(	TCHAR,	TUINT):
+	case CASE(	TCHAR,	TLONG):
+	case CASE(	TCHAR,	TULONG):
+	case CASE(	TCHAR,	TIND):
+		a = AMOVBLSX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			if(f->vconst & 0x80)
+				f->vconst |= 0xffffff00;
+			a = AMOVL;
+		}
+		break;
+
+	case CASE(	TUCHAR,	TSHORT):
+	case CASE(	TUCHAR,	TUSHORT):
+	case CASE(	TUCHAR,	TINT):
+	case CASE(	TUCHAR,	TUINT):
+	case CASE(	TUCHAR,	TLONG):
+	case CASE(	TUCHAR,	TULONG):
+	case CASE(	TUCHAR,	TIND):
+		a = AMOVBLZX;
+		if(f->op == OCONST) {
+			f->vconst &= 0xff;
+			a = AMOVL;
+		}
+		break;
+
+/*
+ * float to fix
+ */
+	case CASE(	TFLOAT,	TCHAR):
+	case CASE(	TFLOAT,	TUCHAR):
+	case CASE(	TFLOAT,	TSHORT):
+	case CASE(	TFLOAT,	TUSHORT):
+	case CASE(	TFLOAT,	TINT):
+	case CASE(	TFLOAT,	TLONG):
+	case CASE(	TFLOAT,	TIND):
+
+	case CASE(	TDOUBLE,TCHAR):
+	case CASE(	TDOUBLE,TUCHAR):
+	case CASE(	TDOUBLE,TSHORT):
+	case CASE(	TDOUBLE,TUSHORT):
+	case CASE(	TDOUBLE,TINT):
+	case CASE(	TDOUBLE,TLONG):
+	case CASE(	TDOUBLE,TIND):
+		if(fproundflg) {
+			regsalloc(&nod, &regnode);
+			gins(AFMOVLP, f, &nod);
+			gmove(&nod, t);
+			return;
+		}
+		regsalloc(&nod, &regnode);
+		regsalloc(&nod1, &regnode);
+		gins(AFSTCW, Z, &nod1);
+		nod1.xoffset += 2;
+		gins(AMOVW, nodconst(0xf7f), &nod1);
+		gins(AFLDCW, &nod1, Z);
+		gins(AFMOVLP, f, &nod);
+		nod1.xoffset -= 2;
+		gins(AFLDCW, &nod1, Z);
+		gmove(&nod, t);
+		return;
+
+/*
+ * float to ulong
+ */
+	case CASE(	TDOUBLE,	TULONG):
+	case CASE(	TFLOAT,	TULONG):
+	case CASE(	TDOUBLE,	TUINT):
+	case CASE(	TFLOAT,	TUINT):
+		regsalloc(&nod, &regnode);
+		gmove(f, &fregnode0);
+		gins(AFADDD, nodfconst(-2147483648.), &fregnode0);
+		gins(AFMOVLP, f, &nod);
+		gins(ASUBL, nodconst(-2147483648u), &nod);
+		gmove(&nod, t);
+		return;
+
+/*
+ * ulong to float
+ */
+	case CASE(	TULONG,	TDOUBLE):
+	case CASE(	TULONG,	TFLOAT):
+	case CASE(	TUINT,	TDOUBLE):
+	case CASE(	TUINT,	TFLOAT):
+		regalloc(&nod, f, f);
+		gmove(f, &nod);
+		regsalloc(&nod1, &regnode);
+		gmove(&nod, &nod1);
+		gins(AFMOVL, &nod1, &fregnode0);
+		gins(ACMPL, &nod, nodconst(0));
+		gins(AJGE, Z, Z);
+		p1 = p;
+		gins(AFADDD, nodfconst(4294967296.), &fregnode0);
+		patch(p1, pc);
+		regfree(&nod);
+		return;
+
+/*
+ * fix to float
+ */
+	case CASE(	TCHAR,	TFLOAT):
+	case CASE(	TUCHAR,	TFLOAT):
+	case CASE(	TSHORT,	TFLOAT):
+	case CASE(	TUSHORT,TFLOAT):
+	case CASE(	TINT,	TFLOAT):
+	case CASE(	TLONG,	TFLOAT):
+	case CASE(	TIND,	TFLOAT):
+
+	case CASE(	TCHAR,	TDOUBLE):
+	case CASE(	TUCHAR,	TDOUBLE):
+	case CASE(	TSHORT,	TDOUBLE):
+	case CASE(	TUSHORT,TDOUBLE):
+	case CASE(	TINT,	TDOUBLE):
+	case CASE(	TLONG,	TDOUBLE):
+	case CASE(	TIND,	TDOUBLE):
+		regsalloc(&nod, &regnode);
+		gmove(f, &nod);
+		gins(AFMOVL, &nod, &fregnode0);
+		return;
+
+/*
+ * float to float
+ */
+	case CASE(	TFLOAT,	TFLOAT):
+	case CASE(	TDOUBLE,TFLOAT):
+
+	case CASE(	TFLOAT,	TDOUBLE):
+	case CASE(	TDOUBLE,TDOUBLE):
+		a = AFMOVD;	break;
+	}
+	if(a == AMOVL || a == AFMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+doindex(Node *n)
+{
+	Node nod, nod1;
+	long v;
+
+if(debug['Y'])
+prtree(n, "index");
+
+if(n->left->complex >= FNX)
+print("botch in doindex\n");
+
+	regalloc(&nod, &regnode, Z);
+	v = constnode.vconst;
+	cgen(n->right, &nod);
+	idx.ptr = D_NONE;
+	if(n->left->op == OCONST)
+		idx.ptr = D_CONST;
+	else if(n->left->op == OREGISTER)
+//	else if(n->left->op == OREGISTER && typeil[n->left->type->etype])
+		idx.ptr = n->left->reg;
+	else if(n->left->op != OADDR) {
+		reg[D_BP]++;	// cant be used as a base
+		regalloc(&nod1, &regnode, Z);
+		cgen(n->left, &nod1);
+		idx.ptr = nod1.reg;
+		regfree(&nod1);
+		reg[D_BP]--;
+	}
+	idx.reg = nod.reg;
+	regfree(&nod);
+	constnode.vconst = v;
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	if(f != Z && f->op == OINDEX)
+		doindex(f);
+	if(t != Z && t->op == OINDEX)
+		doindex(t);
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+fgopcode(int o, Node *f, Node *t, int pop, int rev)
+{
+	int a, et;
+	Node nod;
+
+	et = TLONG;
+	if(f != Z && f->type != T)
+		et = f->type->etype;
+	if(!typefd[et]) {
+		diag(f, "fop: integer %O", o);
+		return;
+	}
+	if(debug['M']) {
+		if(t != Z && t->type != T)
+			print("gop: %O %O-%s Z\n", o, f->op, tnames[et]);
+		else
+			print("gop: %O %O-%s %O-%s\n", o,
+				f->op, tnames[et], t->op, tnames[t->type->etype]);
+	}
+	a = AGOK;
+	switch(o) {
+
+	case OASADD:
+	case OADD:
+		if(et == TFLOAT)
+			a = AFADDF;
+		else
+		if(et == TDOUBLE) {
+			a = AFADDD;
+			if(pop)
+				a = AFADDDP;
+		}
+		break;
+
+	case OASSUB:
+	case OSUB:
+		if(et == TFLOAT) {
+			a = AFSUBF;
+			if(rev)
+				a = AFSUBRF;
+		} else
+		if(et == TDOUBLE) {
+			a = AFSUBD;
+			if(pop)
+				a = AFSUBDP;
+			if(rev) {
+				a = AFSUBRD;
+				if(pop)
+					a = AFSUBRDP;
+			}
+		}
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT)
+			a = AFMULF;
+		else
+		if(et == TDOUBLE) {
+			a = AFMULD;
+			if(pop)
+				a = AFMULDP;
+		}
+		break;
+
+	case OASMOD:
+	case OMOD:
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = AFDIVF;
+			if(rev)
+				a = AFDIVRF;
+		} else
+		if(et == TDOUBLE) {
+			a = AFDIVD;
+			if(pop)
+				a = AFDIVDP;
+			if(rev) {
+				a = AFDIVRD;
+				if(pop)
+					a = AFDIVRDP;
+			}
+		}
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+		pop += rev;
+		if(et == TFLOAT) {
+			a = AFCOMF;
+			if(pop) {
+				a = AFCOMFP;
+				if(pop > 1)
+					a = AGOK;
+			}
+		} else
+		if(et == TDOUBLE) {
+			a = AFCOMF;
+			if(pop) {
+				a = AFCOMDP;
+				if(pop > 1)
+					a = AFCOMDPP;
+			}
+		}
+		gins(a, f, t);
+		regalloc(&nod, &regnode, Z);
+		if(nod.reg != D_AX) {
+			regfree(&nod);
+			nod.reg = D_AX;
+			gins(APUSHL, &nod, Z);
+			gins(AWAIT, Z, Z);
+			gins(AFSTSW, Z, &nod);
+			gins(ASAHF, Z, Z);
+			gins(APOPL, Z, &nod);
+		} else {
+			gins(AWAIT, Z, Z);
+			gins(AFSTSW, Z, &nod);
+			gins(ASAHF, Z, Z);
+			regfree(&nod);
+		}
+		switch(o) {
+		case OEQ:	a = AJEQ; break;
+		case ONE:	a = AJNE; break;
+		case OLT:	a = AJCS; break;
+		case OLE:	a = AJLS; break;
+		case OGE:	a = AJCC; break;
+		case OGT:	a = AJHI; break;
+		}
+		gins(a, Z, Z);
+		return;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	gins(a, f, t);
+}
+
+void
+gopcode(int o, Type *ty, Node *f, Node *t)
+{
+	int a, et;
+
+	et = TLONG;
+	if(ty != T)
+		et = ty->etype;
+	if(typefd[et] && o != OADDR && o != OFUNC) {
+		diag(f, "gop: float %O", o);
+		return;
+	}
+	if(debug['M']) {
+		if(f != Z && f->type != T)
+			print("gop: %O %O[%s],", o, f->op, tnames[et]);
+		else
+			print("gop: %O Z,", o);
+		if(t != Z && t->type != T)
+			print("%O[%s]\n", t->op, tnames[t->type->etype]);
+		else
+			print("Z\n");
+	}
+	a = AGOK;
+	switch(o) {
+	case OCOM:
+		a = ANOTL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ANOTB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ANOTW;
+		break;
+
+	case ONEG:
+		a = ANEGL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ANEGB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ANEGW;
+		break;
+
+	case OADDR:
+		a = ALEAL;
+		break;
+
+	case OASADD:
+	case OADD:
+		a = AADDL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AADDB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AADDW;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASUBB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASUBW;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AORL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AORB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AORW;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AANDL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AANDB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AANDW;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXORL;
+		if(et == TCHAR || et == TUCHAR)
+			a = AXORB;
+		if(et == TSHORT || et == TUSHORT)
+			a = AXORW;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASHRL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASHRB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASHRW;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASARL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASARB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASARW;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASALL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ASALB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ASALW;
+		break;
+
+	case OFUNC:
+		a = ACALL;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(f->op == OREGISTER && t != Z && isreg(t, D_AX) && reg[D_DX] == 0)
+			t = Z;
+		a = AIMULL;
+		break;
+
+	case OASMOD:
+	case OMOD:
+	case OASDIV:
+	case ODIV:
+		a = AIDIVL;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULL;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVL;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		a = ACMPL;
+		if(et == TCHAR || et == TUCHAR)
+			a = ACMPB;
+		if(et == TSHORT || et == TUSHORT)
+			a = ACMPW;
+		gins(a, f, t);
+		switch(o) {
+		case OEQ:	a = AJEQ; break;
+		case ONE:	a = AJNE; break;
+		case OLT:	a = AJLT; break;
+		case OLE:	a = AJLE; break;
+		case OGE:	a = AJGE; break;
+		case OGT:	a = AJGT; break;
+		case OLO:	a = AJCS; break;
+		case OLS:	a = AJLS; break;
+		case OHS:	a = AJCC; break;
+		case OHI:	a = AJHI; break;
+		}
+		gins(a, Z, Z);
+		return;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	gins(a, f, t);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_EXTERN;
+	p->from.sym = s;
+	p->from.scale = (profileflg ? 0 : NOPROF);
+	if(s->class == CSTATIC)
+		p->from.type = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	long v;
+
+	if(n->op == OCONST && !typefd[n->type->etype]) {
+		v = n->vconst;
+		if(v >= -32766L && v < 32766L)
+			return 1;
+	}
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+
+	int o;
+
+	if(typechlp[t->etype]){
+		if(exregoffset >= 32)
+			return 0;
+		o = exregoffset;
+		exregoffset += 4;
+		return o+1;	/* +1 to avoid 0 == failure; naddr case OEXREG will -1. */
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/*[TXXX]*/	
+	SZ_CHAR,	/*[TCHAR]*/	
+	SZ_CHAR,	/*[TUCHAR]*/
+	SZ_SHORT,	/*[TSHORT]*/
+	SZ_SHORT,	/*[TUSHORT]*/
+	SZ_INT,		/*[TINT]*/
+	SZ_INT,		/*[TUINT]*/
+	SZ_LONG,	/*[TLONG]*/
+	SZ_LONG,	/*[TULONG]*/
+	SZ_VLONG,	/*[TVLONG]*/
+	SZ_VLONG,	/*[TUVLONG]*/
+	SZ_FLOAT,	/*[TFLOAT]*/
+	SZ_DOUBLE,	/*[TDOUBLE]*/
+	SZ_IND,		/*[TIND]*/
+	0,		/*[TFUNC]*/
+	-1,		/*[TARRAY]*/
+	0,		/*[TVOID]*/
+	-1,		/*[TSTRUCT]*/
+	-1,		/*[TUNION]*/
+	SZ_INT,		/*[TENUM]*/
+};
+long	ncast[NTYPE] =
+{
+	0,				/*[TXXX]*/
+	BCHAR|BUCHAR,			/*[TCHAR]*/
+	BCHAR|BUCHAR,			/*[TUCHAR]*/	
+	BSHORT|BUSHORT,			/*[TSHORT]*/
+	BSHORT|BUSHORT,			/*[TUSHORT]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TINT]*/		
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TUINT]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TLONG]*/
+	BINT|BUINT|BLONG|BULONG|BIND,	/*[TULONG]*/
+	BVLONG|BUVLONG,			/*[TVLONG]*/
+	BVLONG|BUVLONG,			/*[TUVLONG]*/
+	BFLOAT,				/*[TFLOAT]*/
+	BDOUBLE,			/*[TDOUBLE]*/
+	BLONG|BULONG|BIND,		/*[TIND]*/
+	0,				/*[TFUNC]*/
+	0,				/*[TARRAY]*/
+	0,				/*[TVOID]*/
+	BSTRUCT,			/*[TSTRUCT]*/
+	BUNION,				/*[TUNION]*/
+	0,				/*[TENUM]*/
+};
--- /dev/null
+++ b/utils/8l/asm.c
@@ -1,0 +1,523 @@
+#include	"l.h"
+
+#define	Dbufslop	100
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	switch(s->type) {
+	case STEXT:
+		break;
+	case SDATA:
+		if(dlm)
+			return s->value+INITDAT;
+	default:
+		diag("entry not text: %s", s->name);
+	}
+	return s->value;
+}
+
+/* these need to take long arguments to be compatible with elf.c */
+void
+wputl(long w)
+{
+	cput(w);
+	cput(w>>8);
+}
+
+void
+wput(long w)
+{
+	cput(w>>8);
+	cput(w);
+}
+
+void
+lput(long l)
+{
+	cput(l>>24);
+	cput(l>>16);
+	cput(l>>8);
+	cput(l);
+}
+
+void
+lputl(long l)
+{
+	cput(l);
+	cput(l>>8);
+	cput(l>>16);
+	cput(l>>24);
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s && n > 0; s++){
+		cput(*s);
+		n--;
+	}
+	while(n > 0){
+		cput(0);
+		n--;
+	}
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long v, magic;
+	int a;
+	uchar *op1;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asmb\n", cputime());
+	Bflush(&bso);
+
+	seek(cout, HEADR, 0);
+	pc = INITTEXT;
+	curp = firstp;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->pc != pc) {
+			if(!debug['a'])
+				print("%P\n", curp);
+			diag("phase error %lux sb %lux in %s", p->pc, pc, TNAME);
+			pc = p->pc;
+		}
+		curp = p;
+		asmins(p);
+		if(cbc < sizeof(and))
+			cflush();
+		a = (andptr - and);
+		if(debug['a']) {
+			Bprint(&bso, pcstr, pc);
+			for(op1 = and; op1 < andptr; op1++)
+				Bprint(&bso, "%.2ux", *op1 & 0xff);
+			Bprint(&bso, "\t%P\n", curp);
+		}
+		if(dlm) {
+			if(p->as == ATEXT)
+				reloca = nil;
+			else if(reloca != nil)
+				diag("reloc failure: %P", curp);
+		}
+		memmove(cbp, and, a);
+		cbp += a;
+		pc += a;
+		cbc -= a;
+	}
+	cflush();
+	switch(HEADTYPE) {
+	default:
+		diag("unknown header type %ld", HEADTYPE);
+	case 0:
+		seek(cout, rnd(HEADR+textsize, 8192), 0);
+		break;
+	case 1:
+		textsize = rnd(HEADR+textsize, 4096)-HEADR;
+		seek(cout, textsize+HEADR, 0);
+		break;
+	case 2:
+	case 5:
+		seek(cout, HEADR+textsize, 0);
+		break;
+	case 3:
+	case 4:
+		seek(cout, HEADR+rnd(textsize, INITRND), 0);
+		break;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f datblk\n", cputime());
+	Bflush(&bso);
+
+	if(dlm){
+		char buf[8];
+
+		write(cout, buf, INITDAT-textsize);
+		textsize = INITDAT;
+	}
+
+	for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) {
+		if(datsize-v > sizeof(buf)-Dbufslop)
+			datblk(v, sizeof(buf)-Dbufslop);
+		else
+			datblk(v, datsize-v);
+	}
+
+	symsize = 0;
+	spsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		default:
+		case 0:
+			seek(cout, rnd(HEADR+textsize, 8192)+datsize, 0);
+			break;
+		case 1:
+			seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0);
+			break;
+		case 2:
+		case 5:
+			seek(cout, HEADR+textsize+datsize, 0);
+			break;
+		case 3:
+		case 4:
+			debug['s'] = 1;
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sp\n", cputime());
+		Bflush(&bso);
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		if(dlm)
+			asmdyn();
+		cflush();
+	}
+	else if(dlm){
+		seek(cout, HEADR+textsize+datsize, 0);
+		asmdyn();
+		cflush();
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f headr\n", cputime());
+	Bflush(&bso);
+	seek(cout, 0L, 0);
+	switch(HEADTYPE) {
+	default:
+	case 0:	/* garbage */
+		lput(0x160L<<16);		/* magic and sections */
+		lput(0L);			/* time and date */
+		lput(rnd(HEADR+textsize, 4096)+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+		lput((0413<<16)|0437L);		/* magic and version */
+		lput(rnd(HEADR+textsize, 4096));/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT-HEADR);		/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT+datsize);		/* va of base of bss */
+		lput(~0L);			/* gp reg mask */
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(~0L);			/* gp value ?? */
+		break;
+	case 1:	/* unix coff */
+		/*
+		 * file header
+		 */
+		lputl(0x0004014c);		/* 4 sections, magic */
+		lputl(0);			/* unix time stamp */
+		lputl(0);			/* symbol table */
+		lputl(0);			/* nsyms */
+		lputl(0x0003001c);		/* flags, sizeof a.out header */
+		/*
+		 * a.out header
+		 */
+		lputl(0x10b);			/* magic, version stamp */
+		lputl(rnd(textsize, INITRND));	/* text sizes */
+		lputl(datsize);			/* data sizes */
+		lputl(bsssize);			/* bss sizes */
+		lput(entryvalue());		/* va of entry */
+		lputl(INITTEXT);		/* text start */
+		lputl(INITDAT);			/* data start */
+		/*
+		 * text section header
+		 */
+		strnput(".text", 8);
+		lputl(HEADR);			/* pa */
+		lputl(HEADR);			/* va */
+		lputl(textsize);		/* text size */
+		lputl(HEADR);			/* file offset */
+		lputl(0);			/* relocation */
+		lputl(0);			/* line numbers */
+		lputl(0);			/* relocation, line numbers */
+		lputl(0x20);			/* flags text only */
+		/*
+		 * data section header
+		 */
+		strnput(".data", 8);
+		lputl(INITDAT);			/* pa */
+		lputl(INITDAT);			/* va */
+		lputl(datsize);			/* data size */
+		lputl(HEADR+textsize);		/* file offset */
+		lputl(0);			/* relocation */
+		lputl(0);			/* line numbers */
+		lputl(0);			/* relocation, line numbers */
+		lputl(0x40);			/* flags data only */
+		/*
+		 * bss section header
+		 */
+		strnput(".bss", 8);
+		lputl(INITDAT+datsize);		/* pa */
+		lputl(INITDAT+datsize);		/* va */
+		lputl(bsssize);			/* bss size */
+		lputl(0);			/* file offset */
+		lputl(0);			/* relocation */
+		lputl(0);			/* line numbers */
+		lputl(0);			/* relocation, line numbers */
+		lputl(0x80);			/* flags bss only */
+		/*
+		 * comment section header
+		 */
+		strnput(".comment", 8);
+		lputl(0);			/* pa */
+		lputl(0);			/* va */
+		lputl(symsize+lcsize);		/* comment size */
+		lputl(HEADR+textsize+datsize);	/* file offset */
+		lputl(HEADR+textsize+datsize);	/* offset of syms */
+		lputl(HEADR+textsize+datsize+symsize);/* offset of line numbers */
+		lputl(0);			/* relocation, line numbers */
+		lputl(0x200);			/* flags comment only */
+		break;
+	case 2:	/* plan9 */
+		magic = 4*11*11+7;
+		if(dlm)
+			magic |= 0x80000000;
+		lput(magic);			/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(spsize);			/* sp offsets */
+		lput(lcsize);			/* line offsets */
+		break;
+	case 3:
+		/* MS-DOS .COM */
+		break;
+	case 4:
+		/* fake MS-DOS .EXE */
+		v = rnd(HEADR+textsize, INITRND)+datsize;
+		wputl(0x5A4D);			/* 'MZ' */
+		wputl(v % 512);			/* bytes in last page */
+		wputl(rnd(v, 512)/512);		/* total number of pages */
+		wputl(0x0000);			/* number of reloc items */
+		v = rnd(HEADR-(INITTEXT & 0xFFFF), 16);
+		wputl(v/16);			/* size of header */
+		wputl(0x0000);			/* minimum allocation */
+		wputl(0xFFFF);			/* maximum allocation */
+		wputl(0x0000);			/* initial ss value */
+		wputl(0x0100);			/* initial sp value */
+		wputl(0x0000);			/* complemented checksum */
+		v = entryvalue();
+		wputl(v);			/* initial ip value (!) */
+		wputl(0x0000);			/* initial cs value */
+		wputl(0x0000);
+		wputl(0x0000);
+		wputl(0x003E);			/* reloc table offset */
+		wputl(0x0000);			/* overlay number */
+		break;
+	case 5:
+		elf32(I386, ELFDATA2LSB, 0, nil);
+		break;
+	}
+	cflush();
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+datblk(long s, long n)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+Dbufslop);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->from.scale;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(&p->to.ieee);
+				cast = (char*)&fl;
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[fnuxi4[j]] & 0xff);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)&p->to.ieee;
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[fnuxi8[j]] & 0xff);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			if(debug['a'] && i == 0) {
+				Bprint(&bso, pcstr, l+s+INITDAT);
+				for(j=0; j<c; j++)
+					Bprint(&bso, "%.2ux", p->to.scon[j] & 0xff);
+				Bprint(&bso, "\t%P\n", curp);
+			}
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.scon[i];
+				l++;
+			}
+			break;
+		default:
+			fl = p->to.offset;
+			if(p->to.type == D_ADDR) {
+				if(p->to.index != D_STATIC && p->to.index != D_EXTERN)
+					diag("DADDR type%P", p);
+				if(p->to.sym) {
+					if(p->to.sym->type == SUNDEF)
+						ckoff(p->to.sym, fl);
+					fl += p->to.sym->value;
+					if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF)
+						fl += INITDAT;
+					if(dlm)
+						dynreloc(p->to.sym, l+s+INITDAT, 1);
+				}
+			}
+			cast = (char*)&fl;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi1[j]] & 0xff);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi2[j]] & 0xff);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				if(debug['a'] && i == 0) {
+					Bprint(&bso, pcstr, l+s+INITDAT);
+					for(j=0; j<c; j++)
+						Bprint(&bso, "%.2ux", cast[inuxi4[j]] & 0xff);
+					Bprint(&bso, "\t%P\n", curp);
+				}
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/utils/8l/l.h
@@ -1,0 +1,364 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../8c/8.out.h"
+#include	"../ld/elf.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext?curtext->from.sym->name:noname)
+
+#define	cput(c)\
+	{ *cbp++ = c;\
+	if(--cbc <= 0)\
+		cflush(); }
+
+#define	LIBNAMELEN	300
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Sym	Sym;
+typedef	struct	Auto	Auto;
+typedef	struct	Optab	Optab;
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char	u0scon[8];
+		Prog	*u0cond;	/* not used, but should be D_BRANCH */
+		Ieee	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	short	type;
+	uchar	index;
+	char	scale;
+};
+
+#define	offset	u0.u0offset
+#define	scon	u0.u0scon
+#define	cond	u0.u0cond
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog	*forwd;
+	Prog*	link;
+	Prog*	pcond;	/* work on this */
+	long	pc;
+	long	line;
+	short	as;
+	char	width;		/* fake for DATA */
+	char	ft;		/* oclass cache */
+	char	tt;
+	uchar	mark;	/* work on these */
+	uchar	back;
+};
+struct	Auto
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	uchar	subtype;
+	ushort	file;
+	long	value;
+	long	sig;
+	Sym*	link;
+};
+struct	Optab
+{
+	short	as;
+	uchar*	ytab;
+	uchar	prefix;
+	uchar	op[10];
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SFILE,
+	SCONST,
+	SUNDEF,
+
+	SIMPORT,
+	SEXPORT,
+
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 4,
+	STRINGSZ	= 200,
+	MINLC		= 1,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+
+	Yxxx		= 0,
+	Ynone,
+	Yi0,
+	Yi1,
+	Yi8,
+	Yi32,
+	Yiauto,
+	Yal,
+	Ycl,
+	Yax,
+	Ycx,
+	Yrb,
+	Yrl,
+	Yrf,
+	Yf0,
+	Yrx,
+	Ymb,
+	Yml,
+	Ym,
+	Ybr,
+	Ycol,
+
+	Ycs,	Yss,	Yds,	Yes,	Yfs,	Ygs,
+	Ygdtr,	Yidtr,	Yldtr,	Ymsw,	Ytask,
+	Ycr0,	Ycr1,	Ycr2,	Ycr3,	Ycr4,	Ycr5,	Ycr6,	Ycr7,
+	Ydr0,	Ydr1,	Ydr2,	Ydr3,	Ydr4,	Ydr5,	Ydr6,	Ydr7,
+	Ytr0,	Ytr1,	Ytr2,	Ytr3,	Ytr4,	Ytr5,	Ytr6,	Ytr7,
+	Ymax,
+
+	Zxxx		= 0,
+
+	Zlit,
+	Z_rp,
+	Zbr,
+	Zcall,
+	Zib_,
+	Zib_rp,
+	Zibo_m,
+	Zil_,
+	Zil_rp,
+	Zilo_m,
+	Zjmp,
+	Zloop,
+	Zm_o,
+	Zm_r,
+	Zaut_r,
+	Zo_m,
+	Zpseudo,
+	Zr_m,
+	Zrp_,
+	Z_ib,
+	Z_il,
+	Zm_ibo,
+	Zm_ilo,
+	Zib_rr,
+	Zil_rr,
+	Zclr,
+	Zbyte,
+	Zmov,
+	Zmax,
+
+	Px		= 0,
+	Pe		= 0x66,	/* operand escape */
+	Pm		= 0x0f,	/* 2byte opcode escape */
+	Pq		= 0xff,	/* both escape */
+	Pb		= 0xfe,	/* byte operands */
+
+	Roffset	= 22,		/* no. bits for offset in relocation address */
+	Rindex	= 10,		/* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"R"	uint
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+EXTERN	long	HEADR;
+EXTERN	long	HEADTYPE;
+EXTERN	long	INITDAT;
+EXTERN	long	INITRND;
+EXTERN	long	INITTEXT;
+EXTERN	long	INITTEXTP;
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	long	casepc;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	char*	pcstr;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	Prog*	edatap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	char	literal[32];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi8[8];
+EXTERN	char	fnuxi4[4];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	char	ycover[Ymax*Ymax];
+EXTERN	uchar*	andptr;
+EXTERN	uchar	and[30];
+EXTERN	char	reg[D_NONE];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	nsymbol;
+EXTERN	char*	noname;
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	long	spsize;
+EXTERN	Sym*	symlist;
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+
+EXTERN	Adr*	reloca;
+EXTERN	int	doexp, dlm;
+EXTERN	int	imports, nimports;
+EXTERN	int	exports, nexports, allexport;
+EXTERN	char*	EXPTAB;
+EXTERN	Prog	undefp;
+
+#define	UP	(&undefp)
+
+extern	Optab	optab[];
+extern	char*	anames[];
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Pconv(Fmt*);
+int	Rconv(Fmt*);
+int	Sconv(Fmt*);
+void	addhist(long, int);
+void	addlibpath(char*);
+Prog*	appendp(Prog*);
+void	asmb(void);
+void	asmdyn(void);
+void	asmins(Prog*);
+void	asmlc(void);
+void	asmsp(void);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brchain(Prog*);
+Prog*	brloop(Prog*);
+void	cflush(void);
+void	ckoff(Sym*, long);
+Prog*	copyp(Prog*);
+double	cputime(void);
+void	datblk(long, long);
+void	diag(char*, ...);
+void	dodata(void);
+void	doinit(void);
+void	doprof1(void);
+void	doprof2(void);
+void	dostkoff(void);
+void	dynreloc(Sym*, ulong, int);
+long	entryvalue(void);
+void	errorexit(void);
+void	export(void);
+int	fileexists(char*);
+int	find1(long, int);
+int	find2(long, int);
+char*	findlib(char*);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+void	import(void);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	lput(long);
+void	lputl(long);
+void	llput(vlong v);
+void	llputl(vlong v);
+void	main(int, char*[]);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	nuxiinit(void);
+void	objfile(char*);
+int	opsize(Prog*);
+void	patch(void);
+Prog*	prg(void);
+void	readundefs(char*, int);
+int	relinv(int);
+long	reuse(Prog*, Sym*);
+long	rnd(long, long);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	undefsym(Sym*);
+long	vaddr(Adr*);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+int	zaddr(uchar*, Adr*, Sym*[]);
+void	zerosig(char*);
+
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"A"	int
--- /dev/null
+++ b/utils/8l/list.c
@@ -1,0 +1,292 @@
+#include	"l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('R', Rconv);
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('P', Pconv);
+}
+
+static	Prog	*bigP;
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+
+	p = va_arg(fp->args, Prog*);
+	bigP = p;
+	switch(p->as) {
+	case ATEXT:
+		if(p->from.scale) {
+			snprint(str, sizeof(str), "(%ld)	%A	%D,%d,%D",
+				p->line, p->as, &p->from, p->from.scale, &p->to);
+			break;
+		}
+	default:
+		snprint(str, sizeof(str), "(%ld)	%A	%D,%D",
+			p->line, p->as, &p->from, &p->to);
+		break;
+	case ADATA:
+	case AINIT:
+	case ADYNT:
+		snprint(str, sizeof(str), "(%ld)	%A	%D/%d,%D",
+			p->line, p->as, &p->from, p->from.scale, &p->to);
+		break;
+	}
+	bigP = P;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	int i;
+
+	i = va_arg(fp->args, int);
+	return fmtstrcpy(fp, anames[i]);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ+40], s[20];
+	Adr *a;
+	int i;
+
+	a = va_arg(fp->args, Adr*);
+	i = a->type;
+	if(i >= D_INDIR) {
+		if(a->offset)
+			snprint(str, sizeof(str), "%ld(%R)", a->offset, i-D_INDIR);
+		else
+			snprint(str, sizeof(str), "(%R)", i-D_INDIR);
+		goto brk;
+	}
+	switch(i) {
+
+	default:
+		snprint(str, sizeof(str), "%R", i);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		break;
+
+	case D_BRANCH:
+		if(bigP != P && bigP->pcond != P)
+			if(a->sym != S)
+				snprint(str, sizeof(str), "%lux+%s", bigP->pcond->pc,
+					a->sym->name);
+			else
+				snprint(str, sizeof(str), "%lux", bigP->pcond->pc);
+		else
+			snprint(str, sizeof(str), "%ld(PC)", a->offset);
+		break;
+
+	case D_EXTERN:
+		snprint(str, sizeof(str), "%s+%ld(SB)", a->sym->name, a->offset);
+		break;
+
+	case D_STATIC:
+		snprint(str, sizeof(str), "%s<%d>+%ld(SB)", a->sym->name,
+			a->sym->version, a->offset);
+		break;
+
+	case D_AUTO:
+		snprint(str, sizeof(str), "%s+%ld(SP)", a->sym->name, a->offset);
+		break;
+
+	case D_PARAM:
+		if(a->sym)
+			snprint(str, sizeof(str), "%s+%ld(FP)", a->sym->name, a->offset);
+		else
+			snprint(str, sizeof(str), "%ld(FP)", a->offset);
+		break;
+
+	case D_CONST:
+		snprint(str, sizeof(str), "$%ld", a->offset);
+		break;
+
+	case D_FCONST:
+		snprint(str, sizeof(str), "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l);
+		break;
+
+	case D_SCONST:
+		snprint(str, sizeof(str), "$\"%S\"", a->scon);
+		break;
+
+	case D_ADDR:
+		a->type = a->index;
+		a->index = D_NONE;
+		snprint(str, sizeof(str), "$%D", a);
+		a->index = a->type;
+		a->type = D_ADDR;
+		goto conv;
+	}
+brk:
+	if(a->index != D_NONE) {
+		snprint(s, sizeof(s), "(%R*%d)", a->index, a->scale);
+		strcat(str, s);
+	}
+conv:
+	return fmtstrcpy(fp, str);
+}
+
+char*	regstr[] =
+{
+	"AL",		/* [D_AL] */
+	"CL",
+	"DL",
+	"BL",
+	"AH",
+	"CH",
+	"DH",
+	"BH",
+
+	"AX",		/* [D_AX] */
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+
+	"F0",		/* [D_F0] */
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+
+	"CS",		/* [D_CS] */
+	"SS",
+	"DS",
+	"ES",
+	"FS",
+	"GS",
+
+	"GDTR",		/* [D_GDTR] */
+	"IDTR",		/* [D_IDTR] */
+	"LDTR",		/* [D_LDTR] */
+	"MSW",		/* [D_MSW] */
+	"TASK",		/* [D_TASK] */
+
+	"CR0",		/* [D_CR] */
+	"CR1",
+	"CR2",
+	"CR3",
+	"CR4",
+	"CR5",
+	"CR6",
+	"CR7",
+
+	"DR0",		/* [D_DR] */
+	"DR1",
+	"DR2",
+	"DR3",
+	"DR4",
+	"DR5",
+	"DR6",
+	"DR7",
+
+	"TR0",		/* [D_TR] */
+	"TR1",
+	"TR2",
+	"TR3",
+	"TR4",
+	"TR5",
+	"TR6",
+	"TR7",
+
+	"NONE",		/* [D_NONE] */
+};
+
+int
+Rconv(Fmt *fp)
+{
+	char str[20];
+	int r;
+
+	r = va_arg(fp->args, int);
+	if(r >= D_AL && r <= D_NONE)
+		snprint(str, sizeof(str), "%s", regstr[r-D_AL]);
+	else
+		snprint(str, sizeof(str), "gok(%d)", r);
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[30], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(double); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		default:
+			if(c < 040 || c >= 0177)
+				break;	/* not portable */
+			p[-1] = c;
+			continue;
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 20 && !debug['A']) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/8l/mkfile
@@ -1,0 +1,35 @@
+<../../mkconfig
+
+TARG=8l
+
+OFILES=\
+	asm.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	list.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+	elf.$O\
+
+HFILES=\
+	l.h\
+	../8c/8.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include -I.
+
+enam.$O:	../8c/enam.c
+	$CC $CFLAGS ../8c/enam.c
+elf.$O:	../ld/elf.c
+	$CC $CFLAGS ../ld/elf.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/8l/obj.c
@@ -1,0 +1,1617 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= '8';
+char	*thestring 	= "386";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H0 -T0x40004C -D0x10000000	is garbage unix
+ *	-H1 -T0xd0 -R4			is unix coff
+ *	-H2 -T4128 -R4096		is plan9 format
+ *	-H3 -Tx -Rx			is MS-DOS .COM
+ *	-H4 -Tx -Rx			is fake MS-DOS .EXE
+ *	-H5 -T0x80100020 -R4096		is ELF
+ */
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+static int
+isobjfile(char *f)
+{
+	int n, v;
+	Biobuf *b;
+	char buf1[5], buf2[SARMAG];
+
+	b = Bopen(f, OREAD);
+	if(b == nil)
+		return 0;
+	n = Bread(b, buf1, 5);
+	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+		v = 1;	/* good enough for our purposes */
+	else{
+		Bseek(b, 0, 0);
+		n = Bread(b, buf2, SARMAG);
+		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+	}
+	Bterm(b);
+	return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, c;
+	char *a;
+	char name[LIBNAMELEN];
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	memset(debug, 0, sizeof(debug));
+	nerrors = 0;
+	outfile = "8.out";
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITTEXTP = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o': /* output to (next arg) */
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'x':	/* produce export table */
+		doexp = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1])){
+			a = ARGF();
+			if(strcmp(a, "*") == 0)
+				allexport = 1;
+			else
+				readundefs(a, SEXPORT);
+		}
+		break;
+	case 'u':	/* produce dynamically loadable module */
+		dlm = 1;
+		debug['l']++;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SIMPORT);
+		break;
+	} ARGEND
+	USED(argc);
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	a = getenv("ccroot");
+	if(a != nil && *a != '\0') {
+		if(!fileexists(a)) {
+			diag("nonexistent $ccroot: %s", a);
+			errorexit();
+		}
+	}else
+		a = "";
+	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+	addlibpath(name);
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 1;
+		if(debug['B'])
+			HEADTYPE = 2;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+
+	case 0:	/* this is garbage */
+		HEADR = 20L+56L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x40004CL;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000L;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 1:	/* is unix coff */
+		HEADR = 0xd0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xd0;
+		if(INITDAT == -1)
+			INITDAT = 0x400000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4096+32;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* MS-DOS .COM */
+		HEADR = 0;
+		if(INITTEXT == -1)
+			INITTEXT = 0x0100;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 4:	/* fake MS-DOS .EXE */
+		HEADR = 0x200;
+		if(INITTEXT == -1)
+			INITTEXT = 0x0100;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		HEADR += (INITTEXT & 0xFFFF);
+		if(debug['v'])
+			Bprint(&bso, "HEADR = 0x%ld\n", HEADR);
+		break;
+	case 5:	/* elf executable */
+		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0x80100020L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%ld -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	for(i=1; optab[i].as; i++)
+		if(i != optab[i].as) {
+			diag("phase error in optab: %d", i);
+			errorexit();
+		}
+
+	for(i=0; i<Ymax; i++)
+		ycover[i*Ymax + i] = 1;
+
+	ycover[Yi0*Ymax + Yi8] = 1;
+	ycover[Yi1*Ymax + Yi8] = 1;
+
+	ycover[Yi0*Ymax + Yi32] = 1;
+	ycover[Yi1*Ymax + Yi32] = 1;
+	ycover[Yi8*Ymax + Yi32] = 1;
+
+	ycover[Yal*Ymax + Yrb] = 1;
+	ycover[Ycl*Ymax + Yrb] = 1;
+	ycover[Yax*Ymax + Yrb] = 1;
+	ycover[Ycx*Ymax + Yrb] = 1;
+	ycover[Yrx*Ymax + Yrb] = 1;
+
+	ycover[Yax*Ymax + Yrx] = 1;
+	ycover[Ycx*Ymax + Yrx] = 1;
+
+	ycover[Yax*Ymax + Yrl] = 1;
+	ycover[Ycx*Ymax + Yrl] = 1;
+	ycover[Yrx*Ymax + Yrl] = 1;
+
+	ycover[Yf0*Ymax + Yrf] = 1;
+
+	ycover[Yal*Ymax + Ymb] = 1;
+	ycover[Ycl*Ymax + Ymb] = 1;
+	ycover[Yax*Ymax + Ymb] = 1;
+	ycover[Ycx*Ymax + Ymb] = 1;
+	ycover[Yrx*Ymax + Ymb] = 1;
+	ycover[Yrb*Ymax + Ymb] = 1;
+	ycover[Ym*Ymax + Ymb] = 1;
+
+	ycover[Yax*Ymax + Yml] = 1;
+	ycover[Ycx*Ymax + Yml] = 1;
+	ycover[Yrx*Ymax + Yml] = 1;
+	ycover[Yrl*Ymax + Yml] = 1;
+	ycover[Ym*Ymax + Yml] = 1;
+
+	for(i=0; i<D_NONE; i++) {
+		reg[i] = -1;
+		if(i >= D_AL && i <= D_BH)
+			reg[i] = (i-D_AL) & 7;
+		if(i >= D_AX && i <= D_DI)
+			reg[i] = (i-D_AX) & 7;
+		if(i >= D_F0 && i <= D_F0+7)
+			reg[i] = (i-D_F0) & 7;
+	}
+
+	zprg.link = P;
+	zprg.pcond = P;
+	zprg.back = 2;
+	zprg.as = AGOK;
+	zprg.from.type = D_NONE;
+	zprg.from.index = D_NONE;
+	zprg.from.scale = 1;
+	zprg.to = zprg.from;
+
+	pcstr = "%.6lux ";
+	nuxiinit();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	edatap = P;
+	pc = 0;
+	dtype = 4;
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		errorexit();
+	if(doexp || dlm){
+		EXPTAB = "_exporttab";
+		zerosig(EXPTAB);
+		zerosig("etext");
+		zerosig("edata");
+		zerosig("end");
+		if(dlm){
+			import();
+			HEADTYPE = 2;
+			INITTEXT = INITDAT = 0;
+			INITRND = 8;
+			INITENTRY = EXPTAB;
+		}
+		export();
+	}
+	patch();
+	follow();
+	dodata();
+	dostkoff();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	span();
+	doinit();
+	asmb();
+	undef();
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld symbols\n", nsymbol);
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+	int i;
+	char name[LIBNAMELEN];
+
+	for(i = 0; i < nlibdir; i++) {
+		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+		if(fileexists(name))
+			return libdir[i];
+	}
+	return nil;
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[LIBNAMELEN], pname[LIBNAMELEN];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	if(file[0] == '-' && file[1] == 'l') {
+		snprint(pname, sizeof(pname), "lib%s.a", file+2);
+		e = findlib(pname);
+		if(e == nil) {
+			diag("cannot find library: %s", file);
+			errorexit();
+		}
+		snprint(name, sizeof(name), "%s/%s", e, pname);
+		file = name;
+	}
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open %s: %r", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			/* need readn to read the dumps (at least) */
+			l = readn(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int c, t, i;
+	int l;
+	Sym *s;
+	Auto *u;
+
+	t = p[0];
+
+	c = 1;
+	if(t & T_INDEX) {
+		a->index = p[c];
+		a->scale = p[c+1];
+		c += 2;
+	} else {
+		a->index = D_NONE;
+		a->scale = 0;
+	}
+	a->offset = 0;
+	if(t & T_OFFSET) {
+		a->offset = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
+		c += 4;
+	}
+	a->sym = S;
+	if(t & T_SYM) {
+		a->sym = h[p[c]];
+		c++;
+	}
+	a->type = D_NONE;
+	if(t & T_FCONST) {
+		a->ieee.l = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
+		a->ieee.h = p[c+4] | (p[c+5]<<8) | (p[c+6]<<16) | (p[c+7]<<24);
+		c += 8;
+		a->type = D_FCONST;
+	} else
+	if(t & T_SCONST) {
+		for(i=0; i<NSNAME; i++)
+			a->scon[i] = p[c+i];
+		c += NSNAME;
+		a->type = D_SCONST;
+	}
+	if(t & T_TYPE) {
+		a->type = p[c];
+		c++;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+
+	t = a->type;
+	if(t != D_AUTO && t != D_PARAM)
+		return c;
+	l = a->offset;
+	for(u=curauto; u; u=u->link) {
+		if(u->asym == s)
+		if(u->type == t) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+	}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = t;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+
+	cleanname(name);
+
+	if(search){
+		p = findlib(name);
+		if(p != nil){
+			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+			name = fn2;
+		}
+	}
+
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	long ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	int v, o, r, skip;
+	Sym *h[NSYM], *s, *di;
+	ulong sig;
+	static int files;
+	static char **filen;
+	char **nfilen;
+
+	if((files&15) == 0){
+		nfilen = malloc((files+16)*sizeof(char*));
+		memmove(nfilen, filen, files*sizeof(char*));
+		free(filen);
+		filen = nfilen;
+	}
+	filen[files++] = strdup(pn);
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0] | (bloc[1] << 8);
+	if(o <= AXXX || o >= ALAST) {
+		if(o < 0)
+			goto eof;
+		diag("%s: opcode out of range %d", pn, o);
+		print("	probably not a .8 file\n");
+		errorexit();
+	}
+
+	if(o == ANAME || o == ASIGNAME) {
+		sig = 0;
+		if(o == ASIGNAME) {
+			sig = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[2];	/* type */
+		o = bloc[3];	/* sym */
+		bloc += 4;
+		c -= 4;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['S'] && r == 0)
+			sig = 1729;
+		if(sig != 0){
+			if(s->sig != 0 && s->sig != sig)
+				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+			s->sig = sig;
+			s->file = files-1;
+		}
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->line = bloc[2] | (bloc[3] << 8) | (bloc[4] << 16) | (bloc[5] << 24);
+	p->back = 2;
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(p->as) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("%s: redefinition: %s in %s",
+				pn, s->name, TNAME);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		goto loop;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->from.scale = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_ADDR;
+		p->to.index = D_EXTERN;
+		goto data;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		goto data;
+
+	case ADATA:
+	data:
+		if(edatap == P)
+			datap = p;
+		else
+			edatap->link = p;
+		edatap = p;
+		p->link = P;
+		goto loop;
+
+	case AGOK:
+		diag("%s: GOK opcode in %s", pn, TNAME);
+		pc++;
+		goto loop;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		s = p->from.sym;
+		if(s == S) {
+			diag("%s: no TEXT symbol: %P", pn, p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->from.scale & DUPOK) {
+				skip = 1;
+				goto casdef;
+			}
+			diag("%s: redefinition: %s\n%P", pn, s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->pcond = p;
+		etextp = p;
+		goto loop;
+
+	case AFMOVF:
+	case AFADDF:
+	case AFSUBF:
+	case AFSUBRF:
+	case AFMULF:
+	case AFDIVF:
+	case AFDIVRF:
+	case AFCOMF:
+	case AFCOMFP:
+		if(skip)
+			goto casdef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(&p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_EXTERN;
+				t->from.sym = s;
+				t->from.scale = 4;
+				t->to = p->from;
+				if(edatap == P)
+					datap = t;
+				else
+					edatap->link = t;
+				edatap = t;
+				t->link = P;
+			}
+			p->from.type = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = 0;
+		}
+		goto casdef;
+
+	case AFMOVD:
+	case AFADDD:
+	case AFSUBD:
+	case AFSUBRD:
+	case AFMULD:
+	case AFDIVD:
+	case AFDIVRD:
+	case AFCOMD:
+	case AFCOMDP:
+		if(skip)
+			goto casdef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee.l, p->from.ieee.h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_EXTERN;
+				t->from.sym = s;
+				t->from.scale = 8;
+				t->to = p->from;
+				if(edatap == P)
+					datap = t;
+				else
+					edatap->link = t;
+				edatap = t;
+				t->link = P;
+			}
+			p->from.type = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = 0;
+		}
+		goto casdef;
+
+	casdef:
+	default:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		goto loop;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int l, c;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	h &= 0xffffff;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l + 1);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->sig = 0;
+	hash[h] = s;
+	nsymbol++;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+Prog*
+copyp(Prog *q)
+{
+	Prog *p;
+
+	p = prg();
+	*p = *q;
+	return p;
+}
+
+Prog*
+appendp(Prog *q)
+{
+	Prog *p;
+
+	p = prg();
+	p->link = q->link;
+	q->link = p;
+	p->line = q->line;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->from.scale = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADDL;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_EXTERN;
+	q->from.sym = s;
+	q->from.scale = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				p->from.scale = 1;
+				ps2 = p;
+			}
+			if(p->from.sym == s4) {
+				p->from.scale = 1;
+				ps4 = p;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+
+			if(p->from.scale & NOPROF) {	/* dont profile */
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JMPL	profin
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = AJMP;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->pcond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = ACALL;
+			p->to.type = D_BRANCH;
+			p->pcond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JAL	profout
+			 */
+			p->as = ACALL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->pcond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		fnuxi4[i] = c;
+		fnuxi8[i] = c;
+		fnuxi8[i+4] = c+4;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+int
+find2(long l, int c)
+{
+	short *p;
+	int i;
+
+	p = (short*)&l;
+	for(i=0; i<4; i+=2) {
+		if(((*p >> 8) & 0xff) == c)
+			return i;
+		if((*p++ & 0xff) == c)
+			return i+1;
+	}
+	return 0;
+}
+
+long
+ieeedtof(Ieee *e)
+{
+	int exp;
+	long v;
+
+	if(e->h == 0)
+		return 0;
+	exp = (e->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (e->h & 0xfffffL) << 3;
+	v |= (e->l >> 29) & 0x7L;
+	if((e->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= e->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
+
+void
+undefsym(Sym *s)
+{
+	int n;
+
+	n = imports;
+	if(s->value != 0)
+		diag("value != 0 on SXREF");
+	if(n >= 1<<Rindex)
+		diag("import index %d out of range", n);
+	s->value = n<<Roffset;
+	s->type = SUNDEF;
+	imports++;
+}
+
+void
+zerosig(char *sp)
+{
+	Sym *s;
+
+	s = lookup(sp, 0);
+	s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+	int i, n;
+	Sym *s;
+	Biobuf *b;
+	char *l, buf[256], *fields[64];
+
+	if(f == nil)
+		return;
+	b = Bopen(f, OREAD);
+	if(b == nil){
+		diag("could not open %s: %r", f);
+		errorexit();
+	}
+	while((l = Brdline(b, '\n')) != nil){
+		n = Blinelen(b);
+		if(n >= sizeof(buf)){
+			diag("%s: line too long", f);
+			errorexit();
+		}
+		memmove(buf, l, n);
+		buf[n-1] = '\0';
+		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+		if(n == nelem(fields)){
+			diag("%s: bad format", f);
+			errorexit();
+		}
+		for(i = 0; i < n; i++){
+			s = lookup(fields[i], 0);
+			s->type = SXREF;
+			s->subtype = t;
+			if(t == SIMPORT)
+				nimports++;
+			else
+				nexports++;
+		}
+	}
+	Bterm(b);
+}
--- /dev/null
+++ b/utils/8l/optab.c
@@ -1,0 +1,654 @@
+#include	"l.h"
+
+uchar	ynone[] =
+{
+	Ynone,	Ynone,	Zlit,	1,
+	0
+};
+uchar	ytext[] =
+{
+	Ymb,	Yi32,	Zpseudo,1,
+	0
+};
+uchar	ynop[] =
+{
+	Ynone,	Ynone,	Zpseudo,1,
+	Ynone,	Yml,	Zpseudo,1,
+	Ynone,	Yrf,	Zpseudo,1,
+	Yml,	Ynone,	Zpseudo,1,
+	Yrf,	Ynone,	Zpseudo,1,
+	0
+};
+uchar	yxorb[] =
+{
+	Yi32,	Yal,	Zib_,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	yxorl[] =
+{
+	Yi8,	Yml,	Zibo_m,	2,
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yaddl[] =
+{
+	Yi8,	Yml,	Zibo_m,	2,
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yincb[] =
+{
+	Ynone,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yincl[] =
+{
+	Ynone,	Yrl,	Z_rp,	1,
+	Ynone,	Yml,	Zo_m,	2,
+	0
+};
+uchar	ycmpb[] =
+{
+	Yal,	Yi32,	Z_ib,	1,
+	Ymb,	Yi32,	Zm_ibo,	2,
+	Ymb,	Yrb,	Zm_r,	1,
+	Yrb,	Ymb,	Zr_m,	1,
+	0
+};
+uchar	ycmpl[] =
+{
+	Yml,	Yi8,	Zm_ibo,	2,
+	Yax,	Yi32,	Z_il,	1,
+	Yml,	Yi32,	Zm_ilo,	2,
+	Yml,	Yrl,	Zm_r,	1,
+	Yrl,	Yml,	Zr_m,	1,
+	0
+};
+uchar	yshb[] =
+{
+	Yi1,	Ymb,	Zo_m,	2,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Ycx,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yshl[] =
+{
+	Yi1,	Yml,	Zo_m,	2,
+	Yi32,	Yml,	Zibo_m,	2,
+	Ycl,	Yml,	Zo_m,	2,
+	Ycx,	Yml,	Zo_m,	2,
+	0
+};
+uchar	ytestb[] =
+{
+	Yi32,	Yal,	Zib_,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	ytestl[] =
+{
+	Yi32,	Yax,	Zil_,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	ymovb[] =
+{
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	Yi32,	Yrb,	Zib_rp,	1,
+	Yi32,	Ymb,	Zibo_m,	2,
+	0
+};
+uchar	ymovl[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	Yi0,	Yrl,	Zclr,	1+2,
+//	Yi0,	Yml,	Zibo_m,	2,	// shorter but slower AND $0,dst
+	Yi32,	Yrl,	Zil_rp,	1,
+	Yi32,	Yml,	Zilo_m,	2,
+	Yiauto,	Yrl,	Zaut_r,	2,
+	0
+};
+uchar	ym_rl[] =
+{
+	Ym,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yrl_m[] =
+{
+	Yrl,	Ym,	Zr_m,	1,
+	0
+};
+uchar	ymb_rl[] =
+{
+	Ymb,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yml_rl[] =
+{
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	yrl_ml[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	0
+};
+uchar	yml_mb[] =
+{
+	Yrb,	Ymb,	Zr_m,	1,
+	Ymb,	Yrb,	Zm_r,	1,
+	0
+};
+uchar	yml_ml[] =
+{
+	Yrl,	Yml,	Zr_m,	1,
+	Yml,	Yrl,	Zm_r,	1,
+	0
+};
+uchar	ydivl[] =
+{
+	Yml,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ydivb[] =
+{
+	Ymb,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	yimul[] =
+{
+	Yml,	Ynone,	Zm_o,	2,
+	Yi8,	Yrl,	Zib_rr,	1,
+	Yi32,	Yrl,	Zil_rr,	1,
+	0
+};
+uchar	ybyte[] =
+{
+	Yi32,	Ynone,	Zbyte,	1,
+	0
+};
+uchar	yin[] =
+{
+	Yi32,	Ynone,	Zib_,	1,
+	Ynone,	Ynone,	Zlit,	1,
+	0
+};
+uchar	yint[] =
+{
+	Yi32,	Ynone,	Zib_,	1,
+	0
+};
+uchar	ypushl[] =
+{
+	Yrl,	Ynone,	Zrp_,	1,
+	Ym,	Ynone,	Zm_o,	2,
+	Yi8,	Ynone,	Zib_,	1,
+	Yi32,	Ynone,	Zil_,	1,
+	0
+};
+uchar	ypopl[] =
+{
+	Ynone,	Yrl,	Z_rp,	1,
+	Ynone,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yscond[] =
+{
+	Ynone,	Ymb,	Zo_m,	2,
+	0
+};
+uchar	yjcond[] =
+{
+	Ynone,	Ybr,	Zbr,	1,
+	0
+};
+uchar	yloop[] =
+{
+	Ynone,	Ybr,	Zloop,	1,
+	0
+};
+uchar	ycall[] =
+{
+	Ynone,	Yml,	Zo_m,	2,
+	Ynone,	Ybr,	Zcall,	1,
+	0
+};
+uchar	yjmp[] =
+{
+	Ynone,	Yml,	Zo_m,	2,
+	Ynone,	Ybr,	Zjmp,	1,
+	0
+};
+
+uchar	yfmvd[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yf0,	Ym,	Zo_m,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfmvdp[] =
+{
+	Yf0,	Ym,	Zo_m,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfmvf[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yf0,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yfmvx[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	0
+};
+uchar	yfmvp[] =
+{
+	Yf0,	Ym,	Zo_m,	2,
+	0
+};
+uchar	yfadd[] =
+{
+	Ym,	Yf0,	Zm_o,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfaddp[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,
+	0
+};
+uchar	yfxch[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,
+	Yrf,	Yf0,	Zm_o,	2,
+	0
+};
+uchar	ycompp[] =
+{
+	Yf0,	Yrf,	Zo_m,	2,	/* botch is really f0,f1 */
+	0
+};
+uchar	ystsw[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ynone,	Yax,	Zlit,	1,
+	0
+};
+uchar	ystcw[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ym,	Ynone,	Zm_o,	2,
+	0
+};
+uchar	ysvrs[] =
+{
+	Ynone,	Ym,	Zo_m,	2,
+	Ym,	Ynone,	Zm_o,	2,
+	0
+};
+
+Optab optab[] =
+/*	as, ytab, andproto, opcode */
+{
+	{ AXXX },
+	{ AAAA,		ynone,	Px, 0x37 },
+	{ AAAD,		ynone,	Px, 0xd5,0x0a },
+	{ AAAM,		ynone,	Px, 0xd4,0x0a },
+	{ AAAS,		ynone,	Px, 0x3f },
+	{ AADCB,	yxorb,	Pb, 0x14,0x80,(02),0x10,0x10 },
+	{ AADCL,	yxorl,	Px, 0x83,(02),0x15,0x81,(02),0x11,0x13 },
+	{ AADCW,	yxorl,	Pe, 0x83,(02),0x15,0x81,(02),0x11,0x13 },
+	{ AADDB,	yxorb,	Px, 0x04,0x80,(00),0x00,0x02 },
+	{ AADDL,	yaddl,	Px, 0x83,(00),0x05,0x81,(00),0x01,0x03 },
+	{ AADDW,	yaddl,	Pe, 0x83,(00),0x05,0x81,(00),0x01,0x03 },
+	{ AADJSP },
+	{ AANDB,	yxorb,	Pb, 0x24,0x80,(04),0x20,0x22 },
+	{ AANDL,	yxorl,	Px, 0x83,(04),0x25,0x81,(04),0x21,0x23 },
+	{ AANDW,	yxorl,	Pe, 0x83,(04),0x25,0x81,(04),0x21,0x23 },
+	{ AARPL,	yrl_ml,	Px, 0x63 },
+	{ ABOUNDL,	yrl_m,	Px, 0x62 },
+	{ ABOUNDW,	yrl_m,	Pe, 0x62 },
+	{ ABSFL,	yml_rl,	Pm, 0xbc },
+	{ ABSFW,	yml_rl,	Pq, 0xbc },
+	{ ABSRL,	yml_rl,	Pm, 0xbd },
+	{ ABSRW,	yml_rl,	Pq, 0xbd },
+	{ ABTL,		yml_rl,	Pm, 0xa3 },
+	{ ABTW,		yml_rl,	Pq, 0xa3 },
+	{ ABTCL,	yml_rl,	Pm, 0xbb },
+	{ ABTCW,	yml_rl,	Pq, 0xbb },
+	{ ABTRL,	yml_rl,	Pm, 0xb3 },
+	{ ABTRW,	yml_rl,	Pq, 0xb3 },
+	{ ABTSL,	yml_rl,	Pm, 0xab },
+	{ ABTSW,	yml_rl,	Pq, 0xab },
+	{ ABYTE,	ybyte,	Px, 1 },
+	{ ACALL,	ycall,	Px, 0xff,(02),0xe8 },
+	{ ACLC,		ynone,	Px, 0xf8 },
+	{ ACLD,		ynone,	Px, 0xfc },
+	{ ACLI,		ynone,	Px, 0xfa },
+	{ ACLTS,	ynone,	Pm, 0x06 },
+	{ ACMC,		ynone,	Px, 0xf5 },
+	{ ACMPB,	ycmpb,	Pb, 0x3c,0x80,(07),0x38,0x3a },
+	{ ACMPL,	ycmpl,	Px, 0x83,(07),0x3d,0x81,(07),0x39,0x3b },
+	{ ACMPW,	ycmpl,	Pe, 0x83,(07),0x3d,0x81,(07),0x39,0x3b },
+	{ ACMPSB,	ynone,	Pb, 0xa6 },
+	{ ACMPSL,	ynone,	Px, 0xa7 },
+	{ ACMPSW,	ynone,	Pe, 0xa7 },
+	{ ADAA,		ynone,	Px, 0x27 },
+	{ ADAS,		ynone,	Px, 0x2f },
+	{ ADATA },
+	{ ADECB,	yincb,	Pb, 0xfe,(01) },
+	{ ADECL,	yincl,	Px, 0x48,0xff,(01) },
+	{ ADECW,	yincl,	Pe, 0x48,0xff,(01) },
+	{ ADIVB,	ydivb,	Pb, 0xf6,(06) },
+	{ ADIVL,	ydivl,	Px, 0xf7,(06) },
+	{ ADIVW,	ydivl,	Pe, 0xf7,(06) },
+	{ AENTER },				/* botch */
+	{ AGLOBL },
+	{ AGOK },
+	{ AHISTORY },
+	{ AHLT,		ynone,	Px, 0xf4 },
+	{ AIDIVB,	ydivb,	Pb, 0xf6,(07) },
+	{ AIDIVL,	ydivl,	Px, 0xf7,(07) },
+	{ AIDIVW,	ydivl,	Pe, 0xf7,(07) },
+	{ AIMULB,	ydivb,	Pb, 0xf6,(05) },
+	{ AIMULL,	yimul,	Px, 0xf7,(05),0x6b,0x69 },
+	{ AIMULW,	yimul,	Pe, 0xf7,(05),0x6b,0x69 },
+	{ AINB,		yin,	Pb, 0xe4,0xec },
+	{ AINL,		yin,	Px, 0xe5,0xed },
+	{ AINW,		yin,	Pe, 0xe5,0xed },
+	{ AINCB,	yincb,	Pb, 0xfe,(00) },
+	{ AINCL,	yincl,	Px, 0x40,0xff,(00) },
+	{ AINCW,	yincl,	Pe, 0x40,0xff,(00) },
+	{ AINSB,	ynone,	Pb, 0x6c },
+	{ AINSL,	ynone,	Px, 0x6d },
+	{ AINSW,	ynone,	Pe, 0x6d },
+	{ AINT,		yint,	Px, 0xcd },
+	{ AINTO,	ynone,	Px, 0xce },
+	{ AIRETL,	ynone,	Px, 0xcf },
+	{ AIRETW,	ynone,	Pe, 0xcf },
+	{ AJCC,		yjcond,	Px, 0x73,0x83,(00) },
+	{ AJCS,		yjcond,	Px, 0x72,0x82 },
+	{ AJCXZ,	yloop,	Px, 0xe3 },
+	{ AJEQ,		yjcond,	Px, 0x74,0x84 },
+	{ AJGE,		yjcond,	Px, 0x7d,0x8d },
+	{ AJGT,		yjcond,	Px, 0x7f,0x8f },
+	{ AJHI,		yjcond,	Px, 0x77,0x87 },
+	{ AJLE,		yjcond,	Px, 0x7e,0x8e },
+	{ AJLS,		yjcond,	Px, 0x76,0x86 },
+	{ AJLT,		yjcond,	Px, 0x7c,0x8c },
+	{ AJMI,		yjcond,	Px, 0x78,0x88 },
+	{ AJMP,		yjmp,	Px, 0xff,(04),0xeb,0xe9 },
+	{ AJNE,		yjcond,	Px, 0x75,0x85 },
+	{ AJOC,		yjcond,	Px, 0x71,0x81,(00) },
+	{ AJOS,		yjcond,	Px, 0x70,0x80,(00) },
+	{ AJPC,		yjcond,	Px, 0x7b,0x8b },
+	{ AJPL,		yjcond,	Px, 0x79,0x89 },
+	{ AJPS,		yjcond,	Px, 0x7a,0x8a },
+	{ ALAHF,	ynone,	Px, 0x9f },
+	{ ALARL,	yml_rl,	Pm, 0x02 },
+	{ ALARW,	yml_rl,	Pq, 0x02 },
+	{ ALEAL,	ym_rl,	Px, 0x8d },
+	{ ALEAW,	ym_rl,	Pe, 0x8d },
+	{ ALEAVEL,	ynone,	Px, 0xc9 },
+	{ ALEAVEW,	ynone,	Pe, 0xc9 },
+	{ ALOCK,	ynone,	Px, 0xf0 },
+	{ ALODSB,	ynone,	Pb, 0xac },
+	{ ALODSL,	ynone,	Px, 0xad },
+	{ ALODSW,	ynone,	Pe, 0xad },
+	{ ALONG,	ybyte,	Px, 4 },
+	{ ALOOP,	yloop,	Px, 0xe2 },
+	{ ALOOPEQ,	yloop,	Px, 0xe1 },
+	{ ALOOPNE,	yloop,	Px, 0xe0 },
+	{ ALSLL,	yml_rl,	Pm, 0x03  },
+	{ ALSLW,	yml_rl,	Pq, 0x03  },
+	{ AMOVB,	ymovb,	Pb, 0x88,0x8a,0xb0,0xc6,(00) },
+	{ AMOVL,	ymovl,	Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00) },
+	{ AMOVW,	ymovl,	Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00) },
+	{ AMOVBLSX,	ymb_rl,	Pm, 0xbe },
+	{ AMOVBLZX,	ymb_rl,	Pm, 0xb6 },
+	{ AMOVBWSX,	ymb_rl,	Pq, 0xbe },
+	{ AMOVBWZX,	ymb_rl,	Pq, 0xb6 },
+	{ AMOVWLSX,	yml_rl,	Pm, 0xbf },
+	{ AMOVWLZX,	yml_rl,	Pm, 0xb7 },
+	{ AMOVSB,	ynone,	Pb, 0xa4 },
+	{ AMOVSL,	ynone,	Px, 0xa5 },
+	{ AMOVSW,	ynone,	Pe, 0xa5 },
+	{ AMULB,	ydivb,	Pb, 0xf6,(04) },
+	{ AMULL,	ydivl,	Px, 0xf7,(04) },
+	{ AMULW,	ydivl,	Pe, 0xf7,(04) },
+	{ ANAME },
+	{ ANEGB,	yscond,	Px, 0xf6,(03) },
+	{ ANEGL,	yscond,	Px, 0xf7,(03) },
+	{ ANEGW,	yscond,	Pe, 0xf7,(03) },
+	{ ANOP,		ynop,	Px,0,0 },
+	{ ANOTB,	yscond,	Px, 0xf6,(02) },
+	{ ANOTL,	yscond,	Px, 0xf7,(02) },
+	{ ANOTW,	yscond,	Pe, 0xf7,(02) },
+	{ AORB,		yxorb,	Pb, 0x0c,0x80,(01),0x08,0x0a },
+	{ AORL,		yxorl,	Px, 0x83,(01),0x0d,0x81,(01),0x09,0x0b },
+	{ AORW,		yxorl,	Pe, 0x83,(01),0x0d,0x81,(01),0x09,0x0b },
+	{ AOUTB,	yin,	Pb, 0xe6,0xee },
+	{ AOUTL,	yin,	Px, 0xe7,0xef },
+	{ AOUTW,	yin,	Pe, 0xe7,0xef },
+	{ AOUTSB,	ynone,	Pb, 0x6e },
+	{ AOUTSL,	ynone,	Px, 0x6f },
+	{ AOUTSW,	ynone,	Pe, 0x6f },
+	{ APOPAL,	ynone,	Px, 0x61 },
+	{ APOPAW,	ynone,	Pe, 0x61 },
+	{ APOPFL,	ynone,	Px, 0x9d },
+	{ APOPFW,	ynone,	Pe, 0x9d },
+	{ APOPL,	ypopl,	Px, 0x58,0x8f,(00) },
+	{ APOPW,	ypopl,	Pe, 0x58,0x8f,(00) },
+	{ APUSHAL,	ynone,	Px, 0x60 },
+	{ APUSHAW,	ynone,	Pe, 0x60 },
+	{ APUSHFL,	ynone,	Px, 0x9c },
+	{ APUSHFW,	ynone,	Pe, 0x9c },
+	{ APUSHL,	ypushl,	Px, 0x50,0xff,(06),0x6a,0x68 },
+	{ APUSHW,	ypushl,	Pe, 0x50,0xff,(06),0x6a,0x68 },
+	{ ARCLB,	yshb,	Pb, 0xd0,(02),0xc0,(02),0xd2,(02) },
+	{ ARCLL,	yshl,	Px, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) },
+	{ ARCLW,	yshl,	Pe, 0xd1,(02),0xc1,(02),0xd3,(02),0xd3,(02) },
+	{ ARCRB,	yshb,	Pb, 0xd0,(03),0xc0,(03),0xd2,(03) },
+	{ ARCRL,	yshl,	Px, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) },
+	{ ARCRW,	yshl,	Pe, 0xd1,(03),0xc1,(03),0xd3,(03),0xd3,(03) },
+	{ AREP,		ynone,	Px, 0xf3 },
+	{ AREPN,	ynone,	Px, 0xf2 },
+	{ ARET,		ynone,	Px, 0xc3 },
+	{ AROLB,	yshb,	Pb, 0xd0,(00),0xc0,(00),0xd2,(00) },
+	{ AROLL,	yshl,	Px, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) },
+	{ AROLW,	yshl,	Pe, 0xd1,(00),0xc1,(00),0xd3,(00),0xd3,(00) },
+	{ ARORB,	yshb,	Pb, 0xd0,(01),0xc0,(01),0xd2,(01) },
+	{ ARORL,	yshl,	Px, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) },
+	{ ARORW,	yshl,	Pe, 0xd1,(01),0xc1,(01),0xd3,(01),0xd3,(01) },
+	{ ASAHF,	ynone,	Px, 0x9e },
+	{ ASALB,	yshb,	Pb, 0xd0,(04),0xc0,(04),0xd2,(04) },
+	{ ASALL,	yshl,	Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASALW,	yshl,	Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASARB,	yshb,	Pb, 0xd0,(07),0xc0,(07),0xd2,(07) },
+	{ ASARL,	yshl,	Px, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) },
+	{ ASARW,	yshl,	Pe, 0xd1,(07),0xc1,(07),0xd3,(07),0xd3,(07) },
+	{ ASBBB,	yxorb,	Pb, 0x1c,0x80,(03),0x18,0x1a },
+	{ ASBBL,	yxorl,	Px, 0x83,(03),0x1d,0x81,(03),0x19,0x1b },
+	{ ASBBW,	yxorl,	Pe, 0x83,(03),0x1d,0x81,(03),0x19,0x1b },
+	{ ASCASB,	ynone,	Pb, 0xae },
+	{ ASCASL,	ynone,	Px, 0xaf },
+	{ ASCASW,	ynone,	Pe, 0xaf },
+	{ ASETCC,	yscond,	Pm, 0x93,(00) },
+	{ ASETCS,	yscond,	Pm, 0x92,(00) },
+	{ ASETEQ,	yscond,	Pm, 0x94,(00) },
+	{ ASETGE,	yscond,	Pm, 0x9d,(00) },
+	{ ASETGT,	yscond,	Pm, 0x9f,(00) },
+	{ ASETHI,	yscond,	Pm, 0x97,(00) },
+	{ ASETLE,	yscond,	Pm, 0x9e,(00) },
+	{ ASETLS,	yscond,	Pm, 0x96,(00) },
+	{ ASETLT,	yscond,	Pm, 0x9c,(00) },
+	{ ASETMI,	yscond,	Pm, 0x98,(00) },
+	{ ASETNE,	yscond,	Pm, 0x95,(00) },
+	{ ASETOC,	yscond,	Pm, 0x91,(00) },
+	{ ASETOS,	yscond,	Pm, 0x90,(00) },
+	{ ASETPC,	yscond,	Pm, 0x96,(00) },
+	{ ASETPL,	yscond,	Pm, 0x99,(00) },
+	{ ASETPS,	yscond,	Pm, 0x9a,(00) },
+	{ ACDQ,		ynone,	Px, 0x99 },
+	{ ACWD,		ynone,	Pe, 0x99 },
+	{ ASHLB,	yshb,	Pb, 0xd0,(04),0xc0,(04),0xd2,(04) },
+	{ ASHLL,	yshl,	Px, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASHLW,	yshl,	Pe, 0xd1,(04),0xc1,(04),0xd3,(04),0xd3,(04) },
+	{ ASHRB,	yshb,	Pb, 0xd0,(05),0xc0,(05),0xd2,(05) },
+	{ ASHRL,	yshl,	Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) },
+	{ ASHRW,	yshl,	Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) },
+	{ ASTC,		ynone,	Px, 0xf9 },
+	{ ASTD,		ynone,	Px, 0xfd },
+	{ ASTI,		ynone,	Px, 0xfb },
+	{ ASTOSB,	ynone,	Pb, 0xaa },
+	{ ASTOSL,	ynone,	Px, 0xab },
+	{ ASTOSW,	ynone,	Pe, 0xab },
+	{ ASUBB,	yxorb,	Pb, 0x2c,0x80,(05),0x28,0x2a },
+	{ ASUBL,	yaddl,	Px, 0x83,(05),0x2d,0x81,(05),0x29,0x2b },
+	{ ASUBW,	yaddl,	Pe, 0x83,(05),0x2d,0x81,(05),0x29,0x2b },
+	{ ASYSCALL,	ynone,	Px, 0xcd,100 },
+	{ ATESTB,	ytestb,	Pb, 0xa8,0xf6,(00),0x84,0x84 },
+	{ ATESTL,	ytestl,	Px, 0xa9,0xf7,(00),0x85,0x85 },
+	{ ATESTW,	ytestl,	Pe, 0xa9,0xf7,(00),0x85,0x85 },
+	{ ATEXT,	ytext,	Px },
+	{ AVERR,	ydivl,	Pm, 0x00,(04) },
+	{ AVERW,	ydivl,	Pm, 0x00,(05) },
+	{ AWAIT,	ynone,	Px, 0x9b },
+	{ AWORD,	ybyte,	Px, 2 },
+	{ AXCHGB,	yml_mb,	Pb, 0x86,0x86 },
+	{ AXCHGL,	yml_ml,	Px, 0x87,0x87 },
+	{ AXCHGW,	yml_ml,	Pe, 0x87,0x87 },
+	{ AXLAT,	ynone,	Px, 0xd7 },
+	{ AXORB,	yxorb,	Pb, 0x34,0x80,(06),0x30,0x32 },
+	{ AXORL,	yxorl,	Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
+	{ AXORW,	yxorl,	Pe, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
+
+	{ AFMOVB,	yfmvx,	Px, 0xdf,(04) },
+	{ AFMOVBP,	yfmvp,	Px, 0xdf,(06) },
+	{ AFMOVD,	yfmvd,	Px, 0xdd,(00),0xdd,(02),0xd9,(00),0xdd,(02) },
+	{ AFMOVDP,	yfmvdp,	Px, 0xdd,(03),0xdd,(03) },
+	{ AFMOVF,	yfmvf,	Px, 0xd9,(00),0xd9,(02) },
+	{ AFMOVFP,	yfmvp,	Px, 0xd9,(03) },
+	{ AFMOVL,	yfmvf,	Px, 0xdb,(00),0xdb,(02) },
+	{ AFMOVLP,	yfmvp,	Px, 0xdb,(03) },
+	{ AFMOVV,	yfmvx,	Px, 0xdf,(05) },
+	{ AFMOVVP,	yfmvp,	Px, 0xdf,(07) },
+	{ AFMOVW,	yfmvf,	Px, 0xdf,(00),0xdf,(02) },
+	{ AFMOVWP,	yfmvp,	Px, 0xdf,(03) },
+	{ AFMOVX,	yfmvx,	Px, 0xdb,(05) },
+	{ AFMOVXP,	yfmvp,	Px, 0xdb,(07) },
+
+	{ AFCOMB },
+	{ AFCOMBP },
+	{ AFCOMD,	yfadd,	Px, 0xdc,(02),0xd8,(02),0xdc,(02) },	/* botch */
+	{ AFCOMDP,	yfadd,	Px, 0xdc,(03),0xd8,(03),0xdc,(03) },	/* botch */
+	{ AFCOMDPP,	ycompp,	Px, 0xde,(03) },
+	{ AFCOMF,	yfmvx,	Px, 0xd8,(02) },
+	{ AFCOMFP,	yfmvx,	Px, 0xd8,(03) },
+	{ AFCOML,	yfmvx,	Px, 0xda,(02) },
+	{ AFCOMLP,	yfmvx,	Px, 0xda,(03) },
+	{ AFCOMW,	yfmvx,	Px, 0xde,(02) },
+	{ AFCOMWP,	yfmvx,	Px, 0xde,(03) },
+
+	{ AFUCOM,	ycompp,	Px, 0xdd,(04) },
+	{ AFUCOMP,	ycompp, Px, 0xdd,(05) },
+	{ AFUCOMPP,	ycompp,	Px, 0xda,(13) },
+
+	{ AFADDDP,	yfaddp,	Px, 0xde,(00) },
+	{ AFADDW,	yfmvx,	Px, 0xde,(00) },
+	{ AFADDL,	yfmvx,	Px, 0xda,(00) },
+	{ AFADDF,	yfmvx,	Px, 0xd8,(00) },
+	{ AFADDD,	yfadd,	Px, 0xdc,(00),0xd8,(00),0xdc,(00) },
+
+	{ AFMULDP,	yfaddp,	Px, 0xde,(01) },
+	{ AFMULW,	yfmvx,	Px, 0xde,(01) },
+	{ AFMULL,	yfmvx,	Px, 0xda,(01) },
+	{ AFMULF,	yfmvx,	Px, 0xd8,(01) },
+	{ AFMULD,	yfadd,	Px, 0xdc,(01),0xd8,(01),0xdc,(01) },
+
+	{ AFSUBDP,	yfaddp,	Px, 0xde,(05) },
+	{ AFSUBW,	yfmvx,	Px, 0xde,(04) },
+	{ AFSUBL,	yfmvx,	Px, 0xda,(04) },
+	{ AFSUBF,	yfmvx,	Px, 0xd8,(04) },
+	{ AFSUBD,	yfadd,	Px, 0xdc,(04),0xd8,(04),0xdc,(05) },
+
+	{ AFSUBRDP,	yfaddp,	Px, 0xde,(04) },
+	{ AFSUBRW,	yfmvx,	Px, 0xde,(05) },
+	{ AFSUBRL,	yfmvx,	Px, 0xda,(05) },
+	{ AFSUBRF,	yfmvx,	Px, 0xd8,(05) },
+	{ AFSUBRD,	yfadd,	Px, 0xdc,(05),0xd8,(05),0xdc,(04) },
+
+	{ AFDIVDP,	yfaddp,	Px, 0xde,(07) },
+	{ AFDIVW,	yfmvx,	Px, 0xde,(06) },
+	{ AFDIVL,	yfmvx,	Px, 0xda,(06) },
+	{ AFDIVF,	yfmvx,	Px, 0xd8,(06) },
+	{ AFDIVD,	yfadd,	Px, 0xdc,(06),0xd8,(06),0xdc,(07) },
+
+	{ AFDIVRDP,	yfaddp,	Px, 0xde,(06) },
+	{ AFDIVRW,	yfmvx,	Px, 0xde,(07) },
+	{ AFDIVRL,	yfmvx,	Px, 0xda,(07) },
+	{ AFDIVRF,	yfmvx,	Px, 0xd8,(07) },
+	{ AFDIVRD,	yfadd,	Px, 0xdc,(07),0xd8,(07),0xdc,(06) },
+
+	{ AFXCHD,	yfxch,	Px, 0xd9,(01),0xd9,(01) },
+	{ AFFREE },
+	{ AFLDCW,	ystcw,	Px, 0xd9,(05),0xd9,(05) },
+	{ AFLDENV,	ystcw,	Px, 0xd9,(04),0xd9,(04) },
+	{ AFRSTOR,	ysvrs,	Px, 0xdd,(04),0xdd,(04) },
+	{ AFSAVE,	ysvrs,	Px, 0xdd,(06),0xdd,(06) },
+	{ AFSTCW,	ystcw,	Px, 0xd9,(07),0xd9,(07) },
+	{ AFSTENV,	ystcw,	Px, 0xd9,(06),0xd9,(06) },
+	{ AFSTSW,	ystsw,	Px, 0xdd,(07),0xdf,0xe0 },
+	{ AF2XM1,	ynone,	Px, 0xd9, 0xf0 },
+	{ AFABS,	ynone,	Px, 0xd9, 0xe1 },
+	{ AFCHS,	ynone,	Px, 0xd9, 0xe0 },
+	{ AFCLEX,	ynone,	Px, 0xdb, 0xe2 },
+	{ AFCOS,	ynone,	Px, 0xd9, 0xff },
+	{ AFDECSTP,	ynone,	Px, 0xd9, 0xf6 },
+	{ AFINCSTP,	ynone,	Px, 0xd9, 0xf7 },
+	{ AFINIT,	ynone,	Px, 0xdb, 0xe3 },
+	{ AFLD1,	ynone,	Px, 0xd9, 0xe8 },
+	{ AFLDL2E,	ynone,	Px, 0xd9, 0xea },
+	{ AFLDL2T,	ynone,	Px, 0xd9, 0xe9 },
+	{ AFLDLG2,	ynone,	Px, 0xd9, 0xec },
+	{ AFLDLN2,	ynone,	Px, 0xd9, 0xed },
+	{ AFLDPI,	ynone,	Px, 0xd9, 0xeb },
+	{ AFLDZ,	ynone,	Px, 0xd9, 0xee },
+	{ AFNOP,	ynone,	Px, 0xd9, 0xd0 },
+	{ AFPATAN,	ynone,	Px, 0xd9, 0xf3 },
+	{ AFPREM,	ynone,	Px, 0xd9, 0xf8 },
+	{ AFPREM1,	ynone,	Px, 0xd9, 0xf5 },
+	{ AFPTAN,	ynone,	Px, 0xd9, 0xf2 },
+	{ AFRNDINT,	ynone,	Px, 0xd9, 0xfc },
+	{ AFSCALE,	ynone,	Px, 0xd9, 0xfd },
+	{ AFSIN,	ynone,	Px, 0xd9, 0xfe },
+	{ AFSINCOS,	ynone,	Px, 0xd9, 0xfb },
+	{ AFSQRT,	ynone,	Px, 0xd9, 0xfa },
+	{ AFTST,	ynone,	Px, 0xd9, 0xe4 },
+	{ AFXAM,	ynone,	Px, 0xd9, 0xe5 },
+	{ AFXTRACT,	ynone,	Px, 0xd9, 0xf4 },
+	{ AFYL2X,	ynone,	Px, 0xd9, 0xf1 },
+	{ AFYL2XP1,	ynone,	Px, 0xd9, 0xf9 },
+	{ AEND },
+	0
+};
--- /dev/null
+++ b/utils/8l/pass.c
@@ -1,0 +1,767 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i;
+	Sym *s;
+	Prog *p;
+	long t, u;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		t = p->from.offset + p->width;
+		if(t > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+	}
+	/* allocate small guys */
+	datsize = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SDATA)
+		if(s->type != SBSS)
+			continue;
+		t = s->value;
+		if(t == 0) {
+			diag("%s: no size", s->name);
+			t = 1;
+		}
+		t = rnd(t, 4);;
+		s->value = t;
+		if(t > MINSIZ)
+			continue;
+		s->value = datsize;
+		datsize += t;
+		s->type = SDATA1;
+	}
+
+	/* allocate the rest of the data */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SDATA) {
+			if(s->type == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		t = s->value;
+		s->value = datsize;
+		datsize += t;
+	}
+
+	if(debug['j']) {
+		/*
+		 * pad data with bss that fits up to next
+		 * 8k boundary, then push data to 8k
+		 */
+		u = rnd(datsize, 8192);
+		u -= datsize;
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SBSS)
+				continue;
+			t = s->value;
+			if(t > u)
+				continue;
+			u -= t;
+			s->value = datsize;
+			s->type = SDATA;
+			datsize += t;
+		}
+		datsize += u;
+	}
+
+	/* now the bss */
+	bsssize = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		t = s->value;
+		s->value = bsssize + datsize;
+		bsssize += t;
+	}
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SBSS, datsize);
+	xdefine("end", SBSS, bsssize + datsize);
+	/* etext is defined in span.c */
+}
+
+Prog*
+brchain(Prog *p)
+{
+	int i;
+
+	for(i=0; i<20; i++) {
+		if(p == P || p->as != AJMP)
+			return p;
+		p = p->pcond;
+	}
+	return P;
+}
+
+void
+follow(void)
+{
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+	lastp->link = P;
+	firstp = firstp->link;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q;
+	int i;
+	enum as a;
+
+loop:
+	if(p == P)
+		return;
+	if(p->as == ATEXT)
+		curtext = p;
+	if(p->as == AJMP)
+	if((q = p->pcond) != P) {
+		p->mark = 1;
+		p = q;
+		if(p->mark == 0)
+			goto loop;
+	}
+	if(p->mark) {
+		/* copy up to 4 instructions to avoid branch */
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == P)
+				break;
+			if(q == lastp)
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			switch(a) {
+			case AJMP:
+			case ARET:
+			case AIRETL:
+
+			case APUSHL:
+			case APUSHFL:
+			case APUSHW:
+			case APUSHFW:
+			case APOPL:
+			case APOPFL:
+			case APOPW:
+			case APOPFW:
+				goto brk;
+			}
+			if(q->pcond == P || q->pcond->mark)
+				continue;
+			if(a == ACALL || a == ALOOP)
+				continue;
+			for(;;) {
+				if(p->as == ANOP) {
+					p = p->link;
+					continue;
+				}
+				q = copyp(p);
+				p = p->link;
+				q->mark = 1;
+				lastp->link = q;
+				lastp = q;
+				if(q->as != a || q->pcond == P || q->pcond->mark)
+					continue;
+
+				q->as = relinv(q->as);
+				p = q->pcond;
+				q->pcond = q->link;
+				q->link = p;
+				xfol(q->link);
+				p = q->link;
+				if(p->mark)
+					return;
+				goto loop;
+			}
+		} /* */
+	brk:;
+		q = prg();
+		q->as = AJMP;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->pcond = p;
+		p = q;
+	}
+	p->mark = 1;
+	lastp->link = p;
+	lastp = p;
+	a = p->as;
+	if(a == AJMP || a == ARET || a == AIRETL)
+		return;
+	if(p->pcond != P)
+	if(a != ACALL) {
+		q = brchain(p->link);
+		if(q != P && q->mark)
+		if(a != ALOOP) {
+			p->as = relinv(a);
+			p->link = p->pcond;
+			p->pcond = q;
+		}
+		xfol(p->link);
+		q = brchain(p->pcond);
+		if(q->mark) {
+			p->pcond = q;
+			return;
+		}
+		p = q;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+int
+relinv(int a)
+{
+
+	switch(a) {
+	case AJEQ:	return AJNE;
+	case AJNE:	return AJEQ;
+	case AJLE:	return AJGT;
+	case AJLS:	return AJHI;
+	case AJLT:	return AJGE;
+	case AJMI:	return AJPL;
+	case AJGE:	return AJLT;
+	case AJPL:	return AJMI;
+	case AJGT:	return AJLE;
+	case AJHI:	return AJLS;
+	case AJCS:	return AJCC;
+	case AJCC:	return AJCS;
+	case AJPS:	return AJPC;
+	case AJPC:	return AJPS;
+	case AJOS:	return AJOC;
+	case AJOC:	return AJOS;
+	}
+	diag("unknown relation: %s in %s", anames[a], TNAME);
+	return a;
+}
+
+void
+doinit(void)
+{
+	Sym *s;
+	Prog *p;
+	int x;
+
+	for(p = datap; p != P; p = p->link) {
+		x = p->to.type;
+		if(x != D_EXTERN && x != D_STATIC)
+			continue;
+		s = p->to.sym;
+		if(s->type == 0 || s->type == SXREF)
+			diag("undefined %s initializer of %s",
+				s->name, p->from.sym->name);
+		p->to.offset += s->value;
+		p->to.type = D_CONST;
+		if(s->type == SDATA || s->type == SBSS)
+			p->to.offset += INITDAT;
+	}
+}
+
+void
+patch(void)
+{
+	long c;
+	Prog *p, *q;
+	Sym *s;
+	long vexit;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f mkfwd\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->as == ACALL || p->as == ARET) {
+			s = p->to.sym;
+			if(s) {
+				if(debug['c'])
+					Bprint(&bso, "%s calls %s\n", TNAME, s->name);
+				switch(s->type) {
+				default:
+					/* diag prints TNAME first */
+					diag("undefined: %s", s->name);
+					s->type = STEXT;
+					s->value = vexit;
+					break;	/* or fall through to set offset? */
+				case STEXT:
+					p->to.offset = s->value;
+					break;
+				case SUNDEF:
+					p->pcond = UP;
+					p->to.offset = 0;
+					break;
+				}
+				p->to.type = D_BRANCH;
+			}
+		}
+		if(p->to.type != D_BRANCH || p->pcond == UP)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range in %s\n%P", TNAME, p);
+			p->to.type = D_NONE;
+		}
+		p->pcond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		p->mark = 0;	/* initialization for follow */
+		if(p->pcond != P && p->pcond != UP) {
+			p->pcond = brloop(p->pcond);
+			if(p->pcond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->pcond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	int i;
+	long dwn[LOG], cnt[LOG];
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	int c;
+	Prog *q;
+
+	c = 0;
+	for(q = p; q != P; q = q->pcond) {
+		if(q->as != AJMP)
+			break;
+		c++;
+		if(c >= 5000)
+			return P;
+	}
+	return q;
+}
+
+void
+dostkoff(void)
+{
+	Prog *p, *q;
+	long autoffset, deltasp;
+	int a, f, curframe, curbecome, maxbecome;
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == (D_INDIR+D_SP))
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			curtext = p;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+			break;
+		}
+	}
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case ACALL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				f = maxbecome - curtext->from.sym->frame;
+				if(f <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += f;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, f);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	autoffset = 0;
+	deltasp = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autoffset = p->to.offset;
+			if(autoffset < 0)
+				autoffset = 0;
+			if(autoffset) {
+				p = appendp(p);
+				p->as = AADJSP;
+				p->from.type = D_CONST;
+				p->from.offset = autoffset;
+			}
+			deltasp = autoffset;
+		}
+		a = p->from.type;
+		if(a == D_AUTO)
+			p->from.offset += deltasp;
+		if(a == D_PARAM)
+			p->from.offset += deltasp + 4;
+		a = p->to.type;
+		if(a == D_AUTO)
+			p->to.offset += deltasp;
+		if(a == D_PARAM)
+			p->to.offset += deltasp + 4;
+
+		switch(p->as) {
+		default:
+			continue;
+		case APUSHL:
+		case APUSHFL:
+			deltasp += 4;
+			continue;
+		case APUSHW:
+		case APUSHFW:
+			deltasp += 2;
+			continue;
+		case APOPL:
+		case APOPFL:
+			deltasp -= 4;
+			continue;
+		case APOPW:
+		case APOPFW:
+			deltasp -= 2;
+			continue;
+		case ARET:
+			break;
+		}
+
+		if(autoffset != deltasp)
+			diag("unbalanced PUSH/POP");
+		if(p->from.type == D_CONST)
+			goto become;
+
+		if(autoffset) {
+			q = p;
+			p = appendp(p);
+			p->as = ARET;
+
+			q->as = AADJSP;
+			q->from.type = D_CONST;
+			q->from.offset = -autoffset;
+		}
+		continue;
+
+	become:
+		q = p;
+		p = appendp(p);
+		p->as = AJMP;
+		p->to = q->to;
+		p->pcond = q->pcond;
+
+		q->as = AADJSP;
+		q->from = zprg.from;
+		q->from.type = D_CONST;
+		q->from.offset = -autoffset;
+		q->to = zprg.to;
+		continue;
+	}
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+void
+import(void)
+{
+	int i;
+	Sym *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+				if(s->value != 0)
+					diag("value != 0 on SXREF");
+				undefsym(s);
+				if(debug['X'])
+					Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value);
+				if(debug['S'])
+					s->sig = 0;
+			}
+}
+
+void
+ckoff(Sym *s, long v)
+{
+	if(v < 0 || v >= 1<<Roffset)
+		diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+	Prog *p;
+
+	p = prg();
+	if(edatap == P)
+		datap = p;
+	else
+		edatap->link = p;
+	edatap = p;
+	p->as = ADATA;
+	p->width = w;
+	p->from.scale = w;
+	p->from.type = t;
+	p->from.sym = s;
+	p->from.offset = o;
+	p->to.type = D_CONST;
+	return p;
+}
+
+void
+export(void)
+{
+	int i, j, n, off, nb, sv, ne;
+	Sym *s, *et, *str, **esyms;
+	Prog *p;
+	char buf[NSNAME], *t;
+
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type != SXREF && s->type != SUNDEF && (nexports == 0 && s->sig != 0 || s->subtype == SEXPORT || allexport))
+				n++;
+	esyms = malloc(n*sizeof(Sym*));
+	ne = n;
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type != SXREF && s->type != SUNDEF && (nexports == 0 && s->sig != 0 || s->subtype == SEXPORT || allexport))
+				esyms[n++] = s;
+	for(i = 0; i < ne-1; i++)
+		for(j = i+1; j < ne; j++)
+			if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+				s = esyms[i];
+				esyms[i] = esyms[j];
+				esyms[j] = s;
+			}
+
+	nb = 0;
+	off = 0;
+	et = lookup(EXPTAB, 0);
+	if(et->type != 0 && et->type != SXREF)
+		diag("%s already defined", EXPTAB);
+	et->type = SDATA;
+	str = lookup(".string", 0);
+	if(str->type == 0)
+		str->type = SDATA;
+	sv = str->value;
+	for(i = 0; i < ne; i++){
+		s = esyms[i];
+		if(debug['S'])
+			s->sig = 0;
+		/* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */
+
+		/* signature */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.offset = s->sig;
+
+		/* address */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.type = D_ADDR;
+		p->to.index = D_EXTERN;
+		p->to.sym = s;
+
+		/* string */
+		t = s->name;
+		n = strlen(t)+1;
+		for(;;){
+			buf[nb++] = *t;
+			sv++;
+			if(nb >= NSNAME){
+				p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+				p->to.type = D_SCONST;
+				memmove(p->to.scon, buf, NSNAME);
+				nb = 0;
+			}
+			if(*t++ == 0)
+				break;
+		}
+
+		/* name */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.type = D_ADDR;
+		p->to.index = D_STATIC;
+		p->to.sym = str;
+		p->to.offset = sv-n;
+	}
+
+	if(nb > 0){
+		p = newdata(str, sv-nb, nb, D_STATIC);
+		p->to.type = D_SCONST;
+		memmove(p->to.scon, buf, nb);
+	}
+
+	for(i = 0; i < 3; i++){
+		newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+	}
+	et->value = off;
+	if(sv == 0)
+		sv = 1;
+	str->value = sv;
+	exports = ne;
+	free(esyms);
+}
--- /dev/null
+++ b/utils/8l/span.c
@@ -1,0 +1,1415 @@
+#include	"l.h"
+
+void
+span(void)
+{
+	Prog *p, *q;
+	long v, c, idat;
+	int m, n, again;
+
+	xdefine("etext", STEXT, 0L);
+	idat = INITDAT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		n = 0;
+		if(p->to.type == D_BRANCH)
+			if(p->pcond == P)
+				p->pcond = p;
+		if((q = p->pcond) != P)
+			if(q->back != 2)
+				n = 1;
+		p->back = n;
+		if(p->as == AADJSP) {
+			p->to.type = D_SP;
+			v = -p->from.offset;
+			p->from.offset = v;
+			p->as = AADDL;
+			if(v < 0) {
+				p->as = ASUBL;
+				v = -v;
+				p->from.offset = v;
+			}
+			if(v == 0)
+				p->as = ANOP;
+		}
+	}
+	n = 0;
+
+start:
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+	c = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->to.type == D_BRANCH)
+			if(p->back)
+				p->pc = c;
+		asmins(p);
+		p->pc = c;
+		m = andptr-and;
+		p->mark = m;
+		c += m;
+	}
+
+loop:
+	n++;
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span %d\n", cputime(), n);
+	Bflush(&bso);
+	if(n > 50) {
+		print("span must be looping\n");
+		errorexit();
+	}
+	again = 0;
+	c = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->to.type == D_BRANCH) {
+			if(p->back)
+				p->pc = c;
+			asmins(p);
+			m = andptr-and;
+			if(m != p->mark) {
+				p->mark = m;
+				again++;
+			}
+		}
+		p->pc = c;
+		c += p->mark;
+	}
+	if(again) {
+		textsize = c;
+		goto loop;
+	}
+	if(INITRND) {
+		INITDAT = rnd(c, INITRND);
+		if(INITDAT != idat) {
+			idat = INITDAT;
+			goto start;
+		}
+	}
+	xdefine("etext", STEXT, c);
+	if(debug['v'])
+		Bprint(&bso, "etext = %lux\n", c);
+	Bflush(&bso);
+	for(p = textp; p != P; p = p->pcond)
+		p->from.sym->value = p->pc;
+	textsize = c - INITTEXT;
+}
+
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+	if(s->type == STEXT && s->value == 0)
+		s->value = v;
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	lput(v);
+	if(ver)
+		t += 'a' - 'A';
+	cput(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		cput(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			cput(s[i]);
+			cput(s[i+1]);
+		}
+		cput(0);
+		cput(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			cput(s[i]);
+		cput(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->pcond) {
+		s = p->from.sym;
+		if(s->type != STEXT)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		putsymb(s->name, 'T', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			cput(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			cput(0);	/* 0 vv +lc */
+			cput(s>>24);
+			cput(s>>16);
+			cput(s>>8);
+			cput(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			cput(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			cput(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		cput(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+int
+prefixof(Adr *a)
+{
+	switch(a->type) {
+	case D_INDIR+D_CS:
+		return 0x2e;
+	case D_INDIR+D_DS:
+		return 0x3e;
+	case D_INDIR+D_ES:
+		return 0x26;
+	case D_INDIR+D_FS:
+		return 0x64;
+	case D_INDIR+D_GS:
+		return 0x65;
+	}
+	return 0;
+}
+
+int
+oclass(Adr *a)
+{
+	long v;
+
+	if(a->type >= D_INDIR || a->index != D_NONE) {
+		if(a->index != D_NONE && a->scale == 0) {
+			if(a->type == D_ADDR) {
+				switch(a->index) {
+				case D_EXTERN:
+				case D_STATIC:
+					return Yi32;
+				case D_AUTO:
+				case D_PARAM:
+					return Yiauto;
+				}
+				return Yxxx;
+			}
+			return Ycol;
+		}
+		return Ym;
+	}
+	switch(a->type)
+	{
+	case D_AL:
+		return Yal;
+
+	case D_AX:
+		return Yax;
+
+	case D_CL:
+		return Ycl;
+
+	case D_DL:
+	case D_BL:
+	case D_AH:
+	case D_CH:
+	case D_DH:
+	case D_BH:
+		return Yrb;
+
+	case D_CX:
+		return Ycx;
+
+	case D_DX:
+	case D_BX:
+		return Yrx;
+
+	case D_SP:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		return Yrl;
+
+	case D_F0+0:
+		return	Yf0;
+
+	case D_F0+1:
+	case D_F0+2:
+	case D_F0+3:
+	case D_F0+4:
+	case D_F0+5:
+	case D_F0+6:
+	case D_F0+7:
+		return	Yrf;
+
+	case D_NONE:
+		return Ynone;
+
+	case D_CS:	return	Ycs;
+	case D_SS:	return	Yss;
+	case D_DS:	return	Yds;
+	case D_ES:	return	Yes;
+	case D_FS:	return	Yfs;
+	case D_GS:	return	Ygs;
+
+	case D_GDTR:	return	Ygdtr;
+	case D_IDTR:	return	Yidtr;
+	case D_LDTR:	return	Yldtr;
+	case D_MSW:	return	Ymsw;
+	case D_TASK:	return	Ytask;
+
+	case D_CR+0:	return	Ycr0;
+	case D_CR+1:	return	Ycr1;
+	case D_CR+2:	return	Ycr2;
+	case D_CR+3:	return	Ycr3;
+	case D_CR+4:	return	Ycr4;
+	case D_CR+5:	return	Ycr5;
+	case D_CR+6:	return	Ycr6;
+	case D_CR+7:	return	Ycr7;
+
+	case D_DR+0:	return	Ydr0;
+	case D_DR+1:	return	Ydr1;
+	case D_DR+2:	return	Ydr2;
+	case D_DR+3:	return	Ydr3;
+	case D_DR+4:	return	Ydr4;
+	case D_DR+5:	return	Ydr5;
+	case D_DR+6:	return	Ydr6;
+	case D_DR+7:	return	Ydr7;
+
+	case D_TR+0:	return	Ytr0;
+	case D_TR+1:	return	Ytr1;
+	case D_TR+2:	return	Ytr2;
+	case D_TR+3:	return	Ytr3;
+	case D_TR+4:	return	Ytr4;
+	case D_TR+5:	return	Ytr5;
+	case D_TR+6:	return	Ytr6;
+	case D_TR+7:	return	Ytr7;
+
+	case D_EXTERN:
+	case D_STATIC:
+	case D_AUTO:
+	case D_PARAM:
+		return Ym;
+
+	case D_CONST:
+	case D_ADDR:
+		if(a->sym == S) {
+			v = a->offset;
+			if(v == 0)
+				return Yi0;
+			if(v == 1)
+				return Yi1;
+			if(v >= -128 && v <= 127)
+				return Yi8;
+		}
+		return Yi32;
+
+	case D_BRANCH:
+		return Ybr;
+	}
+	return Yxxx;
+}
+
+void
+asmidx(Adr *a, int base)
+{
+	int i;
+
+	switch(a->index) {
+	default:
+		goto bad;
+
+	case D_NONE:
+		i = 4 << 3;
+		goto bas;
+
+	case D_AX:
+	case D_CX:
+	case D_DX:
+	case D_BX:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		i = reg[a->index] << 3;
+		break;
+	}
+	switch(a->scale) {
+	default:
+		goto bad;
+	case 1:
+		break;
+	case 2:
+		i |= (1<<6);
+		break;
+	case 4:
+		i |= (2<<6);
+		break;
+	case 8:
+		i |= (3<<6);
+		break;
+	}
+bas:
+	switch(base) {
+	default:
+		goto bad;
+	case D_NONE:	/* must be mod=00 */
+		i |= 5;
+		break;
+	case D_AX:
+	case D_CX:
+	case D_DX:
+	case D_BX:
+	case D_SP:
+	case D_BP:
+	case D_SI:
+	case D_DI:
+		i |= reg[base];
+		break;
+	}
+	*andptr++ = i;
+	return;
+bad:
+	diag("asmidx: bad address %D", a);
+	*andptr++ = 0;
+	return;
+}
+
+static void
+put4(long v)
+{
+	if(dlm && curp != P && reloca != nil){
+		dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1);
+		reloca = nil;
+	}
+	andptr[0] = v;
+	andptr[1] = v>>8;
+	andptr[2] = v>>16;
+	andptr[3] = v>>24;
+	andptr += 4;
+}
+
+long
+vaddr(Adr *a)
+{
+	int t;
+	long v;
+	Sym *s;
+
+	t = a->type;
+	v = a->offset;
+	if(t == D_ADDR)
+		t = a->index;
+	switch(t) {
+	case D_STATIC:
+	case D_EXTERN:
+		s = a->sym;
+		if(s != nil) {
+			if(dlm && curp != P)
+				reloca = a;
+			switch(s->type) {
+			case SUNDEF:
+				ckoff(s, v);
+			case STEXT:
+			case SCONST:
+				v += s->value;
+				break;
+			default:
+				v += INITDAT + s->value;
+			}
+		}
+	}
+	return v;
+}
+
+void
+asmand(Adr *a, int r)
+{
+	long v;
+	int t;
+	Adr aa;
+
+	v = a->offset;
+	t = a->type;
+	if(a->index != D_NONE) {
+		if(t >= D_INDIR) {
+			t -= D_INDIR;
+			if(t == D_NONE) {
+				*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				put4(v);
+				return;
+			}
+			if(v == 0 && t != D_BP) {
+				*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				*andptr++ = (1 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, t);
+				*andptr++ = v;
+				return;
+			}
+			*andptr++ = (2 << 6) | (4 << 0) | (r << 3);
+			asmidx(a, t);
+			put4(v);
+			return;
+		}
+		switch(t) {
+		default:
+			goto bad;
+		case D_STATIC:
+		case D_EXTERN:
+			aa.type = D_NONE+D_INDIR;
+			break;
+		case D_AUTO:
+		case D_PARAM:
+			aa.type = D_SP+D_INDIR;
+			break;
+		}
+		aa.offset = vaddr(a);
+		aa.index = a->index;
+		aa.scale = a->scale;
+		asmand(&aa, r);
+		return;
+	}
+	if(t >= D_AL && t <= D_F0+7) {
+		if(v)
+			goto bad;
+		*andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3);
+		return;
+	}
+	if(t >= D_INDIR) {
+		t -= D_INDIR;
+		if(t == D_NONE || D_CS <= t && t <= D_GS) {
+			*andptr++ = (0 << 6) | (5 << 0) | (r << 3);
+			put4(v);
+			return;
+		}
+		if(t == D_SP) {
+			if(v == 0) {
+				*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, D_SP);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				*andptr++ = (1 << 6) | (4 << 0) | (r << 3);
+				asmidx(a, D_SP);
+				*andptr++ = v;
+				return;
+			}
+			*andptr++ = (2 << 6) | (4 << 0) | (r << 3);
+			asmidx(a, D_SP);
+			put4(v);
+			return;
+		}
+		if(t >= D_AX && t <= D_DI) {
+			if(v == 0 && t != D_BP) {
+				*andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3);
+				return;
+			}
+			if(v >= -128 && v < 128) {
+				andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3);
+				andptr[1] = v;
+				andptr += 2;
+				return;
+			}
+			*andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3);
+			put4(v);
+			return;
+		}
+		goto bad;
+	}
+	switch(a->type) {
+	default:
+		goto bad;
+	case D_STATIC:
+	case D_EXTERN:
+		aa.type = D_NONE+D_INDIR;
+		break;
+	case D_AUTO:
+	case D_PARAM:
+		aa.type = D_SP+D_INDIR;
+		break;
+	}
+	aa.index = D_NONE;
+	aa.scale = 1;
+	aa.offset = vaddr(a);
+	asmand(&aa, r);
+	return;
+bad:
+	diag("asmand: bad address %D", a);
+	return;
+}
+
+#define	E	0xff
+uchar	ymovtab[] =
+{
+/* push */
+	APUSHL,	Ycs,	Ynone,	0,	0x0e,E,0,0,
+	APUSHL,	Yss,	Ynone,	0,	0x16,E,0,0,
+	APUSHL,	Yds,	Ynone,	0,	0x1e,E,0,0,
+	APUSHL,	Yes,	Ynone,	0,	0x06,E,0,0,
+	APUSHL,	Yfs,	Ynone,	0,	0x0f,0xa0,E,0,
+	APUSHL,	Ygs,	Ynone,	0,	0x0f,0xa8,E,0,
+
+	APUSHW,	Ycs,	Ynone,	0,	Pe,0x0e,E,0,
+	APUSHW,	Yss,	Ynone,	0,	Pe,0x16,E,0,
+	APUSHW,	Yds,	Ynone,	0,	Pe,0x1e,E,0,
+	APUSHW,	Yes,	Ynone,	0,	Pe,0x06,E,0,
+	APUSHW,	Yfs,	Ynone,	0,	Pe,0x0f,0xa0,E,
+	APUSHW,	Ygs,	Ynone,	0,	Pe,0x0f,0xa8,E,
+
+/* pop */
+	APOPL,	Ynone,	Yds,	0,	0x1f,E,0,0,
+	APOPL,	Ynone,	Yes,	0,	0x07,E,0,0,
+	APOPL,	Ynone,	Yss,	0,	0x17,E,0,0,
+	APOPL,	Ynone,	Yfs,	0,	0x0f,0xa1,E,0,
+	APOPL,	Ynone,	Ygs,	0,	0x0f,0xa9,E,0,
+
+	APOPW,	Ynone,	Yds,	0,	Pe,0x1f,E,0,
+	APOPW,	Ynone,	Yes,	0,	Pe,0x07,E,0,
+	APOPW,	Ynone,	Yss,	0,	Pe,0x17,E,0,
+	APOPW,	Ynone,	Yfs,	0,	Pe,0x0f,0xa1,E,
+	APOPW,	Ynone,	Ygs,	0,	Pe,0x0f,0xa9,E,
+
+/* mov seg */
+	AMOVW,	Yes,	Yml,	1,	0x8c,0,0,0,
+	AMOVW,	Ycs,	Yml,	1,	0x8c,1,0,0,
+	AMOVW,	Yss,	Yml,	1,	0x8c,2,0,0,
+	AMOVW,	Yds,	Yml,	1,	0x8c,3,0,0,
+	AMOVW,	Yfs,	Yml,	1,	0x8c,4,0,0,
+	AMOVW,	Ygs,	Yml,	1,	0x8c,5,0,0,
+
+	AMOVW,	Yml,	Yes,	2,	0x8e,0,0,0,
+	AMOVW,	Yml,	Ycs,	2,	0x8e,1,0,0,
+	AMOVW,	Yml,	Yss,	2,	0x8e,2,0,0,
+	AMOVW,	Yml,	Yds,	2,	0x8e,3,0,0,
+	AMOVW,	Yml,	Yfs,	2,	0x8e,4,0,0,
+	AMOVW,	Yml,	Ygs,	2,	0x8e,5,0,0,
+
+/* mov cr */
+	AMOVL,	Ycr0,	Yml,	3,	0x0f,0x20,0,0,
+	AMOVL,	Ycr2,	Yml,	3,	0x0f,0x20,2,0,
+	AMOVL,	Ycr3,	Yml,	3,	0x0f,0x20,3,0,
+	AMOVL,	Ycr4,	Yml,	3,	0x0f,0x20,4,0,
+
+	AMOVL,	Yml,	Ycr0,	4,	0x0f,0x22,0,0,
+	AMOVL,	Yml,	Ycr2,	4,	0x0f,0x22,2,0,
+	AMOVL,	Yml,	Ycr3,	4,	0x0f,0x22,3,0,
+	AMOVL,	Yml,	Ycr4,	4,	0x0f,0x22,4,0,
+
+/* mov dr */
+	AMOVL,	Ydr0,	Yml,	3,	0x0f,0x21,0,0,
+	AMOVL,	Ydr6,	Yml,	3,	0x0f,0x21,6,0,
+	AMOVL,	Ydr7,	Yml,	3,	0x0f,0x21,7,0,
+
+	AMOVL,	Yml,	Ydr0,	4,	0x0f,0x23,0,0,
+	AMOVL,	Yml,	Ydr6,	4,	0x0f,0x23,6,0,
+	AMOVL,	Yml,	Ydr7,	4,	0x0f,0x23,7,0,
+
+/* mov tr */
+	AMOVL,	Ytr6,	Yml,	3,	0x0f,0x24,6,0,
+	AMOVL,	Ytr7,	Yml,	3,	0x0f,0x24,7,0,
+
+	AMOVL,	Yml,	Ytr6,	4,	0x0f,0x26,6,E,
+	AMOVL,	Yml,	Ytr7,	4,	0x0f,0x26,7,E,
+
+/* lgdt, sgdt, lidt, sidt */
+	AMOVL,	Ym,	Ygdtr,	4,	0x0f,0x01,2,0,
+	AMOVL,	Ygdtr,	Ym,	3,	0x0f,0x01,0,0,
+	AMOVL,	Ym,	Yidtr,	4,	0x0f,0x01,3,0,
+	AMOVL,	Yidtr,	Ym,	3,	0x0f,0x01,1,0,
+
+/* lldt, sldt */
+	AMOVW,	Yml,	Yldtr,	4,	0x0f,0x00,2,0,
+	AMOVW,	Yldtr,	Yml,	3,	0x0f,0x00,0,0,
+
+/* lmsw, smsw */
+	AMOVW,	Yml,	Ymsw,	4,	0x0f,0x01,6,0,
+	AMOVW,	Ymsw,	Yml,	3,	0x0f,0x01,4,0,
+
+/* ltr, str */
+	AMOVW,	Yml,	Ytask,	4,	0x0f,0x00,3,0,
+	AMOVW,	Ytask,	Yml,	3,	0x0f,0x00,1,0,
+
+/* load full pointer */
+	AMOVL,	Yml,	Ycol,	5,	0,0,0,0,
+	AMOVW,	Yml,	Ycol,	5,	Pe,0,0,0,
+
+/* double shift */
+	ASHLL,	Ycol,	Yml,	6,	0xa4,0xa5,0,0,
+	ASHRL,	Ycol,	Yml,	6,	0xac,0xad,0,0,
+
+/* extra imul */
+	AIMULW,	Yml,	Yrl,	7,	Pq,0xaf,0,0,
+	AIMULL,	Yml,	Yrl,	7,	Pm,0xaf,0,0,
+	0
+};
+
+int
+isax(Adr *a)
+{
+
+	switch(a->type) {
+	case D_AX:
+	case D_AL:
+	case D_AH:
+	case D_INDIR+D_AX:
+		return 1;
+	}
+	if(a->index == D_AX)
+		return 1;
+	return 0;
+}
+
+void
+subreg(Prog *p, int from, int to)
+{
+
+	if(debug['Q'])
+		print("\n%P	s/%R/%R/\n", p, from, to);
+
+	if(p->from.type == from)
+		p->from.type = to;
+	if(p->to.type == from)
+		p->to.type = to;
+
+	if(p->from.index == from)
+		p->from.index = to;
+	if(p->to.index == from)
+		p->to.index = to;
+
+	from += D_INDIR;
+	if(p->from.type == from)
+		p->from.type = to+D_INDIR;
+	if(p->to.type == from)
+		p->to.type = to+D_INDIR;
+
+	if(debug['Q'])
+		print("%P\n", p);
+}
+
+void
+doasm(Prog *p)
+{
+	Optab *o;
+	Prog *q, pp;
+	uchar *t;
+	int z, op, ft, tt;
+	long v, pre;
+
+	pre = prefixof(&p->from);
+	if(pre)
+		*andptr++ = pre;
+	pre = prefixof(&p->to);
+	if(pre)
+		*andptr++ = pre;
+
+	o = &optab[p->as];
+	ft = oclass(&p->from) * Ymax;
+	tt = oclass(&p->to) * Ymax;
+	t = o->ytab;
+	if(t == 0) {
+		diag("asmins: noproto %P", p);
+		return;
+	}
+	for(z=0; *t; z+=t[3],t+=4)
+		if(ycover[ft+t[0]])
+		if(ycover[tt+t[1]])
+			goto found;
+	goto domov;
+
+found:
+	switch(o->prefix) {
+	case Pq:	/* 16 bit escape and opcode escape */
+		*andptr++ = Pe;
+		*andptr++ = Pm;
+		break;
+
+	case Pm:	/* opcode escape */
+		*andptr++ = Pm;
+		break;
+
+	case Pe:	/* 16 bit escape */
+		*andptr++ = Pe;
+		break;
+
+	case Pb:	/* botch */
+		break;
+	}
+	v = vaddr(&p->from);
+	op = o->op[z];
+	switch(t[2]) {
+	default:
+		diag("asmins: unknown z %d %P", t[2], p);
+		return;
+
+	case Zpseudo:
+		break;
+
+	case Zlit:
+		for(; op = o->op[z]; z++)
+			*andptr++ = op;
+		break;
+
+	case Zm_r:
+		*andptr++ = op;
+		asmand(&p->from, reg[p->to.type]);
+		break;
+
+	case Zaut_r:
+		*andptr++ = 0x8d;	/* leal */
+		if(p->from.type != D_ADDR)
+			diag("asmins: Zaut sb type ADDR");
+		p->from.type = p->from.index;
+		p->from.index = D_NONE;
+		asmand(&p->from, reg[p->to.type]);
+		p->from.index = p->from.type;
+		p->from.type = D_ADDR;
+		break;
+
+	case Zm_o:
+		*andptr++ = op;
+		asmand(&p->from, o->op[z+1]);
+		break;
+
+	case Zr_m:
+		*andptr++ = op;
+		asmand(&p->to, reg[p->from.type]);
+		break;
+
+	case Zo_m:
+		*andptr++ = op;
+		asmand(&p->to, o->op[z+1]);
+		break;
+
+	case Zm_ibo:
+		v = vaddr(&p->to);
+		*andptr++ = op;
+		asmand(&p->from, o->op[z+1]);
+		*andptr++ = v;
+		break;
+
+	case Zibo_m:
+		*andptr++ = op;
+		asmand(&p->to, o->op[z+1]);
+		*andptr++ = v;
+		break;
+
+	case Z_ib:
+		v = vaddr(&p->to);
+	case Zib_:
+		*andptr++ = op;
+		*andptr++ = v;
+		break;
+
+	case Zib_rp:
+		*andptr++ = op + reg[p->to.type];
+		*andptr++ = v;
+		break;
+
+	case Zil_rp:
+		*andptr++ = op + reg[p->to.type];
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zib_rr:
+		*andptr++ = op;
+		asmand(&p->to, reg[p->to.type]);
+		*andptr++ = v;
+		break;
+
+	case Z_il:
+		v = vaddr(&p->to);
+	case Zil_:
+		*andptr++ = op;
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zm_ilo:
+		v = vaddr(&p->to);
+		*andptr++ = op;
+		asmand(&p->from, o->op[z+1]);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zilo_m:
+		*andptr++ = op;
+		asmand(&p->to, o->op[z+1]);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Zil_rr:
+		*andptr++ = op;
+		asmand(&p->to, reg[p->to.type]);
+		if(o->prefix == Pe) {
+			*andptr++ = v;
+			*andptr++ = v>>8;
+		}
+		else
+			put4(v);
+		break;
+
+	case Z_rp:
+		*andptr++ = op + reg[p->to.type];
+		break;
+
+	case Zrp_:
+		*andptr++ = op + reg[p->from.type];
+		break;
+
+	case Zclr:
+		*andptr++ = op;
+		asmand(&p->to, reg[p->to.type]);
+		break;
+
+	case Zbr:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v >= -128 && v <= 127) {
+				*andptr++ = op;
+				*andptr++ = v;
+			} else {
+				v -= 6-2;
+				*andptr++ = 0x0f;
+				*andptr++ = o->op[z+1];
+				*andptr++ = v;
+				*andptr++ = v>>8;
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+			}
+		}
+		break;
+
+	case Zcall:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 5;
+			if(dlm && curp != P && p->to.sym->type == SUNDEF){
+				/* v = 0 - p->pc - 5; */
+				v = 0;
+				ckoff(p->to.sym, v);
+				v += p->to.sym->value;
+				dynreloc(p->to.sym, p->pc+1, 0);
+			}
+			*andptr++ = op;
+			*andptr++ = v;
+			*andptr++ = v>>8;
+			*andptr++ = v>>16;
+			*andptr++ = v>>24;
+		}
+		break;
+
+	case Zjmp:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v >= -128 && v <= 127) {
+				*andptr++ = op;
+				*andptr++ = v;
+			} else {
+				v -= 5-2;
+				*andptr++ = o->op[z+1];
+				*andptr++ = v;
+				*andptr++ = v>>8;
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+			}
+		}
+		break;
+
+	case Zloop:
+		q = p->pcond;
+		if(q) {
+			v = q->pc - p->pc - 2;
+			if(v < -128 || v > 127)
+				diag("loop too far: %P", p);
+			*andptr++ = op;
+			*andptr++ = v;
+		}
+		break;
+
+	case Zbyte:
+		*andptr++ = v;
+		if(op > 1) {
+			*andptr++ = v>>8;
+			if(op > 2) {
+				*andptr++ = v>>16;
+				*andptr++ = v>>24;
+			}
+		}
+		break;
+
+	case Zmov:
+		goto domov;
+	}
+	return;
+
+domov:
+	for(t=ymovtab; *t; t+=8)
+		if(p->as == t[0])
+		if(ycover[ft+t[1]])
+		if(ycover[tt+t[2]])
+			goto mfound;
+bad:
+	/*
+	 * here, the assembly has failed.
+	 * if its a byte instruction that has
+	 * unaddressable registers, try to
+	 * exchange registers and reissue the
+	 * instruction with the operands renamed.
+	 */
+	pp = *p;
+	z = p->from.type;
+	if(z >= D_BP && z <= D_DI) {
+		if(isax(&p->to)) {
+			*andptr++ = 0x87;			/* xchg lhs,bx */
+			asmand(&p->from, reg[D_BX]);
+			subreg(&pp, z, D_BX);
+			doasm(&pp);
+			*andptr++ = 0x87;			/* xchg lhs,bx */
+			asmand(&p->from, reg[D_BX]);
+		} else {
+			*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
+			subreg(&pp, z, D_AX);
+			doasm(&pp);
+			*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
+		}
+		return;
+	}
+	z = p->to.type;
+	if(z >= D_BP && z <= D_DI) {
+		if(isax(&p->from)) {
+			*andptr++ = 0x87;			/* xchg rhs,bx */
+			asmand(&p->to, reg[D_BX]);
+			subreg(&pp, z, D_BX);
+			doasm(&pp);
+			*andptr++ = 0x87;			/* xchg rhs,bx */
+			asmand(&p->to, reg[D_BX]);
+		} else {
+			*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
+			subreg(&pp, z, D_AX);
+			doasm(&pp);
+			*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
+		}
+		return;
+	}
+	diag("doasm: notfound t2=%ux from=%ux to=%ux %P", t[2], p->from.type, p->to.type, p);
+	return;
+
+mfound:
+	switch(t[3]) {
+	default:
+		diag("asmins: unknown mov %d %P", t[3], p);
+		break;
+
+	case 0:	/* lit */
+		for(z=4; t[z]!=E; z++)
+			*andptr++ = t[z];
+		break;
+
+	case 1:	/* r,m */
+		*andptr++ = t[4];
+		asmand(&p->to, t[5]);
+		break;
+
+	case 2:	/* m,r */
+		*andptr++ = t[4];
+		asmand(&p->from, t[5]);
+		break;
+
+	case 3:	/* r,m - 2op */
+		*andptr++ = t[4];
+		*andptr++ = t[5];
+		asmand(&p->to, t[6]);
+		break;
+
+	case 4:	/* m,r - 2op */
+		*andptr++ = t[4];
+		*andptr++ = t[5];
+		asmand(&p->from, t[6]);
+		break;
+
+	case 5:	/* load full pointer, trash heap */
+		if(t[4])
+			*andptr++ = t[4];
+		switch(p->to.index) {
+		default:
+			goto bad;
+		case D_DS:
+			*andptr++ = 0xc5;
+			break;
+		case D_SS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb2;
+			break;
+		case D_ES:
+			*andptr++ = 0xc4;
+			break;
+		case D_FS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb4;
+			break;
+		case D_GS:
+			*andptr++ = 0x0f;
+			*andptr++ = 0xb5;
+			break;
+		}
+		asmand(&p->from, reg[p->to.type]);
+		break;
+
+	case 6:	/* double shift */
+		z = p->from.type;
+		switch(z) {
+		default:
+			goto bad;
+		case D_CONST:
+			*andptr++ = 0x0f;
+			*andptr++ = t[4];
+			asmand(&p->to, reg[p->from.index]);
+			*andptr++ = p->from.offset;
+			break;
+		case D_CL:
+		case D_CX:
+			*andptr++ = 0x0f;
+			*andptr++ = t[5];
+			asmand(&p->to, reg[p->from.index]);
+			break;
+		}
+		break;
+
+	case 7: /* imul rm,r */
+		*andptr++ = t[4];
+		*andptr++ = t[5];
+		asmand(&p->from, reg[p->to.type]);
+		break;
+	}
+}
+
+void
+asmins(Prog *p)
+{
+
+	andptr = and;
+	doasm(p);
+}
+
+enum{
+	ABSD = 0,
+	ABSU = 1,
+	RELD = 2,
+	RELU = 3,
+};
+
+int modemap[4] = { 0, 1, -1, 2, };
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+	int n;
+	int t;
+	uchar *m;
+	ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+	int t;
+	uchar *m, *nm;
+	ulong *a, *na;
+
+	t = r->t;
+	r->t += 64;
+	m = r->m;
+	a = r->a;
+	r->m = nm = malloc(r->t*sizeof(uchar));
+	r->a = na = malloc(r->t*sizeof(ulong));
+	memmove(nm, m, t*sizeof(uchar));
+	memmove(na, a, t*sizeof(ulong));
+	free(m);
+	free(a);
+}
+
+void
+dynreloc(Sym *s, ulong v, int abs)
+{
+	int i, k, n;
+	uchar *m;
+	ulong *a;
+	Reloc *r;
+
+	if(s->type == SUNDEF)
+		k = abs ? ABSU : RELU;
+	else
+		k = abs ? ABSD : RELD;
+	/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */
+	k = modemap[k];
+	r = &rels;
+	n = r->n;
+	if(n >= r->t)
+		grow(r);
+	m = r->m;
+	a = r->a;
+	for(i = n; i > 0; i--){
+		if(v < a[i-1]){	/* happens occasionally for data */
+			m[i] = m[i-1];
+			a[i] = a[i-1];
+		}
+		else
+			break;
+	}
+	m[i] = k;
+	a[i] = v;
+	r->n++;
+}
+
+static int
+sput(char *s)
+{
+	char *p;
+
+	p = s;
+	while(*s)
+		cput(*s++);
+	cput(0);
+	return s-p+1;
+}
+
+void
+asmdyn()
+{
+	int i, n, t, c;
+	Sym *s;
+	ulong la, ra, *a;
+	vlong off;
+	uchar *m;
+	Reloc *r;
+
+	cflush();
+	off = seek(cout, 0, 1);
+	lput(0);
+	t = 0;
+	lput(imports);
+	t += 4;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type == SUNDEF){
+				lput(s->sig);
+				t += 4;
+				t += sput(s->name);
+			}
+
+	la = 0;
+	r = &rels;
+	n = r->n;
+	m = r->m;
+	a = r->a;
+	lput(n);
+	t += 4;
+	for(i = 0; i < n; i++){
+		ra = *a-la;
+		if(*a < la)
+			diag("bad relocation order");
+		if(ra < 256)
+			c = 0;
+		else if(ra < 65536)
+			c = 1;
+		else
+			c = 2;
+		cput((c<<6)|*m++);
+		t++;
+		if(c == 0){
+			cput(ra);
+			t++;
+		}
+		else if(c == 1){
+			wput(ra);
+			t += 2;
+		}
+		else{
+			lput(ra);
+			t += 4;
+		}
+		la = *a++;
+	}
+
+	cflush();
+	seek(cout, off, 0);
+	lput(t);
+
+	if(debug['v']){
+		Bprint(&bso, "import table entries = %d\n", imports);
+		Bprint(&bso, "export table entries = %d\n", exports);
+	}
+}
--- /dev/null
+++ b/utils/9c/9.out.h
@@ -1,0 +1,419 @@
+/*
+ * powerpc 64
+ */
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+enum
+{
+	REGZERO		= 0,	/* set to zero */
+	REGSP		= 1,
+	REGSB		= 2,
+	REGRET		= 3,
+	REGARG		= 3,
+	REGMIN		= 7,	/* register variables allocated from here to REGMAX */
+	REGMAX		= 27,
+	REGEXT		= 30,	/* external registers allocated from here down */
+	REGTMP		= 31,	/* used by the linker */
+
+	FREGRET		= 0,
+	FREGMIN		= 17,	/* first register variable */
+	FREGEXT		= 26,	/* first external register */
+	FREGCVI		= 27, /* floating conversion constant */
+	FREGZERO	= 28,	/* both float and double */
+	FREGHALF	= 29,	/* double */
+	FREGONE		= 30,	/* double */
+	FREGTWO		= 31	/* double */
+/*
+ * GENERAL:
+ *
+ * compiler allocates R3 up as temps
+ * compiler allocates register variables R7-R27
+ * compiler allocates external registers R30 down
+ *
+ * compiler allocates register variables F17-F26
+ * compiler allocates external registers F26 down
+ */
+};
+
+enum	as
+{
+	AXXX	= 0,
+	AADD,
+	AADDCC,
+	AADDV,
+	AADDVCC,
+	AADDC,
+	AADDCCC,
+	AADDCV,
+	AADDCVCC,
+	AADDME,
+	AADDMECC,
+	AADDMEVCC,
+	AADDMEV,
+	AADDE,
+	AADDECC,
+	AADDEVCC,
+	AADDEV,
+	AADDZE,
+	AADDZECC,
+	AADDZEVCC,
+	AADDZEV,
+	AAND,
+	AANDCC,
+	AANDN,
+	AANDNCC,
+	ABC,
+	ABCL,
+	ABEQ,
+	ABGE,
+	ABGT,
+	ABL,
+	ABLE,
+	ABLT,
+	ABNE,
+	ABR,
+	ABVC,
+	ABVS,
+	ACMP,
+	ACMPU,
+	ACNTLZW,
+	ACNTLZWCC,
+	ACRAND,
+	ACRANDN,
+	ACREQV,
+	ACRNAND,
+	ACRNOR,
+	ACROR,
+	ACRORN,
+	ACRXOR,
+	ADIVW,
+	ADIVWCC,
+	ADIVWVCC,
+	ADIVWV,
+	ADIVWU,
+	ADIVWUCC,
+	ADIVWUVCC,
+	ADIVWUV,
+	AEQV,
+	AEQVCC,
+	AEXTSB,
+	AEXTSBCC,
+	AEXTSH,
+	AEXTSHCC,
+	AFABS,
+	AFABSCC,
+	AFADD,
+	AFADDCC,
+	AFADDS,
+	AFADDSCC,
+	AFCMPO,
+	AFCMPU,
+	AFCTIW,
+	AFCTIWCC,
+	AFCTIWZ,
+	AFCTIWZCC,
+	AFDIV,
+	AFDIVCC,
+	AFDIVS,
+	AFDIVSCC,
+	AFMADD,
+	AFMADDCC,
+	AFMADDS,
+	AFMADDSCC,
+	AFMOVD,
+	AFMOVDCC,
+	AFMOVDU,
+	AFMOVS,
+	AFMOVSU,
+	AFMSUB,
+	AFMSUBCC,
+	AFMSUBS,
+	AFMSUBSCC,
+	AFMUL,
+	AFMULCC,
+	AFMULS,
+	AFMULSCC,
+	AFNABS,
+	AFNABSCC,
+	AFNEG,
+	AFNEGCC,
+	AFNMADD,
+	AFNMADDCC,
+	AFNMADDS,
+	AFNMADDSCC,
+	AFNMSUB,
+	AFNMSUBCC,
+	AFNMSUBS,
+	AFNMSUBSCC,
+	AFRSP,
+	AFRSPCC,
+	AFSUB,
+	AFSUBCC,
+	AFSUBS,
+	AFSUBSCC,
+	AMOVMW,
+	ALSW,
+	ALWAR,
+	AMOVWBR,
+	AMOVB,
+	AMOVBU,
+	AMOVBZ,
+	AMOVBZU,
+	AMOVH,
+	AMOVHBR,
+	AMOVHU,
+	AMOVHZ,
+	AMOVHZU,
+	AMOVW,
+	AMOVWU,
+	AMOVFL,
+	AMOVCRFS,
+	AMTFSB0,
+	AMTFSB0CC,
+	AMTFSB1,
+	AMTFSB1CC,
+	AMULHW,
+	AMULHWCC,
+	AMULHWU,
+	AMULHWUCC,
+	AMULLW,
+	AMULLWCC,
+	AMULLWVCC,
+	AMULLWV,
+	ANAND,
+	ANANDCC,
+	ANEG,
+	ANEGCC,
+	ANEGVCC,
+	ANEGV,
+	ANOR,
+	ANORCC,
+	AOR,
+	AORCC,
+	AORN,
+	AORNCC,
+	AREM,
+	AREMCC,
+	AREMV,
+	AREMVCC,
+	AREMU,
+	AREMUCC,
+	AREMUV,
+	AREMUVCC,
+	ARFI,
+	ARLWMI,
+	ARLWMICC,
+	ARLWNM,
+	ARLWNMCC,
+	ASLW,
+	ASLWCC,
+	ASRW,
+	ASRAW,
+	ASRAWCC,
+	ASRWCC,
+	ASTSW,
+	ASTWCCC,
+	ASUB,
+	ASUBCC,
+	ASUBVCC,
+	ASUBC,
+	ASUBCCC,
+	ASUBCV,
+	ASUBCVCC,
+	ASUBME,
+	ASUBMECC,
+	ASUBMEVCC,
+	ASUBMEV,
+	ASUBV,
+	ASUBE,
+	ASUBECC,
+	ASUBEV,
+	ASUBEVCC,
+	ASUBZE,
+	ASUBZECC,
+	ASUBZEVCC,
+	ASUBZEV,
+	ASYNC,
+	AXOR,
+	AXORCC,
+
+	ADCBF,
+	ADCBI,
+	ADCBST,
+	ADCBT,
+	ADCBTST,
+	ADCBZ,
+	AECIWX,
+	AECOWX,
+	AEIEIO,
+	AICBI,
+	AISYNC,
+	APTESYNC,
+	ATLBIE,
+	ATLBIEL,
+	ATLBSYNC,
+	ATW,
+
+	ASYSCALL,
+	ADATA,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	ANAME,
+	ANOP,
+	ARETURN,
+	ATEXT,
+	AWORD,
+	AEND,
+	ADYNT,
+	AINIT,
+	ASIGNAME,
+
+	ARFCI,
+
+	/* optional on 32-bit */
+	AFRES,
+	AFRESCC,
+	AFRSQRTE,
+	AFRSQRTECC,
+	AFSEL,
+	AFSELCC,
+	AFSQRT,
+	AFSQRTCC,
+	AFSQRTS,
+	AFSQRTSCC,
+
+	/* 64-bit */
+	
+	ACNTLZD,
+	ACNTLZDCC,
+	ACMPW,	/* CMP with L=0 */
+	ACMPWU,
+	ADIVD,
+	ADIVDCC,
+	ADIVDVCC,
+	ADIVDV,
+	ADIVDU,
+	ADIVDUCC,
+	ADIVDUVCC,
+	ADIVDUV,
+	AEXTSW,
+	AEXTSWCC,
+	/* AFCFIW; AFCFIWCC */
+	AFCFID,
+	AFCFIDCC,
+	AFCTID,
+	AFCTIDCC,
+	AFCTIDZ,
+	AFCTIDZCC,
+	ALDAR,
+	AMOVD,
+	AMOVDU,
+	AMOVWZ,
+	AMOVWZU,
+	AMULHD,
+	AMULHDCC,
+	AMULHDU,
+	AMULHDUCC,
+	AMULLD,
+	AMULLDCC,
+	AMULLDVCC,
+	AMULLDV,
+	ARFID,
+	ARLDMI,
+	ARLDMICC,
+	ARLDC,
+	ARLDCCC,
+	ARLDCR,
+	ARLDCRCC,
+	ARLDCL,
+	ARLDCLCC,
+	ASLBIA,
+	ASLBIE,
+	ASLBMFEE,
+	ASLBMFEV,
+	ASLBMTE,
+	ASLD,
+	ASLDCC,
+	ASRD,
+	ASRAD,
+	ASRADCC,
+	ASRDCC,
+	ASTDCCC,
+	ATD,
+
+	/* 64-bit pseudo operation */
+	ADWORD,
+	AREMD,
+	AREMDCC,
+	AREMDV,
+	AREMDVCC,
+	AREMDU,
+	AREMDUCC,
+	AREMDUV,
+	AREMDUVCC,
+
+	/* more 64-bit operations */
+	AHRFID,
+
+	ALAST
+};
+
+/* type/name */
+enum
+{
+	D_GOK	= 0,
+	D_NONE,
+
+/* name */
+	D_EXTERN,
+	D_STATIC,
+	D_AUTO,
+	D_PARAM,
+
+/* type */
+	D_BRANCH,
+	D_OREG,
+	D_CONST,
+	D_FCONST,
+	D_SCONST,
+	D_REG,
+	D_FPSCR,
+	D_MSR,
+	D_FREG,
+	D_CREG,
+	D_SPR,
+	D_OPT,	/* branch/trap option */
+	D_FILE,
+	D_FILE1,
+	D_DCR,	/* device control register */
+	D_DCONST,
+
+/* reg names iff type is D_SPR */
+	D_XER	= 1,
+	D_LR	= 8,
+	D_CTR	= 9
+	/* and many supervisor level registers */
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/NOTICE
@@ -1,0 +1,35 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+	Portions Copyright © 1997-1999 Vita Nuova Limited
+	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+	Portions Copyright © 2004,2006 Bruce Ellis
+	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
--- /dev/null
+++ b/utils/acid/386
@@ -1,0 +1,146 @@
+// 386 support
+
+defn acidinit()			// Called after all the init modules are loaded
+{
+	bplist = {};
+	bpfmt = 'b';
+
+	srcpath = {
+		"./",
+		"/sys/src/libc/port/",
+		"/sys/src/libc/9sys/",
+		"/sys/src/libc/386/"
+	};
+
+	srcfiles = {};			// list of loaded files
+	srctext = {};			// the text of the files
+	Labspoff = 4;		// adjustment to Label's sp
+	Labpcoff = 0;		// adjustment to Label's pc
+}
+
+defn linkreg(addr)
+{
+	return 0;
+}
+
+defn stk()				// trace
+{
+	_stk(*PC, *SP, 0, 0);
+}
+
+defn lstk()				// trace with locals
+{
+	_stk(*PC, *SP, 0, 1);
+}
+
+defn kstk()				// kernel stack
+{
+	_stk(*PC-Labpcoff, *SP-Labspoff, 0, 0);
+}
+
+defn lkstk()				// kernel stack and locals
+{
+	_stk(*PC-Labpcoff, *SP-Labspoff, 0, 1);
+}
+defn gpr()		// print general(hah hah!) purpose registers
+{
+	print("AX\t", *AX, " BX\t", *BX, " CX\t", *CX, " DX\t", *DX, "\n");
+	print("DI\t", *DI, " SI\t", *SI, " BP\t", *BP, "\n");
+}
+
+defn spr()				// print special processor registers
+{
+	local pc;
+	local cause;
+
+	pc = *PC;
+	print("PC\t", pc, " ", fmt(pc, 'a'), "  ");
+	pfl(pc);
+	print("SP\t", *SP, " ECODE ", *ECODE, " EFLAG ", *EFLAGS, "\n");
+	print("CS\t", *CS, " DS\t ", *DS, " SS\t", *SS, "\n");
+	print("GS\t", *GS, " FS\t ", *FS, " ES\t", *ES, "\n");
+	
+	cause = *TRAP;
+	print("TRAP\t", cause, " ", reason(cause), "\n");
+}
+
+defn regs()				// print all registers
+{
+	spr();
+	gpr();
+}
+
+defn pstop(pid)
+{
+	local l;
+	local pc;
+
+	pc = *PC;
+
+	print(pid,": ", reason(*TRAP), "\t");
+	print(fmt(pc, 'a'), "\t", fmt(pc, 'i'), "\n");
+
+	if notes then {
+		if notes[0] != "sys: breakpoint" then {
+			print("Notes pending:\n");
+			l = notes;
+			while l do {
+				print("\t", head l, "\n");
+				l = tail l;
+			}
+		}
+	}
+}
+
+aggr Ureg
+{
+	'U' 0 di;
+	'U' 4 si;
+	'U' 8 bp;
+	'U' 12 nsp;
+	'U' 16 bx;
+	'U' 20 dx;
+	'U' 24 cx;
+	'U' 28 ax;
+	'U' 32 gs;
+	'U' 36 fs;
+	'U' 40 es;
+	'U' 44 ds;
+	'U' 48 trap;
+	'U' 52 ecode;
+	'U' 56 pc;
+	'U' 60 cs;
+	'U' 64 flags;
+	{
+	'U' 68 usp;
+	'U' 68 sp;
+	};
+	'U' 72 ss;
+};
+
+defn
+Ureg(addr) {
+	complex Ureg addr;
+	print("	di	", addr.di, "\n");
+	print("	si	", addr.si, "\n");
+	print("	bp	", addr.bp, "\n");
+	print("	nsp	", addr.nsp, "\n");
+	print("	bx	", addr.bx, "\n");
+	print("	dx	", addr.dx, "\n");
+	print("	cx	", addr.cx, "\n");
+	print("	ax	", addr.ax, "\n");
+	print("	gs	", addr.gs, "\n");
+	print("	fs	", addr.fs, "\n");
+	print("	es	", addr.es, "\n");
+	print("	ds	", addr.ds, "\n");
+	print("	trap	", addr.trap, "\n");
+	print("	ecode	", addr.ecode, "\n");
+	print("	pc	", addr.pc, "\n");
+	print("	cs	", addr.cs, "\n");
+	print("	flags	", addr.flags, "\n");
+	print("	sp	", addr.sp, "\n");
+	print("}\n");
+	print("	ss	", addr.ss, "\n");
+};
+
+print("/sys/lib/acid/386");
--- /dev/null
+++ b/utils/acid/B.sh
@@ -1,0 +1,7 @@
+#!/bin/sh
+line=`/bin/echo $1 | /bin/sed 's/-//'`
+if [ "x$EDITOR" = "x" ] ; then
+	vi +$line $2
+else
+	$EDITOR +$line $2
+fi
--- /dev/null
+++ b/utils/acid/acid.h
@@ -1,0 +1,317 @@
+/* acid.h */
+enum
+{
+	Eof		= -1,
+	Strsize		= 4096,
+	Hashsize	= 128,
+	Maxarg		= 512,
+	NFD		= 100,
+	Maxproc		= 50,
+	Maxval		= 10,
+	Mempergc	= 1024*1024,
+};
+
+#pragma varargck type "L"	void
+
+typedef struct Node	Node;
+typedef struct String	String;
+typedef struct Lsym	Lsym;
+typedef struct List	List;
+typedef struct Store	Store;
+typedef struct Gc	Gc;
+typedef struct Strc	Strc;
+typedef struct Rplace	Rplace;
+typedef struct Ptab	Ptab;
+typedef struct Value	Value;
+typedef struct Type	Type;
+typedef struct Frtype	Frtype;
+
+Extern int	kernel;
+Extern int	remote;
+Extern int rdebug;
+Extern int remfd;
+Extern int	protodebug;
+Extern int	text;
+Extern int	silent;
+Extern Fhdr	fhdr;
+Extern int	line;
+Extern Biobuf*	bout;
+Extern Biobuf*	io[32];
+Extern int	iop;
+Extern char	symbol[Strsize];
+Extern int	interactive;
+Extern Node*	code;
+Extern int	na;
+Extern int	wtflag;
+Extern Map*	cormap;
+Extern Map*	symmap;
+Extern Lsym*	hash[Hashsize];
+Extern long	dogc;
+Extern Rplace*	ret;
+Extern char*	filename;
+Extern char*	aout;
+Extern int	gotint;
+Extern long	flen;
+Extern Gc*	gcl;
+Extern int	stacked;
+Extern jmp_buf	err;
+Extern Node*	prnt;
+Extern Node*	fomt;
+Extern List*	tracelist;
+Extern int	initialising;
+Extern int	quiet;
+extern void	(*expop[])(Node*, Node*);
+#define expr(n, r) (r)->nstore.comt=0; (*expop[(n)->op])(n, r);
+
+enum
+{
+	TINT,
+	TFLOAT,
+	TSTRING,
+	TLIST,
+	TCODE,
+};
+
+struct Type
+{
+	Type*	next;
+	int	offset;
+	char	fmt;
+	char	depth;
+	Lsym*	type;
+	Lsym*	tag;
+	Lsym*	base;
+};
+
+struct Frtype
+{
+	Lsym*	var;
+	Type*	type;
+	Frtype*	next;
+};
+
+struct Ptab
+{
+	int	pid;
+	int	ctl;
+};
+Extern Ptab	ptab[Maxproc];
+
+struct Rplace
+{
+	jmp_buf	rlab;
+	Node*	stak;
+	Node*	val;
+	Lsym*	local;
+	Lsym**	tail;
+};
+
+struct Gc
+{
+	char	gcmark;
+	Gc*	gclink;
+};
+
+struct Store
+{
+	char	fmt;
+	Type*	comt;
+	union {
+		vlong	sival;
+		double	sfval;
+		String*	sstring;
+		List*	sl;
+		Node*	scc;
+	} u0;
+};
+
+struct List
+{
+	Gc	lgc;
+	List*	next;
+	char	type;
+	Store	lstore;
+};
+
+struct Value
+{
+	char	set;
+	char	type;
+	Store	vstore;
+	Value*	pop;
+	Lsym*	scope;
+	Rplace*	ret;
+};
+
+struct Lsym
+{
+	char*	name;
+	int	lexval;
+	Lsym*	hash;
+	Value*	v;
+	Type*	lt;
+	Node*	proc;
+	Frtype*	local;
+	void	(*builtin)(Node*, Node*);
+};
+
+struct Node
+{
+	Gc	ngc;
+	char	op;
+	char	type;
+	Node*	left;
+	Node*	right;
+	Lsym*	sym;
+	Store	nstore;
+};
+#define ZN	(Node*)0
+
+struct String
+{
+	Gc	sgc;
+	char	*string;
+	int	len;
+};
+
+List*	addlist(List*, List*);
+List*	al(int);
+Node*	an(int, Node*, Node*);
+void	append(Node*, Node*, Node*);
+int	boolx(Node*);
+void	build(Node*);
+void	call(char*, Node*, Node*, Node*, Node*);
+void	checkqid(int, int);
+void	cmd(void);
+Node*	con(int);
+List*	construct(Node*);
+void	ctrace(int);
+void	decl(Node*);
+void	defcomplex(Node*, Node*);
+void	deinstall(int);
+void	delete(List*, int n, Node*);
+void	detach(void);
+void	dostop(int);
+Lsym*	enter(char*, int);
+void	error(char*, ...);
+void	execute(Node*);
+void	fatal(char*, ...);
+ulong	findframe(ulong);
+void	flatten(Node**, Node*);
+void	gc(void);
+char*	getstatus(int);
+void*	gmalloc(long);
+void	indir(Map*, ulong, char, Node*);
+void install(int);
+void	installbuiltin(void);
+void	kinit(void);
+int	Lfmt(Fmt*);
+int	listcmp(List*, List*);
+int	listlen(List*);
+List*	listvar(char*, long);
+void	loadmodule(char*);
+void	loadvars(void);
+Lsym*	look(char*);
+void	ltag(char*);
+void	setup_os_notify(void);
+void	marklist(List*);
+Lsym*	mkvar(char*);
+void	msg(int, char*);
+void	notes(int);
+int	nproc(char**);
+void	nthelem(List*, int, Node*);
+int	numsym(char);
+void	odot(Node*, Node*);
+int	opentty(char*, int);
+void	closetty(int);
+void	pcode(Node*, int);
+void	pexpr(Node*);
+int	popio(void);
+void	pstr(String*);
+void	pushfile(char*);
+void	pushstr(Node*);
+ulong	raddr(char*);
+void	readtext(char*);
+int	remcondset(char, ulong);
+int	remcondstartstop(int);
+int	remget(struct segment*, ulong, long, char*, int);
+int	remoteio(int, char*, char*, int);
+int	remote_read(int, char*, int);
+int	remote_write(int, char*, int);
+int	remput(struct segment*, ulong, long, char*, int);
+void	restartio(void);
+uvlong	rget(Map*, char*);
+String	*runenode(Rune*);
+char*	runcmd(char*);
+int	scmp(String*, String*);
+void	setdbg_opt(char, int);
+int	sendremote(int, char*);
+void	sproc(int);
+String*	stradd(String*, String*);
+String*	strnode(char*);
+String*	strnodlen(char*, int);
+char*	mysystem(void);
+void	trlist(Map*, uvlong, uvlong, Symbol*);
+void	unwind(void);
+void	userinit(void);
+void	varreg(void);
+void	varsym(void);
+char*	waitfor(int);
+void	whatis(Lsym*);
+void	windir(Map*, Node*, Node*, Node*);
+void	yyerror(char*, ...);
+int	yylex(void);
+int	yyparse(void);
+
+enum
+{
+	ONAME,
+	OCONST,
+	OMUL,
+	ODIV,
+	OMOD,
+	OADD,
+	OSUB,
+	ORSH,
+	OLSH,
+	OLT,
+	OGT,
+	OLEQ,
+	OGEQ,
+	OEQ,
+	ONEQ,
+	OLAND,
+	OXOR,
+	OLOR,
+	OCAND,
+	OCOR,
+	OASGN,
+	OINDM,
+	OEDEC,
+	OEINC,
+	OPINC,
+	OPDEC,
+	ONOT,
+	OIF,
+	ODO,
+	OLIST,
+	OCALL,
+	OCTRUCT,
+	OWHILE,
+	OELSE,
+	OHEAD,
+	OTAIL,
+	OAPPEND,
+	ORET,
+	OINDEX,
+	OINDC,
+	ODOT,
+	OLOCAL,
+	OFRAME,
+	OCOMPLEX,
+	ODELETE,
+	OCAST,
+	OFMT,
+	OEVAL,
+	OWHAT,
+};
--- /dev/null
+++ b/utils/acid/arm
@@ -1,0 +1,1 @@
+please remove this file
--- /dev/null
+++ b/utils/acid/builtin.c
@@ -1,0 +1,1285 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#include "regexp.h"
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+void	cvtatof(Node*, Node*);
+void	cvtatoi(Node*, Node*);
+void	cvtitoa(Node*, Node*);
+void	bprint(Node*, Node*);
+void	funcbound(Node*, Node*);
+void	printto(Node*, Node*);
+void	getfile(Node*, Node*);
+void	fmt(Node*, Node*);
+void	pcfile(Node*, Node*);
+void	pcline(Node*, Node*);
+void	setproc(Node*, Node*);
+void	strace(Node*, Node*);
+void	follow(Node*, Node*);
+void	reason(Node*, Node*);
+void	newproc(Node*, Node*);
+void	startstop(Node*, Node*);
+void	match(Node*, Node*);
+void	status(Node*, Node*);
+void	dokill(Node*,Node*);
+void	waitstop(Node*, Node*);
+void	stop(Node*, Node*);
+void	start(Node*, Node*);
+void	filepc(Node*, Node*);
+void	doerror(Node*, Node*);
+void	rc(Node*, Node*);
+void	doaccess(Node*, Node*);
+void	map(Node*, Node*);
+void	readfile(Node*, Node*);
+void	interpret(Node*, Node*);
+void	include(Node*, Node*);
+void	regexp(Node*, Node*);
+void	_bpcondset(Node*, Node*);
+void	_bpconddel(Node*, Node*);
+void	setdebug(Node*, Node*);
+
+typedef struct Btab Btab;
+struct Btab
+{
+	char	*name;
+	void	(*fn)(Node*, Node*);
+} tab[] =
+{
+	"atof",		cvtatof,
+	"atoi",		cvtatoi,
+	"error",	doerror,
+	"file",		getfile,
+	"readfile",	readfile,
+	"access",	doaccess,
+	"filepc",	filepc,
+	"fnbound",	funcbound,
+	"fmt",		fmt,
+	"follow",	follow,
+	"itoa",		cvtitoa,
+	"kill",		dokill,
+	"match",	match,
+	"newproc",	newproc,
+	"pcfile",	pcfile,
+	"pcline",	pcline,
+	"print",	bprint,
+	"printto",	printto,
+	"rc",		rc,
+	"reason",	reason,
+	"setproc",	setproc,
+	"sh",		rc,
+	"start",	start,
+	"startstop",	startstop,
+	"status",	status,
+	"stop",		stop,
+	"strace",	strace,
+	"waitstop",	waitstop,
+	"map",		map,
+	"interpret",	interpret,
+	"include",	include,
+	"regexp",	regexp,
+	"debug",	setdebug,
+	"_bpcondset",	_bpcondset,
+	"_bpconddel",	_bpconddel,
+	0
+};
+
+void
+mkprint(Lsym *s)
+{
+	prnt = gmalloc(sizeof(Node));
+	prnt->op = OCALL;
+	prnt->left = gmalloc(sizeof(Node));
+	prnt->left->sym = s;
+}
+
+void
+installbuiltin(void)
+{
+	Btab *b;
+	Lsym *s;
+
+	b = tab;
+	while(b->name) {
+		s = look(b->name);
+		if(s == 0)
+			s = enter(b->name, Tid);
+
+		s->builtin = b->fn;
+		if(b->fn == bprint)
+			mkprint(s);
+		b++;
+	}
+}
+
+void
+match(Node *r, Node *args)
+{
+	int i;
+	List *f;
+	Node *av[Maxarg];
+	Node resi, resl;
+
+	na = 0;
+	flatten(av, args);
+	if(na != 2)
+		error("match(obj, list): arg count");
+
+	expr(av[1], &resl);
+	if(resl.type != TLIST)
+		error("match(obj, list): need list");
+	expr(av[0], &resi);
+
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+	r->nstore.u0.sival = -1;
+
+	i = 0;
+	for(f = resl.nstore.u0.sl; f; f = f->next) {
+		if(resi.type == f->type) {
+			switch(resi.type) {
+			case TINT:
+				if(resi.nstore.u0.sival == f->lstore.u0.sival) {
+					r->nstore.u0.sival = i;
+					return;
+				}
+				break;
+			case TFLOAT:
+				if(resi.nstore.u0.sfval == f->lstore.u0.sfval) {
+					r->nstore.u0.sival = i;
+					return;
+				}
+				break;
+			case TSTRING:
+				if(scmp(resi.nstore.u0.sstring, f->lstore.u0.sstring)) {
+					r->nstore.u0.sival = i;
+					return;
+				}
+				break;
+			case TLIST:
+				error("match(obj, list): not defined for list");
+			}
+		}
+		i++;
+	}
+}
+
+void
+newproc(Node *r, Node *args)
+{
+	int i;
+	Node res;
+	char *p, *e;
+	char *argv[Maxarg], buf[Strsize];
+
+	i = 1;
+	argv[0] = aout;
+
+	if(args) {
+		expr(args, &res);
+		if(res.type != TSTRING)
+			error("newproc(): arg not string");
+		if(res.nstore.u0.sstring->len >= sizeof(buf))
+			error("newproc(): too many arguments");
+		memmove(buf, res.nstore.u0.sstring->string, res.nstore.u0.sstring->len);
+		buf[res.nstore.u0.sstring->len] = '\0';
+		p = buf;
+		e = buf+res.nstore.u0.sstring->len;
+		for(;;) {
+			while(p < e && (*p == '\t' || *p == ' '))
+				*p++ = '\0';
+			if(p >= e)
+				break;
+			argv[i++] = p;
+			if(i >= Maxarg)
+				error("newproc: too many arguments");
+			while(p < e && *p != '\t' && *p != ' ')
+				p++;
+		}
+	}
+	argv[i] = 0;
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+	r->nstore.u0.sival = nproc(argv);
+}
+void
+startstop(Node *r, Node *args)
+{
+	Node res;
+
+	if(args == 0)
+		error("startstop(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("startstop(pid): arg type");
+	if(rdebug) {
+		Lsym *s;
+		r->op = OCONST;
+		r->type = TINT;
+		r->nstore.u0.sival = remcondstartstop(res.nstore.u0.sival);
+		r->nstore.fmt = 'D';
+
+		s = look("_breakid");
+		if(s)
+			s->v->vstore.u0.sival = (int)r->nstore.u0.sival;
+	} else
+		msg(res.nstore.u0.sival, "startstop");
+	notes(res.nstore.u0.sival);
+	dostop(res.nstore.u0.sival);
+}
+
+void
+waitstop(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("waitstop(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("waitstop(pid): arg type");
+
+	Bflush(bout);
+	msg(res.nstore.u0.sival, "waitstop");
+	notes(res.nstore.u0.sival);
+	dostop(res.nstore.u0.sival);
+}
+
+void
+start(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("start(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("start(pid): arg type");
+
+	msg(res.nstore.u0.sival, "start");
+}
+
+void
+stop(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("stop(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("stop(pid): arg type");
+
+	Bflush(bout);
+	msg(res.nstore.u0.sival, "stop");
+	notes(res.nstore.u0.sival);
+	dostop(res.nstore.u0.sival);
+}
+
+void
+dokill(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("kill(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("kill(pid): arg type");
+
+	msg(res.nstore.u0.sival, "kill");
+	deinstall(res.nstore.u0.sival);
+}
+
+void
+status(Node *r, Node *args)
+{
+	Node res;
+	char *p;
+
+	USED(r);
+	if(args == 0)
+		error("status(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("status(pid): arg type");
+
+	p = getstatus(res.nstore.u0.sival);
+	r->nstore.u0.sstring = strnode(p);
+	r->op = OCONST;
+	r->nstore.fmt = 's';
+	r->type = TSTRING;
+}
+
+void
+reason(Node *r, Node *args)
+{
+	Node res;
+
+	if(args == 0)
+		error("reason(cause): no cause");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("reason(cause): arg type");
+
+	r->op = OCONST;
+	r->type = TSTRING;
+	r->nstore.fmt = 's';
+	r->nstore.u0.sstring = strnode((*machdata->excep)(cormap, rget));
+}
+
+void
+follow(Node *r, Node *args)
+{
+	int n, i;
+	Node res;
+	uvlong f[10];
+	List **tail, *l;
+
+	if(args == 0)
+		error("follow(addr): no addr");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("follow(addr): arg type");
+
+	n = (*machdata->foll)(cormap, res.nstore.u0.sival, rget, f);
+	if (n < 0)
+		error("follow(addr): %r");
+	tail = &r->nstore.u0.sl;
+	for(i = 0; i < n; i++) {
+		l = al(TINT);
+		l->lstore.u0.sival = f[i];
+		l->lstore.fmt = 'X';
+		*tail = l;
+		tail = &l->next;
+	}
+}
+
+void
+funcbound(Node *r, Node *args)
+{
+	int n;
+	Node res;
+	uvlong bounds[2];
+	List *l;
+
+	if(args == 0)
+		error("fnbound(addr): no addr");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("fnbound(addr): arg type");
+
+	n = fnbound(res.nstore.u0.sival, bounds);
+	if (n != 0) {
+		r->nstore.u0.sl = al(TINT);
+		l = r->nstore.u0.sl;
+		l->lstore.u0.sival = bounds[0];
+		l->lstore.fmt = 'X';
+		l->next = al(TINT);
+		l = l->next;
+		l->lstore.u0.sival = bounds[1];
+		l->lstore.fmt = 'X';
+	}
+}
+
+void
+setproc(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("setproc(pid): no pid");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("setproc(pid): arg type");
+
+	sproc(res.nstore.u0.sival);
+}
+
+void
+filepc(Node *r, Node *args)
+{
+	Node res;
+	char *p, c;
+
+	if(args == 0)
+		error("filepc(filename:line): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("filepc(filename:line): arg type");
+
+	p = strchr(res.nstore.u0.sstring->string, ':');
+	if(p == 0)
+		error("filepc(filename:line): bad arg format");
+
+	c = *p;
+	*p++ = '\0';
+	r->nstore.u0.sival = file2pc(res.nstore.u0.sstring->string, atoi(p));
+	p[-1] = c;
+	if(r->nstore.u0.sival == -1)
+		error("filepc(filename:line): can't find address");
+
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+}
+
+void
+interpret(Node *r, Node *args)
+{
+	Node res;
+	int isave;
+
+	if(args == 0)
+		error("interpret(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("interpret(string): arg type");
+
+	pushstr(&res);
+
+	isave = interactive;
+	interactive = 0;
+	r->nstore.u0.sival = yyparse();
+	interactive = isave;
+	popio();
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+}
+
+void
+include(Node *r, Node *args)
+{
+	Node res;
+	int isave;
+
+	if(args == 0)
+		error("include(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("include(string): arg type");
+
+	pushfile(res.nstore.u0.sstring->string);
+
+	isave = interactive;
+	interactive = 0;
+	r->nstore.u0.sival = yyparse();
+	interactive = isave;
+	popio();
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+}
+
+void
+rc(Node *r, Node *args)
+{
+	Node res;
+
+	char *p, *q;
+
+	USED(r);
+	if(args == 0)
+		error("error(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("error(string): arg type");
+
+	p = runcmd(res.nstore.u0.sstring->string);
+	q = strrchr(p, ':');
+	if (q)
+		p = q+1;
+
+	r->op = OCONST;
+	r->type = TSTRING;
+	r->nstore.u0.sstring = strnode(p);
+	r->nstore.fmt = 's';
+}
+
+void
+doerror(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(args == 0)
+		error("error(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("error(string): arg type");
+
+	error(res.nstore.u0.sstring->string);
+}
+
+void
+doaccess(Node *r, Node *args)
+{
+	Node res;
+
+	if(args == 0)
+		error("access(filename): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("access(filename): arg type");
+
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.u0.sival = 0;		
+	if(access(res.nstore.u0.sstring->string, OREAD) == 0)
+		r->nstore.u0.sival = 1;
+}
+
+void
+readfile(Node *r, Node *args)
+{
+	Node res;
+	int n, fd;
+	char *buf;
+	Dir *db;
+
+	if(args == 0)
+		error("readfile(filename): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("readfile(filename): arg type");
+
+	fd = open(res.nstore.u0.sstring->string, OREAD);
+	if(fd < 0)
+		return;
+
+	db = dirfstat(fd);
+	if(db == nil || db->length == 0)
+		n = 8192;
+	else
+		n = db->length;
+	free(db);
+
+	buf = gmalloc(n);
+	n = read(fd, buf, n);
+
+	if(n > 0) {
+		r->op = OCONST;
+		r->type = TSTRING;
+		r->nstore.u0.sstring = strnodlen(buf, n);
+		r->nstore.fmt = 's';
+	}
+	free(buf);
+	close(fd);
+}
+
+void
+getfile(Node *r, Node *args)
+{
+	int n;
+	char *p;
+	Node res;
+	String *s;
+	Biobuf *bp;
+	List **l, *new;
+
+	if(args == 0)
+		error("file(filename): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("file(filename): arg type");
+
+	r->op = OCONST;
+	r->type = TLIST;
+	r->nstore.u0.sl = 0;
+
+	p = res.nstore.u0.sstring->string;
+	bp = Bopen(p, OREAD);
+	if(bp == 0)
+		return;
+
+	l = &r->nstore.u0.sl;
+	for(;;) {
+		p = Brdline(bp, '\n');
+		n = BLINELEN(bp);
+		if(p == 0) {
+			if(n == 0)
+				break;
+			s = strnodlen(0, n);
+			Bread(bp, s->string, n);
+		}
+		else
+			s = strnodlen(p, n-1);
+
+		new = al(TSTRING);
+		new->lstore.u0.sstring = s;
+		new->lstore.fmt = 's';
+		*l = new;
+		l = &new->next;
+	}
+	Bterm(bp);
+}
+
+void
+cvtatof(Node *r, Node *args)
+{
+	Node res;
+
+	if(args == 0)
+		error("atof(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("atof(string): arg type");
+
+	r->op = OCONST;
+	r->type = TFLOAT;
+	r->nstore.u0.sfval = atof(res.nstore.u0.sstring->string);
+	r->nstore.fmt = 'f';
+}
+
+void
+cvtatoi(Node *r, Node *args)
+{
+	Node res;
+
+	if(args == 0)
+		error("atoi(string): arg count");
+	expr(args, &res);
+	if(res.type != TSTRING)
+		error("atoi(string): arg type");
+
+	r->op = OCONST;
+	r->type = TINT;
+	r->nstore.u0.sival = strtoul(res.nstore.u0.sstring->string, 0, 0);
+	r->nstore.fmt = 'D';
+}
+
+void
+cvtitoa(Node *r, Node *args)
+{
+	Node res;
+	char buf[128];
+
+	if(args == 0)
+		error("itoa(integer): arg count");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("itoa(integer): arg type");
+
+	sprint(buf, "%d", (int)res.nstore.u0.sival);
+	r->op = OCONST;
+	r->type = TSTRING;
+	r->nstore.u0.sstring = strnode(buf);
+	r->nstore.fmt = 's';
+}
+
+List*
+mapent(Map *m)
+{
+	int i;
+	List *l, *n, **t, *h;
+
+	h = 0;
+	t = &h;
+	for(i = 0; i < m->nsegs; i++) {
+		if(m->seg[i].inuse == 0)
+			continue;
+		l = al(TSTRING);
+		n = al(TLIST);
+		n->lstore.u0.sl = l;
+		*t = n;
+		t = &n->next;
+		l->lstore.u0.sstring = strnode(m->seg[i].name);
+		l->lstore.fmt = 's';
+		l->next = al(TINT);
+		l = l->next;
+		l->lstore.u0.sival = m->seg[i].b;
+		l->lstore.fmt = 'X';
+		l->next = al(TINT);
+		l = l->next;
+		l->lstore.u0.sival = m->seg[i].e;
+		l->lstore.fmt = 'X';
+		l->next = al(TINT);
+		l = l->next;
+		l->lstore.u0.sival = m->seg[i].f;
+		l->lstore.fmt = 'X';
+	}
+	return h;
+}
+
+void
+map(Node *r, Node *args)
+{
+	int i;
+	Map *m;
+	List *l;
+	char *ent;
+	Node *av[Maxarg], res;
+
+	na = 0;
+	flatten(av, args);
+
+	if(na != 0) {
+		expr(av[0], &res);
+		if(res.type != TLIST)
+			error("map(list): map needs a list");
+		if(listlen(res.nstore.u0.sl) != 4)
+			error("map(list): list must have 4 entries");
+
+		l = res.nstore.u0.sl;
+		if(l->type != TSTRING)
+			error("map name must be a string");
+		ent = l->lstore.u0.sstring->string;
+		m = symmap;
+		i = findseg(m, ent);
+		if(i < 0) {
+			m = cormap;
+			i = findseg(m, ent);
+		}
+		if(i < 0)
+			error("%s is not a map entry", ent);	
+		l = l->next;
+		if(l->type != TINT)
+			error("map entry not int");
+		m->seg[i].b = l->lstore.u0.sival;
+		if (strcmp(ent, "text") == 0)
+			textseg(l->lstore.u0.sival, &fhdr);
+		l = l->next;
+		if(l->type != TINT)
+			error("map entry not int");
+		m->seg[i].e = l->lstore.u0.sival;
+		l = l->next;
+		if(l->type != TINT)
+			error("map entry not int");
+		m->seg[i].f = l->lstore.u0.sival;
+	}
+
+	r->type = TLIST;
+	r->nstore.u0.sl = 0;
+	if(symmap)
+		r->nstore.u0.sl = mapent(symmap);
+	if(cormap) {
+		if(r->nstore.u0.sl == 0)
+			r->nstore.u0.sl = mapent(cormap);
+		else {
+			for(l = r->nstore.u0.sl; l->next; l = l->next)
+				;
+			l->next = mapent(cormap);
+		}
+	}
+}
+
+void 
+flatten(Node **av, Node *n)
+{
+	if(n == 0)
+		return;
+
+	switch(n->op) {
+	case OLIST:
+		flatten(av, n->left);
+		flatten(av, n->right);
+		break;
+	default:
+		av[na++] = n;
+		if(na >= Maxarg)
+			error("too many function arguments");
+		break;
+	}
+}
+
+void
+strace(Node *r, Node *args)
+{
+	Node *av[Maxarg], *n, res;
+	ulong pc, sp;
+
+	na = 0;
+	flatten(av, args);
+	if(na != 3)
+		error("strace(pc, sp, link): arg count");
+
+	n = av[0];
+	expr(n, &res);
+	if(res.type != TINT)
+		error("strace(pc, sp, link): pc bad type");
+	pc = res.nstore.u0.sival;
+
+	n = av[1];
+	expr(n, &res);
+	if(res.type != TINT)
+		error("strace(pc, sp, link): sp bad type");
+	sp = res.nstore.u0.sival;
+
+	n = av[2];
+	expr(n, &res);
+	if(res.type != TINT)
+		error("strace(pc, sp, link): link bad type");
+
+	tracelist = 0;
+	if ((*machdata->ctrace)(cormap, pc, sp, res.nstore.u0.sival, trlist) <= 0)
+		error("no stack frame");
+	r->type = TLIST;
+	r->nstore.u0.sl = tracelist;
+}
+
+void
+regerror(char *msg)
+{
+	error(msg);
+}
+
+void
+regexp(Node *r, Node *args)
+{
+	Node res;
+	Reprog *rp;
+	Node *av[Maxarg];
+
+	na = 0;
+	flatten(av, args);
+	if(na != 2)
+		error("regexp(pattern, string): arg count");
+	expr(av[0], &res);
+	if(res.type != TSTRING)
+		error("regexp(pattern, string): pattern must be string");
+	rp = regcomp(res.nstore.u0.sstring->string);
+	if(rp == 0)
+		return;
+
+	expr(av[1], &res);
+	if(res.type != TSTRING)
+		error("regexp(pattern, string): bad string");
+
+	r->nstore.fmt = 'D';
+	r->type = TINT;
+	r->nstore.u0.sival = regexec(rp, res.nstore.u0.sstring->string, 0, 0);
+	free(rp);
+}
+
+char vfmt[] = "aBbcCdDfFgGiIoOqQrRsuUVxXYZ";
+
+void
+fmt(Node *r, Node *args)
+{
+	Node res;
+	Node *av[Maxarg];
+
+	na = 0;
+	flatten(av, args);
+	if(na != 2)
+		error("fmt(obj, fmt): arg count");
+	expr(av[1], &res);
+	if(res.type != TINT || strchr(vfmt, res.nstore.u0.sival) == 0)
+		error("fmt(obj, fmt): bad format '%c'", (char)res.nstore.u0.sival);
+	expr(av[0], r);
+	r->nstore.fmt = res.nstore.u0.sival;
+}
+
+void
+patom(char type, Store *res)
+{
+	int i;
+	char buf[512];
+	extern char *typenames[];
+
+	switch(res->fmt) {
+	case 'c':
+		Bprint(bout, "%c", (int)res->u0.sival);
+		break;
+	case 'C':
+		if(res->u0.sival < ' ' || res->u0.sival >= 0x7f)
+			Bprint(bout, "%3d", (int)res->u0.sival&0xff);
+		else
+			Bprint(bout, "%3c", (int)res->u0.sival);
+		break;
+	case 'r':
+		Bprint(bout, "%C", (int)res->u0.sival);
+		break;
+	case 'B':
+		memset(buf, '0', 34);
+		buf[1] = 'b';
+		for(i = 0; i < 32; i++) {
+			if(res->u0.sival & (1<<i))
+				buf[33-i] = '1';
+		}
+		buf[35] = '\0';
+		Bprint(bout, "%s", buf);
+		break;
+	case 'b':
+		Bprint(bout, "%3d", (int)res->u0.sival&0xff);
+		break;
+	case 'X':
+		Bprint(bout, "%.8ux", (int)res->u0.sival);
+		break;
+	case 'x':
+		Bprint(bout, "%.4ux", (int)res->u0.sival&0xffff);
+		break;
+	case 'Y':
+		Bprint(bout, "%.16llux", res->u0.sival);
+		break;
+	case 'D':
+		Bprint(bout, "%d", (int)res->u0.sival);
+		break;
+	case 'd':
+		Bprint(bout, "%d", (ushort)res->u0.sival);
+		break;
+	case 'u':
+		Bprint(bout, "%ud", (int)res->u0.sival&0xffff);
+		break;
+	case 'U':
+		Bprint(bout, "%lud", (ulong)res->u0.sival);
+		break;
+	case 'Z':
+		Bprint(bout, "%llud", res->u0.sival);
+		break;
+	case 'V':
+		Bprint(bout, "%lld", res->u0.sival);
+		break;
+	case 'o':
+		Bprint(bout, "0%.11uo", (int)res->u0.sival&0xffff);
+		break;
+	case 'O':
+		Bprint(bout, "0%.6uo", (int)res->u0.sival);
+		break;
+	case 'q':
+		Bprint(bout, "0%.11o", (short)(res->u0.sival&0xffff));
+		break;
+	case 'Q':
+		Bprint(bout, "0%.6o", (int)res->u0.sival);
+		break;
+	case 'f':
+	case 'F':
+		if(type != TFLOAT)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
+		else
+			Bprint(bout, "%g", res->u0.sfval);
+		break;
+	case 's':
+	case 'g':
+	case 'G':
+		if(type != TSTRING)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
+		else
+			Bwrite(bout, res->u0.sstring->string, res->u0.sstring->len);
+		break;
+	case 'R':
+		if(type != TSTRING)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
+		else
+			Bprint(bout, "%S", (Rune*)res->u0.sstring->string);
+		break;
+	case 'a':
+	case 'A':
+		symoff(buf, sizeof(buf), res->u0.sival, CANY);
+		Bprint(bout, "%s", buf);
+		break;
+	case 'I':
+	case 'i':
+		if(type != TINT)
+			Bprint(bout, "*%c<%s>*", res->fmt, typenames[type]);
+		else {
+			if ((*machdata->das)(symmap, res->u0.sival, res->fmt, buf, sizeof(buf)) < 0)
+				Bprint(bout, "no instruction: %r");
+			else
+				Bprint(bout, "%s", buf);
+		}
+		break;
+	}
+}
+
+void
+blprint(List *l)
+{
+	Bprint(bout, "{");
+	while(l) {
+		switch(l->type) {
+		default:
+			patom(l->type, &l->lstore);
+			break;
+		case TSTRING:
+			Bputc(bout, '"');
+			patom(l->type, &l->lstore);
+			Bputc(bout, '"');
+			break;
+		case TLIST:
+			blprint(l->lstore.u0.sl);
+			break;
+		case TCODE:
+			pcode(l->lstore.u0.scc, 0);
+			break;
+		}
+		l = l->next;
+		if(l)
+			Bprint(bout, ", ");
+	}
+	Bprint(bout, "}");
+}
+
+int
+comx(Node res)
+{
+	Lsym *sl;
+	Node *n, xx;
+
+	if(res.nstore.fmt != 'a' && res.nstore.fmt != 'A')
+		return 0;
+
+	if(res.nstore.comt == 0 || res.nstore.comt->base == 0)
+		return 0;
+
+	sl = res.nstore.comt->base;
+	if(sl->proc) {
+		res.left = ZN;
+		res.right = ZN;
+		n = an(ONAME, ZN, ZN);
+		n->sym = sl;
+		n = an(OCALL, n, &res);
+			n->left->sym = sl;
+		expr(n, &xx);
+		return 1;
+	}
+	print("(%s)", sl->name);
+	return 0;
+}
+
+void
+bprint(Node *r, Node *args)
+{
+	int i, nas;
+	Node res, *av[Maxarg];
+
+	USED(r);
+	na = 0;
+	flatten(av, args);
+	nas = na;
+	for(i = 0; i < nas; i++) {
+		expr(av[i], &res);
+		switch(res.type) {
+		default:
+			if(comx(res))
+				break;
+			patom(res.type, &res.nstore);
+			break;
+		case TCODE:
+			pcode(res.nstore.u0.scc, 0);
+			break;
+		case TLIST:
+			blprint(res.nstore.u0.sl);
+			break;
+		}
+	}
+	if(ret == 0)
+		Bputc(bout, '\n');
+}
+
+void
+printto(Node *r, Node *args)
+{
+	int fd;
+	Biobuf *b;
+	int i, nas;
+	Node res, *av[Maxarg];
+
+	USED(r);
+	na = 0;
+	flatten(av, args);
+	nas = na;
+
+	expr(av[0], &res);
+	if(res.type != TSTRING)
+		error("printto(string, ...): need string");
+
+	fd = create(res.nstore.u0.sstring->string, OWRITE, 0666);
+	if(fd < 0)
+		fd = open(res.nstore.u0.sstring->string, OWRITE);
+	if(fd < 0)
+		error("printto: open %s: %r", res.nstore.u0.sstring->string);
+
+	b = gmalloc(sizeof(Biobuf));
+	Binit(b, fd, OWRITE);
+
+	Bflush(bout);
+	io[iop++] = bout;
+	bout = b;
+
+	for(i = 1; i < nas; i++) {
+		expr(av[i], &res);
+		switch(res.type) {
+		default:
+			if(comx(res))
+				break;
+			patom(res.type, &res.nstore);
+			break;
+		case TLIST:
+			blprint(res.nstore.u0.sl);
+			break;
+		}
+	}
+	if(ret == 0)
+		Bputc(bout, '\n');
+
+	Bterm(b);
+	close(fd);
+	free(b);
+	bout = io[--iop];
+}
+
+void
+pcfile(Node *r, Node *args)
+{
+	Node res;
+	char *p, buf[128];
+
+	if(args == 0)
+		error("pcfile(addr): arg count");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("pcfile(addr): arg type");
+
+	r->type = TSTRING;
+	r->nstore.fmt = 's';
+	if(fileline(buf, sizeof(buf), res.nstore.u0.sival) == 0) {
+		r->nstore.u0.sstring = strnode("?file?");
+		return;
+	}
+	p = strrchr(buf, ':');
+	if(p == 0)
+		error("pcfile(addr): funny file %s", buf);
+	*p = '\0';
+	r->nstore.u0.sstring = strnode(buf);	
+}
+
+void
+pcline(Node *r, Node *args)
+{
+	Node res;
+	char *p, buf[128];
+
+	if(args == 0)
+		error("pcline(addr): arg count");
+	expr(args, &res);
+	if(res.type != TINT)
+		error("pcline(addr): arg type");
+
+	r->type = TINT;
+	r->nstore.fmt = 'D';
+	if(fileline(buf, sizeof(buf), res.nstore.u0.sival) == 0) {
+		r->nstore.u0.sival = 0;
+		return;
+	}
+
+	p = strrchr(buf, ':');
+	if(p == 0)
+		error("pcline(addr): funny file %s", buf);
+	r->nstore.u0.sival = atoi(p+1);	
+}
+
+void
+_bpcondset(Node *r, Node *args)
+{
+	Node id, p, addr, conds;
+	Node *av[Maxarg];
+	List *l;
+	char *op;
+	List *val;
+	ulong pid;
+
+	USED(r);
+
+	if(!rdebug)
+		error("_bpcondset(id, pid, addr, conds): only available with remote debugger\n");
+
+	if(args == 0)
+		error("_bpcondset(id, pid, addr, conds): not enough args");
+	na = 0;
+	flatten(av, args);
+	if(na != 4)
+		error("_bpcondset(id, pid, addr, conds): %s args",
+			na > 4 ? "too many" : "too few");
+	expr(av[0], &id);
+	expr(av[1], &p);
+	expr(av[2], &addr);
+	expr(av[3], &conds);
+	if(id.type != TINT)
+		error("_bpcondset(id, pid, addr, conds): id: integer expected");
+	if(p.type != TINT)
+		error("_bpcondset(pid, addr, conds): pid: integer expected");
+	if(addr.type != TINT)
+		error("_bpcondset(pid, addr, conds): addr: integer expected");
+	if(conds.type != TLIST)
+		error("_bpcondset(pid, addr, conds): conds: list expected");
+	l = conds.nstore.u0.sl;
+	remcondset('n', (ulong)id.nstore.u0.sival);
+	pid = (ulong)p.nstore.u0.sival;
+	if (pid != 0)
+		remcondset('k', pid);
+	while(l != nil) {
+		if(l->type != TLIST || listlen(l->lstore.u0.sl) != 2)
+			error("_bpcondset(addr, list): list elements are {\"op\", val} pairs");
+		if(l->lstore.u0.sl->type != TSTRING)
+			error("_bpcondset(addr, list): list elements are {string, val} pairs");
+		op = l->lstore.u0.sl->lstore.u0.sstring->string;
+		val = l->lstore.u0.sl->next;
+		if(val->type != TINT)
+			error("_bpcondset(addr, list): list elements are {string, int} pairs");
+		remcondset(op[0], (ulong)val->lstore.u0.sival);
+		l = l->next;
+	}
+	remcondset('b', (ulong)addr.nstore.u0.sival);
+}
+
+void
+_bpconddel(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	if(!rdebug)
+		error("_bpconddel(id): only available with remote debugger\n");
+
+	expr(args, &res);
+	if(res.type != TINT)
+		error("_bpconddel(id): arg type");
+
+	remcondset('d', (ulong)res.nstore.u0.sival);
+}
+
+void
+setdebug(Node *r, Node *args)
+{
+	Node res;
+
+	USED(r);
+	expr(args, &res);
+	if (res.type != TINT)
+		error("debug(type): bad type");
+	setdbg_opt((char)res.nstore.u0.sival, 1);
+}
+
+
+void
+setdbg_opt(char c, int prflag)
+{
+	switch(c) {
+	case 'p':
+		if (protodebug) {
+			protodebug = 0;
+			if (prflag)
+				print("Serial protocol debug is OFF\n");
+		} else {
+			protodebug = 1;
+			if (prflag)
+				print("Serial protocol debug is ON\n");
+		}
+		break;
+	default:
+		print("Invalid debug flag(%c), supported values: p\n", c);
+		break;
+	}
+}
--- /dev/null
+++ b/utils/acid/dbg.y
@@ -1,0 +1,402 @@
+%{
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+%}
+
+%union
+{
+	Node	*node;
+	Lsym	*sym;
+	ulong	ival;
+	float	fval;
+	String	*string;
+}
+
+%type <node> expr monexpr term stmnt name args zexpr slist
+%type <node> member members mname castexpr idlist
+%type <sym> zname
+
+%left	';'
+%right	'='
+%left	Tfmt
+%left	Toror
+%left	Tandand
+%left	'|'
+%left	'^'
+%left	'&'
+%left	Teq Tneq
+%left	'<' '>' Tleq Tgeq
+%left	Tlsh Trsh
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	Tdec Tinc Tindir '.' '[' '('
+
+%token <sym>	Tid
+%token <ival>	Tconst Tfmt
+%token <fval>	Tfconst
+%token <string>	Tstring
+%token Tif Tdo Tthen Telse Twhile Tloop Thead Ttail Tappend Tfn Tret Tlocal
+%token Tcomplex Twhat Tdelete Teval
+
+%%
+
+prog		: 
+		| prog bigstmnt
+		;
+
+bigstmnt	: stmnt
+		{
+			execute($1);
+			gc();
+			if(interactive)
+				Bprint(bout, "acid: ");
+		}
+		| Tfn Tid '(' args ')' zsemi '{' slist '}'
+		{
+			if($2->builtin)
+				print("warning: %s() is a builtin; definition ignored\n", $2->name);
+			else
+				$2->proc = an(OLIST, $4, $8);
+		}
+		| Tcomplex name '{' members '}' ';'
+		{
+			defcomplex($2, $4);
+		}
+		;
+
+zsemi		:
+		| ';' zsemi
+
+members		: member
+		| members member
+		{
+			$$ = an(OLIST, $1, $2);
+		}
+		;
+
+mname		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		;
+
+member		: Tconst Tconst mname ';'
+		{
+			$3->nstore.u0.sival = $2;
+			$3->nstore.fmt = $1;
+			$$ = $3;
+		}
+		| Tconst mname Tconst mname ';'
+		{
+			$4->nstore.u0.sival = $3;
+			$4->nstore.fmt = $1;
+			$4->right = $2;
+			$$ = $4;
+		}
+		| mname Tconst mname ';'
+		{
+			$3->nstore.u0.sival = $2;
+			$3->left = $1;
+			$$ = $3;
+		}
+		| '{' members '}' ';'
+		{
+			$$ = an(OCTRUCT, $2, ZN);
+		}
+		;
+
+zname		: 
+		{ $$ = 0; }
+		| Tid
+		;
+
+slist		: stmnt
+		| slist stmnt
+		{
+			$$ = an(OLIST, $1, $2);
+		}
+		;
+
+stmnt		: zexpr ';'
+		| '{' slist '}'
+		{
+			$$ = $2;
+		}
+		| Tif expr Tthen stmnt
+		{
+			$$ = an(OIF, $2, $4);
+		}
+		| Tif expr Tthen stmnt Telse stmnt
+		{
+			$$ = an(OIF, $2, an(OELSE, $4, $6));
+		}
+		| Tloop expr ',' expr Tdo stmnt
+		{
+			$$ = an(ODO, an(OLIST, $2, $4), $6);
+		}
+		| Twhile expr Tdo stmnt
+		{
+			$$ = an(OWHILE, $2, $4);
+		}
+		| Tret expr ';'
+		{
+			$$ = an(ORET, $2, ZN);
+		}
+		| Tlocal idlist
+		{
+			$$ = an(OLOCAL, $2, ZN);
+		}
+		| Tcomplex Tid name ';'
+		{
+			$$ = an(OCOMPLEX, $3, ZN);
+			$$->sym = $2;
+		}
+		;
+
+idlist		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		| idlist ',' Tid
+		{
+			$$ = an(ONAME, $1, ZN);
+			$$->sym = $3;
+		}
+		;
+
+zexpr		:
+		{ $$ = 0; }
+		| expr
+		;
+
+expr		: castexpr
+		| expr '*' expr
+		{
+			$$ = an(OMUL, $1, $3); 
+		}
+		| expr '/' expr
+		{
+			$$ = an(ODIV, $1, $3);
+		}
+		| expr '%' expr
+		{
+			$$ = an(OMOD, $1, $3);
+		}
+		| expr '+' expr
+		{
+			$$ = an(OADD, $1, $3);
+		}
+		| expr '-' expr
+		{
+			$$ = an(OSUB, $1, $3);
+		}
+		| expr Trsh expr
+		{
+			$$ = an(ORSH, $1, $3);
+		}
+		| expr Tlsh expr
+		{
+			$$ = an(OLSH, $1, $3);
+		}
+		| expr '<' expr
+		{
+			$$ = an(OLT, $1, $3);
+		}
+		| expr '>' expr
+		{
+			$$ = an(OGT, $1, $3);
+		}
+		| expr Tleq expr
+		{
+			$$ = an(OLEQ, $1, $3);
+		}
+		| expr Tgeq expr
+		{
+			$$ = an(OGEQ, $1, $3);
+		}
+		| expr Teq expr
+		{
+			$$ = an(OEQ, $1, $3);
+		}
+		| expr Tneq expr
+		{
+			$$ = an(ONEQ, $1, $3);
+		}
+		| expr '&' expr
+		{
+			$$ = an(OLAND, $1, $3);
+		}
+		| expr '^' expr
+		{
+			$$ = an(OXOR, $1, $3);
+		}
+		| expr '|' expr
+		{
+			$$ = an(OLOR, $1, $3);
+		}
+		| expr Tandand expr
+		{
+			$$ = an(OCAND, $1, $3);
+		}
+		| expr Toror expr
+		{
+			$$ = an(OCOR, $1, $3);
+		}
+		| expr '=' expr
+		{
+			$$ = an(OASGN, $1, $3);
+		}
+		| expr Tfmt
+		{
+			$$ = an(OFMT, $1, con($2));
+		}
+		;
+
+castexpr	: monexpr
+		| '(' Tid ')' monexpr
+		{
+			$$ = an(OCAST, $4, ZN);
+			$$->sym = $2;
+		}
+		;
+
+monexpr		: term
+		| '*' monexpr 
+		{
+			$$ = an(OINDM, $2, ZN);
+		}
+		| '@' monexpr 
+		{
+			$$ = an(OINDC, $2, ZN);
+		}
+		| '+' monexpr
+		{
+			$$ = con(0);
+			$$ = an(OADD, $2, $$);
+		}
+		| '-' monexpr
+		{
+			$$ = con(0);
+			$$ = an(OSUB, $$, $2);
+		}
+		| Tdec monexpr
+		{
+			$$ = an(OEDEC, $2, ZN);
+		}
+		| Tinc monexpr
+		{
+			$$ = an(OEINC, $2, ZN);
+		}
+		| Thead monexpr
+		{
+			$$ = an(OHEAD, $2, ZN);
+		}
+		| Ttail monexpr
+		{
+			$$ = an(OTAIL, $2, ZN);
+		}
+		| Tappend monexpr ',' monexpr
+		{
+			$$ = an(OAPPEND, $2, $4);
+		}
+		| Tdelete monexpr ',' monexpr
+		{
+			$$ = an(ODELETE, $2, $4);
+		}
+		| '!' monexpr
+		{
+			$$ = an(ONOT, $2, ZN);
+		}
+		| '~' monexpr
+		{
+			$$ = an(OXOR, $2, con(-1));
+		}
+		| Teval monexpr
+		{
+			$$ = an(OEVAL, $2, ZN);	
+		}
+		;
+
+term		: '(' expr ')'
+		{
+			$$ = $2;
+		}
+		| '{' args '}'
+		{
+			$$ = an(OCTRUCT, $2, ZN);
+		}
+		| term '[' expr ']'
+		{
+			$$ = an(OINDEX, $1, $3);
+		}
+		| term Tdec
+		{
+			$$ = an(OPDEC, $1, ZN);
+		}
+		| term '.' Tid
+		{
+			$$ = an(ODOT, $1, ZN);
+			$$->sym = $3;
+		}
+		| term Tindir Tid
+		{
+			$$ = an(ODOT, an(OINDM, $1, ZN), ZN);
+			$$->sym = $3;
+		}
+		| term Tinc
+		{
+			$$ = an(OPINC, $1, ZN);
+		}
+		| name '(' args ')'
+		{
+			$$ = an(OCALL, $1, $3);
+		}
+		| name
+		| Tconst
+		{
+			$$ = con($1);
+		}
+		| Tfconst
+		{
+			$$ = an(OCONST, ZN, ZN);
+			$$->type = TFLOAT;
+			$$->nstore.fmt = 'f';
+			$$->nstore.u0.sfval = $1;
+		}
+		| Tstring
+		{
+			$$ = an(OCONST, ZN, ZN);
+			$$->type = TSTRING;
+			$$->nstore.u0.sstring = $1;
+			$$->nstore.fmt = 's';
+		}
+		| Twhat zname
+		{
+			$$ = an(OWHAT, ZN, ZN);
+			$$->sym = $2;
+		}
+		;
+
+name		: Tid
+		{
+			$$ = an(ONAME, ZN, ZN);
+			$$->sym = $1;
+		}
+		| Tid ':' name
+		{
+			$$ = an(OFRAME, $3, ZN);
+			$$->sym = $1;
+		}
+		;
+
+args		: zexpr
+		| args ','  zexpr
+		{
+			$$ = an(OLIST, $1, $3);
+		}
+		;
--- /dev/null
+++ b/utils/acid/dot.c
@@ -1,0 +1,152 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+Type*
+srch(Type *t, char *s)
+{
+	Type *f;
+
+	f = 0;
+	while(t) {
+		if(strcmp(t->tag->name, s) == 0) {
+			if(f == 0 || t->depth < f->depth)
+				f = t;
+		}
+		t = t->next;
+	}
+	return f;
+}
+
+void
+odot(Node *n, Node *r)
+{
+	char *s;
+	Type *t;
+	Node res;
+	ulong addr;
+
+	s = n->sym->name;
+	if(s == 0)
+		fatal("dodot: no tag");
+
+	expr(n->left, &res);
+	if(res.nstore.comt == 0)
+		error("no type specified for (expr).%s", s);
+
+	if(res.type != TINT)
+		error("pointer must be integer for (expr).%s", s);
+
+	t = srch(res.nstore.comt, s);
+	if(t == 0)
+		error("no tag for (expr).%s", s);
+
+	/* Propagate types */
+	if(t->type) 
+		r->nstore.comt = t->type->lt;
+	
+	addr = res.nstore.u0.sival+t->offset;
+	if(t->fmt == 'a') {
+		r->op = OCONST;
+		r->nstore.fmt = 'a';
+		r->type = TINT;
+		r->nstore.u0.sival = addr;
+	}
+	else 
+		indir(cormap, addr, t->fmt, r);
+
+}
+
+static Type **tail;
+static Lsym *base;
+
+void
+buildtype(Node *m, int d)
+{
+	Type *t;
+
+	if(m == ZN)
+		return;
+
+	switch(m->op) {
+	case OLIST:
+		buildtype(m->left, d);		
+		buildtype(m->right, d);
+		break;
+
+	case OCTRUCT:
+		buildtype(m->left, d+1);
+		break;
+	default:
+		t = gmalloc(sizeof(Type));
+		t->next = 0;
+		t->depth = d;
+		t->tag = m->sym;
+		t->base = base;
+		t->offset = m->nstore.u0.sival;
+		if(m->left) {
+			t->type = m->left->sym;
+			t->fmt = 'a';			
+		}
+		else {
+			t->type = 0;
+			if(m->right)
+				t->type = m->right->sym;
+			t->fmt = m->nstore.fmt;
+		}
+
+		*tail = t;
+		tail = &t->next;
+	}			
+}
+
+void
+defcomplex(Node *tn, Node *m)
+{
+	tail = &tn->sym->lt;
+	base = tn->sym;
+	buildtype(m, 0);
+}
+
+void
+decl(Node *n)
+{
+	Node *l;
+	Value *v;
+	Frtype *f;
+	Lsym *type;
+
+	type = n->sym;
+	if(type->lt == 0)
+		error("%s is not a complex type", type->name);
+
+	l = n->left;
+	if(l->op == ONAME) {
+		v = l->sym->v;
+		v->vstore.comt = type->lt;
+		v->vstore.fmt = 'a';
+		return;
+	}
+
+	/*
+	 * Frame declaration
+	 */
+	for(f = l->sym->local; f; f = f->next) {
+		if(f->var == l->left->sym) {
+			f->type = n->sym->lt;
+			return;
+		}
+	}
+	f = gmalloc(sizeof(Frtype));
+	if(f == 0)
+		fatal("out of memory");
+
+	f->type = type->lt;
+
+	f->var = l->left->sym;
+	f->next = l->sym->local;
+	l->sym->local = f;
+}
--- /dev/null
+++ b/utils/acid/exec.c
@@ -1,0 +1,490 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+void
+error(char *fmt, ...)
+{
+	int i;
+	char buf[2048];
+	va_list arg;
+
+	/* Unstack io channels */
+	if(iop != 0) {
+		for(i = 1; i < iop; i++)
+			Bterm(io[i]);
+		bout = io[0];
+		iop = 0;
+	}
+
+	ret = 0;
+	gotint = 0;
+	Bflush(bout);
+	if(silent)
+		silent = 0;
+	else {
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		fprint(2, "%L: (error) %s\n", buf);
+	}
+	while(popio())
+		;
+	interactive = 1;
+	longjmp(err, 1);
+}
+
+void
+unwind(void)
+{
+	int i;
+	Lsym *s;
+	Value *v;
+
+	for(i = 0; i < Hashsize; i++) {
+		for(s = hash[i]; s; s = s->hash) {
+			while(s->v->pop) {
+				v = s->v->pop;
+				free(s->v);
+				s->v = v;
+			}
+		}
+	}
+}
+
+void
+execute(Node *n)
+{
+	Value *v;
+	Lsym *sl;
+	Node *l, *r;
+	int i, s, e;
+	Node res, xx;
+	static int stmnt;
+
+	if(gotint)
+		error("interrupted");
+
+	if(n == 0)
+		return;
+
+	if(stmnt++ > 5000) {
+		Bflush(bout);
+		stmnt = 0;
+	}
+
+	l = n->left;
+	r = n->right;
+	res.right = 0;
+	res.left = 0;
+	res.sym = 0;
+
+	switch(n->op) {
+	default:
+		expr(n, &res);
+		if(ret || (res.type == TLIST && res.nstore.u0.sl == 0))
+			break;
+		prnt->right = &res;
+		xx.right = 0;
+		xx.left = 0;
+		xx.sym = 0;
+		expr(prnt, &xx);
+		break;
+	case OASGN:
+	case OCALL:
+		expr(n, &res);
+		break;
+	case OCOMPLEX:
+		decl(n);
+		break;
+	case OLOCAL:
+		for(n = n->left; n; n = n->left) {
+			if(ret == 0)
+				error("local not in function");
+			sl = n->sym;
+			if(sl->v->ret == ret)
+				error("%s declared twice", sl->name);
+			v = gmalloc(sizeof(Value));
+			v->ret = ret;
+			v->pop = sl->v;
+			sl->v = v;
+			v->scope = 0;
+			*(ret->tail) = sl;
+			ret->tail = &v->scope;
+			v->set = 0;
+		}
+		break;
+	case ORET:
+		if(ret == 0)
+			error("return not in function");
+		expr(n->left, ret->val);
+		longjmp(ret->rlab, 1);
+	case OLIST:
+		execute(n->left);
+		execute(n->right);
+		break;
+	case OIF:
+		expr(l, &res);
+		if(r && r->op == OELSE) {
+			if(boolx(&res))
+				execute(r->left);
+			else
+				execute(r->right);
+		}
+		else if(boolx(&res))
+			execute(r);
+		break;
+	case OWHILE:
+		for(;;) {
+			expr(l, &res);
+			if(!boolx(&res))
+				break;
+			execute(r);
+		}
+		break;
+	case ODO:
+		expr(l->left, &res);
+		if(res.type != TINT)
+			error("loop must have integer start");
+		s = res.nstore.u0.sival;
+		expr(l->right, &res);
+		if(res.type != TINT)
+			error("loop must have integer end");
+		e = res.nstore.u0.sival;
+		for(i = s; i <= e; i++)
+			execute(r);
+		break;
+	}
+}
+
+int
+boolx(Node *n)
+{
+	int truef = 0;
+
+	if(n->op != OCONST)
+		fatal("bool: not const");
+
+	switch(n->type) {
+	case TINT:
+		if(n->nstore.u0.sival != 0)
+			truef = 1;
+		break;
+	case TFLOAT:
+		if(n->nstore.u0.sfval != 0.0)
+			truef = 1;
+		break;
+	case TSTRING:
+		if(n->nstore.u0.sstring->len)
+			truef = 1;
+		break;
+	case TLIST:
+		if(n->nstore.u0.sl)
+			truef = 1;
+		break;
+	}
+	return truef;
+}
+
+void
+convflt(Node *r, char *flt)
+{
+	char c;
+
+	c = flt[0];
+	if(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
+		r->type = TSTRING;
+		r->nstore.fmt = 's';
+		r->nstore.u0.sstring = strnode(flt);
+	}
+	else {
+		r->type = TFLOAT;
+		r->nstore.u0.sfval = atof(flt);
+	}
+}
+
+void
+indir(Map *m, ulong addr, char fmt, Node *r)
+{
+	int i;
+	ulong ival;
+	uvlong vval;
+	int ret;
+	uchar cval;
+	ushort sval;
+	char buf[512], reg[12];
+
+	r->op = OCONST;
+	r->nstore.fmt = fmt;
+	switch(fmt) {
+	default:
+		error("bad pointer format '%c' for *", fmt);
+	case 'c':
+	case 'C':
+	case 'b':
+		r->type = TINT;
+		ret = get1(m, addr, &cval, 1);
+		if (ret < 0)
+			error("indir: %r");
+		r->nstore.u0.sival = cval;
+		break;
+	case 'x':
+	case 'd':
+	case 'u':
+	case 'o':
+	case 'q':
+	case 'r':
+		r->type = TINT;
+		ret = get2(m, addr, &sval);
+		if (ret < 0)
+			error("indir: %r");
+		r->nstore.u0.sival = sval;
+		break;
+	case 'a':
+	case 'A':
+	case 'B':
+	case 'X':
+	case 'D':
+	case 'U':
+	case 'O':
+	case 'Q':
+		r->type = TINT;
+		ret = get4(m, addr, &ival);
+		if (ret < 0)
+			error("indir: %r");
+		r->nstore.u0.sival = ival;
+		break;
+	case 'V':
+	case 'Y':
+	case 'Z':
+		r->type = TINT;
+		ret = get8(m, addr, &vval);
+		if (ret < 0)
+			error("indir: %r");
+		r->nstore.u0.sival = vval;
+		break;
+	case 's':
+		r->type = TSTRING;
+		for(i = 0; i < sizeof(buf)-1; i++) {
+			ret = get1(m, addr, (uchar*)&buf[i], 1);
+			if (ret < 0)
+				error("indir: %r");
+			addr++;
+			if(buf[i] == '\0')
+				break;
+		}
+		buf[i] = 0;
+		if(i == 0)
+			strcpy(buf, "(null)");
+		r->nstore.u0.sstring = strnode(buf);
+		break;
+	case 'R':
+		r->type = TSTRING;
+		for(i = 0; i < sizeof(buf)-2; i += 2) {
+			ret = get1(m, addr, (uchar*)&buf[i], 2);
+			if (ret < 0)
+				error("indir: %r");
+			addr += 2;
+			if(buf[i] == 0 && buf[i+1] == 0)
+				break;
+		}
+		buf[i++] = 0;
+		buf[i] = 0;
+		r->nstore.u0.sstring = runenode((Rune*)buf);
+		break;
+	case 'i':
+	case 'I':
+		if ((*machdata->das)(m, addr, fmt, buf, sizeof(buf)) < 0)
+			error("indir: %r");
+		r->type = TSTRING;
+		r->nstore.fmt = 's';
+		r->nstore.u0.sstring = strnode(buf);
+		break;
+	case 'f':
+		ret = get1(m, addr, (uchar*)buf, mach->szfloat);
+		if (ret < 0)
+			error("indir: %r");
+		machdata->sftos(buf, sizeof(buf), (void*) buf);
+		convflt(r, buf);
+		break;
+	case 'g':
+		ret = get1(m, addr, (uchar*)buf, mach->szfloat);
+		if (ret < 0)
+			error("indir: %r");
+		machdata->sftos(buf, sizeof(buf), (void*) buf);
+		r->type = TSTRING;
+		r->nstore.u0.sstring = strnode(buf);
+		break;
+	case 'F':
+		ret = get1(m, addr, (uchar*)buf, mach->szdouble);
+		if (ret < 0)
+			error("indir: %r");
+		machdata->dftos(buf, sizeof(buf), (void*) buf);
+		convflt(r, buf);
+		break;
+	case '3':	/* little endian ieee 80 with hole in bytes 8&9 */
+		ret = get1(m, addr, (uchar*)reg, 10);
+		if (ret < 0)
+			error("indir: %r");
+		memmove(reg+10, reg+8, 2);	/* open hole */
+		memset(reg+8, 0, 2);		/* fill it */
+		leieee80ftos(buf, sizeof(buf), reg);
+		convflt(r, buf);
+		break;
+	case '8':	/* big-endian ieee 80 */
+		ret = get1(m, addr, (uchar*)reg, 10);
+		if (ret < 0)
+			error("indir: %r");
+		beieee80ftos(buf, sizeof(buf), reg);
+		convflt(r, buf);
+		break;
+	case 'G':
+		ret = get1(m, addr, (uchar*)buf, mach->szdouble);
+		if (ret < 0)
+			error("indir: %r");
+		machdata->dftos(buf, sizeof(buf), (void*) buf);
+		r->type = TSTRING;
+		r->nstore.u0.sstring = strnode(buf);
+		break;
+	}
+}
+
+void
+windir(Map *m, Node *addr, Node *rval, Node *r)
+{
+	uchar cval;
+	ushort sval;
+	Node res, aes;
+	int ret;
+
+	if(m == 0)
+		error("no map for */@=");
+
+	expr(rval, &res);
+	expr(addr, &aes);
+
+	if(aes.type != TINT)
+		error("bad type lhs of @/*");
+
+	if(m != cormap && wtflag == 0)
+		error("not in write mode");
+
+	r->type = res.type;
+	r->nstore.fmt = res.nstore.fmt;
+	r->nstore = res.nstore;
+
+	switch(res.nstore.fmt) {
+	default:
+		error("bad pointer format '%c' for */@=", res.nstore.fmt);
+	case 'c':
+	case 'C':
+	case 'b':
+		cval = res.nstore.u0.sival;
+		ret = put1(m, aes.nstore.u0.sival, &cval, 1);
+		break;
+	case 'r':
+	case 'x':
+	case 'd':
+	case 'u':
+	case 'o':
+		sval = res.nstore.u0.sival;
+		ret = put2(m, aes.nstore.u0.sival, sval);
+		r->nstore.u0.sival = sval;
+		break;
+	case 'a':
+	case 'A':
+	case 'B':
+	case 'X':
+	case 'D':
+	case 'U':
+	case 'O':
+		ret = put4(m, aes.nstore.u0.sival, res.nstore.u0.sival);
+		break;
+	case 'V':
+	case 'Y':
+	case 'Z':
+		ret = put8(m, aes.nstore.u0.sival, res.nstore.u0.sival);
+		break;
+	case 's':
+	case 'R':
+		ret = put1(m, aes.nstore.u0.sival, (uchar*)res.nstore.u0.sstring->string, res.nstore.u0.sstring->len);
+		break;
+	}
+	if (ret < 0)
+		error("windir: %r");
+}
+
+void
+call(char *fn, Node *parameters, Node *local, Node *body, Node *retexp)
+{
+	int np, i;
+	Rplace rlab;
+	Node *n, res;
+	Value *v, *f;
+	Lsym *s, *next;
+	Node *avp[Maxarg], *ava[Maxarg];
+
+	rlab.local = 0;
+
+	na = 0;
+	flatten(avp, parameters);
+	np = na;
+	na = 0;
+	flatten(ava, local);
+	if(np != na) {
+		if(np < na)
+			error("%s: too few arguments", fn);
+		error("%s: too many arguments", fn);
+	}
+
+	rlab.tail = &rlab.local;
+
+	ret = &rlab;
+	for(i = 0; i < np; i++) {
+		n = ava[i];
+		switch(n->op) {
+		default:
+			error("%s: %d formal not a name", fn, i);
+		case ONAME:
+			expr(avp[i], &res);
+			s = n->sym;
+			break;
+		case OINDM:
+			res.nstore.u0.scc = avp[i];
+			res.type = TCODE;
+			res.nstore.comt = 0;
+			if(n->left->op != ONAME)
+				error("%s: %d formal not a name", fn, i);
+			s = n->left->sym;
+			break;
+		}
+		if(s->v->ret == ret)
+			error("%s already declared at this scope", s->name);
+
+		v = gmalloc(sizeof(Value));
+		v->ret = ret;
+		v->pop = s->v;
+		s->v = v;
+		v->scope = 0;
+		*(rlab.tail) = s;
+		rlab.tail = &v->scope;
+
+		v->vstore = res.nstore;
+		v->type = res.type;
+		v->set = 1;
+	}
+
+	ret->val = retexp;
+	if(setjmp(rlab.rlab) == 0)
+		execute(body);
+
+	for(s = rlab.local; s; s = next) {
+		f = s->v;
+		next = f->scope;
+		s->v = f->pop;
+		free(f);
+	}
+}
--- /dev/null
+++ b/utils/acid/expr.c
@@ -1,0 +1,1032 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+static int fsize[] =
+{
+	0,0,0,0,0,0,0,0,	/* 0-7 */
+	0,0,0,0,0,0,0,0,	/* 8-15 */
+	0,0,0,0,0,0,0,0,	/* 16-23 */
+	0,0,0,0,0,0,0,0,	/* 24-31 */
+	0,0,0,0,0,0,0,0,	/* 32-39 */
+	0,0,0,0,0,0,0,0,	/* 40-47 */
+	0,0,0,0,0,0,0,0,	/* 48-55 */
+	0,0,0,0,0,0,0,0,	/* 56-63 */
+	0,			/* 64 */
+	4,			/* 65	['A'] 4, */
+	4,			/* 66	['B'] 4, */
+	1,			/* 67	['C'] 1, */
+	4,			/* 68	['D'] 4, */
+	0,			/* 69 */
+	8,			/* 70	['F'] 8, */
+	8,			/* 71	['G'] 8, */
+	0,0,0,0,0,0,0,		/* 72-78 */
+	4,			/* 79	['O'] 4, */
+	0,			/* 80 */
+	4,			/* 81	['Q'] 4, */
+	4,			/* 82	['R'] 4, */
+	4,			/* 83	['S'] 4, */
+	0,			/* 84 */
+	4,			/* 85	['U'] 4, */
+	8,			/* 86	['V'] 8, */
+	0,			/* 87 */
+	4,			/* 88	['X'] 4, */
+	8,			/* 89	['Y'] 8, */
+	8,			/* 90	['Z'] 8, */
+	0,0,0,0,0,0,		/* 91-96 */
+	4,			/* 97	['a'] 4, */
+	1,			/* 98	['b'] 1, */
+	1,			/* 99	['c'] 1, */
+	2,			/* 100	['d'] 2, */
+	0,			/* 101 */
+	4,			/* 102	['f'] 4, */
+	4,			/* 103	['g'] 4, */
+	0,0,0,0,0,0,0,		/* 104-110 */
+	2,			/* 111	['o'] 2, */
+	0,			/* 112 */
+	2,			/* 113	['q'] 2, */
+	2,			/* 114	['r'] 2, */
+	4,			/* 115	['s'] 4, */
+	0,			/* 116 */
+	2,			/* 117	['u'] 2, */
+	0,0,			/* 118-119 */
+	2,			/* 120	['x'] 2, */
+};
+
+int
+fmtsize(Value *v)
+{
+	int ret;
+
+	switch(v->vstore.fmt) {
+	default:
+		return  fsize[v->vstore.fmt];
+	case 'i':
+	case 'I':
+		if(v->type != TINT || machdata == 0)
+			error("no size for i fmt pointer ++/--");
+		ret = (*machdata->instsize)(cormap, v->vstore.u0.sival);
+		if(ret < 0) {
+			ret = (*machdata->instsize)(symmap, v->vstore.u0.sival);
+			if(ret < 0)
+				error("%r");
+		}
+		return ret;
+	}
+}
+
+void
+chklval(Node *lp)
+{
+	if(lp->op != ONAME)
+		error("need l-value");
+}
+
+void
+olist(Node *n, Node *res)
+{
+	expr(n->left, res);
+	expr(n->right, res);
+}
+
+void
+oeval(Node *n, Node *res)
+{
+	expr(n->left, res);
+	if(res->type != TCODE)
+		error("bad type for eval");
+	expr(res->nstore.u0.scc, res);
+}
+
+void
+ocast(Node *n, Node *res)
+{
+	if(n->sym->lt == 0)
+		error("%s is not a complex type", n->sym->name);
+
+	expr(n->left, res);
+	res->nstore.comt = n->sym->lt;
+	res->nstore.fmt = 'a';
+}
+
+void
+oindm(Node *n, Node *res)
+{
+	Map *m;
+	Node l;
+
+	m = cormap;
+	if(m == 0)
+		m = symmap;
+	expr(n->left, &l);
+	if(l.type != TINT)
+		error("bad type for *");
+	if(m == 0)
+		error("no map for *");
+	indir(m, l.nstore.u0.sival, l.nstore.fmt, res);
+	res->nstore.comt = l.nstore.comt;
+}
+
+void
+oindc(Node *n, Node *res)
+{
+	Map *m;
+	Node l;
+
+	m = symmap;
+	if(m == 0)
+		m = cormap;
+	expr(n->left, &l);
+	if(l.type != TINT)
+		error("bad type for @");
+	if(m == 0)
+		error("no map for @");
+	indir(m, l.nstore.u0.sival, l.nstore.fmt, res);
+	res->nstore.comt = l.nstore.comt;
+}
+
+void
+oframe(Node *n, Node *res)
+{
+	char *p;
+	Node *lp;
+	uvlong ival;
+	Frtype *f;
+
+	p = n->sym->name;
+	while(*p && *p == '$')
+		p++;
+	lp = n->left;
+	if(localaddr(cormap, p, lp->sym->name, &ival, rget) < 0)
+		error("colon: %r");
+
+	res->nstore.u0.sival = ival;
+	res->op = OCONST;
+	res->nstore.fmt = 'X';
+	res->type = TINT;
+
+	/* Try and set comt */
+	for(f = n->sym->local; f; f = f->next) {
+		if(f->var == lp->sym) {
+			res->nstore.comt = f->type;
+			res->nstore.fmt = 'a';
+			break;
+		}
+	}
+}
+
+void
+oindex(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+
+	if(r.type != TINT)
+		error("bad type for []");
+
+	switch(l.type) {
+	default:
+		error("lhs[] has bad type");
+	case TINT:
+		indir(cormap, l.nstore.u0.sival+(r.nstore.u0.sival*fsize[l.nstore.fmt]), l.nstore.fmt, res);
+		res->nstore.comt = l.nstore.comt;
+		res->nstore.fmt = l.nstore.fmt;
+		break;
+	case TLIST:
+		nthelem(l.nstore.u0.sl, r.nstore.u0.sival, res);
+		break;
+	case TSTRING:
+		res->nstore.u0.sival = 0;
+		if(r.nstore.u0.sival >= 0 && r.nstore.u0.sival < l.nstore.u0.sstring->len) {
+			int xx8;	/* to get around bug in vc */
+			xx8 = r.nstore.u0.sival;
+			res->nstore.u0.sival = l.nstore.u0.sstring->string[xx8];
+		}
+		res->op = OCONST;
+		res->type = TINT;
+		res->nstore.fmt = 'c';
+		break;
+	}
+}
+
+void
+oappend(Node *n, Node *res)
+{
+	Node r, l;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	if(l.type != TLIST)
+		error("must append to list");
+	append(res, &l, &r);
+}
+
+void
+odelete(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	if(l.type != TLIST)
+		error("must delete from list");
+	if(r.type != TINT)
+		error("delete index must be integer");
+
+	delete(l.nstore.u0.sl, r.nstore.u0.sival, res);
+}
+
+void
+ohead(Node *n, Node *res)
+{
+	Node l;
+
+	expr(n->left, &l);
+	if(l.type != TLIST)
+		error("head needs list");
+	res->op = OCONST;
+	if(l.nstore.u0.sl) {
+		res->type = l.nstore.u0.sl->type;
+		res->nstore = l.nstore.u0.sl->lstore;
+	}
+	else {
+		res->type = TLIST;
+		res->nstore.u0.sl = 0;
+	}
+}
+
+void
+otail(Node *n, Node *res)
+{
+	Node l;
+
+	expr(n->left, &l);
+	if(l.type != TLIST)
+		error("tail needs list");
+	res->op = OCONST;
+	res->type = TLIST;
+	if(l.nstore.u0.sl)
+		res->nstore.u0.sl = l.nstore.u0.sl->next;
+	else
+		res->nstore.u0.sl = 0;
+}
+
+void
+oconst(Node *n, Node *res)
+{
+	res->op = OCONST;
+	res->type = n->type;
+	res->nstore = n->nstore;
+	res->nstore.comt = n->nstore.comt;
+}
+
+void
+oname(Node *n, Node *res)
+{
+	Value *v;
+
+	v = n->sym->v;
+	if(v->set == 0)
+		error("%s used but not set", n->sym->name);
+	res->op = OCONST;
+	res->type = v->type;
+	res->nstore = v->vstore;
+	res->nstore.comt = v->vstore.comt;
+}
+
+void
+octruct(Node *n, Node *res)
+{
+	res->op = OCONST;
+	res->type = TLIST;
+	res->nstore.u0.sl = construct(n->left);
+}
+
+void
+oasgn(Node *n, Node *res)
+{
+	Node *lp, r;
+	Value *v;
+
+	lp = n->left;
+	switch(lp->op) {
+	case OINDM:
+		windir(cormap, lp->left, n->right, res);
+		break;
+	case OINDC:
+		windir(symmap, lp->left, n->right, res);
+		break;
+	default:
+		chklval(lp);
+		v = lp->sym->v;
+		expr(n->right, &r);
+		v->set = 1;
+		v->type = r.type;
+		v->vstore = r.nstore;
+		res->op = OCONST;
+		res->type = v->type;
+		res->nstore = v->vstore;
+		res->nstore.comt = v->vstore.comt;
+	}
+}
+
+void
+oadd(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type +");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->nstore.u0.sival = l.nstore.u0.sival+r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sival+r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type +");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval+r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval+r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type +");
+		}
+		break;
+	case TSTRING:
+		if(r.type == TSTRING) {
+			res->type = TSTRING;
+			res->nstore.fmt = 's';
+			res->nstore.u0.sstring = stradd(l.nstore.u0.sstring, r.nstore.u0.sstring); 
+			break;
+		}
+		error("bad rhs for +");
+	case TLIST:
+		res->type = TLIST;
+		switch(r.type) {
+		case TLIST:
+			res->nstore.u0.sl = addlist(l.nstore.u0.sl, r.nstore.u0.sl);
+			break;
+		default:
+			r.left = 0;
+			r.right = 0;
+			res->nstore.u0.sl = addlist(l.nstore.u0.sl, construct(&r));
+			break;
+		}
+	}
+}
+
+void
+osub(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type -");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->nstore.u0.sival = l.nstore.u0.sival-r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sival-r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type -");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval-r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval-r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type -");
+		}
+		break;
+	}
+}
+
+void
+omul(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type *");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			res->nstore.u0.sival = l.nstore.u0.sival*r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sival*r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type *");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval*r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval*r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type *");
+		}
+		break;
+	}
+}
+
+void
+odiv(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TFLOAT;
+	switch(l.type) {
+	default:
+		error("bad lhs type /");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->type = TINT;
+			if(r.nstore.u0.sival == 0)
+				error("zero divide");
+			res->nstore.u0.sival = l.nstore.u0.sival/r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			if(r.nstore.u0.sfval == 0)
+				error("zero divide");
+			res->nstore.u0.sfval = l.nstore.u0.sival/r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type /");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval/r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sfval = l.nstore.u0.sfval/r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type /");
+		}
+		break;
+	}
+}
+
+void
+omod(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type %");
+	res->nstore.u0.sival = l.nstore.u0.sival%r.nstore.u0.sival;
+}
+
+void
+olsh(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type <<");
+	res->nstore.u0.sival = l.nstore.u0.sival<<r.nstore.u0.sival;
+}
+
+void
+orsh(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type >>");
+	res->nstore.u0.sival = l.nstore.u0.sival>>r.nstore.u0.sival;
+}
+
+void
+olt(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type <");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sival < r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sival < r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type <");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sfval < r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sfval < r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type <");
+		}
+		break;
+	}
+}
+
+void
+ogt(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type >");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sival > r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sival > r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type >");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sfval > r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sfval > r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type >");
+		}
+		break;
+	}
+}
+
+void
+oleq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad expr type <=");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sival <= r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sival <= r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad expr type <=");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sfval <= r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sfval <= r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad expr type <=");
+		}
+		break;
+	}
+}
+
+void
+ogeq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	switch(l.type) {
+	default:
+		error("bad lhs type >=");
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sival >= r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sival >= r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type >=");
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sfval >= r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sfval >= r.nstore.u0.sfval;
+			break;
+		default:
+			error("bad rhs type >=");
+		}
+		break;
+	}
+}
+
+void
+oeq(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = 'D';
+	res->op = OCONST;
+	res->type = TINT;
+	res->nstore.u0.sival = 0;
+	switch(l.type) {
+	default:
+		break;
+	case TINT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sival == r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sival == r.nstore.u0.sfval;
+			break;
+		default:
+			break;
+		}
+		break;
+	case TFLOAT:
+		switch(r.type) {
+		case TINT:
+			res->nstore.u0.sival = l.nstore.u0.sfval == r.nstore.u0.sival;
+			break;
+		case TFLOAT:
+			res->nstore.u0.sival = l.nstore.u0.sfval == r.nstore.u0.sfval;
+			break;
+		default:
+			break;
+		}
+		break;
+	case TSTRING:
+		if(r.type == TSTRING) {
+			res->nstore.u0.sival = scmp(r.nstore.u0.sstring, l.nstore.u0.sstring);
+			break;
+		}
+		break;
+	case TLIST:
+		if(r.type == TLIST) {
+			res->nstore.u0.sival = listcmp(l.nstore.u0.sl, r.nstore.u0.sl);
+			break;
+		}
+		break;
+	}
+	if(n->op == ONEQ)
+		res->nstore.u0.sival = !res->nstore.u0.sival;
+}
+
+
+void
+oland(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type &");
+	res->nstore.u0.sival = l.nstore.u0.sival&r.nstore.u0.sival;
+}
+
+void
+oxor(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type ^");
+	res->nstore.u0.sival = l.nstore.u0.sival^r.nstore.u0.sival;
+}
+
+void
+olor(Node *n, Node *res)
+{
+	Node l, r;
+
+	expr(n->left, &l);
+	expr(n->right, &r);
+	res->nstore.fmt = l.nstore.fmt;
+	res->op = OCONST;
+	res->type = TINT;
+	if(l.type != TINT || r.type != TINT)
+		error("bad expr type |");
+	res->nstore.u0.sival = l.nstore.u0.sival|r.nstore.u0.sival;
+}
+
+void
+ocand(Node *n, Node *res)
+{
+	Node l, r;
+
+	res->op = OCONST;
+	res->type = TINT;
+	res->nstore.u0.sival = 0;
+	expr(n->left, &l);
+	res->nstore.fmt = l.nstore.fmt;
+	if(boolx(&l) == 0)
+		return;
+	expr(n->right, &r);
+	if(boolx(&r) == 0)
+		return;
+	res->nstore.u0.sival = 1;
+}
+
+void
+onot(Node *n, Node *res)
+{
+	Node l;
+
+	res->op = OCONST;
+	res->type = TINT;
+	res->nstore.u0.sival = 0;
+	expr(n->left, &l);
+	if(boolx(&l) == 0)
+		res->nstore.u0.sival = 1;
+}
+
+void
+ocor(Node *n, Node *res)
+{
+	Node l, r;
+
+	res->op = OCONST;
+	res->type = TINT;
+	res->nstore.u0.sival = 0;
+	expr(n->left, &l);
+	if(boolx(&l)) {
+		res->nstore.u0.sival = 1;
+		return;
+	}
+	expr(n->right, &r);
+	if(boolx(&r)) {
+		res->nstore.u0.sival = 1;
+		return;
+	}
+}
+
+void
+oeinc(Node *n, Node *res)
+{
+	Value *v;
+
+	chklval(n->left);
+	v = n->left->sym->v;
+	res->op = OCONST;
+	res->type = v->type;
+	switch(v->type) {
+	case TINT:
+		if(n->op == OEDEC)
+			v->vstore.u0.sival -= fmtsize(v);
+		else
+			v->vstore.u0.sival += fmtsize(v);
+		break;			
+	case TFLOAT:
+		if(n->op == OEDEC)
+			v->vstore.u0.sfval--;
+		else
+			v->vstore.u0.sfval++;
+		break;
+	default:
+		error("bad type for pre --/++");
+	}
+	res->nstore = v->vstore;
+}
+
+void
+opinc(Node *n, Node *res)
+{
+	Value *v;
+
+	chklval(n->left);
+	v = n->left->sym->v;
+	res->op = OCONST;
+	res->type = v->type;
+	res->nstore = v->vstore;
+	switch(v->type) {
+	case TINT:
+		if(n->op == OPDEC)
+			v->vstore.u0.sival -= fmtsize(v);
+		else
+			v->vstore.u0.sival += fmtsize(v);
+		break;			
+	case TFLOAT:
+		if(n->op == OPDEC)
+			v->vstore.u0.sfval--;
+		else
+			v->vstore.u0.sfval++;
+		break;
+	default:
+		error("bad type for post --/++");
+	}
+}
+
+void
+ocall(Node *n, Node *res)
+{
+	Lsym *s;
+	Rplace *rsav;
+
+	res->op = OCONST;		/* Default return value */
+	res->type = TLIST;
+	res->nstore.u0.sl = 0;
+
+	chklval(n->left);
+	s = n->left->sym;
+
+	if(s->builtin) {
+		(*s->builtin)(res, n->right);
+		return;
+	}
+	if(s->proc == 0)
+		error("no function %s", s->name);
+
+	rsav = ret;
+	call(s->name, n->right, s->proc->left, s->proc->right, res);
+	ret = rsav;
+}
+
+void
+ofmt(Node *n, Node *res)
+{
+	expr(n->left, res);
+	res->nstore.fmt = n->right->nstore.u0.sival;
+}
+
+void
+owhat(Node *n, Node *res)
+{
+	res->op = OCONST;		/* Default return value */
+	res->type = TLIST;
+	res->nstore.u0.sl = 0;
+	whatis(n->sym);
+}
+
+void (*expop[])(Node*, Node*) =
+{
+	oname,		/* [ONAME]		oname, */
+	oconst,		/* [OCONST]		oconst, */
+	omul,		/* [OMUL]		omul, */
+	odiv,		/* [ODIV]		odiv, */
+	omod,		/* [OMOD]		omod, */
+	oadd,		/* [OADD]		oadd, */
+	osub,		/* [OSUB]		osub, */
+	orsh,		/* [ORSH]		orsh, */
+	olsh,		/* [OLSH]		olsh, */
+	olt,		/* [OLT]		olt, */
+	ogt,		/* [OGT]		ogt, */
+	oleq,		/* [OLEQ]		oleq, */
+	ogeq,		/* [OGEQ]		ogeq, */
+	oeq,		/* [OEQ]		oeq, */
+	oeq,		/* [ONEQ]		oeq, */
+	oland,		/* [OLAND]		oland, */
+	oxor,		/* [OXOR]		oxor, */
+	olor,		/* [OLOR]		olor, */
+	ocand,		/* [OCAND]		ocand, */
+	ocor,		/* [OCOR]		ocor, */
+	oasgn,		/* [OASGN]		oasgn, */
+	oindm,		/* [OINDM]		oindm, */
+	oeinc,		/* [OEDEC]		oeinc, */
+	oeinc,		/* [OEINC]		oeinc, */
+	opinc,		/* [OPINC]		opinc, */
+	opinc,		/* [OPDEC]		opinc, */
+	onot,		/* [ONOT]		onot, */
+	0,		/* [OIF]		0, */
+	0,		/* [ODO]		0, */
+	olist,		/* [OLIST]		olist, */
+	ocall,		/* [OCALL]		ocall, */
+	octruct,	/* [OCTRUCT]		octruct, */
+	0,		/* [OWHILE]		0, */
+	0,		/* [OELSE]		0, */
+	ohead,		/* [OHEAD]		ohead, */
+	otail,		/* [OTAIL]		otail, */
+	oappend,	/* [OAPPEND]		oappend, */
+	0,		/* [ORET]		0, */
+	oindex,		/* [OINDEX]		oindex, */
+	oindc,		/* [OINDC]		oindc, */
+	odot,		/* [ODOT]		odot, */
+	0,		/* [OLOCAL]		0, */
+	oframe,		/* [OFRAME]		oframe, */
+	0,		/* [OCOMPLEX]		0, */
+	odelete,	/* [ODELETE]		odelete, */
+	ocast,		/* [OCAST]		ocast, */
+	ofmt,		/* [OFMT]		ofmt, */
+	oeval,		/* [OEVAL]		oeval, */
+	owhat,		/* [OWHAT]		owhat, */
+};
--- /dev/null
+++ b/utils/acid/lex.c
@@ -1,0 +1,636 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+struct keywd
+{
+	char	*name;
+	int	terminal;
+}
+keywds[] =
+{
+	"do",		Tdo,
+	"if",		Tif,
+	"then",		Tthen,
+	"else",		Telse,
+	"while",	Twhile,
+	"loop",		Tloop,
+	"head",		Thead,
+	"tail",		Ttail,
+	"append",	Tappend,
+	"defn",		Tfn,
+	"return",	Tret,
+	"local",	Tlocal,
+	"aggr",		Tcomplex,
+	"union",	Tcomplex,
+	"adt",		Tcomplex,
+	"complex",	Tcomplex,
+	"delete",	Tdelete,
+	"whatis",	Twhat,
+	"eval",		Teval,
+	0,		0
+};
+
+char cmap[256] =
+{
+	0,0,0,0,0,0,0,0,	/* 0-7 */
+	0,0,0,0,0,0,0,0,	/* 8-15 */
+	0,0,0,0,0,0,0,0,	/* 16-23 */
+	0,0,0,0,0,0,0,0,	/* 24-31 */
+	0,0,			/* 32-33 */
+	'"'+1,			/* 34	['"']	'"'+1,	*/
+	0,0,0,0,0,		/* 35-39 */
+	0,0,0,0,0,0,0,0,	/* 40-47 */
+	'\0'+1,			/* 48	['0']	'\0'+1, */
+	0,0,0,0,0,0,0,		/* 49-55 */
+	0,0,0,0,0,0,0,0,	/* 56-63 */
+	0,0,0,0,0,0,0,0,	/* 64-71 */
+	0,0,0,0,0,0,0,0,	/* 72-79 */
+	0,0,0,0,0,0,0,0,	/* 80-87 */
+	0,0,0,0,		/* 88-91 */
+	'\\'+1,			/* 92	['\\']	'\\'+1,	*/
+	0,0,0,0,		/* 93-96 */
+	'\a'+1,			/* 97	['a']	'\a'+1,	*/
+	'\b'+1,			/* 98	['b']	'\b'+1,	*/
+	0,0,0,			/* 99-101 */
+	'\f'+1,			/* 102	['f']	'\f'+1,	*/
+	0,0,0,0,0,0,0,		/* 103-109 */
+	'\n'+1,			/* 110	['n']	'\n'+1,	*/
+	0,0,0,			/* 111-113 */
+	'\r'+1,			/* 114	['r']	'\r'+1,	*/
+	0,			/* 115 */
+	'\t'+1,			/* 116	['t']	'\t'+1,	*/
+	0,			/* 117 */
+	'\v'+1,			/* 118	['v']	'\v'+1,	*/
+};
+
+void
+kinit(void)
+{
+	int i;
+	
+	for(i = 0; keywds[i].name; i++) 
+		enter(keywds[i].name, keywds[i].terminal);
+}
+
+typedef struct IOstack IOstack;
+struct IOstack
+{
+	char	*name;
+	int	line;
+	char	*text;
+	char	*ip;
+	Biobuf	*fin;
+	IOstack	*prev;
+};
+IOstack *lexio;
+
+void
+pushfile(char *file)
+{
+	Biobuf *b;
+	IOstack *io;
+	char buf[512];
+	extern char *acidlib;
+
+	if(file){
+		b = Bopen(file, OREAD);
+		if(b == nil && strchr(file, '/') == 0){
+			snprint(buf, sizeof(buf)-1, "%s/%s", acidlib, file);
+			b = Bopen(buf, OREAD);
+		}
+	}
+	else{
+		b = gmalloc(sizeof(Biobuf));
+		if (Binit(b, 0, OREAD) == Beof) {
+			free(b);
+			b = 0;
+		}
+		file = "<stdin>";
+	}
+
+	if(b == 0)
+		error("pushfile: %s: %r", file);
+
+	io = gmalloc(sizeof(IOstack));
+	if(io == 0)
+		fatal("no memory");
+	io->name = strdup(file);
+	if(io->name == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->text = 0;
+	io->fin = b;
+	io->prev = lexio;
+	lexio = io;
+}
+
+void
+pushstr(Node *s)
+{
+	IOstack *io;
+
+	io = gmalloc(sizeof(IOstack));
+	if(io == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->name = strdup("<string>");
+	if(io->name == 0)
+		fatal("no memory");
+	io->line = line;
+	line = 1;
+	io->text = strdup(s->nstore.u0.sstring->string);
+	if(io->text == 0)
+		fatal("no memory");
+	io->ip = io->text;
+	io->fin = 0;
+	io->prev = lexio;
+	lexio = io;
+}
+
+void
+restartio(void)
+{
+	Bflush(lexio->fin);
+	Binit(lexio->fin, 0, OREAD);
+}
+
+int
+popio(void)
+{
+	IOstack *s;
+
+	if(lexio == 0)
+		return 0;
+
+	if(lexio->prev == 0){
+		if(lexio->fin)
+			restartio();
+		return 0;
+	}
+
+	if(lexio->fin)
+		Bterm(lexio->fin);
+	else
+		free(lexio->text);
+	free(lexio->name);
+	line = lexio->line;
+	s = lexio;
+	lexio = s->prev;
+	free(s);
+	return 1;
+}
+
+int
+Lfmt(Fmt *f)
+{
+	int i;
+	char buf[1024];
+	IOstack *e;
+
+	e = lexio;
+	if(e) {
+		i = sprint(buf, "%s:%d", e->name, line);
+		while(e->prev) {
+			e = e->prev;
+			if(initialising && e->prev == 0)
+				break;
+			i += sprint(buf+i, " [%s:%d]", e->name, e->line);
+		}
+	} else
+		sprint(buf, "no file:0");
+	return fmtstrcpy(f, buf);
+}
+
+void
+unlexc(int s)
+{
+	if(s == '\n')
+		line--;
+
+	if(lexio->fin)
+		Bungetc(lexio->fin);
+	else
+		lexio->ip--;
+}
+
+int
+lexc(void)
+{
+	int c;
+
+	if(lexio->fin) {
+		c = Bgetc(lexio->fin);
+		if(gotint)
+			error("interrupt");
+		return c;
+	}
+
+	c = *lexio->ip++;
+	if(c == 0)
+		return -1;
+	return c;
+}
+
+int
+escchar(char c)
+{
+	int n;
+	char buf[Strsize];
+
+	if(c >= '0' && c <= '9') {
+		n = 1;
+		buf[0] = c;
+		for(;;) {
+			c = lexc();
+			if(c == Eof)
+				error("%d: <eof> in escape sequence", line);
+			if(strchr("0123456789xX", c) == 0) {
+				unlexc(c);
+				break;
+			}
+			buf[n++] = c;
+		}
+		buf[n] = '\0';
+		return strtol(buf, 0, 0);
+	}
+
+	n = cmap[c];
+	if(n == 0)
+		return c;
+	return n-1;
+}
+
+void
+eatstring(void)
+{
+	int esc, c, cnt;
+	char buf[Strsize];
+
+	esc = 0;
+	for(cnt = 0;;) {
+		c = lexc();
+		switch(c) {
+		case Eof:
+			error("%d: <eof> in string constant", line);
+
+		case '\r':
+		case '\n':
+			error("newline in string constant");
+			goto done;
+
+		case '\\':
+			if(esc)
+				goto Default;
+			esc = 1;
+			break;
+
+		case '"':
+			if(esc == 0)
+				goto done;
+
+			/* Fall through */
+		default:
+		Default:
+			if(esc) {
+				c = escchar(c);
+				esc = 0;
+			}
+			buf[cnt++] = c;
+			break;
+		}
+		if(cnt >= Strsize)
+			error("string token too long");
+	}
+done:
+	buf[cnt] = '\0';
+	yylval.string = strnode(buf);
+}
+
+void
+eatnl(void)
+{
+	int c;
+
+	line++;
+	for(;;) {
+		c = lexc();
+		if(c == Eof)
+			error("eof in comment");
+		if(c == '\n')
+			return;
+	}
+}
+
+int
+yylex(void)
+{
+	int c;
+	extern char vfmt[];
+
+loop:
+	Bflush(bout);
+	c = lexc();
+	switch(c) {
+	case Eof:
+		if(gotint) {
+			gotint = 0;
+			stacked = 0;
+			Bprint(bout, "\nacid: ");
+			goto loop;
+		}
+		return Eof;
+
+	case '"':
+		eatstring();
+		return Tstring;
+
+	case ' ':
+	case '\r':
+	case '\t':
+		goto loop;
+
+	case '\n':
+		line++;
+		if(interactive == 0)
+			goto loop;
+		if(stacked) {
+			print("\t");
+			goto loop;
+		}
+		return ';';
+
+	case '.':
+		c = lexc();
+		unlexc(c);
+		if(isdigit(c))
+			return numsym('.');
+
+		return '.';
+ 
+	case '(':
+	case ')':
+	case '[':
+	case ']':
+	case ';':
+	case ':':
+	case ',':
+	case '~':
+	case '?':
+	case '*':
+	case '@':
+	case '^':
+	case '%':
+		return c;
+	case '{':
+		stacked++;
+		return c;
+	case '}':
+		stacked--;
+		return c;
+
+	case '\\':
+		c = lexc();
+		if(strchr(vfmt, c) == 0) {
+			unlexc(c);
+			return '\\';
+		}
+		yylval.ival = c;
+		return Tfmt;
+
+	case '!':
+		c = lexc();
+		if(c == '=')
+			return Tneq;
+		unlexc(c);
+		return '!';
+
+	case '+':
+		c = lexc();
+		if(c == '+')
+			return Tinc;
+		unlexc(c);
+		return '+';
+
+	case '/':
+		c = lexc();
+		if(c == '/') {
+			eatnl();
+			goto loop;
+		}
+		unlexc(c);
+		return '/';
+
+	case '\'':
+		c = lexc();
+		if(c == '\\')
+			yylval.ival = escchar(lexc());
+		else
+			yylval.ival = c;
+		c = lexc();
+		if(c != '\'') {
+			error("missing '");
+			unlexc(c);
+		}
+		return Tconst;
+
+	case '&':
+		c = lexc();
+		if(c == '&')
+			return Tandand;
+		unlexc(c);
+		return '&';
+
+	case '=':
+		c = lexc();
+		if(c == '=')
+			return Teq;
+		unlexc(c);
+		return '=';
+
+	case '|':
+		c = lexc();
+		if(c == '|')
+			return Toror;
+		unlexc(c);
+		return '|';
+
+	case '<':
+		c = lexc();
+		if(c == '=')
+			return Tleq;
+		if(c == '<')
+			return Tlsh;
+		unlexc(c);
+		return '<';
+
+	case '>':
+		c = lexc();
+		if(c == '=')
+			return Tgeq;
+		if(c == '>')
+			return Trsh;
+		unlexc(c);
+		return '>';
+
+	case '-':
+		c = lexc();
+
+		if(c == '>')
+			return Tindir;
+
+		if(c == '-')
+			return Tdec;
+		unlexc(c);
+		return '-';
+
+	default:
+		return numsym(c);
+	}
+}
+
+int
+numsym(char first)
+{
+	int c, isbin, isfloat, ishex;
+	char *sel, *p;
+	Lsym *s;
+
+	symbol[0] = first;
+	p = symbol;
+
+	ishex = 0;
+	isbin = 0;
+	isfloat = 0;
+	if(first == '.')
+		isfloat = 1;
+
+	if(isdigit(*p++) || isfloat) {
+		for(;;) {
+			c = lexc();
+			if(c < 0)
+				error("%d: <eof> eating symbols", line);
+
+			if(c == '\r')
+				continue;
+			if(c == '\n')
+				line++;
+			sel = "01234567890.xb";
+			if(ishex)
+				sel = "01234567890abcdefABCDEF";
+			else if(isbin)
+				sel = "01";
+			else if(isfloat)
+				sel = "01234567890eE-+";
+
+			if(strchr(sel, c) == 0) {
+				unlexc(c);
+				break;
+			}
+			if(c == '.')
+				isfloat = 1;
+			if(!isbin && c == 'x')
+				ishex = 1;
+			if(!ishex && c == 'b')
+				isbin = 1;
+			*p++ = c;
+		}
+		*p = '\0';
+		if(isfloat) {
+			yylval.fval = atof(symbol);
+			return Tfconst;
+		}
+
+		if(isbin)
+			yylval.ival = strtoul(symbol+2, 0, 2);
+		else
+			yylval.ival = strtoul(symbol, 0, 0);
+		return Tconst;
+	}
+
+	for(;;) {
+		c = lexc();
+		if(c < 0)
+			error("%d <eof> eating symbols", line);
+		if(c == '\n')
+			line++;
+		if(c != '_' && c != '$' && c <= '~' && !isalnum(c)) {	/* checking against ~ lets UTF names through */
+			unlexc(c);
+			break;
+		}
+		*p++ = c;
+	}
+
+	*p = '\0';
+
+	s = look(symbol);
+	if(s == 0)
+		s = enter(symbol, Tid);
+
+	yylval.sym = s;
+	return s->lexval;
+}
+
+Lsym*
+enter(char *name, int t)
+{
+	Lsym *s;
+	ulong h;
+	char *p;
+	Value *v;
+
+	h = 0;
+	for(p = name; *p; p++)
+		h = h*3 + *p;
+	h %= Hashsize;
+
+	s = gmalloc(sizeof(Lsym));
+	s->name = strdup(name);
+
+	s->hash = hash[h];
+	hash[h] = s;
+	s->lexval = t;
+
+	v = gmalloc(sizeof(Value));
+	s->v = v;
+
+	v->vstore.fmt = 'X';
+	v->type = TINT;
+
+	return s;
+}
+
+Lsym*
+look(char *name)
+{
+	Lsym *s;
+	ulong h;
+	char *p;
+
+	h = 0;
+	for(p = name; *p; p++)
+		h = h*3 + *p;
+	h %= Hashsize;
+
+	for(s = hash[h]; s; s = s->hash)
+		if(strcmp(name, s->name) == 0)
+			return s;
+	return 0;
+}
+
+Lsym*
+mkvar(char *s)
+{
+	Lsym *l;
+
+	l = look(s);
+	if(l == 0)
+		l = enter(s, Tid);
+	return l;
+}
--- /dev/null
+++ b/utils/acid/list.c
@@ -1,0 +1,276 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+static List **tail;
+
+List*
+construct(Node *l)
+{
+	List *lh, **save;
+
+	save = tail;
+	lh = 0;
+	tail = &lh;
+	build(l);
+	tail = save;
+
+	return lh;
+}
+
+int
+listlen(List *l)
+{
+	int len;
+
+	len = 0;
+	while(l) {
+		len++;
+		l = l->next;
+	}
+	return len;
+}
+
+void
+build(Node *n)
+{
+	List *l;
+	Node res;
+
+	if(n == 0)
+		return;
+
+	switch(n->op) {
+	case OLIST:
+		build(n->left);
+		build(n->right);
+		return;
+	default:
+		expr(n, &res);
+		l = al(res.type);
+		l->lstore = res.nstore;
+		*tail = l;
+		tail = &l->next;	
+	}
+}
+
+List*
+addlist(List *l, List *r)
+{
+	List *f;
+
+	if(l == 0)
+		return r;
+
+	for(f = l; f->next; f = f->next)
+		;
+	f->next = r;
+
+	return l;
+}
+
+void
+append(Node *r, Node *list, Node *val)
+{
+	List *l, *f;
+
+	l = al(val->type);
+	l->lstore = val->nstore;
+	l->next = 0;
+
+	r->op = OCONST;
+	r->type = TLIST;
+
+	if(list->nstore.u0.sl == 0) {
+		list->nstore.u0.sl = l;
+		r->nstore.u0.sl = l;
+		return;
+	}
+	for(f = list->nstore.u0.sl; f->next; f = f->next)
+		;
+	f->next = l;
+	r->nstore.u0.sl = list->nstore.u0.sl;
+}
+
+int
+listcmp(List *l, List *r)
+{
+	if(l == r)
+		return 1;
+
+	while(l) {
+		if(r == 0)
+			return 0;
+		if(l->type != r->type)
+			return 0;
+		switch(l->type) {
+		case TINT:
+			if(l->lstore.u0.sival != r->lstore.u0.sival)
+				return 0;
+			break;
+		case TFLOAT:
+			if(l->lstore.u0.sfval != r->lstore.u0.sfval)
+				return 0;
+			break;
+		case TSTRING:
+			if(scmp(l->lstore.u0.sstring, r->lstore.u0.sstring) == 0)
+				return 0;
+			break;
+		case TLIST:
+			if(listcmp(l->lstore.u0.sl, r->lstore.u0.sl) == 0)
+				return 0;
+			break;
+		}
+		l = l->next;
+		r = r->next;
+	}
+	if(l != r)
+		return 0;
+	return 1;
+}
+
+void
+nthelem(List *l, int n, Node *res)
+{
+	if(n < 0)
+		error("negative index in []");
+
+	while(l && n--)
+		l = l->next;
+
+	res->op = OCONST;
+	if(l == 0) {
+		res->type = TLIST;
+		res->nstore.u0.sl = 0;
+		return;
+	}
+	res->type = l->type;
+	res->nstore = l->lstore;
+}
+
+void
+delete(List *l, int n, Node *res)
+{
+	List **tl;
+
+	if(n < 0)
+		error("negative index in delete");
+
+	res->op = OCONST;
+	res->type = TLIST;
+	res->nstore.u0.sl = l;
+
+	for(tl = &res->nstore.u0.sl; l && n--; l = l->next)
+		tl = &l->next;
+
+	if(l == 0)
+		error("element beyond end of list");
+	*tl = l->next;
+}
+
+List*
+listvar(char *s, long v)
+{
+	List *l, *tl;
+
+	tl = al(TLIST);
+
+	l = al(TSTRING);
+	tl->lstore.u0.sl = l;
+	l->lstore.fmt = 's';
+	l->lstore.u0.sstring = strnode(s);
+	l->next = al(TINT);
+	l = l->next;
+	l->lstore.fmt = 'X';
+	l->lstore.u0.sival = v;
+
+	return tl;
+}
+
+static List*
+listlocals(Map *map, Symbol *fn, ulong fp)
+{
+	int i;
+	uvlong val;
+	Symbol s;
+	List **tail, *l2;
+
+	l2 = 0;
+	tail = &l2;
+	s = *fn;
+
+	for(i = 0; localsym(&s, i); i++) {
+		if(s.class != CAUTO)
+			continue;
+		if(s.name[0] == '.')
+			continue;
+
+		if(geta(map, fp-s.value, &val) > 0) {
+			*tail = listvar(s.name, val);
+			tail = &(*tail)->next;
+		}
+	}
+	return l2;
+}
+
+static List*
+listparams(Map *map, Symbol *fn, ulong fp)
+{
+	int i;
+	Symbol s;
+	uvlong v;
+	List **tail, *l2;
+
+	l2 = 0;
+	tail = &l2;
+	fp += mach->szaddr;			/* skip saved pc */
+	s = *fn;
+	for(i = 0; localsym(&s, i); i++) {
+		if (s.class != CPARAM)
+			continue;
+
+		if(geta(map, fp+s.value, &v) > 0) {
+			*tail = listvar(s.name, v);
+			tail = &(*tail)->next;
+		}
+	}
+	return l2;
+}
+
+void
+trlist(Map *map, uvlong pc, uvlong sp, Symbol *sym)
+{
+	List *q, *l;
+
+	static List **tail;
+
+	if (tracelist == 0) {		/* first time */
+		tracelist = al(TLIST);
+		tail = &tracelist;
+	}
+
+	q = al(TLIST);
+	*tail = q;
+	tail = &q->next;
+
+	l = al(TINT);			/* Function address */
+	q->lstore.u0.sl = l;
+	l->lstore.u0.sival = sym->value;
+	l->lstore.fmt = 'X';
+
+	l->next = al(TINT);		/* called from address */
+	l = l->next;
+	l->lstore.u0.sival = pc;
+	l->lstore.fmt = 'X';
+
+	l->next = al(TLIST);		/* make list of params */
+	l = l->next;
+	l->lstore.u0.sl = listparams(map, sym, sp);
+
+	l->next = al(TLIST);		/* make list of locals */
+	l = l->next;
+	l->lstore.u0.sl = listlocals(map, sym, sp);
+}
--- /dev/null
+++ b/utils/acid/main.c
@@ -1,0 +1,615 @@
+/*#include <u.h>*/
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#define Extern
+#include "acid.h"
+#include "y.tab.h"
+
+char	*argv0;
+char *acidlib;
+static Biobuf	bioout;
+static char	prog[128];
+static char*	lm[16];
+static int	nlm;
+static char*	mtype;
+
+static	int attachfiles(char*, int);
+int	xfmt(Fmt*);
+extern int	gfltconv(Fmt*), _ifmt(Fmt*);
+int	isnumeric(char*);
+void	die(void);
+
+void
+usage(void)
+{
+	fprint(2, "usage: acid [-l module] [-m machine] [-qrw] [-k] [-d flag] [-R tty] [pid] [file]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Dir *db;
+	Lsym *l;
+	Node *n;
+	char buf[128], *s;
+	int pid, i;
+	char *p;
+	char afile[512];
+
+	argv0 = argv[0];
+	pid = 0;
+	aout = "v.out";
+	quiet = 1;
+	/* turn off all debugging */
+	protodebug = 0;
+
+	mtype = 0;
+	ARGBEGIN{
+	case 'm':
+		mtype = ARGF();
+		break;
+	case 'w':
+		wtflag = 1;
+		break;
+	case 'l':
+		s = ARGF();
+		if(s == 0)
+			usage();
+		lm[nlm++] = s;
+		break;
+	case 'd':
+		p = ARGF();
+		if (p == 0)
+			usage();
+		while (*p) {
+			setdbg_opt(*p, 0); /* don't print set message */
+			p++;
+		}
+		break;
+	case 'k':
+		kernel++;
+		break;
+	case 'q':
+		quiet = 0;
+		break;
+	case 'r':
+		pid = 1;
+		remote++;
+		kernel++;
+		break;
+	case 'R':
+		pid = 1;
+		rdebug++;
+		s = ARGF();
+		if(s == 0)
+			usage();
+		remfd = opentty(s, 0);
+		if(remfd < 0){
+			fprint(2, "acid: can't open %s: %r\n", s);
+			exits("open");
+		}
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0) {
+		if(remote || rdebug)
+			aout = argv[0];
+		else
+		if(isnumeric(argv[0])) {
+			pid = atoi(argv[0]);
+			sprint(prog, "/proc/%d/text", pid);
+			aout = prog;
+			if(argc > 1)
+				aout = argv[1];
+			else if(kernel)
+				aout = mysystem();
+		}
+		else {
+			if(kernel) {
+				print("-k requires a pid");
+				kernel = 0;
+			}
+			aout = argv[0];
+		}
+	} else if(rdebug)
+		aout = "/386/bpc";
+	else if(remote)
+		aout = "/mips/bcarrera";
+
+	fmtinstall('x', xfmt);
+	fmtinstall('L', Lfmt);
+	fmtinstall('f', gfltconv);
+	fmtinstall('F', gfltconv);
+	fmtinstall('g', gfltconv);
+	fmtinstall('G', gfltconv);
+	fmtinstall('e', gfltconv);
+	fmtinstall('E', gfltconv);
+	Binit(&bioout, 1, OWRITE);
+	bout = &bioout;
+
+	kinit();
+	initialising = 1;
+	pushfile(0);
+	loadvars();
+	installbuiltin();
+
+	if(mtype && machbyname(mtype) == 0)
+		print("unknown machine %s", mtype);
+
+	if (attachfiles(aout, pid) < 0)
+		varreg();		/* use default register set on error */
+
+	acidlib = getenv("ACIDLIB");
+	if(acidlib == nil){
+		p = getenv("ROOT");
+		if(p == nil)
+			p = "/usr/inferno";
+		snprint(afile, sizeof(afile)-1, "%s/lib/acid", p);
+		acidlib = strdup(afile);
+	}
+
+	snprint(afile, sizeof(afile)-1, "%s/port", acidlib);
+	loadmodule(afile);
+	for(i = 0; i < nlm; i++) {
+		if((db = dirstat(lm[i])) != nil) {
+			free(db);
+			loadmodule(lm[i]);
+		} else {
+			sprint(buf, "%s/%s", acidlib, lm[i]);
+			loadmodule(buf);
+		}
+	}
+
+	userinit();
+	varsym();
+
+	l = look("acidmap");
+	if(l && l->proc) {
+		n = an(ONAME, ZN, ZN);
+		n->sym = l;
+		n = an(OCALL, n, ZN);
+		execute(n);
+	}
+
+	interactive = 1;
+	initialising = 0;
+	line = 1;
+
+	setup_os_notify();
+
+	for(;;) {
+		if(setjmp(err)) {
+			Binit(&bioout, 1, OWRITE);
+			unwind();
+		}
+		stacked = 0;
+
+		Bprint(bout, "acid: ");
+
+		if(yyparse() != 1)
+			die();
+		restartio();
+
+		unwind();
+	}
+	/* not reached */
+}
+
+static int
+attachfiles(char *aout, int pid)
+{
+	interactive = 0;
+	if(setjmp(err))
+		return -1;
+
+	if(aout) {				/* executable given */
+		if(wtflag)
+			text = open(aout, ORDWR);
+		else
+			text = open(aout, OREAD);
+
+		if(text < 0)
+			error("%s: can't open %s: %r\n", argv0, aout);
+		readtext(aout);
+	}
+	if(pid)					/* pid given */
+		sproc(pid);
+	return 0;
+}
+
+void
+die(void)
+{
+	Lsym *s;
+	List *f;
+
+	Bprint(bout, "\n");
+
+	s = look("proclist");
+	if(!rdebug && s && s->v->type == TLIST) {
+		for(f = s->v->vstore.u0.sl; f; f = f->next)
+			Bprint(bout, "echo kill > /proc/%d/ctl\n", (int)f->lstore.u0.sival);
+	}
+	exits(0);
+}
+
+void
+userinit(void)
+{
+	Lsym *l;
+	Node *n;
+	char buf[512], *p;
+
+
+	p = getenv("home");
+	if(p == 0)
+		p = getenv("HOME");
+	if(p != 0) {
+		snprint(buf, sizeof(buf)-1, "%s/lib/acid", p);
+		silent = 1;
+		loadmodule(buf);
+	}
+
+	if(rdebug){
+		snprint(buf, sizeof(buf)-1, "%s/rdebug", acidlib);
+		loadmodule(buf);
+	}
+
+	snprint(buf, sizeof(buf)-1, "%s/%s", acidlib, mach->name);
+	loadmodule(buf);
+
+	interactive = 0;
+	if(setjmp(err)) {
+		unwind();
+		return;
+	}
+	l = look("acidinit");
+	if(l && l->proc) {
+		n = an(ONAME, ZN, ZN);
+		n->sym = l;
+		n = an(OCALL, n, ZN);
+		execute(n);
+	}
+}
+
+void
+loadmodule(char *s)
+{
+	interactive = 0;
+	if(setjmp(err)) {
+		unwind();
+		return;
+	}
+	pushfile(s);
+	silent = 0;
+	yyparse();
+	popio();
+	return;
+}
+
+void
+readtext(char *s)
+{
+	Dir *d;
+	Lsym *l;
+	Value *v;
+	Symbol sym;
+	ulong length;
+	extern Machdata mipsmach;
+
+	if(mtype != 0){
+		symmap = newmap(0, 1);
+		if(symmap == 0)
+			print("%s: (error) loadmap: cannot make symbol map\n", argv0);
+		length = 1<<24;
+		d = dirfstat(text);
+		if(d != nil) {
+			length = d->length;
+			free(d);
+		}
+		setmap(symmap, text, 0, length, 0, "binary");
+		free(d);
+		return;
+	}
+
+	machdata = &mipsmach;
+
+	if(!crackhdr(text, &fhdr)) {
+		print("can't decode file header\n");
+		return;
+	}
+
+	symmap = loadmap(0, text, &fhdr);
+	if(symmap == 0)
+		print("%s: (error) loadmap: cannot make symbol map\n", argv0);
+
+	if(syminit(text, &fhdr) < 0) {
+		print("%s: (error) syminit: %r\n", argv0);
+		return;
+	}
+	print("%s:%s\n\n", s, fhdr.name);
+
+	if(mach->sbreg && lookup(0, mach->sbreg, &sym)) {
+		mach->sb = sym.value;
+		l = enter("SB", Tid);
+		l->v->vstore.fmt = 'X';
+		l->v->vstore.u0.sival = mach->sb;
+		l->v->type = TINT;
+		l->v->set = 1;
+	}
+
+	l = mkvar("objtype");
+	v = l->v;
+	v->vstore.fmt = 's';
+	v->set = 1;
+	v->vstore.u0.sstring = strnode(mach->name);
+	v->type = TSTRING;
+
+	l = mkvar("textfile");
+	v = l->v;
+	v->vstore.fmt = 's';
+	v->set = 1;
+	v->vstore.u0.sstring = strnode(s);
+	v->type = TSTRING;
+
+	machbytype(fhdr.type);
+	varreg();
+}
+
+Node*
+an(int op, Node *l, Node *r)
+{
+	Node *n;
+
+	n = gmalloc(sizeof(Node));
+	n->ngc.gclink = gcl;
+	gcl = &n->ngc;
+	n->op = op;
+	n->left = l;
+	n->right = r;
+	return n;
+}
+
+List*
+al(int t)
+{
+	List *l;
+
+	l = gmalloc(sizeof(List));
+	l->type = t;
+	l->lgc.gclink = gcl;
+	gcl = &l->lgc;
+	return l;
+}
+
+Node*
+con(int v)
+{
+	Node *n;
+
+	n = an(OCONST, ZN, ZN);
+	n->nstore.u0.sival = v;
+	n->nstore.fmt = 'X';
+	n->type = TINT;
+	return n;
+}
+
+void
+fatal(char *fmt, ...)
+{
+	char buf[128];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s: %L (fatal problem) %s\n", argv0, buf);
+	exits(buf);
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[128];
+	va_list arg;
+
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, near symbol '%s'", symbol);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%L: %s\n", buf);
+}
+
+void
+marktree(Node *n)
+{
+
+	if(n == 0)
+		return;
+
+	marktree(n->left);
+	marktree(n->right);
+
+	n->ngc.gcmark = 1;
+	if(n->op != OCONST)
+		return;
+
+	switch(n->type) {
+	case TSTRING:
+		n->nstore.u0.sstring->sgc.gcmark = 1;
+		break;
+	case TLIST:
+		marklist(n->nstore.u0.sl);
+		break;
+	case TCODE:
+		marktree(n->nstore.u0.scc);
+		break;
+	}
+}
+
+void
+marklist(List *l)
+{
+	while(l) {
+		l->lgc.gcmark = 1;
+		switch(l->type) {
+		case TSTRING:
+			l->lstore.u0.sstring->sgc.gcmark = 1;
+			break;
+		case TLIST:
+			marklist(l->lstore.u0.sl);
+			break;
+		case TCODE:
+			marktree(l->lstore.u0.scc);
+			break;
+		}
+		l = l->next;
+	}
+}
+
+void
+gc(void)
+{
+	int i;
+	Lsym *f;
+	Value *v;
+	Gc *m, **p, *next;
+
+	if(dogc < Mempergc)
+		return;
+	dogc = 0;
+
+	/* Mark */
+	for(m = gcl; m; m = m->gclink)
+		m->gcmark = 0;
+
+	/* Scan */
+	for(i = 0; i < Hashsize; i++) {
+		for(f = hash[i]; f; f = f->hash) {
+			marktree(f->proc);
+			if(f->lexval != Tid)
+				continue;
+			for(v = f->v; v; v = v->pop) {
+				switch(v->type) {
+				case TSTRING:
+					v->vstore.u0.sstring->sgc.gcmark = 1;
+					break;
+				case TLIST:
+					marklist(v->vstore.u0.sl);
+					break;
+				case TCODE:
+					marktree(v->vstore.u0.scc);
+					break;
+				}
+			}
+		}
+	}
+
+	/* Free */
+	p = &gcl;
+	for(m = gcl; m; m = next) {
+		next = m->gclink;
+		if(m->gcmark == 0) {
+			*p = next;
+			free(m);	/* Sleazy reliance on my malloc */
+		}
+		else
+			p = &m->gclink;
+	}
+}
+
+void*
+gmalloc(long l)
+{
+	void *p;
+
+	dogc += l;
+	p = malloc(l);
+	if(p == 0)
+		fatal("out of memory");
+	memset(p, 0, l);
+	return p;
+}
+
+void
+checkqid(int f1, int pid)
+{
+	int fd;
+	Dir *d1, *d2;
+	char buf[128];
+
+	if(kernel || rdebug)
+		return;
+
+	d1 = dirfstat(f1);
+	if(d1 == nil)
+		fatal("checkqid: (qid not checked) dirfstat: %r");
+
+	sprint(buf, "/proc/%d/text", pid);
+	fd = open(buf, OREAD);
+	if(fd < 0 || (d2 = dirfstat(fd)) == nil){
+		fatal("checkqid: (qid not checked) dirstat %s: %r", buf);
+		return;	/* not reached */
+	}
+
+	close(fd);
+
+	if(d1->qid.path != d2->qid.path || d1->qid.vers != d2->qid.vers || d1->qid.type != d2->qid.type){
+		print("path %llux %llux vers %lud %lud type %d %d\n",
+			d1->qid.path, d2->qid.path, d1->qid.vers, d2->qid.vers, d1->qid.type, d2->qid.type);
+		print("warning: image does not match text for pid %d\n", pid);
+	}
+	free(d1);
+	free(d2);
+}
+
+char*
+mysystem(void)
+{
+	char *cpu, *p, *q;
+	static char kernel[128];
+
+	cpu = getenv("cputype");
+	if(cpu == 0) {
+		cpu = "mips";
+		print("$cputype not set; assuming %s\n", cpu);
+	}
+	p = getenv("terminal");
+	if(p == 0 || (p=strchr(p, ' ')) == 0 || p[1] == ' ' || p[1] == 0) {
+		p = "9power";
+		print("missing or bad $terminal; assuming %s\n", p);
+	}
+	else{
+		p++;
+		q = strchr(p, ' ');
+		if(q)
+			*q = 0;
+		sprint(kernel, "/%s/b%s", cpu, p);
+	}
+	return kernel;
+}
+
+int
+isnumeric(char *s)
+{
+	while(*s) {
+		if(*s < '0' || *s > '9')
+			return 0;
+		s++;
+	}
+	return 1;
+}
+
+int
+xfmt(Fmt *f)
+{
+	f->flags ^= FmtSharp;
+	return _ifmt(f);
+}
--- /dev/null
+++ b/utils/acid/mips
@@ -1,0 +1,217 @@
+// Mips support
+
+defn acidinit()			// Called after all the init modules are loaded
+{
+	bplist = {};
+	bpfmt = 'X';
+
+	srcpath = {
+		"./",
+		"/sys/src/libc/port/",
+		"/sys/src/libc/9sys/",
+		"/sys/src/libc/mips/"
+	};
+
+	srcfiles = {};		// list of loaded files
+	srctext = {};		// the text of the files
+}
+
+defn stk()			// trace
+{
+	_stk(*PC, *SP, linkreg(0), 0);
+}
+
+defn lstk()			// trace with locals
+{
+	_stk(*PC, *SP, linkreg(0), 1);
+}
+
+defn gpr()			// print general purpose registers
+{
+	print("R1\t", *R1, " R2\t", *R2, " R3\t", *R3, "\n");
+	print("R4\t", *R4, " R5\t", *R5, " R6\t", *R6, "\n");
+	print("R7\t", *R7, " R8\t", *R8, " R9\t", *R9, "\n");
+	print("R10\t", *R10, " R11\t", *R11, " R12\t", *R12, "\n");
+	print("R13\t", *R13, " R14\t", *R14, " R15\t", *R15, "\n");
+	print("R16\t", *R16, " R17\t", *R17, " R18\t", *R18, "\n");
+	print("R19\t", *R19, " R20\t", *R20, " R21\t", *R21, "\n");
+	print("R22\t", *R22, " R23\t", *R23, " R24\t", *R24, "\n");
+	print("R25\t", *R25, " R26\t", *R26, " R27\t", *R27, "\n");
+	print("R28\t", *R28, " R29\t", *SP, " R30\t", *R30, "\n");
+	print("R31\t", *R31, "\n");
+}
+
+defn Fpr()
+{
+	print("F0\t",  *fmt(F0, 'G'),  "\tF2\t",  *fmt(F2, 'G'), "\n");
+	print("F4\t",  *fmt(F4, 'G'),  "\tF6\t",  *fmt(F6, 'G'), "\n");
+	print("F8\t",  *fmt(F8, 'G'),  "\tF10\t", *fmt(F10, 'G'), "\n");
+	print("F12\t", *fmt(F12, 'G'), "\tF14\t", *fmt(F14, 'G'), "\n");
+	print("F16\t", *fmt(F16, 'G'), "\tF18\t", *fmt(F18, 'G'), "\n");
+	print("F20\t", *fmt(F20, 'G'), "\tF22\t", *fmt(F22, 'G'), "\n");
+	print("F24\t", *fmt(F24, 'G'), "\tF26\t", *fmt(F26, 'G'), "\n");
+	print("F28\t", *fmt(F28, 'G'), "\tF30\t", *fmt(F30, 'G'), "\n");
+}
+
+defn fpr()
+{
+	print("F0\t",  *fmt(F0, 'g'),  "\tF1\t",  *fmt(F1, 'g'), "\n");
+	print("F2\t",  *fmt(F2, 'g'),  "\tF3\t",  *fmt(F3, 'g'), "\n");
+	print("F4\t",  *fmt(F4, 'g'),  "\tF5\t",  *fmt(F5, 'g'), "\n");
+	print("F6\t",  *fmt(F6, 'g'),  "\tF7\t",  *fmt(F7, 'g'), "\n");
+	print("F8\t",  *fmt(F8, 'g'),  "\tF9\t",  *fmt(F9, 'g'), "\n");
+	print("F10\t", *fmt(F10, 'g'), "\tF11\t", *fmt(F11, 'g'), "\n");
+	print("F12\t", *fmt(F12, 'g'), "\tF13\t", *fmt(F13, 'g'), "\n");
+	print("F14\t", *fmt(F14, 'g'), "\tF15\t", *fmt(F15, 'g'), "\n");
+	print("F16\t", *fmt(F16, 'g'), "\tF17\t", *fmt(F17, 'g'), "\n");
+	print("F18\t", *fmt(F18, 'g'), "\tF19\t", *fmt(F19, 'g'), "\n");
+	print("F20\t", *fmt(F20, 'g'), "\tF21\t", *fmt(F21, 'g'), "\n");
+	print("F22\t", *fmt(F22, 'g'), "\tF23\t", *fmt(F23, 'g'), "\n");
+	print("F24\t", *fmt(F24, 'g'), "\tF25\t", *fmt(F25, 'g'), "\n");
+	print("F26\t", *fmt(F26, 'g'), "\tF27\t", *fmt(F27, 'g'), "\n");
+	print("F28\t", *fmt(F28, 'g'), "\tF29\t", *fmt(F29, 'g'), "\n");
+	print("F30\t", *fmt(F30, 'g'), "\tF31\t", *fmt(F31, 'g'), "\n");
+}
+
+defn spr()				// print special processor registers
+{
+	local pc, link, cause;
+
+	pc = *PC;
+	print("PC\t", pc, " ", fmt(pc, 'a'), "  ");
+	pfl(pc);
+
+	link = *R31;
+	print("SP\t", *SP, "\tLINK\t", link, " ", fmt(link, 'a'), " ");
+	pfl(link);
+
+	cause = *CAUSE;
+	print("STATUS\t", *STATUS, "\tCAUSE\t", cause, " ", reason(cause), "\n");
+	print("TLBVIR\t", *TLBVIRT, "\tBADVADR\t", *BADVADDR, "\n");
+
+	print("HI\t", *HI, "\tLO\t", *LO, "\n");
+}
+
+defn regs()				// print all registers
+{
+	spr();
+	gpr();
+}
+
+defn pstop(pid)
+{
+	local l, pc;
+
+	pc = *PC;
+
+	print(pid,": ", reason(*CAUSE), "\t");
+	print(fmt(pc, 'a'), "\t", fmt(pc, 'i'), "\n");
+
+	if notes then {
+		if notes[0] != "sys: breakpoint" then {
+			print("Notes pending:\n");
+			l = notes;
+			while l do {
+				print("\t", head l, "\n");
+				l = tail l;
+			}
+		}
+	}
+}
+
+sizeofUreg = 152;
+aggr Ureg
+{
+	'X' 0 status;
+	'X' 4 pc;
+	{
+	'X' 8 sp;
+	'X' 8 usp;
+	};
+	'X' 12 cause;
+	'X' 16 badvaddr;
+	'X' 20 tlbvirt;
+	'X' 24 hi;
+	'X' 28 lo;
+	'X' 32 r31;
+	'X' 36 r30;
+	'X' 40 r28;
+	'X' 44 r27;
+	'X' 48 r26;
+	'X' 52 r25;
+	'X' 56 r24;
+	'X' 60 r23;
+	'X' 64 r22;
+	'X' 68 r21;
+	'X' 72 r20;
+	'X' 76 r19;
+	'X' 80 r18;
+	'X' 84 r17;
+	'X' 88 r16;
+	'X' 92 r15;
+	'X' 96 r14;
+	'X' 100 r13;
+	'X' 104 r12;
+	'X' 108 r11;
+	'X' 112 r10;
+	'X' 116 r9;
+	'X' 120 r8;
+	'X' 124 r7;
+	'X' 128 r6;
+	'X' 132 r5;
+	'X' 136 r4;
+	'X' 140 r3;
+	'X' 144 r2;
+	'X' 148 r1;
+};
+
+defn
+Ureg(addr) {
+	complex Ureg addr;
+	print("	status	", addr.status, "\n");
+	print("	pc	", addr.pc, "\n");
+	print("	sp	", addr.sp, "\n");
+	print("	cause	", addr.cause, "\n");
+	print("	badvaddr	", addr.badvaddr, "\n");
+	print("	tlbvirt	", addr.tlbvirt, "\n");
+	print("	hi	", addr.hi, "\n");
+	print("	lo	", addr.lo, "\n");
+	print("	r31	", addr.r31, "\n");
+	print("	r30	", addr.r30, "\n");
+	print("	r28	", addr.r28, "\n");
+	print("	r27	", addr.r27, "\n");
+	print("	r26	", addr.r26, "\n");
+	print("	r25	", addr.r25, "\n");
+	print("	r24	", addr.r24, "\n");
+	print("	r23	", addr.r23, "\n");
+	print("	r22	", addr.r22, "\n");
+	print("	r21	", addr.r21, "\n");
+	print("	r20	", addr.r20, "\n");
+	print("	r19	", addr.r19, "\n");
+	print("	r18	", addr.r18, "\n");
+	print("	r17	", addr.r17, "\n");
+	print("	r16	", addr.r16, "\n");
+	print("	r15	", addr.r15, "\n");
+	print("	r14	", addr.r14, "\n");
+	print("	r13	", addr.r13, "\n");
+	print("	r12	", addr.r12, "\n");
+	print("	r11	", addr.r11, "\n");
+	print("	r10	", addr.r10, "\n");
+	print("	r9	", addr.r9, "\n");
+	print("	r8	", addr.r8, "\n");
+	print("	r7	", addr.r7, "\n");
+	print("	r6	", addr.r6, "\n");
+	print("	r5	", addr.r5, "\n");
+	print("	r4	", addr.r4, "\n");
+	print("	r3	", addr.r3, "\n");
+	print("	r2	", addr.r2, "\n");
+	print("	r1	", addr.r1, "\n");
+};
+
+defn linkreg(addr)
+{
+	complex Ureg addr;
+	return addr.r31\X;
+}
+
+print("/sys/lib/acid/mips");
--- /dev/null
+++ b/utils/acid/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=acid
+
+OFILES=	main.$O\
+	y.tab.$O\
+	lex.$O\
+	util.$O\
+	exec.$O\
+	expr.$O\
+	list.$O\
+	builtin.$O\
+	proc.$O\
+	dot.$O\
+	print.$O\
+	os-$TARGMODEL.$O\
+	rdebug.$O\
+
+YFILES=dbg.y
+HFILES=acid.h y.tab.h
+
+LIBS=mach math bio regexp 9
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../include
--- /dev/null
+++ b/utils/acid/os-Nt.c
@@ -1,0 +1,199 @@
+/*
+ * Windows Nt
+ */
+
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+#include <signal.h>
+
+#include <windows.h>
+
+#define MAXBUFSIZ 16640 /* 2 STYX messages plus headers  */
+#define NT_DEBUG
+int	nt_debug = 0;
+
+int
+opentty(char *tty, int baud)
+{
+	HANDLE	comport;
+	DCB	dcb;
+	COMMTIMEOUTS	timeouts;
+
+	comport = CreateFile(tty, GENERIC_READ|GENERIC_WRITE,
+				0, 0, OPEN_EXISTING,
+				FILE_ATTRIBUTE_NORMAL, 0);
+	if (comport == INVALID_HANDLE_VALUE) {
+		werrstr("could not create port %s", tty);
+		return -1;
+	}
+
+	if (SetupComm(comport, MAXBUFSIZ, MAXBUFSIZ) != TRUE) {
+		werrstr("could not set up %s Comm port", tty);
+		CloseHandle(comport);
+		return -1;
+	}
+
+	if (GetCommState(comport, &dcb) != TRUE) {
+		werrstr("could not get %s comstate", tty);
+		CloseHandle(comport);
+		return -1;
+	}
+
+	if (baud == 0) {
+		dcb.BaudRate = 19200;
+	} else {
+		dcb.BaudRate = baud;
+	}
+	dcb.ByteSize = 8;
+	dcb.fParity = 0;
+	dcb.Parity = NOPARITY;
+	dcb.StopBits = ONESTOPBIT;
+	dcb.fInX = 0;
+	dcb.fOutX = 0;
+	dcb.fAbortOnError = 1;
+
+	if (SetCommState(comport, &dcb) != TRUE) {
+		werrstr("could not set %s comstate", tty);
+		CloseHandle(comport);
+		return -1;
+	}
+	
+	timeouts.ReadIntervalTimeout = 2;
+	/* char time in milliseconds, at 19.2K char time is .4 ms */
+	timeouts.ReadTotalTimeoutMultiplier = 0; /* was 100; */
+	timeouts.ReadTotalTimeoutConstant = 200; /* was 500; */
+	timeouts.WriteTotalTimeoutMultiplier = 0; /* was 10; */
+	timeouts.WriteTotalTimeoutConstant = 400; /* was 20; */
+
+	SetCommTimeouts(comport, &timeouts);
+
+	EscapeCommFunction(comport, SETDTR);
+
+	return (int) comport;
+}
+
+int
+remote_read(int fd, char *buf, int bytes)
+{
+	DWORD numread = 0;
+	BOOL rtn;
+
+#ifdef NT_DEBUG
+	if (nt_debug) {
+		print("NT:rread fd %x bytes: %d", fd, bytes);
+	}
+#endif
+	rtn = ReadFile((HANDLE) fd, buf, bytes, &numread, 0);
+#ifdef NT_DEBUG
+	if (nt_debug) {
+		print(" numread: %d rtn: %x\n", numread, rtn);
+		if (numread) {
+			char *cp;
+			int i;
+	
+			cp = (char *) buf;
+			for (i=0; i < numread; i++) {
+				print(" %2.2x", *cp++);
+			}
+			print("\n");
+		}
+	}
+#endif
+	if (!rtn) 
+		return -1;
+	else
+		return numread;
+}
+
+int
+remote_write(int fd, char *buf, int bytes)
+{
+	DWORD numwrt = 0;
+	BOOL	rtn;
+	char	*cp;
+	int	i;
+
+#ifdef NT_DEBUG
+	if (nt_debug) {
+		print("NT:rwrite fd %x bytes: %d", fd, bytes);
+		print("\n");
+		cp = (char *) buf;
+		for (i=0; i < bytes; i++) {
+			print(" %2.2x", *cp++);
+		}
+		print("\n");
+	}
+#endif
+	while (bytes > 0) {
+		rtn = WriteFile((HANDLE) fd, buf, bytes, &numwrt, 0);
+		if (!rtn) {
+			break;
+		}
+		buf += numwrt;
+		bytes -= numwrt;
+	}
+	return numwrt;
+}
+
+void
+detach(void)
+{
+	/* ??? */
+}
+
+char *
+waitfor(int pid)
+{
+	fprint(2, "wait unimplemented");
+	return 0;
+}
+
+int
+fork(void)
+{
+	fprint(2, "fork unimplemented");
+	return -1;
+}
+
+char *
+runcmd(char *cmd)
+{
+	fprint(2, "runcmd unimplemented");
+	return 0;
+}
+
+void (*notefunc)(int);
+
+void
+os_notify(void (*func)(int))
+{
+	notefunc = func;
+	signal(SIGINT, func);
+	return 0;
+}
+
+void
+catcher(int sig)
+{
+	if (sig == SIGINT) {
+		gotint = 1;
+		signal(SIGINT, notefunc);
+	}
+}
+
+void
+setup_os_notify(void)
+{
+	os_notify(catcher);
+}
+
+int
+nproc(char **argv)
+{
+	fprint(2, "nproc not implemented\n");
+	return -1;
+}
--- /dev/null
+++ b/utils/acid/os-Plan9.c
@@ -1,0 +1,157 @@
+/*
+ * Plan9
+ */
+
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+int
+opentty(char *tty, int baud)
+{
+	int fd, cfd;
+	char ctty[100];
+
+	if(tty == 0)
+		tty = "/dev/eia0";
+	sprint(ctty, "%sctl", tty);
+	fd = open(tty, 2);
+	if(fd < 0)
+		return -1;
+	if(baud){
+		cfd = open(ctty, 1);
+		if(cfd < 0)
+			return fd;
+		fprint(cfd, "b%d", baud);
+		close(cfd);
+	}
+	return fd;
+}
+
+void
+detach(void)
+{
+	rfork(RFNAMEG|RFNOTEG|RFREND);
+}
+
+char *
+waitfor(int pid)
+{
+	Waitmsg *w;
+	static char buf[ERRMAX];
+
+	for(;;) {
+		w = wait();
+		if(w == nil)
+			error("wait %r");
+		if(w->pid == pid){
+			strecpy(buf, buf+ERRMAX, w->msg);
+			free(w);
+			return buf;
+		}
+		free(w);
+	}
+	/* not reached */
+}
+
+char *
+runcmd(char *cmd)
+{
+	char *argv[4];
+	int pid;
+
+	argv[0] = "/bin/rc";
+	argv[1] = "-c";
+	argv[2] = cmd;
+	argv[3] = 0;
+
+	pid = fork();
+	switch(pid) {
+	case -1:
+		error("fork %r");
+	case 0:
+		exec("/bin/rc", argv);
+		exits(0);
+	default:
+		return waitfor(pid);
+	}
+	/* not reached */
+}
+
+void
+catcher(void *junk, char *s)
+{
+	USED(junk);
+
+	if(strstr(s, "interrupt")) {
+		gotint = 1;
+		noted(NCONT);
+	}
+	noted(NDFLT);
+}
+
+void (*notefunc)(void *, char *);
+
+void
+setup_os_notify(void)
+{
+	notify(catcher);
+}
+
+int
+nproc(char **argv)
+{
+	char buf[128];
+	int pid, i, fd;
+
+	pid = fork();
+	switch(pid) {
+	case -1:
+		error("new: fork %r");
+	case 0:
+		rfork(RFNAMEG|RFNOTEG);
+
+		sprint(buf, "/proc/%d/ctl", getpid());
+		fd = open(buf, ORDWR);
+		if(fd < 0)
+			fatal("new: open %s: %r", buf);
+		write(fd, "hang", 4);
+		close(fd);
+
+		close(0);
+		close(1);
+		close(2);
+		for(i = 3; i < NFD; i++)
+			close(i);
+
+		open("/dev/cons", OREAD);
+		open("/dev/cons", OWRITE);
+		open("/dev/cons", OWRITE);
+		exec(argv[0], argv);
+		fatal("new: exec %s: %r");
+	default:
+		install(pid);
+		msg(pid, "waitstop");
+		notes(pid);
+		sproc(pid);
+		dostop(pid);
+		break;
+	}
+
+	return pid;
+}
+
+int
+remote_read(int fd, char *buf, int bytes)
+{
+	return read(fd, buf, bytes);
+}
+
+int
+remote_write(int fd, char *buf, int bytes)
+{
+	return write(fd, buf, bytes);
+}
--- /dev/null
+++ b/utils/acid/os-Posix.c
@@ -1,0 +1,185 @@
+#include <lib9.h>
+#include <bio.h>
+#include <sys/types.h>
+#include <termios.h>
+#undef getwd
+#undef getwd
+#include <unistd.h>
+#include "mach.h"
+#define	Extern extern
+#include "acid.h"
+#include <signal.h>
+#include <sys/wait.h>
+
+static void
+setraw(int fd, int baud)
+{
+	struct termios sg;
+
+	switch(baud){
+	case 1200:	baud = B1200; break;
+	case 2400:	baud = B2400; break;
+	case 4800:	baud = B4800; break;
+	case 9600:	baud = B9600; break;
+	case 19200:	baud = B19200; break;
+	case 38400:	baud = B38400; break;
+	default:
+		werrstr("unknown speed %d", baud);
+		return;
+	}
+	if(tcgetattr(fd, &sg) >= 0) {
+		sg.c_iflag = sg.c_oflag = sg.c_lflag = 0;
+		sg.c_cflag &= ~CSIZE;
+		sg.c_cflag |= CS8 | CREAD;
+		sg.c_cflag &= ~(PARENB|PARODD);
+		sg.c_cc[VMIN] = 1;
+		sg.c_cc[VTIME] = 0;
+		if(baud) {
+			cfsetispeed(&sg, baud);
+			cfsetospeed(&sg, baud);
+		}
+		tcsetattr(fd, TCSANOW, &sg);
+	}
+}
+
+int
+opentty(char *tty, int baud)
+{
+	int fd;
+
+	if(baud == 0)
+		baud = 19200;
+	fd = open(tty, 2);
+	if(fd < 0)
+		return -1;
+	setraw(fd, baud);
+	return fd;
+}
+
+void
+detach(void)
+{
+	setpgid(0, 0);
+}
+
+char *
+waitfor(int pid)
+{
+	int n, status;
+	static char buf[32];
+
+	for(;;) {
+		n = wait(&status);
+		if(n < 0)
+			error("wait %r");
+		if(n == pid) {
+			sprint(buf, "%d", status);
+			return buf;
+		}
+	}
+}
+
+char *
+runcmd(char *cmd)
+{
+	char *argv[4];
+	int pid;
+
+	argv[0] = "/bin/sh";
+	argv[1] = "-c";
+	argv[2] = cmd;
+	argv[3] = 0;
+
+	pid = fork();
+	switch(pid) {
+	case -1:
+		error("fork %r");
+	case 0:
+		execv("/bin/sh", argv);
+		exits(0);
+	default:
+		return waitfor(pid);
+	}
+	return 0;
+}
+
+void (*notefunc)(int);
+
+void
+os_notify(void (*func)(int))
+{
+	notefunc = func;
+	signal(SIGINT, func);
+}
+
+void
+catcher(int sig)
+{
+	if(sig==SIGINT) {
+		gotint = 1;
+		signal(SIGINT, notefunc);
+	}
+}
+
+void
+setup_os_notify(void)
+{
+	os_notify(catcher);
+}
+
+int
+nproc(char **argv)
+{
+	char buf[128];
+	int pid, i, fd;
+
+	if(rdebug)
+		error("can't newproc in remote mode");
+
+	pid = fork();
+	switch(pid) {
+	case -1:
+		error("new: fork %r");
+	case 0:
+		detach();
+
+		sprint(buf, "/proc/%d/ctl", getpid());
+		fd = open(buf, ORDWR);
+		if(fd < 0)
+			fatal("new: open %s: %r", buf);
+		write(fd, "hang", 4);
+		close(fd);
+
+		close(0);
+		close(1);
+		close(2);
+		for(i = 3; i < NFD; i++)
+			close(i);
+
+		open("/dev/cons", OREAD);
+		open("/dev/cons", OWRITE);
+		open("/dev/cons", OWRITE);
+		execvp(argv[0], argv);
+		fatal("new: execvp %s: %r");
+	default:
+		install(pid);
+		msg(pid, "waitstop");
+		notes(pid);
+		sproc(pid);
+		dostop(pid);
+		break;
+	}
+
+	return pid;
+}
+
+int
+remote_read(int fd, char *buf, int bytes)
+{
+	return read(fd, buf, bytes);
+}
+
+int remote_write(int fd, char *buf, int bytes)
+{
+	return write(fd, buf, bytes);
+}
--- /dev/null
+++ b/utils/acid/port
@@ -1,0 +1,547 @@
+// portable acid for all architectures
+
+defn pfl(addr)
+{
+	print(pcfile(addr), ":", pcline(addr), "\n");
+}
+
+defn
+notestk(addr)
+{
+	local pc, sp;
+	complex Ureg addr;
+
+	pc = addr.pc\X;
+	sp = addr.sp\X;
+
+	print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " ");
+	pfl(pc);
+	_stk(pc, sp, linkreg(addr), 1);
+}
+
+defn labstk(l)				// trace from a label
+{
+	_stk(*(l+4)+Labpcoff, *l+Labspoff, linkreg(0), 0);
+}
+
+defn params(param)
+{
+	while param do {
+		sym = head param;
+		print(sym[0], "=", sym[1]);
+		param = tail param;
+		if param then
+			print (",");
+	}	
+}
+
+defn locals(l)
+{
+	local sym;
+
+	while l do {
+		sym = head l;
+		print("\t", sym[0], "=", sym[1], "\n");
+		l = tail l;
+	}	
+}
+
+defn _stk(pc, sp, link, dolocals)
+{
+	local stk;
+
+	print("At pc:", pc, ":", fmt(pc, 'a'), " ");
+	pfl(pc);
+
+	stk = strace(pc, sp, link);
+
+	while stk do {
+		frame = head stk;
+		print(fmt(frame[0], 'a'), "(");
+		params(frame[2]);
+		print(") ", pcfile(frame[0]), ":", pcline(frame[0]));
+		print("\n\tcalled from ", fmt(frame[1], 'a'), " ");
+		pfl(frame[1]);
+		stk = tail stk;
+		if dolocals then
+			locals(frame[3]);
+	}
+}
+
+defn findsrc(file)
+{
+	local lst, src;
+
+	if file[0] == '/' then {
+		src = file(file);
+		if src != {} then {
+			srcfiles = append srcfiles, file;
+			srctext = append srctext, src;
+			return src;
+		}
+		return {};
+	}
+
+	lst = srcpath;
+	while head lst do {
+		src = file(head lst+file);
+		if src != {} then {
+			srcfiles = append srcfiles, file;
+			srctext = append srctext, src;
+			return src;
+		}
+		lst = tail lst;
+	}
+}
+
+defn line(addr)
+{
+	local src, file;
+
+	file = pcfile(addr);
+	src = match(file, srcfiles);
+
+	if src >= 0 then
+		src = srctext[src];
+	else
+		src = findsrc(file);
+
+	if src == {} then {
+		print("no source for ", file, "\n");
+		return {};
+	}
+	line = pcline(addr)-1;
+	print(file, ":", src[line], "\n");
+}
+
+defn addsrcdir(dir)
+{
+	dir = dir+"/";
+
+	if match(dir, srcpath) >= 0 then {
+		print("already in srcpath\n");
+		return {};
+	}
+
+	srcpath = {dir}+srcpath;
+}
+
+defn source()
+{
+	local l;
+
+	l = srcpath;
+	while l do {
+		print(head l, "\n");
+		l = tail l;
+	}
+	l = srcfiles;
+
+	while l do {
+		print("\t", head l, "\n");
+		l = tail l;
+	}
+}
+
+defn Bsrc(addr)
+{
+	local lst;
+
+	lst = srcpath;
+	file = pcfile(addr);
+	if file[0] == '/' && access(file) then {
+		rc("B "+itoa(-pcline(addr))+" "+file);
+		return {};
+	}
+	while head lst do {
+		name = head lst+file;
+		if access(name) then {
+			rc("B "+itoa(-pcline(addr))+" "+name);
+			return {};
+		}
+		lst = tail lst;
+	}
+	print("no source for ", file, "\n");
+}
+
+defn src(addr)
+{
+	local src, file, line, cline, text;
+
+	file = pcfile(addr);
+	src = match(file, srcfiles);
+
+	if src >= 0 then
+		src = srctext[src];
+	else
+		src = findsrc(file);
+
+	if src == {} then {
+		print("no source for ", file, "\n");
+		return {};
+	}
+
+	cline = pcline(addr)-1;
+	print(file, ":", cline, "\n");
+	line = cline-5;
+	loop 0,10 do {
+		if line >= 0 then {
+			if line == cline then
+				print(">");
+			else
+				print(" ");
+			text = src[line];
+			if text == {} then
+				return {};
+			print(line, "\t", text, "\n");
+		}
+		line = line+1;
+	}
+}
+
+defn stopped(pid)		// called from acid when a process changes state
+{
+	pstop(pid);		// stub so this is easy to replace
+}
+
+defn procs()			// print status of processes
+{
+	local c, lst, cpid;
+
+	cpid = pid;
+	lst = proclist;
+	while lst do {
+		np = head lst;
+		setproc(np);
+		if np == cpid then
+			c = '>';
+		else
+			c = ' ';
+		print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n");
+		lst = tail lst;
+	}
+	pid = cpid;
+	if pid != 0 then
+		setproc(pid);
+}
+
+defn asm(addr)
+{
+	local bound;
+
+	bound = fnbound(addr);
+
+	addr = fmt(addr, 'i');
+	loop 1,30 do {
+		print(fmt(addr, 'a'), " ", fmt(addr, 'X'));
+		print("\t", @addr++, "\n");
+		if bound != {} && addr > bound[1] then {
+			lasmaddr = addr;
+			return {};
+		}
+	}
+	lasmaddr = addr;
+}
+
+defn casm()
+{
+	asm(lasmaddr);
+}
+
+defn new()
+{
+	bplist = {};
+	newproc(progargs);
+	// Dont miss the delay slot calls
+	bpset(follow(main)[0]);
+	cont();
+	bpdel(*PC);
+}
+
+defn stmnt()			// step one statement
+{
+	local line;
+
+	line = pcline(*PC);
+	while 1 do {
+		step();
+		if line != pcline(*PC) then {
+			src(*PC);
+			return {};
+		}
+	}
+}
+
+defn func()			// step until we leave the current function
+{
+	local bound, end, start, pc;
+
+	bound = fnbound(*PC);
+	if bound == {} then {
+		print("cannot locate text symbol\n");
+		return {};
+	}
+
+	pc = *PC;
+	start = bound[0];
+	end = bound[1];
+	while pc >= start && pc < end do {
+		step();
+		pc = *PC;
+	}
+}
+
+defn next()
+{
+	local sp, bound;
+
+	sp = *SP;
+	bound = fnbound(*PC);
+	stmnt();
+	pc = *PC;
+	if pc >= bound[0] && pc < bound[1] then
+		return {};
+
+	while (pc < bound[0] || pc > bound[1]) && sp >= *SP do {
+		step();
+		pc = *PC;
+	}
+	src(*PC);
+}
+
+defn dump(addr, n, fmt)
+{
+	loop 0, n do {
+		print(fmt(addr, 'X'), ": ");
+		addr = mem(addr, fmt);
+	}
+}
+
+defn mem(addr, fmt)
+{
+
+	local i, c, n;
+
+	i = 0;
+	while fmt[i] != 0 do {
+		c = fmt[i];
+		n = 0;
+		while '0' <= fmt[i] && fmt[i] <= '9' do {
+			n = 10*n + fmt[i]-'0';
+			i = i+1;
+		}
+		if n <= 0 then n = 1;
+		addr = fmt(addr, fmt[i]);
+		while n > 0 do {
+			print(*addr++, " ");
+			n = n-1;
+		}
+		i = i+1;
+	}
+	print("\n");
+	return addr;
+}
+
+defn symbols(pattern)
+{
+	local l, s;
+
+	l = symbols;
+	while l do {
+		s = head l;
+		if regexp(pattern, s[0]) then
+			print(s[0], "\t", s[1], "\t", s[2], "\n");
+		l = tail l;
+	}
+}
+
+defn spsrch(len)
+{
+	local addr, a, s, e;
+
+	addr = *SP;
+	s = origin & 0x7fffffff;
+	e = etext & 0x7fffffff;
+	loop 1, len do {
+		a = *addr++;
+		c = a & 0x7fffffff;
+		if c > s && c < e then {
+			print("src(", a, ")\n");
+			pfl(a);
+		}			
+	}
+}
+
+defn bppush(val)
+{
+	return {"p", val};
+}
+
+defn bpderef()
+{
+	return {"*", 0};
+}
+
+defn bpmask()
+{
+	return {"&", 0};
+}
+
+defn bpeq()
+{
+	return {"=", 0};
+}
+
+defn bpneq()
+{
+	return {"!", 0};
+}
+
+defn bpand()
+{
+	return {"a", 0};
+}
+
+defn bpor()
+{
+	return {"o", 0};
+}
+
+defn bpcondset(pid, addr, conds)
+{
+	local l;
+	local id;
+	local found;
+
+ 	if status(pid) != "Stopped" then {
+ 		print("Waiting...\n");
+ 		stop(pid);
+ 	}
+
+	id = 0;
+	found = 0;
+
+	while !found && id <= 255 do {
+		l = bpl;
+		while l && head head l != id do {
+			l = tail l;
+		}
+
+		if !l then
+			found = 1;
+		else
+			id = id + 1;
+	}
+
+	if !found then {
+		print("error: no breakpoints available\n");
+		return -1;
+	}
+
+	bpl = append bpl, {id\d, pid\d, addr\X, conds};
+
+	_bpcondset(id, pid, addr, conds);
+
+	return id;
+}
+
+defn bpconddel(id)
+{
+	local i;
+	local l;
+
+	l = bpl;
+	i = 0;
+	while l do {
+		if id == head head l then {
+			bpl = delete bpl, i;
+			_bpconddel(id);
+			if id == bpid then
+				bpid = -1;
+			return {};
+		}
+		i = i + 1;
+		l = tail l;
+	}
+	print("no breakpoint with id ", id\d, ".\n");
+}
+
+defn bpprint(b)
+{
+	local l;
+
+	print(b[0], "\t", b[1], "\t", fmt(b[2], 'a'), " ", b[2]);
+	print("\t{");
+	l = b[3];
+	while l do {
+		print("\n\t\t\t\t\t", head l);
+		l = tail l;
+	}
+	print(" }\n");
+}
+
+defn bptab()
+{
+	local l;
+
+	l = bpl;
+	print("ID	PID	ADDR			CONDITIONS\n");
+	while l do {
+		bpprint(head l);
+		l = tail l;
+	}
+}
+
+defn cont()
+{
+	local b, c, l, found;
+
+	l = bpl;
+	found = 0;
+	c = curpc();
+	while !found && l do {
+		b = head l;
+		if b[2] == c then {
+			nopstop = 1;
+			step();
+			nopstop = 0;
+			found = 1;
+		} else {
+			l = tail l;
+		}
+	}
+
+	return startstop(pid);
+}
+
+defn bpset(addr)				// set a breakpoint
+{
+	return bpcondset(pid, addr, {});
+}
+
+defn bpdel(id)
+{
+	bpconddel(id);
+}
+
+defn bpaddr(id)
+{
+	local i;
+	local l;
+	local b;
+
+	l = bpl;
+	i = 0;
+	while l do {
+		b = head l;
+		if id == b[0] then
+			return b[2];
+		i = i + 1;
+		l = tail l;
+	}
+	print("bpaddr(", id\d, "): no match\n");
+	return {};
+}
+
+progargs="";
+print("/sys/lib/acid/port");
--- /dev/null
+++ b/utils/acid/print.c
@@ -1,0 +1,444 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+
+static char *binop[] =
+{
+	0,
+	0,
+	"*",	/* [OMUL]	"*", */
+	"/",	/* [ODIV]	"/", */
+	"%",	/* [OMOD]	"%", */
+	"+",	/* [OADD]	"+", */
+	"-",	/* [OSUB]	"-", */
+	">>",	/* [ORSH]	">>", */
+	"<<",	/* [OLSH]	"<<", */
+	"<",	/* [OLT]	"<", */
+	">",	/* [OGT]	">", */
+	"<=",	/* [OLEQ]	"<=", */
+	">=",	/* [OGEQ]	">=", */
+	"==",	/* [OEQ]	"==", */
+	"!=",	/* [ONEQ]	"!=", */
+	"&",	/* [OLAND]	"&", */
+	"^",	/* [OXOR]	"^", */
+	"|",	/* [OLOR]	"|", */
+	"&&",	/* [OCAND]	"&&", */
+	"||",	/* [OCOR]	"||", */
+	" = ",	/* [OASGN]	" = ", */
+};
+
+static char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+char *typenames[] =
+{
+	"integer",	/* [TINT]	"integer", */
+	"float",	/* [TFLOAT]	"float", */
+	"string",	/* [TSTRING]	"string", */
+	"list",		/* [TLIST]	"list", */
+	"code",		/* [TCODE]	"code", */
+};
+
+int
+cmp(void *a, void *b)
+{
+	return strcmp(*(char**)a, *(char**)b);
+}
+
+void
+fundefs(void)
+{
+	Lsym *l;
+	char **vec;
+	int i, j, n, max, col, f, g, s;
+
+	max = 0;
+	f = 0;
+	g = 100;
+	vec = gmalloc(sizeof(char*)*g);
+	if(vec == 0)
+		fatal("out of memory");
+
+	for(i = 0; i < Hashsize; i++) {
+		for(l = hash[i]; l; l = l->hash) {
+			if(l->proc == 0 && l->builtin == 0)
+				continue;
+			n = strlen(l->name);
+			if(n > max)
+				max = n;
+			if(f >= g) {
+				g *= 2;
+				vec = realloc(vec, sizeof(char*)*g);
+				if(vec == 0)
+					fatal("out of memory");
+			}
+			vec[f++] = l->name;
+		}
+	}
+        qsort(vec, f, sizeof(char*), cmp);
+	max++;
+	col = 60/max;
+	s = (f+col-1)/col;
+
+	for(i = 0; i < s; i++) {
+		for(j = i; j < f; j += s)
+			Bprint(bout, "%-*s", max, vec[j]);
+		Bprint(bout, "\n");
+	}
+}
+
+void
+whatis(Lsym *l)
+{
+	int t;
+	int def;
+	Type *ti;
+
+	if(l == 0) {
+		fundefs();
+		return;
+	}
+
+	def = 0;
+	if(l->v->set) {
+		t = l->v->type;
+		Bprint(bout, "%s variable", typenames[t]);
+		if(t == TINT || t == TFLOAT)
+			Bprint(bout, " format %c", l->v->vstore.fmt);
+		if(l->v->vstore.comt)
+			Bprint(bout, " complex %s",
+						l->v->vstore.comt->base->name);
+		Bputc(bout, '\n');
+		def = 1;
+	}
+	if(l->lt) {
+		Bprint(bout, "complex %s {\n", l->name);
+		for(ti = l->lt; ti; ti = ti->next) {
+			if(ti->type) {
+				if(ti->fmt == 'a') {
+					Bprint(bout, "\t%s %d %s;\n",
+					ti->type->name, ti->offset,
+					ti->tag->name);
+				}
+				else {
+					Bprint(bout, "\t'%c' %s %d %s;\n",
+					ti->fmt, ti->type->name, ti->offset,
+					ti->tag->name);
+				}
+			}
+			else
+				Bprint(bout, "\t'%c' %d %s;\n",
+				ti->fmt, ti->offset, ti->tag->name);
+		}
+		Bprint(bout, "};\n");
+		def = 1;
+	}
+	if(l->proc) {
+		Bprint(bout, "defn %s(", l->name);
+		pexpr(l->proc->left);
+		Bprint(bout, ") {\n");
+		pcode(l->proc->right, 1);
+		Bprint(bout, "}\n");
+		def = 1;
+	}
+	if(l->builtin) {
+		Bprint(bout, "builtin function\n");
+		def = 1;
+	}
+	if(def == 0)
+		Bprint(bout, "%s is undefined\n", l->name);
+}
+
+void
+slist(Node *n, int d)
+{
+	if(n == 0)
+		return;
+	if(n->op == OLIST)
+		Bprint(bout, "%.*s{\n", d-1, tabs);
+	pcode(n, d);
+	if(n->op == OLIST)
+		Bprint(bout, "%.*s}\n", d-1, tabs);
+}
+
+void
+pcode(Node *n, int d)
+{
+	Node *r, *l;
+
+	if(n == 0)
+		return;
+
+	r = n->right;
+	l = n->left;
+
+	switch(n->op) {
+	default:
+		Bprint(bout, "%.*s", d, tabs);
+		pexpr(n);
+		Bprint(bout, ";\n");
+		break;
+	case OLIST:
+		pcode(n->left, d);
+		pcode(n->right, d);
+		break;
+	case OLOCAL:
+		Bprint(bout, "%.*slocal", d, tabs);
+		while(l) {
+			Bprint(bout, " %s", l->sym->name);
+			l = l->left;
+			if(l == 0)
+				Bprint(bout, ";\n");
+			else
+				Bprint(bout, ",");
+		}
+		break;
+	case OCOMPLEX:
+		Bprint(bout, "%.*scomplex %s %s;\n", d, tabs, n->sym->name, l->sym->name);
+		break;
+	case OIF:
+		Bprint(bout, "%.*sif ", d, tabs);
+		pexpr(l);
+		d++;
+		Bprint(bout, "%.*sthen\n", d, tabs);
+		if(r && r->op == OELSE) {
+			slist(r->left, d);
+			Bprint(bout, "%.*selse\n", d-1, tabs);
+			slist(r->right, d);
+		}
+		else
+			slist(r, d);
+		break;
+	case OWHILE:
+		Bprint(bout, "%.*swhile ", d, tabs);
+		pexpr(l);
+		d++;
+		Bprint(bout, "%.*sdo\n", d, tabs);
+		slist(r, d);
+		break;
+	case ORET:
+		Bprint(bout, "%.*sreturn ", d, tabs);
+		pexpr(l);
+		Bprint(bout, ";\n");
+		break;
+	case ODO:
+		Bprint(bout, "%.*sloop ", d, tabs);
+		pexpr(l->left);
+		Bprint(bout, ", ");
+		pexpr(l->right);
+		Bprint(bout, " do\n");
+		slist(r, d+1);
+	}
+}
+
+void
+pexpr(Node *n)
+{
+	Node *r, *l;
+
+	if(n == 0)
+		return;
+
+	r = n->right;
+	l = n->left;
+
+	switch(n->op) {
+	case ONAME:
+		Bprint(bout, "%s", n->sym->name);
+		break;
+	case OCONST:
+		switch(n->type) {
+		case TINT:
+			Bprint(bout, "%d", (int)n->nstore.u0.sival);
+			break;
+		case TFLOAT:
+			Bprint(bout, "%g", n->nstore.u0.sfval);
+			break;
+		case TSTRING:
+			pstr(n->nstore.u0.sstring);
+			break;
+		case TLIST:
+			break;
+		}
+		break;
+	case OMUL:
+	case ODIV:
+	case OMOD:
+	case OADD:
+	case OSUB:
+	case ORSH:
+	case OLSH:
+	case OLT:
+	case OGT:
+	case OLEQ:
+	case OGEQ:
+	case OEQ:
+	case ONEQ:
+	case OLAND:
+	case OXOR:
+	case OLOR:
+	case OCAND:
+	case OCOR:
+		Bputc(bout, '(');
+		pexpr(l);
+		Bprint(bout, binop[n->op]);
+		pexpr(r);
+		Bputc(bout, ')');
+		break;
+	case OASGN:
+		pexpr(l);
+		Bprint(bout, binop[n->op]);
+		pexpr(r);
+		break;
+	case OINDM:
+		Bprint(bout, "*");
+		pexpr(l);
+		break;
+	case OEDEC:
+		Bprint(bout, "--");
+		pexpr(l);
+		break;
+	case OEINC:
+		Bprint(bout, "++");
+		pexpr(l);
+		break;
+	case OPINC:
+		pexpr(l);
+		Bprint(bout, "++");
+		break;
+	case OPDEC:
+		pexpr(l);
+		Bprint(bout, "--");
+		break;
+	case ONOT:
+		Bprint(bout, "!");
+		pexpr(l);
+		break;
+	case OLIST:
+		pexpr(l);
+		if(r) {
+			Bprint(bout, ",");
+			pexpr(r);
+		}
+		break;
+	case OCALL:
+		pexpr(l);
+		Bprint(bout, "(");
+		pexpr(r);
+		Bprint(bout, ")");
+		break;
+	case OCTRUCT:
+		Bprint(bout, "{");
+		pexpr(l);
+		Bprint(bout, "}");
+		break;
+	case OHEAD:
+		Bprint(bout, "head ");
+		pexpr(l);
+		break;
+	case OTAIL:
+		Bprint(bout, "tail ");
+		pexpr(l);
+		break;
+	case OAPPEND:
+		Bprint(bout, "append ");
+		pexpr(l);
+		Bprint(bout, ",");
+		pexpr(r);
+		break;
+	case ODELETE:
+		Bprint(bout, "delete ");
+		pexpr(l);
+		Bprint(bout, ",");
+		pexpr(r);
+		break;
+	case ORET:
+		Bprint(bout, "return ");
+		pexpr(l);
+		break;
+	case OINDEX:
+		pexpr(l);
+		Bprint(bout, "[");
+		pexpr(r);
+		Bprint(bout, "]");
+		break;
+	case OINDC:
+		Bprint(bout, "@");
+		pexpr(l);
+		break;
+	case ODOT:
+		pexpr(l);
+		Bprint(bout, ".%s", n->sym->name);
+		break;
+	case OFRAME:
+		Bprint(bout, "%s:%s", n->sym->name, l->sym->name);
+		break;
+	case OCAST:
+		Bprint(bout, "(%s)", n->sym->name);
+		pexpr(l);
+		break;
+	case OFMT:
+		pexpr(l);
+		Bprint(bout, "\\%c", (int)r->nstore.u0.sival);
+		break;
+	case OEVAL:
+		Bprint(bout, "eval ");
+		pexpr(l);
+		break;
+	case OWHAT:
+		Bprint(bout, "whatis");
+		if(n->sym)
+			Bprint(bout, " %s", n->sym->name);
+		break;
+	}
+}
+
+void
+pstr(String *s)
+{
+	int i, c;
+
+	Bputc(bout, '"');
+	for(i = 0; i < s->len; i++) {
+		c = s->string[i];
+		switch(c) {
+		case '\0':
+			c = '0';
+			break;
+		case '\n':
+			c = 'n';
+			break;
+		case '\r':
+			c = 'r';
+			break;
+		case '\t':
+			c = 't';
+			break;
+		case '\b':
+			c = 'b';
+			break;
+		case '\f':
+			c = 'f';
+			break;
+		case '\a':
+			c = 'a';
+			break;
+		case '\v':
+			c = 'v';
+			break;
+		case '\\':
+			c = '\\';
+			break;
+		case '"':
+			c = '"';
+			break;
+		default:
+			Bputc(bout, c);
+			continue;
+		}
+		Bputc(bout, '\\');
+		Bputc(bout, c);
+	}
+	Bputc(bout, '"');
+}
--- /dev/null
+++ b/utils/acid/proc.c
@@ -1,0 +1,274 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+void
+nocore(void)
+{
+	int i;
+
+	if(cormap == 0)
+		return;
+
+	for (i = 0; i < cormap->nsegs; i++)
+		if (cormap->seg[i].mget == 0 && cormap->seg[i].inuse && cormap->seg[i].fd >= 0)
+			close(cormap->seg[i].fd);
+	free(cormap);
+	cormap = 0;
+}
+
+void
+sproc(int pid)
+{
+	Lsym *s;
+	char buf[64];
+	ulong proctab;
+	int fd, i, fcor;
+
+	if(symmap == 0)
+		error("no map");
+
+	if(rdebug) {
+		fcor = -1;
+		proctab = 0;
+		i = remoteio(pid, "proc", buf, sizeof(buf));
+		if(i >= 0) {
+			buf[i] = '\0';
+			proctab = strtoul(buf, 0, 16);
+		} else
+			error("can't access pid %d: %r", pid);
+		s = look("proc");
+		if(s != 0)
+			s->v->vstore.u0.sival = proctab;
+	} else {
+		sprint(buf, "/proc/%d/mem", pid);
+		fcor = open(buf, ORDWR);
+		if(fcor < 0)
+			error("setproc: open %s: %r", buf);
+
+		checkqid(symmap->seg[0].fd, pid);
+
+		if(kernel) {
+			proctab = 0;
+			sprint(buf, "/proc/%d/proc", pid);
+			fd = open(buf, OREAD);
+			if(fd >= 0) {
+				i = read(fd, buf, sizeof(buf));
+				if(i >= 0) {
+					buf[i] = '\0';
+					proctab = strtoul(buf, 0, 0);
+				}
+				close(fd);
+			}
+			s = look("proc");
+			if(s != 0)
+				s->v->vstore.u0.sival = proctab;
+		}
+	}
+
+	s = look("pid");
+	s->v->vstore.u0.sival = pid;
+
+	nocore();
+	if(rdebug) {
+		cormap = attachremt(remfd, &fhdr);
+		for(i = 0; i < cormap->nsegs; i++)
+			setmapio(cormap, i, remget, remput);
+	} else
+		cormap = attachproc(pid, kernel, fcor, &fhdr);
+	if (cormap == 0)
+		error("setproc: cant make coremap");
+	i = findseg(cormap, "text");
+	if (i >= 0)
+		cormap->seg[i].name = "*text";
+	i = findseg(cormap, "data");
+	if (i >= 0)
+		cormap->seg[i].name = "*data";
+	install(pid);
+}
+
+void
+notes(int pid)
+{
+	Lsym *s;
+	Value *v;
+	int i, fd;
+	char buf[128];
+	List *l, **tail;
+
+	s = look("notes");
+	if(s == 0)
+		return;
+	v = s->v;
+
+	if(!rdebug) {
+		sprint(buf, "/proc/%d/note", pid);
+		fd = open(buf, OREAD);
+		if(fd < 0)
+			error("pid=%d: open note: %r", pid);
+	} else
+		fd = -1;
+
+	v->set = 1;
+	v->type = TLIST;
+	v->vstore.u0.sl = 0;
+	tail = &v->vstore.u0.sl;
+	for(;;) {
+		if(rdebug)
+			i = remoteio(pid, "note", buf, sizeof(buf));
+		else
+			i = read(fd, buf, sizeof(buf));
+		if(i <= 0)
+			break;
+		buf[i] = '\0';
+		l = al(TSTRING);
+		l->lstore.u0.sstring = strnode(buf);
+		l->lstore.fmt = 's';
+		*tail = l;
+		tail = &l->next;
+	}
+	if(fd >= 0)
+		close(fd);
+}
+
+void
+dostop(int pid)
+{
+	Lsym *s;
+	Node *np, *p;
+
+	s = look("stopped");
+	if(s && s->proc) {
+		np = an(ONAME, ZN, ZN);
+		np->sym = s;
+		np->nstore.fmt = 'D';
+		np->type = TINT;
+		p = con(pid);
+		p->nstore.fmt = 'D';
+		np = an(OCALL, np, p);
+		execute(np);
+	}
+}
+
+void
+install(int pid)
+{
+	Lsym *s;
+	List *l;
+	char buf[128];
+	int i, fd, new, p;
+
+	new = -1;
+	for(i = 0; i < Maxproc; i++) {
+		p = ptab[i].pid;
+		if(p == pid)
+			return;
+		if(p == 0 && new == -1)
+			new = i;
+	}
+	if(new == -1)
+		error("no free process slots");
+
+	if(!rdebug) {
+		sprint(buf, "/proc/%d/ctl", pid);
+		fd = open(buf, OWRITE);
+		if(fd < 0)
+			error("pid=%d: open ctl: %r", pid);
+	} else
+		fd = -1;
+	ptab[new].pid = pid;
+	ptab[new].ctl = fd;
+
+	s = look("proclist");
+	l = al(TINT);
+	l->lstore.fmt = 'D';
+	l->lstore.u0.sival = pid;
+	l->next = s->v->vstore.u0.sl;
+	s->v->vstore.u0.sl = l;
+	s->v->set = 1;
+}
+
+void
+deinstall(int pid)
+{
+	int i;
+	Lsym *s;
+	List *f, **d;
+
+	for(i = 0; i < Maxproc; i++) {
+		if(ptab[i].pid == pid) {
+			if(ptab[i].ctl >= 0)
+				close(ptab[i].ctl);
+			ptab[i].pid = 0;
+			s = look("proclist");
+			d = &s->v->vstore.u0.sl;
+			for(f = *d; f; f = f->next) {
+				if(f->lstore.u0.sival == pid) {
+					*d = f->next;
+					break;
+				}
+			}
+			s = look("pid");
+			if(s->v->vstore.u0.sival == pid)
+				s->v->vstore.u0.sival = 0;
+			return;
+		}
+	}
+}
+
+void
+msg(int pid, char *msg)
+{
+	int i;
+	int l;
+	int ok;
+	char err[ERRMAX];
+
+	for(i = 0; i < Maxproc; i++) {
+		if(ptab[i].pid == pid) {
+			l = strlen(msg);
+			if(rdebug)
+				ok = sendremote(pid, msg) >= 0;
+			else
+				ok = write(ptab[i].ctl, msg, l) == l;
+			if(!ok) {
+				errstr(err, sizeof err);
+				if(strcmp(err, "process exited") == 0)
+					deinstall(pid);
+				error("msg: pid=%d %s: %s", pid, msg, err);
+			}
+			return;
+		}
+	}
+	error("msg: pid=%d: not found for %s", pid, msg);
+}
+
+char *
+getstatus(int pid)
+{
+	int fd;
+	char *p;
+
+	static char buf[128];
+
+	if(rdebug) {
+		if(remoteio(pid, "status", buf, sizeof(buf)) < 0)
+			error("remote status: pid %d: %r", pid);
+		return buf;
+	}
+	sprint(buf, "/proc/%d/status", pid);
+	fd = open(buf, OREAD);
+	if(fd < 0)
+		error("open %s: %r", buf);
+	read(fd, buf, sizeof(buf));
+	close(fd);
+	p = buf+56+12;			/* Do better! */
+	while(*p == ' ')
+		p--;
+	p[1] = '\0';
+	return buf+56;			/* ditto */
+}
--- /dev/null
+++ b/utils/acid/rdebug.c
@@ -1,0 +1,274 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+#include "../../include/rdbg.h"
+
+/*
+ * remote kernel debugging
+ */
+
+enum {
+	chatty = 0
+};
+
+static	long	myreadn(int, void*, long);
+static	int	rproto(int, char*, int);
+
+void
+remotemap(Map *m, int i)
+{
+	setmapio(m, i, remget, remput);
+}
+
+/*
+ * send a /proc control message to remote pid,
+ * and await a reply
+ */
+int
+sendremote(int pid, char *msg)
+{
+	int tag;
+	char dbg[RDBMSGLEN];
+
+	if(protodebug)
+		fprint(2, "sendremote: pid %d: %s\n", pid, msg);
+	if(strcmp(msg, "startstop") == 0)
+		tag = Tstartstop;
+	else if(strcmp(msg, "waitstop") == 0)
+		tag = Twaitstop;
+	else if(strcmp(msg, "start") == 0)
+		tag = Tstart;
+	else if(strcmp(msg, "stop") == 0)
+		tag = Tstop;
+	else if(strcmp(msg, "kill") == 0)
+		tag = Tkill;
+	else {
+		werrstr("invalid sendremote: %s", msg);
+		return -1;
+	}
+	memset(dbg, 0, sizeof(dbg));
+	dbg[0] = tag;
+	dbg[1] = pid>>24;
+	dbg[2] = pid>>16;
+	dbg[3] = pid>>8;
+	dbg[4] = pid;
+	if(rproto(remfd, dbg, sizeof(dbg)) < 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * read a line from /proc/<pid>/<file> into buf
+ */
+int
+remoteio(int pid, char *file, char *buf, int nb)
+{
+	char dbg[RDBMSGLEN];
+	int tag;
+
+	if(protodebug)
+		fprint(2, "remoteio %d: %s\n", pid, file);
+	memset(buf, 0, nb);
+	if(strcmp(file, "proc") == 0)
+		tag = Tproc;
+	else if(strcmp(file, "status") == 0)
+		tag = Tstatus;
+	else if(strcmp(file, "note") == 0)
+		tag = Trnote;
+	else {
+		werrstr("invalid remoteio: %s", file);
+		return -1;
+	}
+	memset(dbg, 0, sizeof(dbg));
+	dbg[0] = tag;
+	dbg[1] = pid>>24;
+	dbg[2] = pid>>16;
+	dbg[3] = pid>>8;
+	dbg[4] = pid;
+	if(rproto(remfd, dbg, sizeof(dbg)) < 0)
+		return -1;
+	if(nb > sizeof(dbg)-1)
+		nb = sizeof(dbg)-1;
+	memmove(buf, dbg+1, nb);
+	return strlen(buf);
+}
+
+int
+remget(struct segment *s, ulong addr, long off, char *buf, int size)
+{
+	int n, t;
+	char dbg[RDBMSGLEN];
+
+	if (protodebug)
+		fprint(2, "remget addr %#lux off %#lux\n", addr, off);
+	for (t = 0; t < size; t += n) {
+		n = size;
+		if(n > 9)
+			n = 9;
+		memset(dbg, 0, sizeof(dbg));
+		dbg[0] = Tmget;
+		dbg[1] = off>>24;
+		dbg[2] = off>>16;
+		dbg[3] = off>>8;
+		dbg[4] = off;
+		dbg[5] = n;
+		if(rproto(s->fd, dbg, sizeof(dbg)) < 0) {
+			werrstr("can't read address %#lux: %r", addr);
+			return -1;
+		}
+		memmove(buf, dbg+1, n);
+		buf += n;
+	}
+	return t;
+}
+
+int
+remput(struct segment *s, ulong addr, long off, char *buf, int size)
+{
+	int n, i, t;
+	char dbg[RDBMSGLEN];
+
+	if (protodebug)
+		fprint(2, "remput addr %#lux off %#lux\n", addr, off);
+	for (t = 0; t < size; t += n) {
+		n = size;
+		if(n > 4)
+			n = 4;
+		memset(dbg, 0, sizeof(dbg));
+		dbg[0] = Tmput;
+		dbg[1] = off>>24;
+		dbg[2] = off>>16;
+		dbg[3] = off>>8;
+		dbg[4] = off;
+		dbg[5] = n;
+		for(i=0; i<n; i++)
+			dbg[6+i] = *buf++;
+		if(rproto(s->fd, dbg, sizeof(dbg)) < 0) {
+			werrstr("can't write address %#lux: %r", addr);
+			return -1;
+		}
+	}
+	return t;
+}
+
+int
+remcondset(char op, ulong val)
+{
+	char dbg[RDBMSGLEN];
+
+	if (protodebug)
+		fprint(2, "remcondset op %c val: %#lux\n", op, val);
+	memset(dbg, 0, sizeof(dbg));
+
+	dbg[0] = Tcondbreak;
+	dbg[1] = val>>24;
+	dbg[2] = val>>16;
+	dbg[3] = val>>8;
+	dbg[4] = val;
+	dbg[5] = op;
+	if(rproto(remfd, dbg, sizeof(dbg)) < 0) {
+		werrstr("can't set condbreak: %c %#lux: %r", op, val);
+		return -1;
+	}
+	return 0;
+}
+
+int
+remcondstartstop(int pid)
+{
+	char dbg[RDBMSGLEN];
+
+	if (protodebug) 
+		fprint(2, "remcondstartstop pid %d\n", pid);
+	memset(dbg, 0, sizeof(dbg));
+
+	dbg[0] = Tstartstop;
+	dbg[1] = pid>>24;
+	dbg[2] = pid>>16;
+	dbg[3] = pid>>8;
+	dbg[4] = pid;
+
+	if(rproto(remfd, dbg, sizeof(dbg)) < 0) {
+		werrstr("can't send Tstartstop");
+		return -1;
+	}
+
+	return dbg[1];
+}
+
+static int
+rproto(int fd, char *buf, int nb)
+{
+	int tag;
+
+	if (protodebug) {
+		int i;
+		print("rproto remote write fd %d bytes: %d\n", fd, nb);
+		for (i=0; i < nb; i++) {
+			print(" %2.2ux", buf[i]&0xFF);
+		}
+		print("\n");
+	}
+	tag = buf[0];
+	if(remote_write(fd, buf, nb) != nb ||
+	    myreadn(fd, buf, nb) != nb){	/* could set alarm */
+		werrstr("remote i/o: %r");
+		return -1;
+	}
+	if(buf[0] == Rerr){
+		buf[nb-1] = 0;
+		werrstr("remote err: %s", buf+1);
+		return -1;
+	}
+	if(buf[0] != tag+1) {
+		werrstr("remote proto err: %.2ux", buf[0]&0xff);
+		return -1;
+	}
+	if(chatty) {
+		int i;
+		fprint(2, "remote [%d]: ", nb);
+		for(i=0; i<nb; i++)
+			fprint(2, " %.2ux", buf[i]&0xff);
+		fprint(2, "\n");
+	}
+	return nb;
+}
+
+/*
+ * this should probably be in lib9 as readn
+ */
+static long
+myreadn(int f, void *av, long n)
+{
+	char *a;
+	long m, t;
+
+	if (protodebug) {
+		print("remote read fd %d bytes: %ld", f, n);
+	}
+	a = av;
+	t = 0;
+	while(t < n){
+		m = remote_read(f, a+t, n-t);
+		if(m < 0){
+			if(t == 0)
+				return m;
+			break;
+		}
+		if (protodebug) {
+			print(" rtn: %ld\n", m);
+			if (m) {
+				int i;
+		
+				for (i=0; i < m; i++)
+					print(" %2.2ux", a[i+t]&0xFF);
+				print("\n");
+			}
+		}
+		t += m;
+	}
+	return t;
+}
--- /dev/null
+++ b/utils/acid/sparc
@@ -1,0 +1,218 @@
+// Sparc support
+
+defn acidinit()			// Called after all the init modules are loaded
+{
+	bplist = {};
+	bpfmt = 'X';
+
+	srcpath = {
+		"./",
+		"/sys/src/libc/port/",
+		"/sys/src/libc/9sys/",
+		"/sys/src/libc/sparc/"
+	};
+
+	srcfiles = {};		// list of loaded files
+	srctext = {};		// the text of the files
+}
+
+defn stk()			// trace
+{
+	_stk(*PC, *R1, linkreg(0), 0);
+}
+
+defn lstk()			// trace with locals
+{
+	_stk(*PC, *R1, linkreg(0), 1);
+}
+
+defn gpr()			// print general purpose registers
+{
+	print("R1\t", *R1, "R2\t", *R2, "R3\t", *R3, "\n");
+	print("R4\t", *R4, "R5\t", *R5, "R6\t", *R6, "\n");
+	print("R7\t", *R7, "R8\t", *R8, "R9\t", *R9, "\n");
+	print("R10\t", *R10, "R11\t", *R11, "R12\t", *R12, "\n");
+	print("R13\t", *R13, "R14\t", *R14, "R15\t", *R15, "\n");
+	print("R16\t", *R16, "R17\t", *R17, "R18\t", *R18, "\n");
+	print("R19\t", *R19, "R20\t", *R20, "R21\t", *R21, "\n");
+	print("R22\t", *R22, "R23\t", *R23, "R24\t", *R24, "\n");
+	print("R25\t", *R25, "R26\t", *R26, "R27\t", *R27, "\n");
+	print("R28\t", *R28, "R29\t", *R29, "R30\t", *R30, "\n");
+	print("R31\t", *R31, "\n");
+}
+
+defn spr()				// print special processor registers
+{
+	local pc;
+	local link;
+	local cause;
+
+	pc = *PC;
+	print("PC\t", pc, " ", fmt(pc, 'a'), "  ");
+	pfl(pc);
+	print("PSR\t", *PSR, "\n");
+
+	link = *R15;
+	print("SP\t", *R1, "\tLINK\t\t", link, " ", fmt(link, 'a'));
+	pfl(link);
+
+	cause = *TBR;
+	print("Y\t", *Y, "\tCAUSE\t", *Y, cause, " ", reason(cause), "\n");
+}
+
+defn Fpr()
+{
+	print("F0\t",  *fmt(F0, 'G'),  "\tF2\t",  *fmt(F2, 'G'), "\n");
+	print("F4\t",  *fmt(F4, 'G'),  "\tF6\t",  *fmt(F6, 'G'), "\n");
+	print("F8\t",  *fmt(F8, 'G'),  "\tF10\t", *fmt(F10, 'G'), "\n");
+	print("F12\t", *fmt(F12, 'G'), "\tF14\t", *fmt(F14, 'G'), "\n");
+	print("F16\t", *fmt(F16, 'G'), "\tF18\t", *fmt(F18, 'G'), "\n");
+	print("F20\t", *fmt(F20, 'G'), "\tF22\t", *fmt(F22, 'G'), "\n");
+	print("F24\t", *fmt(F24, 'G'), "\tF26\t", *fmt(F26, 'G'), "\n");
+	print("F28\t", *fmt(F28, 'G'), "\tF30\t", *fmt(F30, 'G'), "\n");
+}
+
+defn fpr()
+{
+	print("F0\t",  *fmt(F0, 'g'),  "\tF1\t",  *fmt(F1, 'g'), "\n");
+	print("F2\t",  *fmt(F2, 'g'),  "\tF3\t",  *fmt(F3, 'g'), "\n");
+	print("F4\t",  *fmt(F4, 'g'),  "\tF5\t",  *fmt(F5, 'g'), "\n");
+	print("F6\t",  *fmt(F6, 'g'),  "\tF7\t",  *fmt(F7, 'g'), "\n");
+	print("F8\t",  *fmt(F8, 'g'),  "\tF9\t",  *fmt(F9, 'g'), "\n");
+	print("F10\t", *fmt(F10, 'g'), "\tF11\t", *fmt(F11, 'g'), "\n");
+	print("F12\t", *fmt(F12, 'g'), "\tF13\t", *fmt(F13, 'g'), "\n");
+	print("F14\t", *fmt(F14, 'g'), "\tF15\t", *fmt(F15, 'g'), "\n");
+	print("F16\t", *fmt(F16, 'g'), "\tF17\t", *fmt(F17, 'g'), "\n");
+	print("F18\t", *fmt(F18, 'g'), "\tF19\t", *fmt(F19, 'g'), "\n");
+	print("F20\t", *fmt(F20, 'g'), "\tF21\t", *fmt(F21, 'g'), "\n");
+	print("F22\t", *fmt(F22, 'g'), "\tF23\t", *fmt(F23, 'g'), "\n");
+	print("F24\t", *fmt(F24, 'g'), "\tF25\t", *fmt(F25, 'g'), "\n");
+	print("F26\t", *fmt(F26, 'g'), "\tF27\t", *fmt(F27, 'g'), "\n");
+	print("F28\t", *fmt(F28, 'g'), "\tF29\t", *fmt(F29, 'g'), "\n");
+	print("F30\t", *fmt(F30, 'g'), "\tF31\t", *fmt(F31, 'g'), "\n");
+}
+
+defn regs()				// print all registers
+{
+	spr();
+	gpr();
+}
+
+defn pstop(pid)
+{
+	local l;
+	local pc;
+
+	pc = *PC;
+
+	print(pid,": ", reason(*TBR), "\t");
+	print(fmt(pc, 'a'), "\t", fmt(pc, 'i'), "\n");
+
+	if notes then {
+		if notes[0] != "sys: breakpoint" then {
+			print("Notes pending:\n");
+			l = notes;
+			while l do {
+				print("\t", head l, "\n");
+				l = tail l;
+			}
+		}
+	}
+}
+
+aggr Ureg
+{
+	'U' 0 r0;
+	{
+	'U' 4 sp;
+	'U' 4 usp;
+	'U' 4 r1;
+	};
+	'U' 8 r2;
+	'U' 12 r3;
+	'U' 16 r4;
+	'U' 20 r5;
+	'U' 24 r6;
+	'U' 28 r7;
+	'U' 32 r8;
+	'U' 36 r9;
+	'U' 40 r10;
+	'U' 44 r11;
+	'U' 48 r12;
+	'U' 52 r13;
+	'U' 56 r14;
+	'U' 60 r15;
+	'U' 64 r16;
+	'U' 68 r17;
+	'U' 72 r18;
+	'U' 76 r19;
+	'U' 80 r20;
+	'U' 84 r21;
+	'U' 88 r22;
+	'U' 92 r23;
+	'U' 96 r24;
+	'U' 100 r25;
+	'U' 104 r26;
+	'U' 108 r27;
+	'U' 112 r28;
+	'U' 116 r29;
+	'U' 120 r30;
+	'U' 124 r31;
+	'U' 128 y;
+	'U' 132 tbr;
+	'U' 136 psr;
+	'U' 140 npc;
+	'U' 144 pc;
+	'U' 148 pad;
+};
+
+defn
+Ureg(addr) {
+	complex Ureg addr;
+	print("	r0	", addr.r0, "\n");
+	print("	sp	", addr.sp, "\n");
+	print("	r2	", addr.r2, "\n");
+	print("	r3	", addr.r3, "\n");
+	print("	r4	", addr.r4, "\n");
+	print("	r5	", addr.r5, "\n");
+	print("	r6	", addr.r6, "\n");
+	print("	r7	", addr.r7, "\n");
+	print("	r8	", addr.r8, "\n");
+	print("	r9	", addr.r9, "\n");
+	print("	r10	", addr.r10, "\n");
+	print("	r11	", addr.r11, "\n");
+	print("	r12	", addr.r12, "\n");
+	print("	r13	", addr.r13, "\n");
+	print("	r14	", addr.r14, "\n");
+	print("	r15	", addr.r15, "\n");
+	print("	r16	", addr.r16, "\n");
+	print("	r17	", addr.r17, "\n");
+	print("	r18	", addr.r18, "\n");
+	print("	r19	", addr.r19, "\n");
+	print("	r20	", addr.r20, "\n");
+	print("	r21	", addr.r21, "\n");
+	print("	r22	", addr.r22, "\n");
+	print("	r23	", addr.r23, "\n");
+	print("	r24	", addr.r24, "\n");
+	print("	r25	", addr.r25, "\n");
+	print("	r26	", addr.r26, "\n");
+	print("	r27	", addr.r27, "\n");
+	print("	r28	", addr.r28, "\n");
+	print("	r29	", addr.r29, "\n");
+	print("	r30	", addr.r30, "\n");
+	print("	r31	", addr.r31, "\n");
+	print("	y	", addr.y, "\n");
+	print("	tbr	", addr.tbr, "\n");
+	print("	psr	", addr.psr, "\n");
+	print("	npc	", addr.npc, "\n");
+	print("	pc	", addr.pc, "\n");
+	print("	pad	", addr.pad, "\n");
+};
+
+defn linkreg(addr)
+{
+	complex Ureg addr;
+	return addr.r15\X;
+}
+
+print("/sys/lib/acid/sparc");
--- /dev/null
+++ b/utils/acid/util.c
@@ -1,0 +1,295 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+#include "mach.h"
+#define Extern extern
+#include "acid.h"
+#include "y.tab.h"
+
+static int syren;
+
+Lsym*
+unique(char *buf, Sym *s)
+{
+	Lsym *l;
+	int i, renamed;
+
+	renamed = 0;
+	strcpy(buf, s->name);
+	for(;;) {
+		l = look(buf);
+		if(l == 0 || (l->lexval == Tid && l->v->set == 0))
+			break;
+
+		if(syren == 0 && !quiet) {
+			print("Symbol renames:\n");
+			syren = 1;
+		}
+		i = strlen(buf)+1;
+		memmove(buf+1, buf, i);
+		buf[0] = '$';
+		renamed++;
+		if(renamed > 5 && !quiet) {
+			print("Too many renames; must be X source!\n");
+			break;
+		}
+	}
+	if(renamed && !quiet)
+		print("\t%s=%s %c/%llux\n", s->name, buf, s->type, s->value);
+	if(l == 0)
+		l = enter(buf, Tid);
+	return l;	
+}
+
+void
+varsym(void)
+{
+	int i;
+	Sym *s;
+	long n;
+	Lsym *l;
+	ulong v;
+	char buf[1024];
+	List *list, **tail, *l2, *tl;
+
+	tail = &l2;
+	l2 = 0;
+
+	symbase(&n);
+	for(i = 0; i < n; i++) {
+		s = getsym(i);
+		switch(s->type) {
+		case 'T':
+		case 'L':
+		case 'D':
+		case 'B':
+		case 'b':
+		case 'd':
+		case 'l':
+		case 't':
+			if(s->name[0] == '.')
+				continue;
+
+			v = s->value;
+			tl = al(TLIST);
+			*tail = tl;
+			tail = &tl->next;
+
+			l = unique(buf, s);
+
+			l->v->set = 1;
+			l->v->type = TINT;
+			l->v->vstore.u0.sival = v;
+			if(l->v->vstore.comt == 0)
+				l->v->vstore.fmt = 'X';
+
+			/* Enter as list of { name, type, value } */
+			list = al(TSTRING);
+			tl->lstore.u0.sl = list;
+			list->lstore.u0.sstring = strnode(buf);
+			list->lstore.fmt = 's';
+			list->next = al(TINT);
+			list = list->next;
+			list->lstore.fmt = 'c';
+			list->lstore.u0.sival = s->type;
+			list->next = al(TINT);
+			list = list->next;
+			list->lstore.fmt = 'X';
+			list->lstore.u0.sival = v;
+
+		}
+	}
+	l = mkvar("symbols");
+	l->v->set = 1;
+	l->v->type = TLIST;
+	l->v->vstore.u0.sl = l2;
+	if(l2 == 0)
+		print("no symbol information\n");
+}
+
+void
+varreg(void)
+{
+	Lsym *l;
+	Value *v;
+	Reglist *r;
+	List **tail, *li;
+
+	l = mkvar("registers");
+	v = l->v;
+	v->set = 1;
+	v->type = TLIST;
+	v->vstore.u0.sl = 0;
+	tail = &v->vstore.u0.sl;
+
+	for(r = mach->reglist; r->rname; r++) {
+		l = mkvar(r->rname);
+		v = l->v;
+		v->set = 1;
+		v->vstore.u0.sival = r->roffs;
+		v->vstore.fmt = r->rformat;
+		v->type = TINT;
+
+		li = al(TSTRING);
+		li->lstore.u0.sstring = strnode(r->rname);
+		li->lstore.fmt = 's';
+		*tail = li;
+		tail = &li->next;
+	}
+
+	if(machdata == 0)
+		return;
+
+	l = mkvar("bpinst");	/* Breakpoint text */
+	v = l->v;
+	v->type = TSTRING;
+	v->vstore.fmt = 's';
+	v->set = 1;
+	v->vstore.u0.sstring = gmalloc(sizeof(String));
+	v->vstore.u0.sstring->len = machdata->bpsize;
+	v->vstore.u0.sstring->string = gmalloc(machdata->bpsize);
+	memmove(v->vstore.u0.sstring->string, machdata->bpinst, machdata->bpsize);
+}
+
+void
+loadvars(void)
+{
+	Lsym *l;
+	Value *v;
+
+	l =  mkvar("proc");
+	v = l->v;
+	v->type = TINT;
+	v->vstore.fmt = 'X';
+	v->set = 1;
+	v->vstore.u0.sival = 0;
+
+	l = mkvar("pid");		/* Current process */
+	v = l->v;
+	v->type = TINT;
+	v->vstore.fmt = 'D';
+	v->set = 1;
+	v->vstore.u0.sival = 0;
+
+	mkvar("notes");			/* Pending notes */
+
+	l = mkvar("proclist");		/* Attached processes */
+	l->v->type = TLIST;
+
+	l = mkvar("rdebug");		/* remote debugging enabled? */
+	v = l->v;
+	v->type = TINT;
+	v->vstore.fmt = 'D';
+	v->set = 1;
+	v->vstore.u0.sival = rdebug;
+
+	if(rdebug) {
+		l = mkvar("_breakid");
+		v = l->v;
+		v->type = TINT;
+		v->vstore.fmt = 'D';
+		v->set = 1;
+		v->vstore.u0.sival = -1;
+	}
+}
+
+uvlong
+rget(Map *map, char *reg)
+{
+	Lsym *s;
+	ulong x;
+	uvlong v;
+	int ret;
+
+	s = look(reg);
+	if(s == 0)
+		fatal("rget: %s\n", reg);
+
+	if(s->v->vstore.fmt == 'Y')
+		ret = get8(map, s->v->vstore.u0.sival, &v);
+	else {
+		ret = get4(map, (long)s->v->vstore.u0.sival, &x);
+		v = x;
+	}
+	if(ret < 0)
+		error("can't get register %s: %r\n", reg);
+	return v;
+}
+
+String*
+strnodlen(char *name, int len)
+{
+	String *s;
+
+	s = gmalloc(sizeof(String)+len+1);
+	s->string = (char*)s+sizeof(String);
+	s->len = len;
+	if(name != 0)
+		memmove(s->string, name, len);
+	s->string[len] = '\0';
+
+	s->sgc.gclink = gcl;
+	gcl = &s->sgc;
+
+	return s;
+}
+
+String*
+strnode(char *name)
+{
+	return strnodlen(name, strlen(name));
+}
+
+String*
+runenode(Rune *name)
+{
+	int len;
+	Rune *p;
+	String *s;
+
+	p = name;
+	for(len = 0; *p; p++)
+		len++;
+
+	len++;
+	len *= sizeof(Rune);
+	s = gmalloc(sizeof(String)+len);
+	s->string = (char*)s+sizeof(String);
+	s->len = len;
+	memmove(s->string, name, len);
+
+	s->sgc.gclink = gcl;
+	gcl = &s->sgc;
+
+	return s;
+}
+
+String*
+stradd(String *l, String *r)
+{
+	int len;
+	String *s;
+
+	len = l->len+r->len;
+	s = gmalloc(sizeof(String)+len+1);
+	s->sgc.gclink = gcl;
+	gcl = &s->sgc;
+	s->len = len;
+	s->string = (char*)s+sizeof(String);
+	memmove(s->string, l->string, l->len);
+	memmove(s->string+l->len, r->string, r->len);
+	s->string[s->len] = 0;
+	return s;
+}
+
+int
+scmp(String *sr, String *sl)
+{
+	if(sr->len != sl->len)
+		return 0;
+
+	if(memcmp(sr->string, sl->string, sl->len))
+		return 0;
+
+	return 1;
+}
--- /dev/null
+++ b/utils/awk/FIXES
@@ -1,0 +1,703 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+This file lists all bug fixes, changes, etc., made since the AWK book
+was sent to the printers in August, 1987.
+
+Nov 15, 2000:
+	fixed a bug introduced in august 1997 that caused expressions
+	like $f[1] to be syntax errors.  thanks to arnold robbins for
+	noticing this and providing a fix.
+
+Oct 30, 2000:
+	fixed some nextfile bugs: not handling all cases.  thanks to
+	arnold robbins for pointing this out.  new regressions added.
+
+	close() is now a function.  it returns whatever the library
+	fclose returns, and -1 for closing a file or pipe that wasn't
+	opened.
+
+Sep 24, 2000:
+	permit \n explicitly in character classes; won't work right
+	if comes in as "[\n]" but ok as /[\n]/, because of multiple
+	processing of \'s.  thanks to arnold robbins.
+
+July 5, 2000:
+	minor fiddles in tran.c to keep compilers happy about uschar.
+	thanks to norman wilson.
+
+May 25, 2000:
+	yet another attempt at making 8-bit input work, with another
+	band-aid in b.c (member()), and some (uschar) casts to head 
+	off potential errors in subscripts (like isdigit).  also
+	changed HAT to NCHARS-2.  thanks again to santiago vila.
+
+	changed maketab.c to ignore apparently out of range definitions
+	instead of halting; new freeBSD generates one.  thanks to
+	jon snader <jsnader@ix.netcom.com> for pointing out the problem.
+
+May 2, 2000:
+	fixed an 8-bit problem in b.c by making several char*'s into
+	unsigned char*'s.  not clear i have them all yet.  thanks to
+	Santiago Vila <sanvila@unex.es> for the bug report.
+
+Apr 21, 2000:
+	finally found and fixed a memory leak in function call; it's
+	been there since functions were added ~1983.  thanks to
+	jon bentley for the test case that found it.
+
+	added test in envinit to catch environment "variables" with
+	names begining with '='; thanks to Berend Hasselman.
+
+Jul 28, 1999:
+	added test in defn() to catch function foo(foo), which
+	otherwise recurses until core dump.  thanks to arnold
+	robbins for noticing this.
+
+Jun 20, 1999:
+	added *bp in gettok in lex.c; appears possible to exit function
+	without terminating the string.  thanks to russ cox.
+
+Jun 2, 1999:
+	added function stdinit() to run to initialize files[] array,
+	in case stdin, etc., are not constants; some compilers care.
+
+May 10, 1999:
+	replaced the ERROR ... FATAL, etc., macros with functions
+	based on vprintf, to avoid problems caused by overrunning
+	fixed-size errbuf array.  thanks to ralph corderoy for the
+	impetus, and for pointing out a string termination bug in
+	qstring as well.
+
+Apr 21, 1999:
+	fixed bug that caused occasional core dumps with commandline
+	variable with value ending in \.  (thanks to nelson beebe for
+	the test case.)
+
+Apr 16, 1999:
+	with code kindly provided by Bruce Lilly, awk now parses 
+	/=/ and similar constructs more sensibly in more places.
+	Bruce also provided some helpful test cases.
+
+Apr 5, 1999:
+	changed true/false to True/False in run.c to make it
+	easier to compile with C++.  Added some casts on malloc
+	and realloc to be honest about casts; ditto.  changed
+	ltype int to long in struct rrow to reduce some 64-bit
+	complaints; other changes scattered throughout for the
+	same purpose.  thanks to Nelson Beebe for these portability
+	improvements.
+
+	removed some horrible pointer-int casting in b.c and elsewhere
+	by adding ptoi and itonp to localize the casts, which are
+	all benign.  fixed one incipient bug that showed up on sgi
+	in 64-bit mode.
+
+	reset lineno for new source file; include filename in error
+	message.  also fixed line number error in continuation lines.
+	(thanks to Nelson Beebe for both of these.)
+
+Mar 24, 1999:
+	Nelson Beebe notes that irix 5.3 yacc dies with a bogus
+	error; use a newer version or switch to bison, since sgi
+	is unlikely to fix it.
+
+Mar 5, 1999:
+	changed isnumber to is_number to avoid the problem caused by
+	versions of ctype.h that include the name isnumber.
+
+	distribution now includes a script for building on a Mac,
+	thanks to Dan Allen.
+
+Feb 20, 1999:
+	fixed memory leaks in run.c (call) and tran.c (setfval).
+	thanks to Stephen Nutt for finding these and providing the fixes.
+
+Jan 13, 1999:
+	replaced srand argument by (unsigned int) in run.c;
+	avoids problem on Mac and potentially on Unix & Windows.
+	thanks to Dan Allen.
+
+	added a few (int) casts to silence useless compiler warnings.
+	e.g., errorflag= in run.c jump().
+
+	added proctab.c to the bundle outout; one less thing
+	to have to compile out of the box.
+
+	added calls to _popen and _pclose to the win95 stub for
+	pipes (thanks to Steve Adams for this helpful suggestion).
+	seems to work, though properties are not well understood
+	by me, and it appears that under some circumstances the
+	pipe output is truncated.  Be careful.
+
+Oct 19, 1998:
+	fixed a couple of bugs in getrec: could fail to update $0
+	after a getline var; because inputFS wasn't initialized, 
+	could split $0 on every character, a misleading diversion.
+
+	fixed caching bug in makedfa: LRU was actually removing
+	least often used.
+
+	thanks to ross ridge for finding these, and for providing
+	great bug reports.
+
+May 12, 1998:
+	fixed potential bug in readrec: might fail to update record
+	pointer after growing.  thanks to dan levy for spotting this
+	and suggesting the fix.
+
+Mar 12, 1998:
+	added -V to print version number and die.
+
+Feb 11, 1998:
+	subtle silent bug in lex.c: if the program ended with a number
+	longer than 1 digit, part of the input would be pushed back and
+	parsed again because token buffer wasn't terminated right.
+	example:  awk 'length($0) > 10'.  blush.  at least i found it
+	myself.
+
+Aug 31, 1997:
+	s/adelete/awkdelete/: SGI uses this in malloc.h.
+	thanks to nelson beebe for pointing this one out.
+
+Aug 21, 1997:
+	fixed some bugs in sub and gsub when replacement includes \\.
+	this is a dark, horrible corner, but at least now i believe that
+	the behavior is the same as gawk and the intended posix standard.
+	thanks to arnold robbins for advice here.
+
+Aug 9, 1997:
+	somewhat regretfully, replaced the ancient lex-based lexical
+	analyzer with one written in C.  it's longer, generates less code,
+	and more portable; the old one depended too much on mysterious
+	properties of lex that were not preserved in other environments.
+	in theory these recognize the same language.
+
+	now using strtod to test whether a string is a number, instead of
+	the convoluted original function.  should be more portable and
+	reliable if strtod is implemented right.
+
+	removed now-pointless optimization in makefile that tries to avoid
+	recompilation when awkgram.y is changed but symbols are not.
+
+	removed most fixed-size arrays, though a handful remain, some
+	of which are unchecked.  you have been warned.
+
+Aug 4, 1997:
+	with some trepidation, replaced the ancient code that managed
+	fields and $0 in fixed-size arrays with arrays that grow on
+	demand.  there is still some tension between trying to make this
+	run fast and making it clean; not sure it's right yet.
+
+	the ill-conceived -mr and -mf arguments are now useful only
+	for debugging.  previous dynamic string code removed.
+
+	numerous other minor cleanups along the way.
+
+Jul 30, 1997:
+	using code provided by dan levy (to whom profuse thanks), replaced
+	fixed-size arrays and awkward kludges by a fairly uniform mechanism
+	to grow arrays as needed for printf, sub, gsub, etc.
+
+Jul 23, 1997:
+	falling off the end of a function returns "" and 0, not 0.
+	thanks to arnold robbins.
+
+Jun 17, 1997:
+	replaced several fixed-size arrays by dynamically-created ones
+	in run.c; added overflow tests to some previously unchecked cases.
+	getline, toupper, tolower.
+
+	getline code is still broken in that recursive calls may wind
+	up using the same space.  [fixed later]
+
+	increased RECSIZE to 8192 to push problems further over the horizon.
+
+	added \r to \n as input line separator for programs, not data.
+	damn CRLFs.
+
+	modified format() to permit explicit printf("%c", 0) to include
+	a null byte in output.  thanks to ken stailey for the fix.
+
+	added a "-safe" argument that disables file output (print >,
+	print >>), process creation (cmd|getline, print |, system), and
+	access to the environment (ENVIRON).  this is a first approximation
+	to a "safe" version of awk, but don't rely on it too much.  thanks
+	to joan feigenbaum and matt blaze for the inspiration long ago.
+
+Jul 8, 1996:
+	fixed long-standing bug in sub, gsub(/a/, "\\\\&"); thanks to
+	ralph corderoy.
+
+Jun 29, 1996:
+	fixed awful bug in new field splitting; didn't get all the places
+	where input was done.
+
+Jun 28, 1996:
+	changed field-splitting to conform to posix definition: fields are
+	split using the value of FS at the time of input; it used to be
+	the value when the field or NF was first referred to, a much less
+	predictable definition.  thanks to arnold robbins for encouragement
+	to do the right thing.
+
+May 28, 1996:
+	fixed appalling but apparently unimportant bug in parsing octal
+	numbers in reg exprs.
+
+	explicit hex in reg exprs now limited to 2 chars: \xa, \xaa.
+
+May 27, 1996:
+	cleaned up some declarations so gcc -Wall is now almost silent.
+
+	makefile now includes backup copies of ytab.c and lexyy.c in case
+	one makes before looking; it also avoids recreating lexyy.c unless
+	really needed.
+
+	s/aprintf/awkprint, s/asprintf/awksprintf/ to avoid some name clashes
+	with unwisely-written header files.
+
+	thanks to jeffrey friedl for several of these.
+
+May 26, 1996:
+	an attempt to rationalize the (unsigned) char issue.  almost all
+	instances of unsigned char have been removed; the handful of places
+	in b.c where chars are used as table indices have been hand-crafted.
+	added some latin-1 tests to the regression, but i'm not confident;
+	none of my compilers seem to care much.  thanks to nelson beebe for
+	pointing out some others that do care.
+
+May 2, 1996:
+	removed all register declarations.
+
+	enhanced split(), as in gawk, etc:  split(s, a, "") splits s into
+	a[1]...a[length(s)] with each character a single element.
+
+	made the same changes for field-splitting if FS is "".
+
+	added nextfile, as in gawk: causes immediate advance to next
+	input file. (thanks to arnold robbins for inspiration and code).
+
+	small fixes to regexpr code:  can now handle []], [[], and
+	variants;  [] is now a syntax error, rather than matching 
+	everything;  [z-a] is now empty, not z.  far from complete
+	or correct, however.  (thanks to jeffrey friedl for pointing out
+	some awful behaviors.)
+
+Apr 29, 1996:
+	replaced uchar by uschar everwhere; apparently some compilers
+	usurp this name and this causes conflicts.
+
+	fixed call to time in run.c (bltin); arg is time_t *.
+
+	replaced horrible pointer/long punning in b.c by a legitimate
+	union.  should be safer on 64-bit machines and cleaner everywhere.
+	(thanks to nelson beebe for pointing out some of these problems.)
+
+	replaced nested comments by #if 0...#endif in run.c, lib.c.
+
+	removed getsval, setsval, execute macros from run.c and lib.c.
+	machines are 100x faster than they were when these macros were
+	first used.
+
+	revised filenames: awk.g.y => awkgram.y, awk.lx.l => awklex.l,
+	y.tab.[ch] => ytab.[ch], lex.yy.c => lexyy.c, all in the aid of
+	portability to nameless systems.
+
+	"make bundle" now includes yacc and lex output files for recipients
+	who don't have yacc or lex.
+
+Aug 15, 1995:
+	initialized Cells in setsymtab more carefully; some fields
+	were not set.  (thanks to purify, all of whose complaints i
+	think i now understand.)
+
+	fixed at least one error in gsub that looked at -1-th element
+	of an array when substituting for a null match (e.g., $).
+
+	delete arrayname is now legal; it clears the elements but leaves
+	the array, which may not be the right behavior.
+
+	modified makefile: my current make can't cope with the test used
+	to avoid unnecessary yacc invocations.
+
+Jul 17, 1995:
+	added dynamically growing strings to awk.lx.l and b.c
+	to permit regular expressions to be much bigger.
+	the state arrays can still overflow.
+
+Aug 24, 1994:
+	detect duplicate arguments in function definitions (mdm).
+
+May 11, 1994:
+	trivial fix to printf to limit string size in sub().
+
+Apr 22, 1994:
+	fixed yet another subtle self-assignment problem:
+	$1 = $2; $1 = $1 clobbered $1.
+
+	Regression tests now use private echo, to avoid quoting problems.
+
+Feb 2, 1994:
+	changed error() to print line number as %d, not %g.
+
+Jul 23, 1993:
+	cosmetic changes: increased sizes of some arrays,
+	reworded some error messages.
+
+	added CONVFMT as in posix (just replaced OFMT in getsval)
+
+	FILENAME is now "" until the first thing that causes a file
+	to be opened.
+
+Nov 28, 1992:
+	deleted yyunput and yyoutput from proto.h;
+	different versions of lex give these different declarations.
+
+May 31, 1992:
+	added -mr N and -mf N options: more record and fields.
+	these really ought to adjust automatically.
+
+	cleaned up some error messages; "out of space" now means
+	malloc returned NULL in all cases.
+
+	changed rehash so that if it runs out, it just returns;
+	things will continue to run slow, but maybe a bit longer.
+
+Apr 24, 1992:
+	remove redundant close of stdin when using -f -.
+
+	got rid of core dump with -d; awk -d just prints date.
+
+Apr 12, 1992:
+	added explicit check for /dev/std(in,out,err) in redirection.
+	unlike gawk, no /dev/fd/n yet.
+
+	added (file/pipe) builtin.  hard to test satisfactorily.
+	not posix.
+
+Feb 20, 1992:
+	recompile after abortive changes;  should be unchanged.
+
+Dec 2, 1991:
+	die-casting time:  converted to ansi C, installed that.
+
+Nov 30, 1991:
+	fixed storage leak in freefa, failing to recover [N]CCL.
+	thanks to Bill Jones (jones@cs.usask.ca)
+
+Nov 19, 1991:
+	use RAND_MAX instead of literal in builtin().
+
+Nov 12, 1991:
+	cranked up some fixed-size arrays in b.c, and added a test for
+	overflow in penter.  thanks to mark larsen.
+
+Sep 24, 1991:
+	increased buffer in gsub.  a very crude fix to a general problem.
+	and again on Sep 26.
+
+Aug 18, 1991:
+	enforce variable name syntax for commandline variables: has to
+	start with letter or _.
+
+Jul 27, 1991:
+	allow newline after ; in for statements.
+
+Jul 21, 1991:
+	fixed so that in self-assignment like $1=$1, side effects
+	like recomputing $0 take place.  (this is getting subtle.)
+
+Jun 30, 1991:
+	better test for detecting too-long output record.
+
+Jun 2, 1991:
+	better defense against very long printf strings.
+	made break and continue illegal outside of loops.
+
+May 13, 1991:
+	removed extra arg on gettemp, tempfree.  minor error message rewording.
+
+May 6, 1991:
+	fixed silly bug in hex parsing in hexstr().
+	removed an apparently unnecessary test in isnumber().
+	warn about weird printf conversions.
+	fixed unchecked array overwrite in relex().
+
+	changed for (i in array) to access elements in sorted order.
+	then unchanged it -- it really does run slower in too many cases.
+	left the code in place, commented out.
+
+Feb 10, 1991:
+	check error status on all writes, to avoid banging on full disks.
+
+Jan 28, 1991:
+	awk -f - reads the program from stdin.
+
+Jan 11, 1991:
+	failed to set numeric state on $0 in cmd|getline context in run.c.
+
+Nov 2, 1990:
+	fixed sleazy test for integrality in getsval;  use modf.
+
+Oct 29, 1990:
+	fixed sleazy buggy code in lib.c that looked (incorrectly) for
+	too long input lines.
+
+Oct 14, 1990:
+	fixed the bug on p. 198 in which it couldn't deduce that an
+	argument was an array in some contexts.  replaced the error
+	message in intest() by code that damn well makes it an array.
+
+Oct 8, 1990:
+	fixed horrible bug:  types and values were not preserved in
+	some kinds of self-assignment. (in assign().)
+
+Aug 24, 1990:
+	changed NCHARS to 256 to handle 8-bit characters in strings
+	presented to match(), etc.
+
+Jun 26, 1990:
+	changed struct rrow (awk.h) to use long instead of int for lval,
+	since cfoll() stores a pointer in it.  now works better when int's
+	are smaller than pointers!
+
+May 6, 1990:
+	AVA fixed the grammar so that ! is uniformly of the same precedence as
+	unary + and -.  This renders illegal some constructs like !x=y, which
+	now has to be parenthesized as !(x=y), and makes others work properly:
+	!x+y is (!x)+y, and x!y is x !y, not two pattern-action statements.
+	(These problems were pointed out by Bob Lenk of Posix.)
+
+	Added \x to regular expressions (already in strings).
+	Limited octal to octal digits; \8 and \9 are not octal.
+	Centralized the code for parsing escapes in regular expressions.
+	Added a bunch of tests to T.re and T.sub to verify some of this.
+
+Feb 9, 1990:
+	fixed null pointer dereference bug in main.c:  -F[nothing].  sigh.
+
+	restored srand behavior:  it returns the current seed.
+
+Jan 18, 1990:
+	srand now returns previous seed value (0 to start).
+
+Jan 5, 1990:
+	fix potential problem in tran.c -- something was freed,
+	then used in freesymtab.
+
+Oct 18, 1989:
+	another try to get the max number of open files set with
+	relatively machine-independent code.
+
+	small fix to input() in case of multiple reads after EOF.
+
+Oct 11, 1989:
+	FILENAME is now defined in the BEGIN block -- too many old
+	programs broke.
+
+	"-" means stdin in getline as well as on the commandline.
+
+	added a bunch of casts to the code to tell the truth about
+	char * vs. unsigned char *, a right royal pain.  added a
+	setlocale call to the front of main, though probably no one
+	has it usefully implemented yet.
+
+Aug 24, 1989:
+	removed redundant relational tests against nullnode if parse
+	tree already had a relational at that point.
+
+Aug 11, 1989:
+	fixed bug:  commandline variable assignment has to look like
+	var=something.  (consider the man page for =, in file =.1)
+
+	changed number of arguments to functions to static arrays
+	to avoid repeated malloc calls.
+
+Aug 2, 1989:
+	restored -F (space) separator
+
+Jul 30, 1989:
+	added -v x=1 y=2 ... for immediate commandline variable assignment;
+	done before the BEGIN block for sure.  they have to precede the
+	program if the program is on the commandline.
+	Modified Aug 2 to require a separate -v for each assignment.
+
+Jul 10, 1989:
+	fixed ref-thru-zero bug in environment code in tran.c
+
+Jun 23, 1989:
+	add newline to usage message.
+
+Jun 14, 1989:
+	added some missing ansi printf conversion letters: %i %X %E %G.
+	no sensible meaning for h or L, so they may not do what one expects.
+
+	made %* conversions work.
+
+	changed x^y so that if n is a positive integer, it's done
+	by explicit multiplication, thus achieving maximum accuracy.
+	(this should be done by pow() but it seems not to be locally.)
+	done to x ^= y as well.
+
+Jun 4, 1989:
+	ENVIRON array contains environment: if shell variable V=thing,
+		ENVIRON["V"] is "thing"
+
+	multiple -f arguments permitted.  error reporting is naive.
+	(they were permitted before, but only the last was used.)
+
+	fixed a really stupid botch in the debugging macro dprintf
+
+	fixed order of evaluation of commandline assignments to match
+	what the book claims:  an argument of the form x=e is evaluated
+	at the time it would have been opened if it were a filename (p 63).
+	this invalidates the suggested answer to ex 4-1 (p 195).
+
+	removed some code that permitted -F (space) fieldseparator,
+	since it didn't quite work right anyway.  (restored aug 2)
+
+Apr 27, 1989:
+	Line number now accumulated correctly for comment lines.
+
+Apr 26, 1989:
+	Debugging output now includes a version date,
+	if one compiles it into the source each time.
+
+Apr 9, 1989:
+	Changed grammar to prohibit constants as 3rd arg of sub and gsub;
+	prevents class of overwriting-a-constant errors.  (Last one?)
+	This invalidates the "banana" example on page 43 of the book.
+
+	Added \a ("alert"), \v (vertical tab), \xhhh (hexadecimal),
+	as in ANSI, for strings.  Rescinded the sloppiness that permitted
+	non-octal digits in \ooo.  Warning:  not all compilers and libraries
+	will be able to deal with \x correctly.
+
+Jan 9, 1989:
+	Fixed bug that caused tempcell list to contain a duplicate.
+	The fix is kludgy.
+
+Dec 17, 1988:
+	Catches some more commandline errors in main.
+	Removed redundant decl of modf in run.c (confuses some compilers).
+	Warning:  there's no single declaration of malloc, etc., in awk.h
+	that seems to satisfy all compilers.
+
+Dec 7, 1988:
+	Added a bit of code to error printing to avoid printing nulls.
+	(Not clear that it actually would.)
+
+Nov 27, 1988:
+	With fear and trembling, modified the grammar to permit
+	multiple pattern-action statements on one line without
+	an explicit separator.  By definition, this capitulation
+	to the ghost of ancient implementations remains undefined
+	and thus subject to change without notice or apology.
+	DO NOT COUNT ON IT.
+
+Oct 30, 1988:
+	Fixed bug in call() that failed to recover storage.
+
+	A warning is now generated if there are more arguments
+	in the call than in the definition (in lieu of fixing
+	another storage leak).
+
+Oct 20, 1988:
+	Fixed %c:  if expr is numeric, use numeric value;
+	otherwise print 1st char of string value.  still
+	doesn't work if the value is 0 -- won't print \0.
+
+	Added a few more checks for running out of malloc.
+
+Oct 12, 1988:
+	Fixed bug in call() that freed local arrays twice.
+
+	Fixed to handle deletion of non-existent array right;
+	complains about attempt to delete non-array element.
+
+Sep 30, 1988:
+	Now guarantees to evaluate all arguments of built-in
+	functions, as in C;  the appearance is that arguments
+	are evaluated before the function is called.  Places
+	affected are sub (gsub was ok), substr, printf, and
+	all the built-in arithmetic functions in bltin().
+	A warning is generated if a bltin() is called with
+	the wrong number of arguments.
+
+	This requires changing makeprof on p167 of the book.
+
+Aug 23, 1988:
+	setting FILENAME in BEGIN caused core dump, apparently
+	because it was freeing space not allocated by malloc.
+
+July 24, 1988:
+	fixed egregious error in toupper/tolower functions.
+	still subject to rescinding, however.
+
+July 2, 1988:
+	flush stdout before opening file or pipe
+
+July 2, 1988:
+	performance bug in b.c/cgoto(): not freeing some sets of states.
+	partial fix only right now, and the number of states increased
+	to make it less obvious.
+
+June 1, 1988:
+	check error status on close
+
+May 28, 1988:
+	srand returns seed value it's using.
+	see 1/18/90
+
+May 22, 1988:
+	Removed limit on depth of function calls.
+
+May 10, 1988:
+	Fixed lib.c to permit _ in commandline variable names.
+
+Mar 25, 1988:
+	main.c fixed to recognize -- as terminator of command-
+	line options.  Illegal options flagged.
+	Error reporting slightly cleaned up.
+
+Dec 2, 1987:
+	Newer C compilers apply a strict scope rule to extern
+	declarations within functions.  Two extern declarations in
+	lib.c and tran.c have been moved to obviate this problem.
+
+Oct xx, 1987:
+	Reluctantly added toupper and tolower functions.
+	Subject to rescinding without notice.
+
+Sep 17, 1987:
+	Error-message printer had printf(s) instead of
+	printf("%s",s);  got core dumps when the message
+	included a %.
+
+Sep 12, 1987:
+	Very long printf strings caused core dump;
+	fixed aprintf, asprintf, format to catch them.
+	Can still get a core dump in printf itself.
+
+
--- /dev/null
+++ b/utils/awk/NOTICE
@@ -1,0 +1,7 @@
+This is a copy of Brian Kernighan's awk from netlib,
+which can be used on systems that don't provide a reasonably
+modern version of awk.  Awk is needed for kernel builds,
+amongst other things.
+
+See the README for the terms
+under which it is distributed.
--- /dev/null
+++ b/utils/awk/README
@@ -1,0 +1,83 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+This is the version of awk described in "The AWK Programming Language",
+by Al Aho, Brian Kernighan, and Peter Weinberger
+(Addison-Wesley, 1988, ISBN 0-201-07981-X).
+
+Changes, mostly bug fixes and occasional enhancements, are listed
+in FIXES.  If you distribute this code further, please please please
+distribute FIXES with it.  If you find errors, please report them
+to bwk@bell-labs.com.  Thanks.
+
+The program itself is created by
+	make
+which should produce a sequence of messages roughly like this:
+
+	yacc -d awkgram.y
+
+conflicts: 43 shift/reduce, 85 reduce/reduce
+	mv y.tab.c ytab.c
+	mv y.tab.h ytab.h
+	cc -O -c ytab.c
+	cc -O -c b.c
+	cc -O -c main.c
+	cc -O -c parse.c
+	cc -O maketab.c -o maketab
+	./maketab >proctab.c
+	cc -O -c proctab.c
+	cc -O -c tran.c
+	cc -O -c lib.c
+	cc -O -c run.c
+	cc -O -c lex.c
+	cc -O ytab.o b.o main.o parse.o proctab.o tran.o lib.o run.o lex.o -lm
+
+This produces an executable a.out; you will eventually want to
+move this to some place like /usr/bin/awk.
+
+If your system is does not have yacc or bison (the GNU
+equivalent), you must compile the pieces manually.  We have
+included yacc output in ytab.c and ytab.h, and backup copies in
+case you overwrite them.  We have also included a copy of
+proctab.c so you do not need to run maketab.
+
+NOTE: This version uses ANSI C, as you should also.  We have
+compiled this without any changes using gcc -Wall and/or local C
+compilers on a variety of systems, but new systems or compilers
+may raise some new complaint; reports of difficulties are
+welcome.
+
+This also compiles with Visual C++ on Windows 95 and Windows NT,
+*if* you provide versions of popen and pclose.  The file
+missing95.c contains versions that can be used to get started
+with, though the underlying support has mysterious properties,
+the symptom of which can be truncated pipe output.  Beware.
+
+This is also said to compile on Macintosh systems, using the
+file "buildmac" provided by Dan Allen (danallen@microsoft.com),
+to whom many thanks.  Dan also provided buildwin.bat, a simple
+script for compiling on NT if you prefer.
+
+The version of malloc that comes with some systems is sometimes
+astonishly slow.  If awk seems slow, you might try fixing that.
--- /dev/null
+++ b/utils/awk/awk.1
@@ -1,0 +1,529 @@
+.de EX
+.nf
+.ft CW
+..
+.de EE
+.br
+.fi
+.ft 1
+..
+awk
+.TH AWK 1
+.CT 1 files prog_other
+.SH NAME
+awk \- pattern-directed scanning and processing language
+.SH SYNOPSIS
+.B awk
+[
+.BI \-F
+.I fs
+]
+[
+.BI \-v
+.I var=value
+]
+[
+.I 'prog'
+|
+.BI \-f
+.I progfile
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Awk
+scans each input
+.I file
+for lines that match any of a set of patterns specified literally in
+.IR prog
+or in one or more files
+specified as
+.B \-f
+.IR progfile .
+With each pattern
+there can be an associated action that will be performed
+when a line of a
+.I file
+matches the pattern.
+Each line is matched against the
+pattern portion of every pattern-action statement;
+the associated action is performed for each matched pattern.
+The file name 
+.B \-
+means the standard input.
+Any
+.IR file
+of the form
+.I var=value
+is treated as an assignment, not a filename,
+and is executed at the time it would have been opened if it were a filename.
+The option
+.B \-v
+followed by
+.I var=value
+is an assignment to be done before
+.I prog
+is executed;
+any number of
+.B \-v
+options may be present.
+The
+.B \-F
+.IR fs
+option defines the input field separator to be the regular expression
+.IR fs.
+.PP
+An input line is normally made up of fields separated by white space,
+or by regular expression
+.BR FS .
+The fields are denoted
+.BR $1 ,
+.BR $2 ,
+\&..., while
+.B $0
+refers to the entire line.
+If
+.BR FS
+is null, the input line is split into one field per character.
+.PP
+A pattern-action statement has the form
+.IP
+.IB pattern " { " action " }
+.PP
+A missing 
+.BI { " action " }
+means print the line;
+a missing pattern always matches.
+Pattern-action statements are separated by newlines or semicolons.
+.PP
+An action is a sequence of statements.
+A statement can be one of the following:
+.PP
+.EX
+.ta \w'\f(CWdelete array[expression]'u
+.RS
+.nf
+.ft CW
+if(\fI expression \fP)\fI statement \fP\fR[ \fPelse\fI statement \fP\fR]\fP
+while(\fI expression \fP)\fI statement\fP
+for(\fI expression \fP;\fI expression \fP;\fI expression \fP)\fI statement\fP
+for(\fI var \fPin\fI array \fP)\fI statement\fP
+do\fI statement \fPwhile(\fI expression \fP)
+break
+continue
+{\fR [\fP\fI statement ... \fP\fR] \fP}
+\fIexpression\fP	#\fR commonly\fP\fI var = expression\fP
+print\fR [ \fP\fIexpression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
+printf\fI format \fP\fR[ \fP,\fI expression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
+return\fR [ \fP\fIexpression \fP\fR]\fP
+next	#\fR skip remaining patterns on this input line\fP
+nextfile	#\fR skip rest of this file, open next, start at top\fP
+delete\fI array\fP[\fI expression \fP]	#\fR delete an array element\fP
+delete\fI array\fP	#\fR delete all elements of array\fP
+exit\fR [ \fP\fIexpression \fP\fR]\fP	#\fR exit immediately; status is \fP\fIexpression\fP
+.fi
+.RE
+.EE
+.DT
+.PP
+Statements are terminated by
+semicolons, newlines or right braces.
+An empty
+.I expression-list
+stands for
+.BR $0 .
+String constants are quoted \&\f(CW"\ "\fR,
+with the usual C escapes recognized within.
+Expressions take on string or numeric values as appropriate,
+and are built using the operators
+.B + \- * / % ^
+(exponentiation), and concatenation (indicated by white space).
+The operators
+.B
+! ++ \-\- += \-= *= /= %= ^= > >= < <= == != ?:
+are also available in expressions.
+Variables may be scalars, array elements
+(denoted
+.IB x  [ i ] )
+or fields.
+Variables are initialized to the null string.
+Array subscripts may be any string,
+not necessarily numeric;
+this allows for a form of associative memory.
+Multiple subscripts such as
+.B [i,j,k]
+are permitted; the constituents are concatenated,
+separated by the value of
+.BR SUBSEP .
+.PP
+The
+.B print
+statement prints its arguments on the standard output
+(or on a file if
+.BI > file
+or
+.BI >> file
+is present or on a pipe if
+.BI | cmd
+is present), separated by the current output field separator,
+and terminated by the output record separator.
+.I file
+and
+.I cmd
+may be literal names or parenthesized expressions;
+identical string values in different statements denote
+the same open file.
+The
+.B printf
+statement formats its expression list according to the format
+(see
+.IR printf (3)) .
+The built-in function
+.BI close( expr )
+closes the file or pipe
+.IR expr .
+The built-in function
+.BI fflush( expr )
+flushes any buffered output for the file or pipe
+.IR expr .
+.PP
+The mathematical functions
+.BR exp ,
+.BR log ,
+.BR sqrt ,
+.BR sin ,
+.BR cos ,
+and
+.BR atan2 
+are built in.
+Other built-in functions:
+.TF length
+.TP
+.B length
+the length of its argument
+taken as a string,
+or of
+.B $0
+if no argument.
+.TP
+.B rand
+random number on (0,1)
+.TP
+.B srand
+sets seed for
+.B rand
+and returns the previous seed.
+.TP
+.B int
+truncates to an integer value
+.TP
+.BI substr( s , " m" , " n\fB)
+the
+.IR n -character
+substring of
+.I s
+that begins at position
+.IR m 
+counted from 1.
+.TP
+.BI index( s , " t" )
+the position in
+.I s
+where the string
+.I t
+occurs, or 0 if it does not.
+.TP
+.BI match( s , " r" )
+the position in
+.I s
+where the regular expression
+.I r
+occurs, or 0 if it does not.
+The variables
+.B RSTART
+and
+.B RLENGTH
+are set to the position and length of the matched string.
+.TP
+.BI split( s , " a" , " fs\fB)
+splits the string
+.I s
+into array elements
+.IB a [1] ,
+.IB a [2] ,
+\&...,
+.IB a [ n ] ,
+and returns
+.IR n .
+The separation is done with the regular expression
+.I fs
+or with the field separator
+.B FS
+if
+.I fs
+is not given.
+An empty string as field separator splits the string
+into one array element per character.
+.TP
+.BI sub( r , " t" , " s\fB)
+substitutes
+.I t
+for the first occurrence of the regular expression
+.I r
+in the string
+.IR s .
+If
+.I s
+is not given,
+.B $0
+is used.
+.TP
+.B gsub
+same as
+.B sub
+except that all occurrences of the regular expression
+are replaced;
+.B sub
+and
+.B gsub
+return the number of replacements.
+.TP
+.BI sprintf( fmt , " expr" , " ...\fB )
+the string resulting from formatting
+.I expr ...
+according to the
+.IR printf (3)
+format
+.I fmt
+.TP
+.BI system( cmd )
+executes
+.I cmd
+and returns its exit status
+.TP
+.BI tolower( str )
+returns a copy of
+.I str
+with all upper-case characters translated to their
+corresponding lower-case equivalents.
+.TP
+.BI toupper( str )
+returns a copy of
+.I str
+with all lower-case characters translated to their
+corresponding upper-case equivalents.
+.PD
+.PP
+The ``function''
+.B getline
+sets
+.B $0
+to the next input record from the current input file;
+.B getline
+.BI < file
+sets
+.B $0
+to the next record from
+.IR file .
+.B getline
+.I x
+sets variable
+.I x
+instead.
+Finally,
+.IB cmd " | getline
+pipes the output of
+.I cmd
+into
+.BR getline ;
+each call of
+.B getline
+returns the next line of output from
+.IR cmd .
+In all cases,
+.B getline
+returns 1 for a successful input,
+0 for end of file, and \-1 for an error.
+.PP
+Patterns are arbitrary Boolean combinations
+(with
+.BR "! || &&" )
+of regular expressions and
+relational expressions.
+Regular expressions are as in
+.IR egrep ; 
+see
+.IR grep (1).
+Isolated regular expressions
+in a pattern apply to the entire line.
+Regular expressions may also occur in
+relational expressions, using the operators
+.BR ~
+and
+.BR !~ .
+.BI / re /
+is a constant regular expression;
+any string (constant or variable) may be used
+as a regular expression, except in the position of an isolated regular expression
+in a pattern.
+.PP
+A pattern may consist of two patterns separated by a comma;
+in this case, the action is performed for all lines
+from an occurrence of the first pattern
+though an occurrence of the second.
+.PP
+A relational expression is one of the following:
+.IP
+.I expression matchop regular-expression
+.br
+.I expression relop expression
+.br
+.IB expression " in " array-name
+.br
+.BI ( expr , expr,... ") in " array-name
+.PP
+where a relop is any of the six relational operators in C,
+and a matchop is either
+.B ~
+(matches)
+or
+.B !~
+(does not match).
+A conditional is an arithmetic expression,
+a relational expression,
+or a Boolean combination
+of these.
+.PP
+The special patterns
+.B BEGIN
+and
+.B END
+may be used to capture control before the first input line is read
+and after the last.
+.B BEGIN
+and
+.B END
+do not combine with other patterns.
+.PP
+Variable names with special meanings:
+.TF FILENAME
+.TP
+.B CONVFMT
+conversion format used when converting numbers
+(default
+.BR "%.6g" )
+.TP
+.B FS
+regular expression used to separate fields; also settable
+by option
+.BI \-F fs.
+.TP
+.BR NF
+number of fields in the current record
+.TP
+.B NR
+ordinal number of the current record
+.TP
+.B FNR
+ordinal number of the current record in the current file
+.TP
+.B FILENAME
+the name of the current input file
+.TP
+.B RS
+input record separator (default newline)
+.TP
+.B OFS
+output field separator (default blank)
+.TP
+.B ORS
+output record separator (default newline)
+.TP
+.B OFMT
+output format for numbers (default
+.BR "%.6g" )
+.TP
+.B SUBSEP
+separates multiple subscripts (default 034)
+.TP
+.B ARGC
+argument count, assignable
+.TP
+.B ARGV
+argument array, assignable;
+non-null members are taken as filenames
+.TP
+.B ENVIRON
+array of environment variables; subscripts are names.
+.PD
+.PP
+Functions may be defined (at the position of a pattern-action statement) thus:
+.IP
+.B
+function foo(a, b, c) { ...; return x }
+.PP
+Parameters are passed by value if scalar and by reference if array name;
+functions may be called recursively.
+Parameters are local to the function; all other variables are global.
+Thus local variables may be created by providing excess parameters in
+the function definition.
+.SH EXAMPLES
+.TP
+.EX
+length($0) > 72
+.EE
+Print lines longer than 72 characters.
+.TP
+.EX
+{ print $2, $1 }
+.EE
+Print first two fields in opposite order.
+.PP
+.EX
+BEGIN { FS = ",[ \et]*|[ \et]+" }
+      { print $2, $1 }
+.EE
+.ns
+.IP
+Same, with input fields separated by comma and/or blanks and tabs.
+.PP
+.EX
+.nf
+	{ s += $1 }
+END	{ print "sum is", s, " average is", s/NR }
+.fi
+.EE
+.ns
+.IP
+Add up first column, print sum and average.
+.TP
+.EX
+/start/, /stop/
+.EE
+Print all lines between start/stop pairs.
+.PP
+.EX
+.nf
+BEGIN	{	# Simulate echo(1)
+	for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i]
+	printf "\en"
+	exit }
+.fi
+.EE
+.SH SEE ALSO
+.IR lex (1), 
+.IR sed (1)
+.br
+A. V. Aho, B. W. Kernighan, P. J. Weinberger,
+.I
+The AWK Programming Language,
+Addison-Wesley, 1988.  ISBN 0-201-07981-X
+.SH BUGS
+There are no explicit conversions between numbers and strings.
+To force an expression to be treated as a number add 0 to it;
+to force it to be treated as a string concatenate
+\&\f(CW""\fP to it.
+.br
+The scope rules for variables in functions are a botch;
+the syntax is worse.
--- /dev/null
+++ b/utils/awk/awk.h
@@ -1,0 +1,231 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+typedef double	Awkfloat;
+
+/* unsigned char is more trouble than it's worth */
+
+typedef	unsigned char uschar;
+
+#define	xfree(a)	{ if ((a) != NULL) { free((char *) a); a = NULL; } }
+
+#define	DEBUG
+#ifdef	DEBUG
+			/* uses have to be doubly parenthesized */
+#	define	dprintf(x)	if (dbg) printf x
+#else
+#	define	dprintf(x)
+#endif
+
+extern	char	errbuf[];
+
+extern int	compile_time;	/* 1 if compiling, 0 if running */
+extern int	safe;		/* 0 => unsafe, 1 => safe */
+
+#define	RECSIZE	(8 * 1024)	/* sets limit on records, fields, etc., etc. */
+extern int	recsize;	/* size of current record, orig RECSIZE */
+
+extern char	**FS;
+extern char	**RS;
+extern char	**ORS;
+extern char	**OFS;
+extern char	**OFMT;
+extern Awkfloat *NR;
+extern Awkfloat *FNR;
+extern Awkfloat *NF;
+extern char	**FILENAME;
+extern char	**SUBSEP;
+extern Awkfloat *RSTART;
+extern Awkfloat *RLENGTH;
+
+extern char	*record;	/* points to $0 */
+extern int	lineno;		/* line number in awk program */
+extern int	errorflag;	/* 1 if error has occurred */
+extern int	donefld;	/* 1 if record broken into fields */
+extern int	donerec;	/* 1 if record is valid (no fld has changed */
+extern char	inputFS[];	/* FS at time of input, for field splitting */
+
+extern int	dbg;
+
+extern	char	*patbeg;	/* beginning of pattern matched */
+extern	int	patlen;		/* length of pattern matched.  set in b.c */
+
+/* Cell:  all information about a variable or constant */
+
+typedef struct Cell {
+	uschar	ctype;		/* OCELL, OBOOL, OJUMP, etc. */
+	uschar	csub;		/* CCON, CTEMP, CFLD, etc. */
+	char	*nval;		/* name, for variables only */
+	char	*sval;		/* string value */
+	Awkfloat fval;		/* value as number */
+	int	 tval;		/* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
+	struct Cell *cnext;	/* ptr to next if chained */
+} Cell;
+
+typedef struct Array {		/* symbol table array */
+	int	nelem;		/* elements in table right now */
+	int	size;		/* size of tab */
+	Cell	**tab;		/* hash table pointers */
+} Array;
+
+#define	NSYMTAB	50	/* initial size of a symbol table */
+extern Array	*symtab;
+
+extern Cell	*nrloc;		/* NR */
+extern Cell	*fnrloc;	/* FNR */
+extern Cell	*nfloc;		/* NF */
+extern Cell	*rstartloc;	/* RSTART */
+extern Cell	*rlengthloc;	/* RLENGTH */
+
+/* Cell.tval values: */
+#define	NUM	01	/* number value is valid */
+#define	STR	02	/* string value is valid */
+#define DONTFREE 04	/* string space is not freeable */
+#define	CON	010	/* this is a constant */
+#define	ARR	020	/* this is an array */
+#define	FCN	040	/* this is a function name */
+#define FLD	0100	/* this is a field $1, $2, ... */
+#define	REC	0200	/* this is $0 */
+
+
+/* function types */
+#define	FLENGTH	1
+#define	FSQRT	2
+#define	FEXP	3
+#define	FLOG	4
+#define	FINT	5
+#define	FSYSTEM	6
+#define	FRAND	7
+#define	FSRAND	8
+#define	FSIN	9
+#define	FCOS	10
+#define	FATAN	11
+#define	FTOUPPER 12
+#define	FTOLOWER 13
+#define	FFLUSH	14
+
+/* Node:  parse tree is made of nodes, with Cell's at bottom */
+
+typedef struct Node {
+	int	ntype;
+	struct	Node *nnext;
+	int	lineno;
+	int	nobj;
+	struct	Node *narg[1];	/* variable: actual size set by calling malloc */
+} Node;
+
+#define	NIL	((Node *) 0)
+
+extern Node	*winner;
+extern Node	*nullstat;
+extern Node	*nullnode;
+
+/* ctypes */
+#define OCELL	1
+#define OBOOL	2
+#define OJUMP	3
+
+/* Cell subtypes: csub */
+#define	CFREE	7
+#define CCOPY	6
+#define CCON	5
+#define CTEMP	4
+#define CNAME	3 
+#define CVAR	2
+#define CFLD	1
+#define	CUNK	0
+
+/* bool subtypes */
+#define BTRUE	11
+#define BFALSE	12
+
+/* jump subtypes */
+#define JEXIT	21
+#define JNEXT	22
+#define	JBREAK	23
+#define	JCONT	24
+#define	JRET	25
+#define	JNEXTFILE	26
+
+/* node types */
+#define NVALUE	1
+#define NSTAT	2
+#define NEXPR	3
+
+
+extern	int	pairstack[], paircnt;
+
+#define notlegal(n)	(n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
+#define isvalue(n)	((n)->ntype == NVALUE)
+#define isexpr(n)	((n)->ntype == NEXPR)
+#define isjump(n)	((n)->ctype == OJUMP)
+#define isexit(n)	((n)->csub == JEXIT)
+#define	isbreak(n)	((n)->csub == JBREAK)
+#define	iscont(n)	((n)->csub == JCONT)
+#define	isnext(n)	((n)->csub == JNEXT || (n)->csub == JNEXTFILE)
+#define	isret(n)	((n)->csub == JRET)
+#define isrec(n)	((n)->tval & REC)
+#define isfld(n)	((n)->tval & FLD)
+#define isstr(n)	((n)->tval & STR)
+#define isnum(n)	((n)->tval & NUM)
+#define isarr(n)	((n)->tval & ARR)
+#define isfcn(n)	((n)->tval & FCN)
+#define istrue(n)	((n)->csub == BTRUE)
+#define istemp(n)	((n)->csub == CTEMP)
+#define	isargument(n)	((n)->nobj == ARG)
+/* #define freeable(p)	(!((p)->tval & DONTFREE)) */
+#define freeable(p)	( ((p)->tval & (STR|DONTFREE)) == STR )
+
+/* structures used by regular expression matching machinery, mostly b.c: */
+
+#define NCHARS	(256+1)		/* 256 handles 8-bit chars; 128 does 7-bit */
+				/* watch out in match(), etc. */
+#define NSTATES	32
+
+typedef struct rrow {
+	long	ltype;	/* long avoids pointer warnings on 64-bit */
+	union {
+		int i;
+		Node *np;
+		uschar *up;
+	} lval;		/* because Al stores a pointer in it! */
+	int	*lfollow;
+} rrow;
+
+typedef struct fa {
+	uschar	gototab[NSTATES][NCHARS];
+	uschar	out[NSTATES];
+	uschar	*restr;
+	int	*posns[NSTATES];
+	int	anchor;
+	int	use;
+	int	initstat;
+	int	curstat;
+	int	accept;
+	int	reset;
+	struct	rrow re[1];	/* variable: actual size set by calling malloc */
+} fa;
+
+
+#include "proto.h"
--- /dev/null
+++ b/utils/awk/awkgram.y
@@ -1,0 +1,486 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+%{
+#include <stdio.h>
+#include <string.h>
+#include "awk.h"
+
+void checkdup(Node *list, Cell *item);
+int yywrap(void) { return(1); }
+
+Node	*beginloc = 0;
+Node	*endloc = 0;
+int	infunc	= 0;	/* = 1 if in arglist or body of func */
+int	inloop	= 0;	/* = 1 if in while, for, do */
+char	*curfname = 0;	/* current function name */
+Node	*arglist = 0;	/* list of args for current function */
+%}
+
+%union {
+	Node	*p;
+	Cell	*cp;
+	int	i;
+	char	*s;
+}
+
+%token	<i>	FIRSTTOKEN	/* must be first */
+%token	<p>	PROGRAM PASTAT PASTAT2 XBEGIN XEND
+%token	<i>	NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
+%token	<i>	ARRAY
+%token	<i>	MATCH NOTMATCH MATCHOP
+%token	<i>	FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS
+%token	<i>	AND BOR APPEND EQ GE GT LE LT NE IN
+%token	<i>	ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
+%token	<i>	SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
+%token	<i>	ADD MINUS MULT DIVIDE MOD
+%token	<i>	ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
+%token	<i>	PRINT PRINTF SPRINTF
+%token	<p>	ELSE INTEST CONDEXPR
+%token	<i>	POSTINCR PREINCR POSTDECR PREDECR
+%token	<cp>	VAR IVAR VARNF CALL NUMBER STRING
+%token	<s>	REGEXPR
+
+%type	<p>	pas pattern ppattern plist pplist patlist prarg term re
+%type	<p>	pa_pat pa_stat pa_stats
+%type	<s>	reg_expr
+%type	<p>	simple_stmt opt_simple_stmt stmt stmtlist
+%type	<p>	var varname funcname varlist
+%type	<p>	for if else while
+%type	<i>	do st
+%type	<i>	pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
+%type	<i>	subop print
+
+%right	ASGNOP
+%right	'?'
+%right	':'
+%left	BOR
+%left	AND
+%left	GETLINE
+%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
+%left	ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
+%left	GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
+%left	PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
+%left	REGEXPR VAR VARNF IVAR WHILE '('
+%left	CAT
+%left	'+' '-'
+%left	'*' '/' '%'
+%left	NOT UMINUS
+%right	POWER
+%right	DECR INCR
+%left	INDIRECT
+%token	LASTTOKEN	/* must be last */
+
+%%
+
+program:
+	  pas	{ if (errorflag==0)
+			winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
+	| error	{ yyclearin; bracecheck(); SYNTAX("bailing out"); }
+	;
+
+and:
+	  AND | and NL
+	;
+
+bor:
+	  BOR | bor NL
+	;
+
+comma:
+	  ',' | comma NL
+	;
+
+do:
+	  DO | do NL
+	;
+
+else:
+	  ELSE | else NL
+	;
+
+for:
+	  FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
+		{ --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
+	| FOR '(' opt_simple_stmt ';'  ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
+		{ --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
+	| FOR '(' varname IN varname rparen {inloop++;} stmt
+		{ --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
+	;
+
+funcname:
+	  VAR	{ setfname($1); }
+	| CALL	{ setfname($1); }
+	;
+
+if:
+	  IF '(' pattern rparen		{ $$ = notnull($3); }
+	;
+
+lbrace:
+	  '{' | lbrace NL
+	;
+
+nl:
+	  NL | nl NL
+	;
+
+opt_nl:
+	  /* empty */	{ $$ = 0; }
+	| nl
+	;
+
+opt_pst:
+	  /* empty */	{ $$ = 0; }
+	| pst
+	;
+
+
+opt_simple_stmt:
+	  /* empty */			{ $$ = 0; }
+	| simple_stmt
+	;
+
+pas:
+	  opt_pst			{ $$ = 0; }
+	| opt_pst pa_stats opt_pst	{ $$ = $2; }
+	;
+
+pa_pat:
+	  pattern	{ $$ = notnull($1); }
+	;
+
+pa_stat:
+	  pa_pat			{ $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
+	| pa_pat lbrace stmtlist '}'	{ $$ = stat2(PASTAT, $1, $3); }
+	| pa_pat ',' pa_pat		{ $$ = pa2stat($1, $3, stat2(PRINT, rectonode(), NIL)); }
+	| pa_pat ',' pa_pat lbrace stmtlist '}'	{ $$ = pa2stat($1, $3, $5); }
+	| lbrace stmtlist '}'		{ $$ = stat2(PASTAT, NIL, $2); }
+	| XBEGIN lbrace stmtlist '}'
+		{ beginloc = linkum(beginloc, $3); $$ = 0; }
+	| XEND lbrace stmtlist '}'
+		{ endloc = linkum(endloc, $3); $$ = 0; }
+	| FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
+		{ infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
+	;
+
+pa_stats:
+	  pa_stat
+	| pa_stats opt_pst pa_stat	{ $$ = linkum($1, $3); }
+	;
+
+patlist:
+	  pattern
+	| patlist comma pattern		{ $$ = linkum($1, $3); }
+	;
+
+ppattern:
+	  var ASGNOP ppattern		{ $$ = op2($2, $1, $3); }
+	| ppattern '?' ppattern ':' ppattern %prec '?'
+	 	{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
+	| ppattern bor ppattern %prec BOR
+		{ $$ = op2(BOR, notnull($1), notnull($3)); }
+	| ppattern and ppattern %prec AND
+		{ $$ = op2(AND, notnull($1), notnull($3)); }
+	| ppattern MATCHOP reg_expr	{ $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
+	| ppattern MATCHOP ppattern
+		{ if (constnode($3))
+			$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
+		  else
+			$$ = op3($2, (Node *)1, $1, $3); }
+	| ppattern IN varname		{ $$ = op2(INTEST, $1, makearr($3)); }
+	| '(' plist ')' IN varname	{ $$ = op2(INTEST, $2, makearr($5)); }
+	| ppattern term %prec CAT	{ $$ = op2(CAT, $1, $2); }
+	| re
+	| term
+	;
+
+pattern:
+	  var ASGNOP pattern		{ $$ = op2($2, $1, $3); }
+	| pattern '?' pattern ':' pattern %prec '?'
+	 	{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
+	| pattern bor pattern %prec BOR
+		{ $$ = op2(BOR, notnull($1), notnull($3)); }
+	| pattern and pattern %prec AND
+		{ $$ = op2(AND, notnull($1), notnull($3)); }
+	| pattern EQ pattern		{ $$ = op2($2, $1, $3); }
+	| pattern GE pattern		{ $$ = op2($2, $1, $3); }
+	| pattern GT pattern		{ $$ = op2($2, $1, $3); }
+	| pattern LE pattern		{ $$ = op2($2, $1, $3); }
+	| pattern LT pattern		{ $$ = op2($2, $1, $3); }
+	| pattern NE pattern		{ $$ = op2($2, $1, $3); }
+	| pattern MATCHOP reg_expr	{ $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
+	| pattern MATCHOP pattern
+		{ if (constnode($3))
+			$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
+		  else
+			$$ = op3($2, (Node *)1, $1, $3); }
+	| pattern IN varname		{ $$ = op2(INTEST, $1, makearr($3)); }
+	| '(' plist ')' IN varname	{ $$ = op2(INTEST, $2, makearr($5)); }
+	| pattern '|' GETLINE var	{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else $$ = op3(GETLINE, $4, itonp($2), $1); }
+	| pattern '|' GETLINE		{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
+	| pattern term %prec CAT	{ $$ = op2(CAT, $1, $2); }
+	| re
+	| term
+	;
+
+plist:
+	  pattern comma pattern		{ $$ = linkum($1, $3); }
+	| plist comma pattern		{ $$ = linkum($1, $3); }
+	;
+
+pplist:
+	  ppattern
+	| pplist comma ppattern		{ $$ = linkum($1, $3); }
+	;
+
+prarg:
+	  /* empty */			{ $$ = rectonode(); }
+	| pplist
+	| '(' plist ')'			{ $$ = $2; }
+	;
+
+print:
+	  PRINT | PRINTF
+	;
+
+pst:
+	  NL | ';' | pst NL | pst ';'
+	;
+
+rbrace:
+	  '}' | rbrace NL
+	;
+
+re:
+	   reg_expr
+		{ $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
+	| NOT re	{ $$ = op1(NOT, notnull($2)); }
+	;
+
+reg_expr:
+	  '/' {startreg();} REGEXPR '/'		{ $$ = $3; }
+	;
+
+rparen:
+	  ')' | rparen NL
+	;
+
+simple_stmt:
+	  print prarg '|' term		{ 
+			if (safe) SYNTAX("print | is unsafe");
+			else $$ = stat3($1, $2, itonp($3), $4); }
+	| print prarg APPEND term	{
+			if (safe) SYNTAX("print >> is unsafe");
+			else $$ = stat3($1, $2, itonp($3), $4); }
+	| print prarg GT term		{
+			if (safe) SYNTAX("print > is unsafe");
+			else $$ = stat3($1, $2, itonp($3), $4); }
+	| print prarg			{ $$ = stat3($1, $2, NIL, NIL); }
+	| DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
+	| DELETE varname		 { $$ = stat2(DELETE, makearr($2), 0); }
+	| pattern			{ $$ = exptostat($1); }
+	| error				{ yyclearin; SYNTAX("illegal statement"); }
+	;
+
+st:
+	  nl
+	| ';' opt_nl
+	;
+
+stmt:
+	  BREAK st		{ if (!inloop) SYNTAX("break illegal outside of loops");
+				  $$ = stat1(BREAK, NIL); }
+	| CONTINUE st		{  if (!inloop) SYNTAX("continue illegal outside of loops");
+				  $$ = stat1(CONTINUE, NIL); }
+	| do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
+		{ $$ = stat2(DO, $3, notnull($7)); }
+	| EXIT pattern st	{ $$ = stat1(EXIT, $2); }
+	| EXIT st		{ $$ = stat1(EXIT, NIL); }
+	| for
+	| if stmt else stmt	{ $$ = stat3(IF, $1, $2, $4); }
+	| if stmt		{ $$ = stat3(IF, $1, $2, NIL); }
+	| lbrace stmtlist rbrace { $$ = $2; }
+	| NEXT st	{ if (infunc)
+				SYNTAX("next is illegal inside a function");
+			  $$ = stat1(NEXT, NIL); }
+	| NEXTFILE st	{ if (infunc)
+				SYNTAX("nextfile is illegal inside a function");
+			  $$ = stat1(NEXTFILE, NIL); }
+	| RETURN pattern st	{ $$ = stat1(RETURN, $2); }
+	| RETURN st		{ $$ = stat1(RETURN, NIL); }
+	| simple_stmt st
+	| while {inloop++;} stmt	{ --inloop; $$ = stat2(WHILE, $1, $3); }
+	| ';' opt_nl		{ $$ = 0; }
+	;
+
+stmtlist:
+	  stmt
+	| stmtlist stmt		{ $$ = linkum($1, $2); }
+	;
+
+subop:
+	  SUB | GSUB
+	;
+
+term:
+ 	  term '/' ASGNOP term		{ $$ = op2(DIVEQ, $1, $4); }
+ 	| term '+' term			{ $$ = op2(ADD, $1, $3); }
+	| term '-' term			{ $$ = op2(MINUS, $1, $3); }
+	| term '*' term			{ $$ = op2(MULT, $1, $3); }
+	| term '/' term			{ $$ = op2(DIVIDE, $1, $3); }
+	| term '%' term			{ $$ = op2(MOD, $1, $3); }
+	| term POWER term		{ $$ = op2(POWER, $1, $3); }
+	| '-' term %prec UMINUS		{ $$ = op1(UMINUS, $2); }
+	| '+' term %prec UMINUS		{ $$ = $2; }
+	| NOT term %prec UMINUS		{ $$ = op1(NOT, notnull($2)); }
+	| BLTIN '(' ')'			{ $$ = op2(BLTIN, itonp($1), rectonode()); }
+	| BLTIN '(' patlist ')'		{ $$ = op2(BLTIN, itonp($1), $3); }
+	| BLTIN				{ $$ = op2(BLTIN, itonp($1), rectonode()); }
+	| CALL '(' ')'			{ $$ = op2(CALL, celltonode($1,CVAR), NIL); }
+	| CALL '(' patlist ')'		{ $$ = op2(CALL, celltonode($1,CVAR), $3); }
+	| CLOSE term			{ $$ = op1(CLOSE, $2); }
+	| DECR var			{ $$ = op1(PREDECR, $2); }
+	| INCR var			{ $$ = op1(PREINCR, $2); }
+	| var DECR			{ $$ = op1(POSTDECR, $1); }
+	| var INCR			{ $$ = op1(POSTINCR, $1); }
+	| GETLINE var LT term		{ $$ = op3(GETLINE, $2, itonp($3), $4); }
+	| GETLINE LT term		{ $$ = op3(GETLINE, NIL, itonp($2), $3); }
+	| GETLINE var			{ $$ = op3(GETLINE, $2, NIL, NIL); }
+	| GETLINE			{ $$ = op3(GETLINE, NIL, NIL, NIL); }
+	| INDEX '(' pattern comma pattern ')'
+		{ $$ = op2(INDEX, $3, $5); }
+	| INDEX '(' pattern comma reg_expr ')'
+		{ SYNTAX("index() doesn't permit regular expressions");
+		  $$ = op2(INDEX, $3, (Node*)$5); }
+	| '(' pattern ')'		{ $$ = $2; }
+	| MATCHFCN '(' pattern comma reg_expr ')'
+		{ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
+	| MATCHFCN '(' pattern comma pattern ')'
+		{ if (constnode($5))
+			$$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
+		  else
+			$$ = op3(MATCHFCN, (Node *)1, $3, $5); }
+	| NUMBER			{ $$ = celltonode($1, CCON); }
+	| SPLIT '(' pattern comma varname comma pattern ')'     /* string */
+		{ $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
+	| SPLIT '(' pattern comma varname comma reg_expr ')'    /* const /regexp/ */
+		{ $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
+	| SPLIT '(' pattern comma varname ')'
+		{ $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); }  /* default */
+	| SPRINTF '(' patlist ')'	{ $$ = op1($1, $3); }
+	| STRING	 		{ $$ = celltonode($1, CCON); }
+	| subop '(' reg_expr comma pattern ')'
+		{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
+	| subop '(' pattern comma pattern ')'
+		{ if (constnode($3))
+			$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
+		  else
+			$$ = op4($1, (Node *)1, $3, $5, rectonode()); }
+	| subop '(' reg_expr comma pattern comma var ')'
+		{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
+	| subop '(' pattern comma pattern comma var ')'
+		{ if (constnode($3))
+			$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
+		  else
+			$$ = op4($1, (Node *)1, $3, $5, $7); }
+	| SUBSTR '(' pattern comma pattern comma pattern ')'
+		{ $$ = op3(SUBSTR, $3, $5, $7); }
+	| SUBSTR '(' pattern comma pattern ')'
+		{ $$ = op3(SUBSTR, $3, $5, NIL); }
+	| var
+	;
+
+var:
+	  varname
+	| varname '[' patlist ']'	{ $$ = op2(ARRAY, makearr($1), $3); }
+	| IVAR				{ $$ = op1(INDIRECT, celltonode($1, CVAR)); }
+	| INDIRECT term	 		{ $$ = op1(INDIRECT, $2); }
+	;	
+
+varlist:
+	  /* nothing */		{ arglist = $$ = 0; }
+	| VAR			{ arglist = $$ = celltonode($1,CVAR); }
+	| varlist comma VAR	{
+			checkdup($1, $3);
+			arglist = $$ = linkum($1,celltonode($3,CVAR)); }
+	;
+
+varname:
+	  VAR			{ $$ = celltonode($1, CVAR); }
+	| ARG 			{ $$ = op1(ARG, itonp($1)); }
+	| VARNF			{ $$ = op1(VARNF, (Node *) $1); }
+	;
+
+
+while:
+	  WHILE '(' pattern rparen	{ $$ = notnull($3); }
+	;
+
+%%
+
+void setfname(Cell *p)
+{
+	if (isarr(p))
+		SYNTAX("%s is an array, not a function", p->nval);
+	else if (isfcn(p))
+		SYNTAX("you can't define function %s more than once", p->nval);
+	curfname = p->nval;
+}
+
+int constnode(Node *p)
+{
+	return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
+}
+
+char *strnode(Node *p)
+{
+	return ((Cell *)(p->narg[0]))->sval;
+}
+
+Node *notnull(Node *n)
+{
+	switch (n->nobj) {
+	case LE: case LT: case EQ: case NE: case GT: case GE:
+	case BOR: case AND: case NOT:
+		return n;
+	default:
+		return op2(NE, n, nullnode);
+	}
+}
+
+void checkdup(Node *vl, Cell *cp)	/* check if name already in list */
+{
+	char *s = cp->nval;
+	for ( ; vl; vl = vl->nnext) {
+		if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
+			SYNTAX("duplicate argument %s", s);
+			break;
+		}
+	}
+}
--- /dev/null
+++ b/utils/awk/b.c
@@ -1,0 +1,855 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+/* lasciate ogne speranza, voi ch'entrate. */
+
+#define	DEBUG
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "ytab.h"
+
+#define	HAT	(NCHARS-2)	/* matches ^ in regular expr */
+				/* NCHARS is 2**n */
+#define MAXLIN 22
+
+#define type(v)		(v)->nobj	/* badly overloaded here */
+#define info(v)		(v)->ntype	/* badly overloaded here */
+#define left(v)		(v)->narg[0]
+#define right(v)	(v)->narg[1]
+#define parent(v)	(v)->nnext
+
+#define LEAF	case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
+#define UNARY	case STAR: case PLUS: case QUEST:
+
+/* encoding in tree Nodes:
+	leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL):
+		left is index, right contains value or pointer to value
+	unary (STAR, PLUS, QUEST): left is child, right is null
+	binary (CAT, OR): left and right are children
+	parent contains pointer to parent
+*/
+
+
+int	*setvec;
+int	*tmpset;
+int	maxsetvec = 0;
+
+int	rtok;		/* next token in current re */
+int	rlxval;
+static uschar	*rlxstr;
+static uschar	*prestr;	/* current position in current re */
+static uschar	*lastre;	/* origin of last re */
+
+static	int setcnt;
+static	int poscnt;
+
+char	*patbeg;
+int	patlen;
+
+#define	NFA	20	/* cache this many dynamic fa's */
+fa	*fatab[NFA];
+int	nfatab	= 0;	/* entries in fatab */
+
+fa *makedfa(char *s, int anchor)	/* returns dfa for reg expr s */
+{
+	int i, use, nuse;
+	fa *pfa;
+	static int now = 1;
+
+	if (setvec == 0) {	/* first time through any RE */
+		maxsetvec = MAXLIN;
+		setvec = (int *) malloc(maxsetvec * sizeof(int));
+		tmpset = (int *) malloc(maxsetvec * sizeof(int));
+		if (setvec == 0 || tmpset == 0)
+			overflo("out of space initializing makedfa");
+	}
+
+	if (compile_time)	/* a constant for sure */
+		return mkdfa(s, anchor);
+	for (i = 0; i < nfatab; i++)	/* is it there already? */
+		if (fatab[i]->anchor == anchor
+		  && strcmp(fatab[i]->restr, s) == 0) {
+			fatab[i]->use = now++;
+			return fatab[i];
+		}
+	pfa = mkdfa(s, anchor);
+	if (nfatab < NFA) {	/* room for another */
+		fatab[nfatab] = pfa;
+		fatab[nfatab]->use = now++;
+		nfatab++;
+		return pfa;
+	}
+	use = fatab[0]->use;	/* replace least-recently used */
+	nuse = 0;
+	for (i = 1; i < nfatab; i++)
+		if (fatab[i]->use < use) {
+			use = fatab[i]->use;
+			nuse = i;
+		}
+	freefa(fatab[nuse]);
+	fatab[nuse] = pfa;
+	pfa->use = now++;
+	return pfa;
+}
+
+fa *mkdfa(char *s, int anchor)	/* does the real work of making a dfa */
+				/* anchor = 1 for anchored matches, else 0 */
+{
+	Node *p, *p1;
+	fa *f;
+
+	p = reparse(s);
+	p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
+		/* put ALL STAR in front of reg.  exp. */
+	p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
+		/* put FINAL after reg.  exp. */
+
+	poscnt = 0;
+	penter(p1);	/* enter parent pointers and leaf indices */
+	if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL)
+		overflo("out of space for fa");
+	f->accept = poscnt-1;	/* penter has computed number of positions in re */
+	cfoll(f, p1);	/* set up follow sets */
+	freetr(p1);
+	if ((f->posns[0] = (int *) calloc(1, *(f->re[0].lfollow)*sizeof(int))) == NULL)
+			overflo("out of space in makedfa");
+	if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL)
+		overflo("out of space in makedfa");
+	*f->posns[1] = 0;
+	f->initstat = makeinit(f, anchor);
+	f->anchor = anchor;
+	f->restr = (uschar *) tostring(s);
+	return f;
+}
+
+int makeinit(fa *f, int anchor)
+{
+	int i, k;
+
+	f->curstat = 2;
+	f->out[2] = 0;
+	f->reset = 0;
+	k = *(f->re[0].lfollow);
+	xfree(f->posns[2]);			
+	if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+		overflo("out of space in makeinit");
+	for (i=0; i <= k; i++) {
+		(f->posns[2])[i] = (f->re[0].lfollow)[i];
+	}
+	if ((f->posns[2])[1] == f->accept)
+		f->out[2] = 1;
+	for (i=0; i < NCHARS; i++)
+		f->gototab[2][i] = 0;
+	f->curstat = cgoto(f, 2, HAT);
+	if (anchor) {
+		*f->posns[2] = k-1;	/* leave out position 0 */
+		for (i=0; i < k; i++) {
+			(f->posns[0])[i] = (f->posns[2])[i];
+		}
+
+		f->out[0] = f->out[2];
+		if (f->curstat != 2)
+			--(*f->posns[f->curstat]);
+	}
+	return f->curstat;
+}
+
+void penter(Node *p)	/* set up parent pointers and leaf indices */
+{
+	switch (type(p)) {
+	LEAF
+		info(p) = poscnt;
+		poscnt++;
+		break;
+	UNARY
+		penter(left(p));
+		parent(left(p)) = p;
+		break;
+	case CAT:
+	case OR:
+		penter(left(p));
+		penter(right(p));
+		parent(left(p)) = p;
+		parent(right(p)) = p;
+		break;
+	default:	/* can't happen */
+		FATAL("can't happen: unknown type %d in penter", type(p));
+		break;
+	}
+}
+
+void freetr(Node *p)	/* free parse tree */
+{
+	switch (type(p)) {
+	LEAF
+		xfree(p);
+		break;
+	UNARY
+		freetr(left(p));
+		xfree(p);
+		break;
+	case CAT:
+	case OR:
+		freetr(left(p));
+		freetr(right(p));
+		xfree(p);
+		break;
+	default:	/* can't happen */
+		FATAL("can't happen: unknown type %d in freetr", type(p));
+		break;
+	}
+}
+
+/* in the parsing of regular expressions, metacharacters like . have */
+/* to be seen literally;  \056 is not a metacharacter. */
+
+int hexstr(char **pp)	/* find and eval hex string at pp, return new p */
+{			/* only pick up one 8-bit byte (2 chars) */
+	uschar *p;
+	int n = 0;
+	int i;
+
+	for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) {
+		if (isdigit(*p))
+			n = 16 * n + *p - '0';
+		else if (*p >= 'a' && *p <= 'f')
+			n = 16 * n + *p - 'a' + 10;
+		else if (*p >= 'A' && *p <= 'F')
+			n = 16 * n + *p - 'A' + 10;
+	}
+	*pp = (char *) p;
+	return n;
+}
+
+#define isoctdigit(c) ((c) >= '0' && (c) <= '7')	/* multiple use of arg */
+
+int quoted(char **pp)	/* pick up next thing after a \\ */
+			/* and increment *pp */
+{
+	char *p = *pp;
+	int c;
+
+	if ((c = *p++) == 't')
+		c = '\t';
+	else if (c == 'n')
+		c = '\n';
+	else if (c == 'f')
+		c = '\f';
+	else if (c == 'r')
+		c = '\r';
+	else if (c == 'b')
+		c = '\b';
+	else if (c == '\\')
+		c = '\\';
+	else if (c == 'x') {	/* hexadecimal goo follows */
+		c = hexstr(&p);	/* this adds a null if number is invalid */
+	} else if (isoctdigit(c)) {	/* \d \dd \ddd */
+		int n = c - '0';
+		if (isoctdigit(*p)) {
+			n = 8 * n + *p++ - '0';
+			if (isoctdigit(*p))
+				n = 8 * n + *p++ - '0';
+		}
+		c = n;
+	} /* else */
+		/* c = c; */
+	*pp = p;
+	return c;
+}
+
+char *cclenter(char *argp)	/* add a character class */
+{
+	int i, c, c2;
+	uschar *p = (uschar *) argp;
+	uschar *op, *bp;
+	static uschar *buf = 0;
+	static int bufsz = 100;
+
+	op = p;
+	if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+		FATAL("out of space for character class [%.10s...] 1", p);
+	bp = buf;
+	for (i = 0; (c = *p++) != 0; ) {
+		if (c == '\\') {
+			c = quoted((char **) &p);
+		} else if (c == '-' && i > 0 && bp[-1] != 0) {
+			if (*p != 0) {
+				c = bp[-1];
+				c2 = *p++;
+				if (c2 == '\\')
+					c2 = quoted((char **) &p);
+				if (c > c2) {	/* empty; ignore */
+					bp--;
+					i--;
+					continue;
+				}
+				while (c < c2) {
+					if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, 0))
+						FATAL("out of space for character class [%.10s...] 2", p);
+					*bp++ = ++c;
+					i++;
+				}
+				continue;
+			}
+		}
+		if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, 0))
+			FATAL("out of space for character class [%.10s...] 3", p);
+		*bp++ = c;
+		i++;
+	}
+	*bp = 0;
+	dprintf( ("cclenter: in = |%s|, out = |%s|\n", op, buf) );
+	xfree(op);
+	return (char *) tostring((char *) buf);
+}
+
+void overflo(char *s)
+{
+	FATAL("regular expression too big: %.30s...", s);
+}
+
+void cfoll(fa *f, Node *v)	/* enter follow set of each leaf of vertex v into lfollow[leaf] */
+{
+	int i;
+	int *p;
+
+	switch (type(v)) {
+	LEAF
+		f->re[info(v)].ltype = type(v);
+		f->re[info(v)].lval.np = right(v);
+		while (f->accept >= maxsetvec) {	/* guessing here! */
+			maxsetvec *= 4;
+			setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+			tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+			if (setvec == 0 || tmpset == 0)
+				overflo("out of space in cfoll()");
+		}
+		for (i = 0; i <= f->accept; i++)
+			setvec[i] = 0;
+		setcnt = 0;
+		follow(v);	/* computes setvec and setcnt */
+		if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
+			overflo("out of space building follow set");
+		f->re[info(v)].lfollow = p;
+		*p = setcnt;
+		for (i = f->accept; i >= 0; i--)
+			if (setvec[i] == 1)
+				*++p = i;
+		break;
+	UNARY
+		cfoll(f,left(v));
+		break;
+	case CAT:
+	case OR:
+		cfoll(f,left(v));
+		cfoll(f,right(v));
+		break;
+	default:	/* can't happen */
+		FATAL("can't happen: unknown type %d in cfoll", type(v));
+	}
+}
+
+int first(Node *p)	/* collects initially active leaves of p into setvec */
+			/* returns 1 if p matches empty string */
+{
+	int b, lp;
+
+	switch (type(p)) {
+	LEAF
+		lp = info(p);	/* look for high-water mark of subscripts */
+		while (setcnt >= maxsetvec || lp >= maxsetvec) {	/* guessing here! */
+			maxsetvec *= 4;
+			setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+			tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+			if (setvec == 0 || tmpset == 0)
+				overflo("out of space in first()");
+		}
+		if (setvec[lp] != 1) {
+			setvec[lp] = 1;
+			setcnt++;
+		}
+		if (type(p) == CCL && (*(char *) right(p)) == '\0')
+			return(0);		/* empty CCL */
+		else return(1);
+	case PLUS:
+		if (first(left(p)) == 0) return(0);
+		return(1);
+	case STAR:
+	case QUEST:
+		first(left(p));
+		return(0);
+	case CAT:
+		if (first(left(p)) == 0 && first(right(p)) == 0) return(0);
+		return(1);
+	case OR:
+		b = first(right(p));
+		if (first(left(p)) == 0 || b == 0) return(0);
+		return(1);
+	}
+	FATAL("can't happen: unknown type %d in first", type(p));	/* can't happen */
+	return(-1);
+}
+
+void follow(Node *v)	/* collects leaves that can follow v into setvec */
+{
+	Node *p;
+
+	if (type(v) == FINAL)
+		return;
+	p = parent(v);
+	switch (type(p)) {
+	case STAR:
+	case PLUS:
+		first(v);
+		follow(p);
+		return;
+
+	case OR:
+	case QUEST:
+		follow(p);
+		return;
+
+	case CAT:
+		if (v == left(p)) {	/* v is left child of p */
+			if (first(right(p)) == 0) {
+				follow(p);
+				return;
+			}
+		} else		/* v is right child */
+			follow(p);
+		return;
+	}
+}
+
+int member(int c, char *sarg)	/* is c in s? */
+{
+	uschar *s = (uschar *) sarg;
+
+	while (*s)
+		if (c == *s++)
+			return(1);
+	return(0);
+}
+
+int match(fa *f, char *p0)	/* shortest match ? */
+{
+	int s, ns;
+	uschar *p = (uschar *) p0;
+
+	s = f->reset ? makeinit(f,0) : f->initstat;
+	if (f->out[s])
+		return(1);
+	do {
+		if ((ns = f->gototab[s][*p]) != 0)
+			s = ns;
+		else
+			s = cgoto(f, s, *p);
+		if (f->out[s])
+			return(1);
+	} while (*p++ != 0);
+	return(0);
+}
+
+int pmatch(fa *f, char *p0)	/* longest match, for sub */
+{
+	int s, ns;
+	uschar *p = (uschar *) p0;
+	uschar *q;
+	int i, k;
+
+	s = f->reset ? makeinit(f,1) : f->initstat;
+	patbeg = (char *) p;
+	patlen = -1;
+	do {
+		q = p;
+		do {
+			if (f->out[s])		/* final state */
+				patlen = q-p;
+			if ((ns = f->gototab[s][*q]) != 0)
+				s = ns;
+			else
+				s = cgoto(f, s, *q);
+			if (s == 1) {	/* no transition */
+				if (patlen >= 0) {
+					patbeg = (char *) p;
+					return(1);
+				}
+				else
+					goto nextin;	/* no match */
+			}
+		} while (*q++ != 0);
+		if (f->out[s])
+			patlen = q-p-1;	/* don't count $ */
+		if (patlen >= 0) {
+			patbeg = (char *) p;
+			return(1);
+		}
+	nextin:
+		s = 2;
+		if (f->reset) {
+			for (i = 2; i <= f->curstat; i++)
+				xfree(f->posns[i]);
+			k = *f->posns[0];			
+			if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+				overflo("out of space in pmatch");
+			for (i = 0; i <= k; i++)
+				(f->posns[2])[i] = (f->posns[0])[i];
+			f->initstat = f->curstat = 2;
+			f->out[2] = f->out[0];
+			for (i = 0; i < NCHARS; i++)
+				f->gototab[2][i] = 0;
+		}
+	} while (*p++ != 0);
+	return (0);
+}
+
+int nematch(fa *f, char *p0)	/* non-empty match, for sub */
+{
+	int s, ns;
+	uschar *p = (uschar *) p0;
+	uschar *q;
+	int i, k;
+
+	s = f->reset ? makeinit(f,1) : f->initstat;
+	patlen = -1;
+	while (*p) {
+		q = p;
+		do {
+			if (f->out[s])		/* final state */
+				patlen = q-p;
+			if ((ns = f->gototab[s][*q]) != 0)
+				s = ns;
+			else
+				s = cgoto(f, s, *q);
+			if (s == 1) {	/* no transition */
+				if (patlen > 0) {
+					patbeg = (char *) p;
+					return(1);
+				} else
+					goto nnextin;	/* no nonempty match */
+			}
+		} while (*q++ != 0);
+		if (f->out[s])
+			patlen = q-p-1;	/* don't count $ */
+		if (patlen > 0 ) {
+			patbeg = (char *) p;
+			return(1);
+		}
+	nnextin:
+		s = 2;
+		if (f->reset) {
+			for (i = 2; i <= f->curstat; i++)
+				xfree(f->posns[i]);
+			k = *f->posns[0];			
+			if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+				overflo("out of state space");
+			for (i = 0; i <= k; i++)
+				(f->posns[2])[i] = (f->posns[0])[i];
+			f->initstat = f->curstat = 2;
+			f->out[2] = f->out[0];
+			for (i = 0; i < NCHARS; i++)
+				f->gototab[2][i] = 0;
+		}
+		p++;
+	}
+	return (0);
+}
+
+Node *reparse(char *p)	/* parses regular expression pointed to by p */
+{			/* uses relex() to scan regular expression */
+	Node *np;
+
+	dprintf( ("reparse <%s>\n", p) );
+	lastre = prestr = (uschar *) p;	/* prestr points to string to be parsed */
+	rtok = relex();
+	if (rtok == '\0')
+		FATAL("empty regular expression");
+	np = regexp();
+	if (rtok != '\0')
+		FATAL("syntax error in regular expression %s at %s", lastre, prestr);
+	return(np);
+}
+
+Node *regexp(void)	/* top-level parse of reg expr */
+{
+	return (alt(concat(primary())));
+}
+
+Node *primary(void)
+{
+	Node *np;
+
+	switch (rtok) {
+	case CHAR:
+		np = op2(CHAR, NIL, itonp(rlxval));
+		rtok = relex();
+		return (unary(np));
+	case ALL:
+		rtok = relex();
+		return (unary(op2(ALL, NIL, NIL)));
+	case DOT:
+		rtok = relex();
+		return (unary(op2(DOT, NIL, NIL)));
+	case CCL:
+		np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr));
+		rtok = relex();
+		return (unary(np));
+	case NCCL:
+		np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr));
+		rtok = relex();
+		return (unary(np));
+	case '^':
+		rtok = relex();
+		return (unary(op2(CHAR, NIL, itonp(HAT))));
+	case '$':
+		rtok = relex();
+		return (unary(op2(CHAR, NIL, NIL)));
+	case '(':
+		rtok = relex();
+		if (rtok == ')') {	/* special pleading for () */
+			rtok = relex();
+			return unary(op2(CCL, NIL, (Node *) tostring("")));
+		}
+		np = regexp();
+		if (rtok == ')') {
+			rtok = relex();
+			return (unary(np));
+		}
+		else
+			FATAL("syntax error in regular expression %s at %s", lastre, prestr);
+	default:
+		FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
+	}
+	return 0;	/*NOTREACHED*/
+}
+
+Node *concat(Node *np)
+{
+	switch (rtok) {
+	case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(':
+		return (concat(op2(CAT, np, primary())));
+	}
+	return (np);
+}
+
+Node *alt(Node *np)
+{
+	if (rtok == OR) {
+		rtok = relex();
+		return (alt(op2(OR, np, concat(primary()))));
+	}
+	return (np);
+}
+
+Node *unary(Node *np)
+{
+	switch (rtok) {
+	case STAR:
+		rtok = relex();
+		return (unary(op2(STAR, np, NIL)));
+	case PLUS:
+		rtok = relex();
+		return (unary(op2(PLUS, np, NIL)));
+	case QUEST:
+		rtok = relex();
+		return (unary(op2(QUEST, np, NIL)));
+	default:
+		return (np);
+	}
+}
+
+int relex(void)		/* lexical analyzer for reparse */
+{
+	int c, n;
+	int cflag;
+	static uschar *buf = 0;
+	static int bufsz = 100;
+	uschar *bp;
+
+	switch (c = *prestr++) {
+	case '|': return OR;
+	case '*': return STAR;
+	case '+': return PLUS;
+	case '?': return QUEST;
+	case '.': return DOT;
+	case '\0': prestr--; return '\0';
+	case '^':
+	case '$':
+	case '(':
+	case ')':
+		return c;
+	case '\\':
+		rlxval = quoted((char **) &prestr);
+		return CHAR;
+	default:
+		rlxval = c;
+		return CHAR;
+	case '[': 
+		if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+			FATAL("out of space in reg expr %.10s..", lastre);
+		bp = buf;
+		if (*prestr == '^') {
+			cflag = 1;
+			prestr++;
+		}
+		else
+			cflag = 0;
+		n = 2 * strlen(prestr)+1;
+		if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, 0))
+			FATAL("out of space for reg expr %.10s...", lastre);
+		for (; ; ) {
+			if ((c = *prestr++) == '\\') {
+				*bp++ = '\\';
+				if ((c = *prestr++) == '\0')
+					FATAL("nonterminated character class %.20s...", lastre);
+				*bp++ = c;
+			/* } else if (c == '\n') { */
+			/* 	FATAL("newline in character class %.20s...", lastre); */
+			} else if (c == '\0') {
+				FATAL("nonterminated character class %.20s", lastre);
+			} else if (bp == buf) {	/* 1st char is special */
+				*bp++ = c;
+			} else if (c == ']') {
+				*bp++ = 0;
+				rlxstr = (uschar *) tostring((char *) buf);
+				if (cflag == 0)
+					return CCL;
+				else
+					return NCCL;
+			} else
+				*bp++ = c;
+		}
+	}
+}
+
+int cgoto(fa *f, int s, int c)
+{
+	int i, j, k;
+	int *p, *q;
+
+	if (c < 0 || c > 255)
+		FATAL("can't happen: neg char %d in cgoto", c);
+	while (f->accept >= maxsetvec) {	/* guessing here! */
+		maxsetvec *= 4;
+		setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+		tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+		if (setvec == 0 || tmpset == 0)
+			overflo("out of space in cgoto()");
+	}
+	for (i = 0; i <= f->accept; i++)
+		setvec[i] = 0;
+	setcnt = 0;
+	/* compute positions of gototab[s,c] into setvec */
+	p = f->posns[s];
+	for (i = 1; i <= *p; i++) {
+		if ((k = f->re[p[i]].ltype) != FINAL) {
+			if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np))
+			 || (k == DOT && c != 0 && c != HAT)
+			 || (k == ALL && c != 0)
+			 || (k == CCL && member(c, (char *) f->re[p[i]].lval.up))
+			 || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) {
+				q = f->re[p[i]].lfollow;
+				for (j = 1; j <= *q; j++) {
+					if (q[j] >= maxsetvec) {
+						maxsetvec *= 4;
+						setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+						tmpset = (int *) realloc(setvec, maxsetvec * sizeof(int));
+						if (setvec == 0 || tmpset == 0)
+							overflo("cgoto overflow");
+					}
+					if (setvec[q[j]] == 0) {
+						setcnt++;
+						setvec[q[j]] = 1;
+					}
+				}
+			}
+		}
+	}
+	/* determine if setvec is a previous state */
+	tmpset[0] = setcnt;
+	j = 1;
+	for (i = f->accept; i >= 0; i--)
+		if (setvec[i]) {
+			tmpset[j++] = i;
+		}
+	/* tmpset == previous state? */
+	for (i = 1; i <= f->curstat; i++) {
+		p = f->posns[i];
+		if ((k = tmpset[0]) != p[0])
+			goto different;
+		for (j = 1; j <= k; j++)
+			if (tmpset[j] != p[j])
+				goto different;
+		/* setvec is state i */
+		f->gototab[s][c] = i;
+		return i;
+	  different:;
+	}
+
+	/* add tmpset to current set of states */
+	if (f->curstat >= NSTATES-1) {
+		f->curstat = 2;
+		f->reset = 1;
+		for (i = 2; i < NSTATES; i++)
+			xfree(f->posns[i]);
+	} else
+		++(f->curstat);
+	for (i = 0; i < NCHARS; i++)
+		f->gototab[f->curstat][i] = 0;
+	xfree(f->posns[f->curstat]);
+	if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
+		overflo("out of space in cgoto");
+
+	f->posns[f->curstat] = p;
+	f->gototab[s][c] = f->curstat;
+	for (i = 0; i <= setcnt; i++)
+		p[i] = tmpset[i];
+	if (setvec[f->accept])
+		f->out[f->curstat] = 1;
+	else
+		f->out[f->curstat] = 0;
+	return f->curstat;
+}
+
+
+void freefa(fa *f)	/* free a finite automaton */
+{
+	int i;
+
+	if (f == NULL)
+		return;
+	for (i = 0; i <= f->curstat; i++)
+		xfree(f->posns[i]);
+	for (i = 0; i <= f->accept; i++) {
+		xfree(f->re[i].lfollow);
+		if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
+			xfree((f->re[i].lval.np));
+	}
+	xfree(f->restr);
+	xfree(f);
+}
--- /dev/null
+++ b/utils/awk/buildwin.bat
@@ -1,0 +1,12 @@
+@echo off
+rem buildwin.bat - build AWK under Windows NT using Visual C++.
+rem 22 Jan 1999 - Created by Dan Allen.
+rem
+rem If you delete the call to setlocal it will probably work under Win95/Win98 as well.
+
+setlocal 
+set cl=-w -Ox -QIfdiv- -nologo -link -nologo setargv.obj
+
+cl maketab.c -o maketab.exe
+maketab.exe > proctab.c
+cl -o awk.exe b.c main.c parse.c proctab.c tran.c lib.c run.c lex.c ytab.c missing95.c
--- /dev/null
+++ b/utils/awk/lex.c
@@ -1,0 +1,571 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "awk.h"
+#include "ytab.h"
+
+extern YYSTYPE	yylval;
+extern int	infunc;
+
+int	lineno	= 1;
+int	bracecnt = 0;
+int	brackcnt  = 0;
+int	parencnt = 0;
+
+typedef struct Keyword {
+	char	*word;
+	int	sub;
+	int	type;
+} Keyword;
+
+Keyword keywords[] ={	/* keep sorted: binary searched */
+	{ "BEGIN",	XBEGIN,		XBEGIN },
+	{ "END",	XEND,		XEND },
+	{ "NF",		VARNF,		VARNF },
+	{ "atan2",	FATAN,		BLTIN },
+	{ "break",	BREAK,		BREAK },
+	{ "close",	CLOSE,		CLOSE },
+	{ "continue",	CONTINUE,	CONTINUE },
+	{ "cos",	FCOS,		BLTIN },
+	{ "delete",	DELETE,		DELETE },
+	{ "do",		DO,		DO },
+	{ "else",	ELSE,		ELSE },
+	{ "exit",	EXIT,		EXIT },
+	{ "exp",	FEXP,		BLTIN },
+	{ "fflush",	FFLUSH,		BLTIN },
+	{ "for",	FOR,		FOR },
+	{ "func",	FUNC,		FUNC },
+	{ "function",	FUNC,		FUNC },
+	{ "getline",	GETLINE,	GETLINE },
+	{ "gsub",	GSUB,		GSUB },
+	{ "if",		IF,		IF },
+	{ "in",		IN,		IN },
+	{ "index",	INDEX,		INDEX },
+	{ "int",	FINT,		BLTIN },
+	{ "length",	FLENGTH,	BLTIN },
+	{ "log",	FLOG,		BLTIN },
+	{ "match",	MATCHFCN,	MATCHFCN },
+	{ "next",	NEXT,		NEXT },
+	{ "nextfile",	NEXTFILE,	NEXTFILE },
+	{ "print",	PRINT,		PRINT },
+	{ "printf",	PRINTF,		PRINTF },
+	{ "rand",	FRAND,		BLTIN },
+	{ "return",	RETURN,		RETURN },
+	{ "sin",	FSIN,		BLTIN },
+	{ "split",	SPLIT,		SPLIT },
+	{ "sprintf",	SPRINTF,	SPRINTF },
+	{ "sqrt",	FSQRT,		BLTIN },
+	{ "srand",	FSRAND,		BLTIN },
+	{ "sub",	SUB,		SUB },
+	{ "substr",	SUBSTR,		SUBSTR },
+	{ "system",	FSYSTEM,	BLTIN },
+	{ "tolower",	FTOLOWER,	BLTIN },
+	{ "toupper",	FTOUPPER,	BLTIN },
+	{ "while",	WHILE,		WHILE },
+};
+
+#define DEBUG
+#ifdef	DEBUG
+#define	RET(x)	{ if(dbg)printf("lex %s\n", tokname(x)); return(x); }
+#else
+#define	RET(x)	return(x)
+#endif
+
+int peek(void)
+{
+	int c = input();
+	unput(c);
+	return c;
+}
+
+int gettok(char **pbuf, int *psz)	/* get next input token */
+{
+	int c;
+	char *buf = *pbuf;
+	int sz = *psz;
+	char *bp = buf;
+
+	c = input();
+	if (c == 0)
+		return 0;
+	buf[0] = c;
+	buf[1] = 0;
+	if (!isalnum(c) && c != '.' && c != '_')
+		return c;
+
+	*bp++ = c;
+	if (isalpha(c) || c == '_') {	/* it's a varname */
+		for ( ; (c = input()) != 0; ) {
+			if (bp-buf >= sz)
+				if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
+					FATAL( "out of space for name %.10s...", buf );
+			if (isalnum(c) || c == '_')
+				*bp++ = c;
+			else {
+				*bp = 0;
+				unput(c);
+				break;
+			}
+		}
+		*bp = 0;
+	} else {	/* it's a number */
+		char *rem;
+		/* read input until can't be a number */
+		for ( ; (c = input()) != 0; ) {
+			if (bp-buf >= sz)
+				if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
+					FATAL( "out of space for number %.10s...", buf );
+			if (isdigit(c) || c == 'e' || c == 'E' 
+			  || c == '.' || c == '+' || c == '-')
+				*bp++ = c;
+			else {
+				unput(c);
+				break;
+			}
+		}
+		*bp = 0;
+		strtod(buf, &rem);	/* parse the number */
+		unputstr(rem);		/* put rest back for later */
+		rem[0] = 0;
+	}
+	*pbuf = buf;
+	*psz = sz;
+	return buf[0];
+}
+
+int	word(char *);
+int	string(void);
+int	regexpr(void);
+int	sc	= 0;	/* 1 => return a } right now */
+int	reg	= 0;	/* 1 => return a REGEXPR now */
+
+int yylex(void)
+{
+	int c;
+	static char *buf = 0;
+	static int bufsize = 500;
+
+	if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL)
+		FATAL( "out of space in yylex" );
+	if (sc) {
+		sc = 0;
+		RET('}');
+	}
+	if (reg) {
+		reg = 0;
+		return regexpr();
+	}
+	for (;;) {
+		c = gettok(&buf, &bufsize);
+		if (c == 0)
+			return 0;
+		if (isalpha(c) || c == '_')
+			return word(buf);
+		if (isdigit(c) || c == '.') {
+			yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab);
+			/* should this also have STR set? */
+			RET(NUMBER);
+		}
+	
+		yylval.i = c;
+		switch (c) {
+		case '\n':	/* {EOL} */
+			RET(NL);
+		case '\r':	/* assume \n is coming */
+		case ' ':	/* {WS}+ */
+		case '\t':
+			break;
+		case '#':	/* #.* strip comments */
+			while ((c = input()) != '\n' && c != 0)
+				;
+			unput(c);
+			break;
+		case ';':
+			RET(';');
+		case '\\':
+			if (peek() == '\n') {
+				input();
+			} else if (peek() == '\r') {
+				input(); input();	/* \n */
+				lineno++;
+			} else {
+				RET(c);
+			}
+			break;
+		case '&':
+			if (peek() == '&') {
+				input(); RET(AND);
+			} else 
+				RET('&');
+		case '|':
+			if (peek() == '|') {
+				input(); RET(BOR);
+			} else
+				RET('|');
+		case '!':
+			if (peek() == '=') {
+				input(); yylval.i = NE; RET(NE);
+			} else if (peek() == '~') {
+				input(); yylval.i = NOTMATCH; RET(MATCHOP);
+			} else
+				RET(NOT);
+		case '~':
+			yylval.i = MATCH;
+			RET(MATCHOP);
+		case '<':
+			if (peek() == '=') {
+				input(); yylval.i = LE; RET(LE);
+			} else {
+				yylval.i = LT; RET(LT);
+			}
+		case '=':
+			if (peek() == '=') {
+				input(); yylval.i = EQ; RET(EQ);
+			} else {
+				yylval.i = ASSIGN; RET(ASGNOP);
+			}
+		case '>':
+			if (peek() == '=') {
+				input(); yylval.i = GE; RET(GE);
+			} else if (peek() == '>') {
+				input(); yylval.i = APPEND; RET(APPEND);
+			} else {
+				yylval.i = GT; RET(GT);
+			}
+		case '+':
+			if (peek() == '+') {
+				input(); yylval.i = INCR; RET(INCR);
+			} else if (peek() == '=') {
+				input(); yylval.i = ADDEQ; RET(ASGNOP);
+			} else
+				RET('+');
+		case '-':
+			if (peek() == '-') {
+				input(); yylval.i = DECR; RET(DECR);
+			} else if (peek() == '=') {
+				input(); yylval.i = SUBEQ; RET(ASGNOP);
+			} else
+				RET('-');
+		case '*':
+			if (peek() == '=') {	/* *= */
+				input(); yylval.i = MULTEQ; RET(ASGNOP);
+			} else if (peek() == '*') {	/* ** or **= */
+				input();	/* eat 2nd * */
+				if (peek() == '=') {
+					input(); yylval.i = POWEQ; RET(ASGNOP);
+				} else {
+					RET(POWER);
+				}
+			} else
+				RET('*');
+		case '/':
+			RET('/');
+		case '%':
+			if (peek() == '=') {
+				input(); yylval.i = MODEQ; RET(ASGNOP);
+			} else
+				RET('%');
+		case '^':
+			if (peek() == '=') {
+				input(); yylval.i = POWEQ; RET(ASGNOP);
+			} else
+				RET(POWER);
+
+		case '$':
+			/* BUG: awkward, if not wrong */
+			c = gettok(&buf, &bufsize);
+			if (isalpha(c)) {
+				if (strcmp(buf, "NF") == 0) {	/* very special */
+					unputstr("(NF)");
+					RET(INDIRECT);
+				}
+				c = peek();
+				if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
+					unputstr(buf);
+					RET(INDIRECT);
+				}
+				yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
+				RET(IVAR);
+			} else {
+				unputstr(buf);
+				RET(INDIRECT);
+			}
+	
+		case '}':
+			if (--bracecnt < 0)
+				SYNTAX( "extra }" );
+			sc = 1;
+			RET(';');
+		case ']':
+			if (--brackcnt < 0)
+				SYNTAX( "extra ]" );
+			RET(']');
+		case ')':
+			if (--parencnt < 0)
+				SYNTAX( "extra )" );
+			RET(')');
+		case '{':
+			bracecnt++;
+			RET('{');
+		case '[':
+			brackcnt++;
+			RET('[');
+		case '(':
+			parencnt++;
+			RET('(');
+	
+		case '"':
+			return string();	/* BUG: should be like tran.c ? */
+	
+		default:
+			RET(c);
+		}
+	}
+}
+
+int string(void)
+{
+	int c, n;
+	char *s, *bp;
+	static char *buf = 0;
+	static int bufsz = 500;
+
+	if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of space for strings");
+	for (bp = buf; (c = input()) != '"'; ) {
+		if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, 0))
+			FATAL("out of space for string %.10s...", buf);
+		switch (c) {
+		case '\n':
+		case '\r':
+		case 0:
+			SYNTAX( "non-terminated string %.10s...", buf );
+			lineno++;
+			break;
+		case '\\':
+			c = input();
+			switch (c) {
+			case '"': *bp++ = '"'; break;
+			case 'n': *bp++ = '\n'; break;	
+			case 't': *bp++ = '\t'; break;
+			case 'f': *bp++ = '\f'; break;
+			case 'r': *bp++ = '\r'; break;
+			case 'b': *bp++ = '\b'; break;
+			case 'v': *bp++ = '\v'; break;
+			case 'a': *bp++ = '\007'; break;
+			case '\\': *bp++ = '\\'; break;
+
+			case '0': case '1': case '2': /* octal: \d \dd \ddd */
+			case '3': case '4': case '5': case '6': case '7':
+				n = c - '0';
+				if ((c = peek()) >= '0' && c < '8') {
+					n = 8 * n + input() - '0';
+					if ((c = peek()) >= '0' && c < '8')
+						n = 8 * n + input() - '0';
+				}
+				*bp++ = n;
+				break;
+
+			case 'x':	/* hex  \x0-9a-fA-F + */
+			    {	char xbuf[100], *px;
+				for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) {
+					if (isdigit(c)
+					 || (c >= 'a' && c <= 'f')
+					 || (c >= 'A' && c <= 'F'))
+						*px++ = c;
+					else
+						break;
+				}
+				*px = 0;
+				unput(c);
+	  			sscanf(xbuf, "%x", &n);
+				*bp++ = n;
+				break;
+			    }
+
+			default: 
+				*bp++ = c;
+				break;
+			}
+			break;
+		default:
+			*bp++ = c;
+			break;
+		}
+	}
+	*bp = 0; 
+	s = tostring(buf);
+	*bp++ = ' '; *bp++ = 0;
+	yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
+	RET(STRING);
+}
+
+
+int binsearch(char *w, Keyword *kp, int n)
+{
+	int cond, low, mid, high;
+
+	low = 0;
+	high = n - 1;
+	while (low <= high) {
+		mid = (low + high) / 2;
+		if ((cond = strcmp(w, kp[mid].word)) < 0)
+			high = mid - 1;
+		else if (cond > 0)
+			low = mid + 1;
+		else
+			return mid;
+	}
+	return -1;
+}
+
+int word(char *w) 
+{
+	Keyword *kp;
+	int c, n;
+
+	n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
+	kp = keywords + n;
+	if (n != -1) {	/* found in table */
+		yylval.i = kp->sub;
+		switch (kp->type) {	/* special handling */
+		case FSYSTEM:
+			if (safe)
+				SYNTAX( "system is unsafe" );
+			RET(kp->type);
+		case FUNC:
+			if (infunc)
+				SYNTAX( "illegal nested function" );
+			RET(kp->type);
+		case RETURN:
+			if (!infunc)
+				SYNTAX( "return not in function" );
+			RET(kp->type);
+		case VARNF:
+			yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
+			RET(VARNF);
+		default:
+			RET(kp->type);
+		}
+	}
+	c = peek();	/* look for '(' */
+	if (c != '(' && infunc && (n=isarg(w)) >= 0) {
+		yylval.i = n;
+		RET(ARG);
+	} else {
+		yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
+		if (c == '(') {
+			RET(CALL);
+		} else {
+			RET(VAR);
+		}
+	}
+}
+
+void startreg(void)	/* next call to yyles will return a regular expression */
+{
+	reg = 1;
+}
+
+int regexpr(void)
+{
+	int c;
+	static char *buf = 0;
+	static int bufsz = 500;
+	char *bp;
+
+	if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of space for rex expr");
+	bp = buf;
+	for ( ; (c = input()) != '/' && c != 0; ) {
+		if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, 0))
+			FATAL("out of space for reg expr %.10s...", buf);
+		if (c == '\n') {
+			SYNTAX( "newline in regular expression %.10s...", buf ); 
+			unput('\n');
+			break;
+		} else if (c == '\\') {
+			*bp++ = '\\'; 
+			*bp++ = input();
+		} else {
+			*bp++ = c;
+		}
+	}
+	*bp = 0;
+	yylval.s = tostring(buf);
+	unput('/');
+	RET(REGEXPR);
+}
+
+/* low-level lexical stuff, sort of inherited from lex */
+
+char	ebuf[300];
+char	*ep = ebuf;
+char	yysbuf[100];	/* pushback buffer */
+char	*yysptr = yysbuf;
+FILE	*yyin = 0;
+
+int input(void)	/* get next lexical input character */
+{
+	int c;
+	extern char *lexprog;
+
+	if (yysptr > yysbuf)
+		c = *--yysptr;
+	else if (lexprog != NULL) {	/* awk '...' */
+		if ((c = *lexprog) != 0)
+			lexprog++;
+	} else				/* awk -f ... */
+		c = pgetc();
+	if (c == '\n')
+		lineno++;
+	else if (c == EOF)
+		c = 0;
+	if (ep >= ebuf + sizeof ebuf)
+		ep = ebuf;
+	return *ep++ = c;
+}
+
+void unput(int c)	/* put lexical character back on input */
+{
+	if (c == '\n')
+		lineno--;
+	if (yysptr >= yysbuf + sizeof(yysbuf))
+		FATAL("pushed back too much: %.20s...", yysbuf);
+	*yysptr++ = c;
+	if (--ep < ebuf)
+		ep = ebuf + sizeof(ebuf) - 1;
+}
+
+void unputstr(char *s)	/* put a string back on input */
+{
+	int i;
+
+	for (i = strlen(s)-1; i >= 0; i--)
+		unput(s[i]);
+}
--- /dev/null
+++ b/utils/awk/lib.c
@@ -1,0 +1,682 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "awk.h"
+#include "ytab.h"
+
+FILE	*infile	= NULL;
+char	*file	= "";
+char	*record;
+int	recsize	= RECSIZE;
+char	*fields;
+int	fieldssize = RECSIZE;
+
+Cell	**fldtab;	/* pointers to Cells */
+char	inputFS[100] = " ";
+
+#define	MAXFLD	200
+int	nfields	= MAXFLD;	/* last allocated slot for $i */
+
+int	donefld;	/* 1 = implies rec broken into fields */
+int	donerec;	/* 1 = record is valid (no flds have changed) */
+
+int	lastfld	= 0;	/* last used field */
+int	argno	= 1;	/* current input argument number */
+extern	Awkfloat *ARGC;
+
+static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
+static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
+
+void recinit(unsigned int n)
+{
+	record = (char *) malloc(n);
+	fields = (char *) malloc(n);
+	fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
+	if (record == NULL || fields == NULL || fldtab == NULL)
+		FATAL("out of space for $0 and fields");
+
+	fldtab[0] = (Cell *) malloc(sizeof (Cell));
+	*fldtab[0] = dollar0;
+	fldtab[0]->sval = record;
+	fldtab[0]->nval = tostring("0");
+	makefields(1, nfields);
+}
+
+void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
+{
+	char temp[50];
+	int i;
+
+	for (i = n1; i <= n2; i++) {
+		fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
+		if (fldtab[i] == NULL)
+			FATAL("out of space in makefields %d", i);
+		*fldtab[i] = dollar1;
+		sprintf(temp, "%d", i);
+		fldtab[i]->nval = tostring(temp);
+	}
+}
+
+void initgetrec(void)
+{
+	int i;
+	char *p;
+
+	for (i = 1; i < *ARGC; i++) {
+		if (!isclvar(p = getargv(i))) {	/* find 1st real filename */
+			setsval(lookup("FILENAME", symtab), getargv(i));
+			return;
+		}
+		setclvar(p);	/* a commandline assignment before filename */
+		argno++;
+	}
+	infile = stdin;		/* no filenames, so use stdin */
+}
+
+int getrec(char **pbuf, int *pbufsize, int isrecord)	/* get next input record */
+{			/* note: cares whether buf == record */
+	int c;
+	static int firsttime = 1;
+	char *buf = *pbuf;
+	int bufsize = *pbufsize;
+
+	if (firsttime) {
+		firsttime = 0;
+		initgetrec();
+	}
+	   dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
+		*RS, *FS, *ARGC, *FILENAME) );
+	if (isrecord) {
+		donefld = 0;
+		donerec = 1;
+	}
+	buf[0] = 0;
+	while (argno < *ARGC || infile == stdin) {
+		   dprintf( ("argno=%d, file=|%s|\n", argno, file) );
+		if (infile == NULL) {	/* have to open a new file */
+			file = getargv(argno);
+			if (*file == '\0') {	/* it's been zapped */
+				argno++;
+				continue;
+			}
+			if (isclvar(file)) {	/* a var=value arg */
+				setclvar(file);
+				argno++;
+				continue;
+			}
+			*FILENAME = file;
+			   dprintf( ("opening file %s\n", file) );
+			if (*file == '-' && *(file+1) == '\0')
+				infile = stdin;
+			else if ((infile = fopen(file, "r")) == NULL)
+				FATAL("can't open file %s", file);
+			setfval(fnrloc, 0.0);
+		}
+		c = readrec(&buf, &bufsize, infile);
+		if (c != 0 || buf[0] != '\0') {	/* normal record */
+			if (isrecord) {
+				if (freeable(fldtab[0]))
+					xfree(fldtab[0]->sval);
+				fldtab[0]->sval = buf;	/* buf == record */
+				fldtab[0]->tval = REC | STR | DONTFREE;
+				if (is_number(fldtab[0]->sval)) {
+					fldtab[0]->fval = atof(fldtab[0]->sval);
+					fldtab[0]->tval |= NUM;
+				}
+			}
+			setfval(nrloc, nrloc->fval+1);
+			setfval(fnrloc, fnrloc->fval+1);
+			*pbuf = buf;
+			*pbufsize = bufsize;
+			return 1;
+		}
+		/* EOF arrived on this file; set up next */
+		if (infile != stdin)
+			fclose(infile);
+		infile = NULL;
+		argno++;
+	}
+	*pbuf = buf;
+	*pbufsize = bufsize;
+	return 0;	/* true end of file */
+}
+
+void nextfile(void)
+{
+	if (infile != stdin)
+		fclose(infile);
+	infile = NULL;
+	argno++;
+}
+
+int readrec(char **pbuf, int *pbufsize, FILE *inf)	/* read one record into buf */
+{
+	int sep, c;
+	char *rr, *buf = *pbuf;
+	int bufsize = *pbufsize;
+
+	if (strlen(*FS) >= sizeof(inputFS))
+		FATAL("field separator %.10s... is too long", *FS);
+	strcpy(inputFS, *FS);	/* for subsequent field splitting */
+	if ((sep = **RS) == 0) {
+		sep = '\n';
+		while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
+			;
+		if (c != EOF)
+			ungetc(c, inf);
+	}
+	for (rr = buf; ; ) {
+		for (; (c=getc(inf)) != sep && c != EOF; ) {
+			if (rr-buf+1 > bufsize)
+				if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
+					FATAL("input record `%.30s...' too long", buf);
+			*rr++ = c;
+		}
+		if (**RS == sep || c == EOF)
+			break;
+		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
+			break;
+		if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
+			FATAL("input record `%.30s...' too long", buf);
+		*rr++ = '\n';
+		*rr++ = c;
+	}
+	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
+		FATAL("input record `%.30s...' too long", buf);
+	*rr = 0;
+	   dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
+	*pbuf = buf;
+	*pbufsize = bufsize;
+	return c == EOF && rr == buf ? 0 : 1;
+}
+
+char *getargv(int n)	/* get ARGV[n] */
+{
+	Cell *x;
+	char *s, temp[50];
+	extern Array *ARGVtab;
+
+	sprintf(temp, "%d", n);
+	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
+	s = getsval(x);
+	   dprintf( ("getargv(%d) returns |%s|\n", n, s) );
+	return s;
+}
+
+void setclvar(char *s)	/* set var=value from s */
+{
+	char *p;
+	Cell *q;
+
+	for (p=s; *p != '='; p++)
+		;
+	*p++ = 0;
+	p = qstring(p, '\0');
+	q = setsymtab(s, p, 0.0, STR, symtab);
+	setsval(q, p);
+	if (is_number(q->sval)) {
+		q->fval = atof(q->sval);
+		q->tval |= NUM;
+	}
+	   dprintf( ("command line set %s to |%s|\n", s, p) );
+}
+
+
+void fldbld(void)	/* create fields from current record */
+{
+	/* this relies on having fields[] the same length as $0 */
+	/* the fields are all stored in this one array with \0's */
+	char *r, *fr, sep;
+	Cell *p;
+	int i, j, n;
+
+	if (donefld)
+		return;
+	if (!isstr(fldtab[0]))
+		getsval(fldtab[0]);
+	r = fldtab[0]->sval;
+	n = strlen(r);
+	if (n > fieldssize) {
+		xfree(fields);
+		if ((fields = (char *) malloc(n+1)) == NULL)
+			FATAL("out of space for fields in fldbld %d", n);
+		fieldssize = n;
+	}
+	fr = fields;
+	i = 0;	/* number of fields accumulated here */
+	if (strlen(inputFS) > 1) {	/* it's a regular expression */
+		i = refldbld(r, inputFS);
+	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
+		for (i = 0; ; ) {
+			while (*r == ' ' || *r == '\t' || *r == '\n')
+				r++;
+			if (*r == 0)
+				break;
+			i++;
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			fldtab[i]->sval = fr;
+			fldtab[i]->tval = FLD | STR | DONTFREE;
+			do
+				*fr++ = *r++;
+			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
+			*fr++ = 0;
+		}
+		*fr = 0;
+	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
+		for (i = 0; *r != 0; r++) {
+			char buf[2];
+			i++;
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			buf[0] = *r;
+			buf[1] = 0;
+			fldtab[i]->sval = tostring(buf);
+			fldtab[i]->tval = FLD | STR;
+		}
+		*fr = 0;
+	} else if (*r != 0) {	/* if 0, it's a null field */
+		for (;;) {
+			i++;
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			fldtab[i]->sval = fr;
+			fldtab[i]->tval = FLD | STR | DONTFREE;
+			while (*r != sep && *r != '\n' && *r != '\0')	/* \n is always a separator */
+				*fr++ = *r++;
+			*fr++ = 0;
+			if (*r++ == 0)
+				break;
+		}
+		*fr = 0;
+	}
+	if (i > nfields)
+		FATAL("record `%.30s...' has too many fields; can't happen", r);
+	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
+	lastfld = i;
+	donefld = 1;
+	for (j = 1; j <= lastfld; j++) {
+		p = fldtab[j];
+		if(is_number(p->sval)) {
+			p->fval = atof(p->sval);
+			p->tval |= NUM;
+		}
+	}
+	setfval(nfloc, (Awkfloat) lastfld);
+	if (dbg) {
+		for (j = 0; j <= lastfld; j++) {
+			p = fldtab[j];
+			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
+		}
+	}
+}
+
+void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
+{				/* nvals remain intact */
+	Cell *p;
+	int i;
+
+	for (i = n1; i <= n2; i++) {
+		p = fldtab[i];
+		if (freeable(p))
+			xfree(p->sval);
+		p->sval = "";
+		p->tval = FLD | STR | DONTFREE;
+	}
+}
+
+void newfld(int n)	/* add field n after end of existing lastfld */
+{
+	if (n > nfields)
+		growfldtab(n);
+	cleanfld(lastfld+1, n);
+	lastfld = n;
+	setfval(nfloc, (Awkfloat) n);
+}
+
+Cell *fieldadr(int n)	/* get nth field */
+{
+	if (n < 0)
+		FATAL("trying to access field %d", n);
+	if (n > nfields)	/* fields after NF are empty */
+		growfldtab(n);	/* but does not increase NF */
+	return(fldtab[n]);
+}
+
+void growfldtab(int n)	/* make new fields up to at least $n */
+{
+	int nf = 2 * nfields;
+
+	if (n > nf)
+		nf = n;
+	fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
+	if (fldtab == NULL)
+		FATAL("out of space creating %d fields", nf);
+	makefields(nfields+1, nf);
+	nfields = nf;
+}
+
+int refldbld(char *rec, char *fs)	/* build fields from reg expr in FS */
+{
+	/* this relies on having fields[] the same length as $0 */
+	/* the fields are all stored in this one array with \0's */
+	char *fr;
+	int i, tempstat, n;
+	fa *pfa;
+
+	n = strlen(rec);
+	if (n > fieldssize) {
+		xfree(fields);
+		if ((fields = (char *) malloc(n+1)) == NULL)
+			FATAL("out of space for fields in refldbld %d", n);
+		fieldssize = n;
+	}
+	fr = fields;
+	*fr = '\0';
+	if (*rec == '\0')
+		return 0;
+	pfa = makedfa(fs, 1);
+	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
+	tempstat = pfa->initstat;
+	for (i = 1; ; i++) {
+		if (i > nfields)
+			growfldtab(i);
+		if (freeable(fldtab[i]))
+			xfree(fldtab[i]->sval);
+		fldtab[i]->tval = FLD | STR | DONTFREE;
+		fldtab[i]->sval = fr;
+		   dprintf( ("refldbld: i=%d\n", i) );
+		if (nematch(pfa, rec)) {
+			pfa->initstat = 2;	/* horrible coupling to b.c */
+			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
+			strncpy(fr, rec, patbeg-rec);
+			fr += patbeg - rec + 1;
+			*(fr-1) = '\0';
+			rec = patbeg + patlen;
+		} else {
+			   dprintf( ("no match %s\n", rec) );
+			strcpy(fr, rec);
+			pfa->initstat = tempstat;
+			break;
+		}
+	}
+	return i;		
+}
+
+void recbld(void)	/* create $0 from $1..$NF if necessary */
+{
+	int i;
+	char *r, *p;
+
+	if (donerec == 1)
+		return;
+	r = record;
+	for (i = 1; i <= *NF; i++) {
+		p = getsval(fldtab[i]);
+		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
+			FATAL("created $0 `%.30s...' too long", record);
+		while ((*r = *p++) != 0)
+			r++;
+		if (i < *NF) {
+			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
+				FATAL("created $0 `%.30s...' too long", record);
+			for (p = *OFS; (*r = *p++) != 0; )
+				r++;
+		}
+	}
+	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
+		FATAL("built giant record `%.30s...'", record);
+	*r = '\0';
+	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
+
+	if (freeable(fldtab[0]))
+		xfree(fldtab[0]->sval);
+	fldtab[0]->tval = REC | STR | DONTFREE;
+	fldtab[0]->sval = record;
+
+	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
+	   dprintf( ("recbld = |%s|\n", record) );
+	donerec = 1;
+}
+
+int	errorflag	= 0;
+
+void yyerror(char *s)
+{
+	SYNTAX(s);
+}
+
+void SYNTAX(char *fmt, ...)
+{
+	extern char *cmdname, *curfname;
+	static int been_here = 0;
+	va_list varg;
+
+	if (been_here++ > 2)
+		return;
+	fprintf(stderr, "%s: ", cmdname);
+	va_start(varg, fmt);
+	vfprintf(stderr, fmt, varg);
+	va_end(varg);
+	fprintf(stderr, " at source line %d", lineno);
+	if (curfname != NULL)
+		fprintf(stderr, " in function %s", curfname);
+	if (compile_time == 1 && cursource() != NULL)
+		fprintf(stderr, " source file %s", cursource());
+	fprintf(stderr, "\n");
+	errorflag = 2;
+	eprint();
+}
+
+void fpecatch(int n)
+{
+	FATAL("floating point exception %d", n);
+}
+
+extern int bracecnt, brackcnt, parencnt;
+
+void bracecheck(void)
+{
+	int c;
+	static int beenhere = 0;
+
+	if (beenhere++)
+		return;
+	while ((c = input()) != EOF && c != '\0')
+		bclass(c);
+	bcheck2(bracecnt, '{', '}');
+	bcheck2(brackcnt, '[', ']');
+	bcheck2(parencnt, '(', ')');
+}
+
+void bcheck2(int n, int c1, int c2)
+{
+	if (n == 1)
+		fprintf(stderr, "\tmissing %c\n", c2);
+	else if (n > 1)
+		fprintf(stderr, "\t%d missing %c's\n", n, c2);
+	else if (n == -1)
+		fprintf(stderr, "\textra %c\n", c2);
+	else if (n < -1)
+		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
+}
+
+void FATAL(char *fmt, ...)
+{
+	extern char *cmdname;
+	va_list varg;
+
+	fflush(stdout);
+	fprintf(stderr, "%s: ", cmdname);
+	va_start(varg, fmt);
+	vfprintf(stderr, fmt, varg);
+	va_end(varg);
+	error();
+	if (dbg > 1)		/* core dump if serious debugging on */
+		abort();
+	exit(2);
+}
+
+void WARNING(char *fmt, ...)
+{
+	extern char *cmdname;
+	va_list varg;
+
+	fflush(stdout);
+	fprintf(stderr, "%s: ", cmdname);
+	va_start(varg, fmt);
+	vfprintf(stderr, fmt, varg);
+	va_end(varg);
+	error();
+}
+
+void error()
+{
+	extern Node *curnode;
+
+	fprintf(stderr, "\n");
+	if (compile_time != 2 && NR && *NR > 0) {
+		fprintf(stderr, " input record number %d", (int) (*FNR));
+		if (strcmp(*FILENAME, "-") != 0)
+			fprintf(stderr, ", file %s", *FILENAME);
+		fprintf(stderr, "\n");
+	}
+	if (compile_time != 2 && curnode)
+		fprintf(stderr, " source line number %d", curnode->lineno);
+	else if (compile_time != 2 && lineno)
+		fprintf(stderr, " source line number %d", lineno);
+	if (compile_time == 1 && cursource() != NULL)
+		fprintf(stderr, " source file %s", cursource());
+	fprintf(stderr, "\n");
+	eprint();
+}
+
+void eprint(void)	/* try to print context around error */
+{
+	char *p, *q;
+	int c;
+	static int been_here = 0;
+	extern char ebuf[], *ep;
+
+	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
+		return;
+	p = ep - 1;
+	if (p > ebuf && *p == '\n')
+		p--;
+	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
+		;
+	while (*p == '\n')
+		p++;
+	fprintf(stderr, " context is\n\t");
+	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+		;
+	for ( ; p < q; p++)
+		if (*p)
+			putc(*p, stderr);
+	fprintf(stderr, " >>> ");
+	for ( ; p < ep; p++)
+		if (*p)
+			putc(*p, stderr);
+	fprintf(stderr, " <<< ");
+	if (*ep)
+		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
+			putc(c, stderr);
+			bclass(c);
+		}
+	putc('\n', stderr);
+	ep = ebuf;
+}
+
+void bclass(int c)
+{
+	switch (c) {
+	case '{': bracecnt++; break;
+	case '}': bracecnt--; break;
+	case '[': brackcnt++; break;
+	case ']': brackcnt--; break;
+	case '(': parencnt++; break;
+	case ')': parencnt--; break;
+	}
+}
+
+double errcheck(double x, char *s)
+{
+
+	if (errno == EDOM) {
+		errno = 0;
+		WARNING("%s argument out of domain", s);
+		x = 1;
+	} else if (errno == ERANGE) {
+		errno = 0;
+		WARNING("%s result out of range", s);
+		x = 1;
+	}
+	return x;
+}
+
+int isclvar(char *s)	/* is s of form var=something ? */
+{
+	char *os = s;
+
+	if (!isalpha((uschar) *s) && *s != '_')
+		return 0;
+	for ( ; *s; s++)
+		if (!(isalnum((uschar) *s) || *s == '_'))
+			break;
+	return *s == '=' && s > os && *(s+1) != '=';
+}
+
+/* strtod is supposed to be a proper test of what's a valid number */
+/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
+/* wrong: violates 4.10.1.4 of ansi C standard */
+
+#include <math.h>
+int is_number(char *s)
+{
+	double r;
+	char *ep;
+	errno = 0;
+	r = strtod(s, &ep);
+	if (ep == s || r == HUGE_VAL || errno == ERANGE)
+		return 0;
+	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+		ep++;
+	if (*ep == '\0')
+		return 1;
+	else
+		return 0;
+}
--- /dev/null
+++ b/utils/awk/mac.code
@@ -1,0 +1,65 @@
+This file contains a make shell script and a version
+of the file missing95.c for the Mac, courtesy of
+Dan Allen.
+
+make shell script:
+
+# MPW Shell script to build Awk using Apple's MRC compiler.
+# 22 Jan 1999 - Created by Dan Allen.
+# 25 Mar 1999 - Updated for newer Awk.
+#
+# Porting notes for the Mac:
+# 
+# 1.  main in main.c needs to have its prototype changed to:
+# 
+#     int main(int argc, char *argv[], char *environ[])
+#
+# 2.  popen and pclose in missing95.c need to have as their body the
+#     older style
+#
+#				return NULL;
+#
+#     as parallel pipes are not supported by MPW.
+#
+# 3.  To make your Mac more responsive while long awk scripts run,
+#     you may want to add some SpinCursor calls to support cooperative multitasking.
+#
+# All of these minor changes can be put under "#ifdef powerc" for portability's sake.
+#
+#
+
+If {1} == "clean"
+	Delete -i awk maketab maketab.c.o ytab.c.o b.c.o main.c.o parse.c.o proctab.c proctab.c.o tran.c.o lib.c.o run.c.o lex.c.o missing95.c.o
+Else
+	MRC ytab.c -w off -opt speed
+	MRC b.c -w off -opt speed
+	MRC main.c -w off -opt speed
+	MRC parse.c -w off -opt speed
+	MRC maketab.c -w off -opt speed
+	PPCLink -o maketab maketab.c.o "{PPCLibraries}InterfaceLib" "{PPCLibraries}MathLib" "{PPCLibraries}StdCLib" "{PPCLibraries}StdCRuntime.o" "{PPCLibraries}PPCCRuntime.o" "{PPCLibraries}PPCToolLibs.o" -t MPST -c 'MPS '
+	maketab > proctab.c
+	MRC proctab.c -w off -opt speed
+	MRC tran.c -w off -opt speed
+	MRC lib.c -w off -opt speed
+	MRC run.c -w off -opt speed
+	MRC lex.c -w off -opt speed
+	MRC missing95.c -w off -opt speed
+	PPCLink -o awk ytab.c.o b.c.o main.c.o parse.c.o proctab.c.o tran.c.o lib.c.o run.c.o lex.c.o missing95.c.o "{PPCLibraries}InterfaceLib" "{PPCLibraries}MathLib" "{PPCLibraries}StdCLib" "{PPCLibraries}StdCRuntime.o" "{PPCLibraries}PPCCRuntime.o" "{PPCLibraries}PPCToolLibs.o" -d
+	SetFile awk -d . -m . -t MPST -c 'MPS '
+End
+
+
+missing95.c for the Mac:
+
+/* popen and pclose are not available on the Mac. */
+
+#include <stdio.h>
+
+FILE *popen(char *s, char *m) {
+	return NULL;
+}
+
+int pclose(FILE *f) {
+	return NULL;
+}
+
--- /dev/null
+++ b/utils/awk/main.c
@@ -1,0 +1,197 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+char	*version = "version 20001115";
+
+#define DEBUG
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "awk.h"
+#include "ytab.h"
+
+extern	char	**environ;
+extern	int	nfields;
+
+int	dbg	= 0;
+char	*cmdname;	/* gets argv[0] for error messages */
+extern	FILE	*yyin;	/* lex input file */
+char	*lexprog;	/* points to program argument if it exists */
+extern	int errorflag;	/* non-zero if any syntax errors; set by yyerror */
+int	compile_time = 2;	/* for error printing: */
+				/* 2 = cmdline, 1 = compile, 0 = running */
+
+char	*pfile[20];	/* program filenames from -f's */
+int	npfile = 0;	/* number of filenames */
+int	curpfile = 0;	/* current filename */
+
+int	safe	= 0;	/* 1 => "safe" mode */
+
+int main(int argc, char *argv[])
+{
+	char *fs = NULL, *marg;
+	int temp;
+
+	cmdname = argv[0];
+	if (argc == 1) {
+		fprintf(stderr, "Usage: %s [-f programfile | 'program'] [-Ffieldsep] [-v var=value] [files]\n", cmdname);
+		exit(1);
+	}
+	signal(SIGFPE, fpecatch);
+	yyin = NULL;
+	symtab = makesymtab(NSYMTAB);
+	while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
+		if (strcmp(argv[1], "--") == 0) {	/* explicit end of args */
+			argc--;
+			argv++;
+			break;
+		}
+		switch (argv[1][1]) {
+		case 's':
+			if (strcmp(argv[1], "-safe") == 0)
+				safe = 1;
+			break;
+		case 'f':	/* next argument is program filename */
+			argc--;
+			argv++;
+			if (argc <= 1)
+				FATAL("no program filename");
+			pfile[npfile++] = argv[1];
+			break;
+		case 'F':	/* set field separator */
+			if (argv[1][2] != 0) {	/* arg is -Fsomething */
+				if (argv[1][2] == 't' && argv[1][3] == 0)	/* wart: t=>\t */
+					fs = "\t";
+				else if (argv[1][2] != 0)
+					fs = &argv[1][2];
+			} else {		/* arg is -F something */
+				argc--; argv++;
+				if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0)	/* wart: t=>\t */
+					fs = "\t";
+				else if (argc > 1 && argv[1][0] != 0)
+					fs = &argv[1][0];
+			}
+			if (fs == NULL || *fs == '\0')
+				WARNING("field separator FS is empty");
+			break;
+		case 'v':	/* -v a=1 to be done NOW.  one -v for each */
+			if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1]))
+				setclvar(argv[1]);
+			break;
+		case 'm':	/* more memory: -mr=record, -mf=fields */
+				/* no longer needed */
+			marg = argv[1];
+			if (argv[1][3])
+				temp = atoi(&argv[1][3]);
+			else {
+				argv++; argc--;
+				temp = atoi(&argv[1][0]);
+			}
+			switch (marg[2]) {
+			case 'r':	recsize = temp; break;
+			case 'f':	nfields = temp; break;
+			default: FATAL("unknown option %s\n", marg);
+			}
+			break;
+		case 'd':
+			dbg = atoi(&argv[1][2]);
+			if (dbg == 0)
+				dbg = 1;
+			printf("awk %s\n", version);
+			break;
+		case 'V':	/* added for exptools "standard" */
+			printf("awk %s\n", version);
+			exit(0);
+			break;
+		default:
+			WARNING("unknown option %s ignored", argv[1]);
+			break;
+		}
+		argc--;
+		argv++;
+	}
+	/* argv[1] is now the first argument */
+	if (npfile == 0) {	/* no -f; first argument is program */
+		if (argc <= 1) {
+			if (dbg)
+				exit(0);
+			FATAL("no program given");
+		}
+		   dprintf( ("program = |%s|\n", argv[1]) );
+		lexprog = argv[1];
+		argc--;
+		argv++;
+	}
+	recinit(recsize);
+	syminit();
+	compile_time = 1;
+	argv[0] = cmdname;	/* put prog name at front of arglist */
+	   dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) );
+	arginit(argc, argv);
+	if (!safe)
+		envinit(environ);
+	yyparse();
+	if (fs)
+		*FS = qstring(fs, '\0');
+	   dprintf( ("errorflag=%d\n", errorflag) );
+	if (errorflag == 0) {
+		compile_time = 0;
+		run(winner);
+	} else
+		bracecheck();
+	return(errorflag);
+}
+
+int pgetc(void)		/* get 1 character from awk program */
+{
+	int c;
+
+	for (;;) {
+		if (yyin == NULL) {
+			if (curpfile >= npfile)
+				return EOF;
+			if (strcmp(pfile[curpfile], "-") == 0)
+				yyin = stdin;
+			else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
+				FATAL("can't open file %s", pfile[curpfile]);
+			lineno = 1;
+		}
+		if ((c = getc(yyin)) != EOF)
+			return c;
+		if (yyin != stdin)
+			fclose(yyin);
+		yyin = NULL;
+		curpfile++;
+	}
+}
+
+char *cursource(void)	/* current source file name */
+{
+	if (npfile > 0)
+		return pfile[curpfile];
+	else
+		return NULL;
+}
--- /dev/null
+++ b/utils/awk/makefile
@@ -1,0 +1,81 @@
+# /****************************************************************
+# Copyright (C) Lucent Technologies 1997
+# All Rights Reserved
+# 
+# Permission to use, copy, modify, and distribute this software and
+# its documentation for any purpose and without fee is hereby
+# granted, provided that the above copyright notice appear in all
+# copies and that both that the copyright notice and this
+# permission notice and warranty disclaimer appear in supporting
+# documentation, and that the name Lucent Technologies or any of
+# its entities not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.
+# 
+# LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+# IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+# THIS SOFTWARE.
+# ****************************************************************/
+
+CFLAGS = -g
+CFLAGS = -O2
+CFLAGS =
+
+CC = gcc -Wall -g
+CC = /opt/pure/purify/purify cc
+CC = cc
+
+YACC = bison -y
+YACC = yacc
+YFLAGS = -d
+
+OFILES = b.o main.o parse.o proctab.o tran.o lib.o run.o lex.o
+
+SOURCE = awk.h ytab.c ytab.h proto.h awkgram.y lex.c b.c main.c maketab.c parse.c lib.c run.c tran.c proctab.c missing95.c
+
+LISTING = awk.h proto.h awkgram.y lex.c b.c main.c maketab.c parse.c lib.c run.c tran.c missing95.c
+
+SHIP = README FIXES $(SOURCE) ytab[ch].bak makefile awk.1 buildwin.bat mac.code
+
+a.out:	ytab.o $(OFILES)
+	$(CC) $(CFLAGS) ytab.o $(OFILES) $(ALLOC)  -lm
+
+$(OFILES):	awk.h ytab.h proto.h
+
+ytab.o:	awk.h proto.h awkgram.y
+	$(YACC) $(YFLAGS) awkgram.y
+	mv y.tab.c ytab.c
+	mv y.tab.h ytab.h
+	$(CC) $(CFLAGS) -c ytab.c
+
+proctab.c:	maketab
+	./maketab >proctab.c
+
+maketab:	ytab.h maketab.c
+	$(CC) $(CFLAGS) maketab.c -o maketab
+
+bundle:
+	@cp ytab.h ytabh.bak
+	@cp ytab.c ytabc.bak
+	@bundle $(SHIP)
+
+tar:
+	@cp ytab.h ytabh.bak
+	@cp ytab.c ytabc.bak
+	@bundle $(SHIP) >awk.shar
+	@tar cf awk.tar $(SHIP)
+	gzip awk.tar
+	ls -l awk.tar.gz
+	@zip awk.zip $(SHIP)
+	ls -l awk.zip
+
+names:
+	@echo $(LISTING)
+
+clean:
+	rm -f a.out *.o maketab # proctab.c
--- /dev/null
+++ b/utils/awk/maketab.c
@@ -1,0 +1,168 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+/*
+ * this program makes the table to link function names
+ * and type indices that is used by execute() in run.c.
+ * it finds the indices in ytab.h, produced by yacc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "ytab.h"
+
+struct xx
+{	int token;
+	char *name;
+	char *pname;
+} proc[] = {
+	{ PROGRAM, "program", NULL },
+	{ BOR, "boolop", " || " },
+	{ AND, "boolop", " && " },
+	{ NOT, "boolop", " !" },
+	{ NE, "relop", " != " },
+	{ EQ, "relop", " == " },
+	{ LE, "relop", " <= " },
+	{ LT, "relop", " < " },
+	{ GE, "relop", " >= " },
+	{ GT, "relop", " > " },
+	{ ARRAY, "array", NULL },
+	{ INDIRECT, "indirect", "$(" },
+	{ SUBSTR, "substr", "substr" },
+	{ SUB, "sub", "sub" },
+	{ GSUB, "gsub", "gsub" },
+	{ INDEX, "sindex", "sindex" },
+	{ SPRINTF, "awksprintf", "sprintf " },
+	{ ADD, "arith", " + " },
+	{ MINUS, "arith", " - " },
+	{ MULT, "arith", " * " },
+	{ DIVIDE, "arith", " / " },
+	{ MOD, "arith", " % " },
+	{ UMINUS, "arith", " -" },
+	{ POWER, "arith", " **" },
+	{ PREINCR, "incrdecr", "++" },
+	{ POSTINCR, "incrdecr", "++" },
+	{ PREDECR, "incrdecr", "--" },
+	{ POSTDECR, "incrdecr", "--" },
+	{ CAT, "cat", " " },
+	{ PASTAT, "pastat", NULL },
+	{ PASTAT2, "dopa2", NULL },
+	{ MATCH, "matchop", " ~ " },
+	{ NOTMATCH, "matchop", " !~ " },
+	{ MATCHFCN, "matchop", "matchop" },
+	{ INTEST, "intest", "intest" },
+	{ PRINTF, "awkprintf", "printf" },
+	{ PRINT, "printstat", "print" },
+	{ CLOSE, "closefile", "closefile" },
+	{ DELETE, "awkdelete", "awkdelete" },
+	{ SPLIT, "split", "split" },
+	{ ASSIGN, "assign", " = " },
+	{ ADDEQ, "assign", " += " },
+	{ SUBEQ, "assign", " -= " },
+	{ MULTEQ, "assign", " *= " },
+	{ DIVEQ, "assign", " /= " },
+	{ MODEQ, "assign", " %= " },
+	{ POWEQ, "assign", " ^= " },
+	{ CONDEXPR, "condexpr", " ?: " },
+	{ IF, "ifstat", "if(" },
+	{ WHILE, "whilestat", "while(" },
+	{ FOR, "forstat", "for(" },
+	{ DO, "dostat", "do" },
+	{ IN, "instat", "instat" },
+	{ NEXT, "jump", "next" },
+	{ NEXTFILE, "jump", "nextfile" },
+	{ EXIT, "jump", "exit" },
+	{ BREAK, "jump", "break" },
+	{ CONTINUE, "jump", "continue" },
+	{ RETURN, "jump", "ret" },
+	{ BLTIN, "bltin", "bltin" },
+	{ CALL, "call", "call" },
+	{ ARG, "arg", "arg" },
+	{ VARNF, "getnf", "NF" },
+	{ GETLINE, "getline", "getline" },
+	{ 0, "", "" },
+};
+
+#define SIZE	(LASTTOKEN - FIRSTTOKEN + 1)
+char *table[SIZE];
+char *names[SIZE];
+
+int main(int argc, char *argv[])
+{
+	struct xx *p;
+	int i, n, tok;
+	char c;
+	FILE *fp;
+	char buf[200], name[200], def[200];
+
+	printf("#include <stdio.h>\n");
+	printf("#include \"awk.h\"\n");
+	printf("#include \"ytab.h\"\n\n");
+	for (i = SIZE; --i >= 0; )
+		names[i] = "";
+
+	if ((fp = fopen("ytab.h", "r")) == NULL) {
+		fprintf(stderr, "maketab can't open ytab.h!\n");
+		exit(1);
+	}
+	printf("static char *printname[%d] = {\n", SIZE);
+	i = 0;
+	while (fgets(buf, sizeof buf, fp) != NULL) {
+		n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok);
+		if (c != '#' || (n != 4 && strcmp(def,"define") != 0))	/* not a valid #define */
+			continue;
+		if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
+			fprintf(stderr, "maketab funny token %d %s ignored\n", tok, buf);
+			continue;
+		}
+		names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1);
+		strcpy(names[tok-FIRSTTOKEN], name);
+		printf("\t(char *) \"%s\",\t/* %d */\n", name, tok);
+		i++;
+	}
+	printf("};\n\n");
+
+	for (p=proc; p->token!=0; p++)
+		table[p->token-FIRSTTOKEN] = p->name;
+	printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
+	for (i=0; i<SIZE; i++)
+		if (table[i]==0)
+			printf("\tnullproc,\t/* %s */\n", names[i]);
+		else
+			printf("\t%s,\t/* %s */\n", table[i], names[i]);
+	printf("};\n\n");
+
+	printf("char *tokname(int n)\n");	/* print a tokname() function */
+	printf("{\n");
+	printf("	static char buf[100];\n\n");
+	printf("	if (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
+	printf("		sprintf(buf, \"token %%d\", n);\n");
+	printf("		return buf;\n");
+	printf("	}\n");
+	printf("	return printname[n-FIRSTTOKEN];\n");
+	printf("}\n");
+	return 0;
+}
--- /dev/null
+++ b/utils/awk/missing95.c
@@ -1,0 +1,54 @@
+/* popen and pclose are not part of win 95 and nt,
+   but it appears that _popen and _pclose "work".
+   if this won't load, use the return NULL statements. */
+
+#include <stdio.h>
+FILE *popen(char *s, char *m) {
+	return _popen(s, m);	/* return NULL; */
+}
+
+int pclose(FILE *f) {
+	return _pclose(f);	/* return NULL; */
+}
+
+#include <windows.h>
+#include <winbase.h>
+#include <winsock.h>
+
+/* system doesn't work properly in some 32/64 (WoW64) combinations */
+int system(char *s) {
+	int status, n;
+	PROCESS_INFORMATION pinfo;
+	STARTUPINFO si;
+	char *cmd;
+	char app[256];
+	static char cmdexe[] = "\\cmd.exe";
+
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+//	si.dwFlags = STARTF_USESHOWWINDOW;
+//	si.wShowWindow = SW_SHOW;
+
+	n = GetSystemDirectory(app, sizeof(app)-sizeof(cmdexe));
+	if(n > sizeof(app))
+		return -1;
+	strcat_s(app, sizeof(app), cmdexe);
+	n = strlen(s)+20;
+	cmd = malloc(n);
+	if(cmd == NULL)
+		return -1;
+	strcpy_s(cmd, n, "cmd.exe /c");
+	strcat_s(cmd, n, s);
+	if(!CreateProcess(app, cmd, NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE, NULL/* env*/, NULL /*wdir*/, &si, &pinfo)){
+		fprintf(stderr, "can't create process %s %d\n", s, GetLastError());
+		free(cmd);
+		return -1;
+	}
+	free(cmd);
+	if(WaitForSingleObject(pinfo.hProcess, INFINITE) == WAIT_FAILED)
+		return -1;
+	if(!GetExitCodeProcess(pinfo.hProcess, &status))
+		status = 1;
+	//fprintf(stderr, "status %d\n", status);
+	return status;
+}
--- /dev/null
+++ b/utils/awk/mkfile
@@ -1,0 +1,29 @@
+<../../mkconfig
+
+TARG=awk
+
+OFILES=\
+	b.$O\
+	lex.$O\
+	lib.$O\
+	main.$O\
+	missing95.$O\
+	parse.$O\
+	proctab.$O\
+	run.$O\
+	tran.$O\
+	ytab.$O\
+
+HFILES=\
+	awk.h\
+	proto.h\
+	ytab.h\
+
+LIBS=bio			# order is important
+
+CFLAGS=$CFLAGS -QIfdiv-
+LDFLAGS=$LDFLAGS setargv.obj
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/awk/parse.c
@@ -1,0 +1,276 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "ytab.h"
+
+Node *nodealloc(int n)
+{
+	Node *x;
+
+	x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *));
+	if (x == NULL)
+		FATAL("out of space in nodealloc");
+	x->nnext = NULL;
+	x->lineno = lineno;
+	return(x);
+}
+
+Node *exptostat(Node *a)
+{
+	a->ntype = NSTAT;
+	return(a);
+}
+
+Node *node1(int a, Node *b)
+{
+	Node *x;
+
+	x = nodealloc(1);
+	x->nobj = a;
+	x->narg[0]=b;
+	return(x);
+}
+
+Node *node2(int a, Node *b, Node *c)
+{
+	Node *x;
+
+	x = nodealloc(2);
+	x->nobj = a;
+	x->narg[0] = b;
+	x->narg[1] = c;
+	return(x);
+}
+
+Node *node3(int a, Node *b, Node *c, Node *d)
+{
+	Node *x;
+
+	x = nodealloc(3);
+	x->nobj = a;
+	x->narg[0] = b;
+	x->narg[1] = c;
+	x->narg[2] = d;
+	return(x);
+}
+
+Node *node4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+	Node *x;
+
+	x = nodealloc(4);
+	x->nobj = a;
+	x->narg[0] = b;
+	x->narg[1] = c;
+	x->narg[2] = d;
+	x->narg[3] = e;
+	return(x);
+}
+
+Node *stat1(int a, Node *b)
+{
+	Node *x;
+
+	x = node1(a,b);
+	x->ntype = NSTAT;
+	return(x);
+}
+
+Node *stat2(int a, Node *b, Node *c)
+{
+	Node *x;
+
+	x = node2(a,b,c);
+	x->ntype = NSTAT;
+	return(x);
+}
+
+Node *stat3(int a, Node *b, Node *c, Node *d)
+{
+	Node *x;
+
+	x = node3(a,b,c,d);
+	x->ntype = NSTAT;
+	return(x);
+}
+
+Node *stat4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+	Node *x;
+
+	x = node4(a,b,c,d,e);
+	x->ntype = NSTAT;
+	return(x);
+}
+
+Node *op1(int a, Node *b)
+{
+	Node *x;
+
+	x = node1(a,b);
+	x->ntype = NEXPR;
+	return(x);
+}
+
+Node *op2(int a, Node *b, Node *c)
+{
+	Node *x;
+
+	x = node2(a,b,c);
+	x->ntype = NEXPR;
+	return(x);
+}
+
+Node *op3(int a, Node *b, Node *c, Node *d)
+{
+	Node *x;
+
+	x = node3(a,b,c,d);
+	x->ntype = NEXPR;
+	return(x);
+}
+
+Node *op4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+	Node *x;
+
+	x = node4(a,b,c,d,e);
+	x->ntype = NEXPR;
+	return(x);
+}
+
+Node *celltonode(Cell *a, int b)
+{
+	Node *x;
+
+	a->ctype = OCELL;
+	a->csub = b;
+	x = node1(0, (Node *) a);
+	x->ntype = NVALUE;
+	return(x);
+}
+
+Node *rectonode(void)	/* make $0 into a Node */
+{
+	extern Cell *literal0;
+	return op1(INDIRECT, celltonode(literal0, CUNK));
+}
+
+Node *makearr(Node *p)
+{
+	Cell *cp;
+
+	if (isvalue(p)) {
+		cp = (Cell *) (p->narg[0]);
+		if (isfcn(cp))
+			SYNTAX( "%s is a function, not an array", cp->nval );
+		else if (!isarr(cp)) {
+			xfree(cp->sval);
+			cp->sval = (char *) makesymtab(NSYMTAB);
+			cp->tval = ARR;
+		}
+	}
+	return p;
+}
+
+#define PA2NUM	50	/* max number of pat,pat patterns allowed */
+int	paircnt;		/* number of them in use */
+int	pairstack[PA2NUM];	/* state of each pat,pat */
+
+Node *pa2stat(Node *a, Node *b, Node *c)	/* pat, pat {...} */
+{
+	Node *x;
+
+	x = node4(PASTAT2, a, b, c, itonp(paircnt));
+	if (paircnt++ >= PA2NUM)
+		SYNTAX( "limited to %d pat,pat statements", PA2NUM );
+	x->ntype = NSTAT;
+	return(x);
+}
+
+Node *linkum(Node *a, Node *b)
+{
+	Node *c;
+
+	if (errorflag)	/* don't link things that are wrong */
+		return a;
+	if (a == NULL)
+		return(b);
+	else if (b == NULL)
+		return(a);
+	for (c = a; c->nnext != NULL; c = c->nnext)
+		;
+	c->nnext = b;
+	return(a);
+}
+
+void defn(Cell *v, Node *vl, Node *st)	/* turn on FCN bit in definition, */
+{					/*   body of function, arglist */
+	Node *p;
+	int n;
+
+	if (isarr(v)) {
+		SYNTAX( "`%s' is an array name and a function name", v->nval );
+		return;
+	}
+	if (isarg(v->nval) != -1) {
+		SYNTAX( "`%s' is both function name and argument name", v->nval );
+		return;
+	}
+
+	v->tval = FCN;
+	v->sval = (char *) st;
+	n = 0;	/* count arguments */
+	for (p = vl; p; p = p->nnext)
+		n++;
+	v->fval = n;
+	dprintf( ("defining func %s (%d args)\n", v->nval, n) );
+}
+
+int isarg(char *s)		/* is s in argument list for current function? */
+{			/* return -1 if not, otherwise arg # */
+	extern Node *arglist;
+	Node *p = arglist;
+	int n;
+
+	for (n = 0; p != 0; p = p->nnext, n++)
+		if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
+			return n;
+	return -1;
+}
+
+int ptoi(void *p)	/* convert pointer to integer */
+{
+	return (int) (long) p;	/* swearing that p fits, of course */
+}
+
+Node *itonp(int i)	/* and vice versa */
+{
+	return (Node *) (long) i;
+}
--- /dev/null
+++ b/utils/awk/proctab.c
@@ -1,0 +1,205 @@
+#include <stdio.h>
+#include "awk.h"
+#include "ytab.h"
+
+static char *printname[92] = {
+	(char *) "FIRSTTOKEN",	/* 57346 */
+	(char *) "PROGRAM",	/* 57347 */
+	(char *) "PASTAT",	/* 57348 */
+	(char *) "PASTAT2",	/* 57349 */
+	(char *) "XBEGIN",	/* 57350 */
+	(char *) "XEND",	/* 57351 */
+	(char *) "NL",	/* 57352 */
+	(char *) "ARRAY",	/* 57353 */
+	(char *) "MATCH",	/* 57354 */
+	(char *) "NOTMATCH",	/* 57355 */
+	(char *) "MATCHOP",	/* 57356 */
+	(char *) "FINAL",	/* 57357 */
+	(char *) "DOT",	/* 57358 */
+	(char *) "ALL",	/* 57359 */
+	(char *) "CCL",	/* 57360 */
+	(char *) "NCCL",	/* 57361 */
+	(char *) "CHAR",	/* 57362 */
+	(char *) "OR",	/* 57363 */
+	(char *) "STAR",	/* 57364 */
+	(char *) "QUEST",	/* 57365 */
+	(char *) "PLUS",	/* 57366 */
+	(char *) "AND",	/* 57367 */
+	(char *) "BOR",	/* 57368 */
+	(char *) "APPEND",	/* 57369 */
+	(char *) "EQ",	/* 57370 */
+	(char *) "GE",	/* 57371 */
+	(char *) "GT",	/* 57372 */
+	(char *) "LE",	/* 57373 */
+	(char *) "LT",	/* 57374 */
+	(char *) "NE",	/* 57375 */
+	(char *) "IN",	/* 57376 */
+	(char *) "ARG",	/* 57377 */
+	(char *) "BLTIN",	/* 57378 */
+	(char *) "BREAK",	/* 57379 */
+	(char *) "CLOSE",	/* 57380 */
+	(char *) "CONTINUE",	/* 57381 */
+	(char *) "DELETE",	/* 57382 */
+	(char *) "DO",	/* 57383 */
+	(char *) "EXIT",	/* 57384 */
+	(char *) "FOR",	/* 57385 */
+	(char *) "FUNC",	/* 57386 */
+	(char *) "SUB",	/* 57387 */
+	(char *) "GSUB",	/* 57388 */
+	(char *) "IF",	/* 57389 */
+	(char *) "INDEX",	/* 57390 */
+	(char *) "LSUBSTR",	/* 57391 */
+	(char *) "MATCHFCN",	/* 57392 */
+	(char *) "NEXT",	/* 57393 */
+	(char *) "NEXTFILE",	/* 57394 */
+	(char *) "ADD",	/* 57395 */
+	(char *) "MINUS",	/* 57396 */
+	(char *) "MULT",	/* 57397 */
+	(char *) "DIVIDE",	/* 57398 */
+	(char *) "MOD",	/* 57399 */
+	(char *) "ASSIGN",	/* 57400 */
+	(char *) "ASGNOP",	/* 57401 */
+	(char *) "ADDEQ",	/* 57402 */
+	(char *) "SUBEQ",	/* 57403 */
+	(char *) "MULTEQ",	/* 57404 */
+	(char *) "DIVEQ",	/* 57405 */
+	(char *) "MODEQ",	/* 57406 */
+	(char *) "POWEQ",	/* 57407 */
+	(char *) "PRINT",	/* 57408 */
+	(char *) "PRINTF",	/* 57409 */
+	(char *) "SPRINTF",	/* 57410 */
+	(char *) "ELSE",	/* 57411 */
+	(char *) "INTEST",	/* 57412 */
+	(char *) "CONDEXPR",	/* 57413 */
+	(char *) "POSTINCR",	/* 57414 */
+	(char *) "PREINCR",	/* 57415 */
+	(char *) "POSTDECR",	/* 57416 */
+	(char *) "PREDECR",	/* 57417 */
+	(char *) "VAR",	/* 57418 */
+	(char *) "IVAR",	/* 57419 */
+	(char *) "VARNF",	/* 57420 */
+	(char *) "CALL",	/* 57421 */
+	(char *) "NUMBER",	/* 57422 */
+	(char *) "STRING",	/* 57423 */
+	(char *) "REGEXPR",	/* 57424 */
+	(char *) "GETLINE",	/* 57425 */
+	(char *) "RETURN",	/* 57426 */
+	(char *) "SPLIT",	/* 57427 */
+	(char *) "SUBSTR",	/* 57428 */
+	(char *) "WHILE",	/* 57429 */
+	(char *) "CAT",	/* 57430 */
+	(char *) "NOT",	/* 57431 */
+	(char *) "UMINUS",	/* 57432 */
+	(char *) "POWER",	/* 57433 */
+	(char *) "DECR",	/* 57434 */
+	(char *) "INCR",	/* 57435 */
+	(char *) "INDIRECT",	/* 57436 */
+	(char *) "LASTTOKEN",	/* 57437 */
+};
+
+
+Cell *(*proctab[92])(Node **, int) = {
+	nullproc,	/* FIRSTTOKEN */
+	program,	/* PROGRAM */
+	pastat,	/* PASTAT */
+	dopa2,	/* PASTAT2 */
+	nullproc,	/* XBEGIN */
+	nullproc,	/* XEND */
+	nullproc,	/* NL */
+	array,	/* ARRAY */
+	matchop,	/* MATCH */
+	matchop,	/* NOTMATCH */
+	nullproc,	/* MATCHOP */
+	nullproc,	/* FINAL */
+	nullproc,	/* DOT */
+	nullproc,	/* ALL */
+	nullproc,	/* CCL */
+	nullproc,	/* NCCL */
+	nullproc,	/* CHAR */
+	nullproc,	/* OR */
+	nullproc,	/* STAR */
+	nullproc,	/* QUEST */
+	nullproc,	/* PLUS */
+	boolop,	/* AND */
+	boolop,	/* BOR */
+	nullproc,	/* APPEND */
+	relop,	/* EQ */
+	relop,	/* GE */
+	relop,	/* GT */
+	relop,	/* LE */
+	relop,	/* LT */
+	relop,	/* NE */
+	instat,	/* IN */
+	arg,	/* ARG */
+	bltin,	/* BLTIN */
+	jump,	/* BREAK */
+	closefile,	/* CLOSE */
+	jump,	/* CONTINUE */
+	awkdelete,	/* DELETE */
+	dostat,	/* DO */
+	jump,	/* EXIT */
+	forstat,	/* FOR */
+	nullproc,	/* FUNC */
+	sub,	/* SUB */
+	gsub,	/* GSUB */
+	ifstat,	/* IF */
+	sindex,	/* INDEX */
+	nullproc,	/* LSUBSTR */
+	matchop,	/* MATCHFCN */
+	jump,	/* NEXT */
+	jump,	/* NEXTFILE */
+	arith,	/* ADD */
+	arith,	/* MINUS */
+	arith,	/* MULT */
+	arith,	/* DIVIDE */
+	arith,	/* MOD */
+	assign,	/* ASSIGN */
+	nullproc,	/* ASGNOP */
+	assign,	/* ADDEQ */
+	assign,	/* SUBEQ */
+	assign,	/* MULTEQ */
+	assign,	/* DIVEQ */
+	assign,	/* MODEQ */
+	assign,	/* POWEQ */
+	printstat,	/* PRINT */
+	awkprintf,	/* PRINTF */
+	awksprintf,	/* SPRINTF */
+	nullproc,	/* ELSE */
+	intest,	/* INTEST */
+	condexpr,	/* CONDEXPR */
+	incrdecr,	/* POSTINCR */
+	incrdecr,	/* PREINCR */
+	incrdecr,	/* POSTDECR */
+	incrdecr,	/* PREDECR */
+	nullproc,	/* VAR */
+	nullproc,	/* IVAR */
+	getnf,	/* VARNF */
+	call,	/* CALL */
+	nullproc,	/* NUMBER */
+	nullproc,	/* STRING */
+	nullproc,	/* REGEXPR */
+	getline,	/* GETLINE */
+	jump,	/* RETURN */
+	split,	/* SPLIT */
+	substr,	/* SUBSTR */
+	whilestat,	/* WHILE */
+	cat,	/* CAT */
+	boolop,	/* NOT */
+	arith,	/* UMINUS */
+	arith,	/* POWER */
+	nullproc,	/* DECR */
+	nullproc,	/* INCR */
+	indirect,	/* INDIRECT */
+	nullproc,	/* LASTTOKEN */
+};
+
+char *tokname(int n)
+{
+	static char buf[100];
+
+	if (n < FIRSTTOKEN || n > LASTTOKEN) {
+		sprintf(buf, "token %d", n);
+		return buf;
+	}
+	return printname[n-FIRSTTOKEN];
+}
--- /dev/null
+++ b/utils/awk/proto.h
@@ -1,0 +1,194 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+extern	int	yywrap(void);
+extern	void	setfname(Cell *);
+extern	int	constnode(Node *);
+extern	char	*strnode(Node *);
+extern	Node	*notnull(Node *);
+extern	int	yyparse(void);
+
+extern	int	yylex(void);
+extern	void	startreg(void);
+extern	int	input(void);
+extern	void	unput(int);
+extern	void	unputstr(char *);
+extern	int	yylook(void);
+extern	int	yyback(int *, int);
+extern	int	yyinput(void);
+
+extern	fa	*makedfa(char *, int);
+extern	fa	*mkdfa(char *, int);
+extern	int	makeinit(fa *, int);
+extern	void	penter(Node *);
+extern	void	freetr(Node *);
+extern	int	hexstr(char **);
+extern	int	quoted(char **);
+extern	char	*cclenter(char *);
+extern	void	overflo(char *);
+extern	void	cfoll(fa *, Node *);
+extern	int	first(Node *);
+extern	void	follow(Node *);
+extern	int	member(int, char *);
+extern	int	match(fa *, char *);
+extern	int	pmatch(fa *, char *);
+extern	int	nematch(fa *, char *);
+extern	Node	*reparse(char *);
+extern	Node	*regexp(void);
+extern	Node	*primary(void);
+extern	Node	*concat(Node *);
+extern	Node	*alt(Node *);
+extern	Node	*unary(Node *);
+extern	int	relex(void);
+extern	int	cgoto(fa *, int, int);
+extern	void	freefa(fa *);
+
+extern	int	pgetc(void);
+extern	char	*cursource(void);
+
+extern	Node	*nodealloc(int);
+extern	Node	*exptostat(Node *);
+extern	Node	*node1(int, Node *);
+extern	Node	*node2(int, Node *, Node *);
+extern	Node	*node3(int, Node *, Node *, Node *);
+extern	Node	*node4(int, Node *, Node *, Node *, Node *);
+extern	Node	*stat3(int, Node *, Node *, Node *);
+extern	Node	*op2(int, Node *, Node *);
+extern	Node	*op1(int, Node *);
+extern	Node	*stat1(int, Node *);
+extern	Node	*op3(int, Node *, Node *, Node *);
+extern	Node	*op4(int, Node *, Node *, Node *, Node *);
+extern	Node	*stat2(int, Node *, Node *);
+extern	Node	*stat4(int, Node *, Node *, Node *, Node *);
+extern	Node	*celltonode(Cell *, int);
+extern	Node	*rectonode(void);
+extern	Node	*makearr(Node *);
+extern	Node	*pa2stat(Node *, Node *, Node *);
+extern	Node	*linkum(Node *, Node *);
+extern	void	defn(Cell *, Node *, Node *);
+extern	int	isarg(char *);
+extern	char	*tokname(int);
+extern	Cell	*(*proctab[])(Node **, int);
+extern	int	ptoi(void *);
+extern	Node	*itonp(int);
+
+extern	void	syminit(void);
+extern	void	arginit(int, char **);
+extern	void	envinit(char **);
+extern	Array	*makesymtab(int);
+extern	void	freesymtab(Cell *);
+extern	void	freeelem(Cell *, char *);
+extern	Cell	*setsymtab(char *, char *, double, unsigned int, Array *);
+extern	int	hash(char *, int);
+extern	void	rehash(Array *);
+extern	Cell	*lookup(char *, Array *);
+extern	double	setfval(Cell *, double);
+extern	void	funnyvar(Cell *, char *);
+extern	char	*setsval(Cell *, char *);
+extern	double	getfval(Cell *);
+extern	char	*getsval(Cell *);
+extern	char	*tostring(char *);
+extern	char	*qstring(char *, int);
+
+extern	void	recinit(unsigned int);
+extern	void	initgetrec(void);
+extern	void	makefields(int, int);
+extern	void	growfldtab(int n);
+extern	int	getrec(char **, int *, int);
+extern	void	nextfile(void);
+extern	int	readrec(char **buf, int *bufsize, FILE *inf);
+extern	char	*getargv(int);
+extern	void	setclvar(char *);
+extern	void	fldbld(void);
+extern	void	cleanfld(int, int);
+extern	void	newfld(int);
+extern	int	refldbld(char *, char *);
+extern	void	recbld(void);
+extern	Cell	*fieldadr(int);
+extern	void	yyerror(char *);
+extern	void	fpecatch(int);
+extern	void	bracecheck(void);
+extern	void	bcheck2(int, int, int);
+extern	void	SYNTAX(char *, ...);
+extern	void	FATAL(char *, ...);
+extern	void	WARNING(char *, ...);
+extern	void	error(void);
+extern	void	eprint(void);
+extern	void	bclass(int);
+extern	double	errcheck(double, char *);
+extern	int	isclvar(char *);
+extern	int	is_number(char *);
+
+extern	int	adjbuf(char **pb, int *sz, int min, int q, char **pbp, char *what);
+extern	void	run(Node *);
+extern	Cell	*execute(Node *);
+extern	Cell	*program(Node **, int);
+extern	Cell	*call(Node **, int);
+extern	Cell	*copycell(Cell *);
+extern	Cell	*arg(Node **, int);
+extern	Cell	*jump(Node **, int);
+extern	Cell	*getline(Node **, int);
+extern	Cell	*getnf(Node **, int);
+extern	Cell	*array(Node **, int);
+extern	Cell	*awkdelete(Node **, int);
+extern	Cell	*intest(Node **, int);
+extern	Cell	*matchop(Node **, int);
+extern	Cell	*boolop(Node **, int);
+extern	Cell	*relop(Node **, int);
+extern	void	tfree(Cell *);
+extern	Cell	*gettemp(void);
+extern	Cell	*field(Node **, int);
+extern	Cell	*indirect(Node **, int);
+extern	Cell	*substr(Node **, int);
+extern	Cell	*sindex(Node **, int);
+extern	int	format(char **, int *, char *, Node *);
+extern	Cell	*awksprintf(Node **, int);
+extern	Cell	*awkprintf(Node **, int);
+extern	Cell	*arith(Node **, int);
+extern	double	ipow(double, int);
+extern	Cell	*incrdecr(Node **, int);
+extern	Cell	*assign(Node **, int);
+extern	Cell	*cat(Node **, int);
+extern	Cell	*pastat(Node **, int);
+extern	Cell	*dopa2(Node **, int);
+extern	Cell	*split(Node **, int);
+extern	Cell	*condexpr(Node **, int);
+extern	Cell	*ifstat(Node **, int);
+extern	Cell	*whilestat(Node **, int);
+extern	Cell	*dostat(Node **, int);
+extern	Cell	*forstat(Node **, int);
+extern	Cell	*instat(Node **, int);
+extern	Cell	*bltin(Node **, int);
+extern	Cell	*printstat(Node **, int);
+extern	Cell	*nullproc(Node **, int);
+extern	FILE	*redirect(int, Node *);
+extern	FILE	*openfile(int, char *);
+extern	char	*filename(FILE *);
+extern	Cell	*closefile(Node **, int);
+extern	void	closeall(void);
+extern	Cell	*sub(Node **, int);
+extern	Cell	*gsub(Node **, int);
+
+extern	FILE	*popen(const char *, const char *);
+extern	int	pclose(FILE *);
--- /dev/null
+++ b/utils/awk/run.c
@@ -1,0 +1,1894 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "awk.h"
+#include "ytab.h"
+
+#define tempfree(x)	if (istemp(x)) tfree(x); else
+
+/*
+#undef tempfree
+
+void tempfree(Cell *p) {
+	if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
+		WARNING("bad csub %d in Cell %d %s",
+			p->csub, p->ctype, p->sval);
+	}
+	if (istemp(p))
+		tfree(p);
+}
+*/
+
+#ifdef _NFILE
+#ifndef FOPEN_MAX
+#define FOPEN_MAX _NFILE
+#endif
+#endif
+
+#ifndef	FOPEN_MAX
+#define	FOPEN_MAX	40	/* max number of open files */
+#endif
+
+#ifndef RAND_MAX
+#define RAND_MAX	32767	/* all that ansi guarantees */
+#endif
+
+jmp_buf env;
+extern	int	pairstack[];
+
+Node	*winner = NULL;	/* root of parse tree */
+Cell	*tmps;		/* free temporary cells for execution */
+
+static Cell	truecell	={ OBOOL, BTRUE, 0, 0, 1.0, NUM };
+Cell	*True	= &truecell;
+static Cell	falsecell	={ OBOOL, BFALSE, 0, 0, 0.0, NUM };
+Cell	*False	= &falsecell;
+static Cell	breakcell	={ OJUMP, JBREAK, 0, 0, 0.0, NUM };
+Cell	*jbreak	= &breakcell;
+static Cell	contcell	={ OJUMP, JCONT, 0, 0, 0.0, NUM };
+Cell	*jcont	= &contcell;
+static Cell	nextcell	={ OJUMP, JNEXT, 0, 0, 0.0, NUM };
+Cell	*jnext	= &nextcell;
+static Cell	nextfilecell	={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM };
+Cell	*jnextfile	= &nextfilecell;
+static Cell	exitcell	={ OJUMP, JEXIT, 0, 0, 0.0, NUM };
+Cell	*jexit	= &exitcell;
+static Cell	retcell		={ OJUMP, JRET, 0, 0, 0.0, NUM };
+Cell	*jret	= &retcell;
+static Cell	tempcell	={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE };
+
+Node	*curnode = NULL;	/* the node being executed, for debugging */
+
+/* buffer memory management */
+int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
+	char *whatrtn)
+/* pbuf:    address of pointer to buffer being managed
+ * psiz:    address of buffer size variable
+ * minlen:  minimum length of buffer needed
+ * quantum: buffer size quantum
+ * pbptr:   address of movable pointer into buffer, or 0 if none
+ * whatrtn: name of the calling routine if failure should cause fatal error
+ *
+ * return   0 for realloc failure, !=0 for success
+ */
+{
+	if (minlen > *psiz) {
+		char *tbuf;
+		int rminlen = quantum ? minlen % quantum : 0;
+		int boff = pbptr ? *pbptr - *pbuf : 0;
+		/* round up to next multiple of quantum */
+		if (rminlen)
+			minlen += quantum - rminlen;
+		tbuf = (char *) realloc(*pbuf, minlen);
+		if (tbuf == NULL) {
+			if (whatrtn)
+				FATAL("out of memory in %s", whatrtn);
+			return 0;
+		}
+		*pbuf = tbuf;
+		*psiz = minlen;
+		if (pbptr)
+			*pbptr = tbuf + boff;
+	}
+	return 1;
+}
+
+void run(Node *a)	/* execution of parse tree starts here */
+{
+	extern void stdinit(void);
+
+	stdinit();
+	execute(a);
+	closeall();
+}
+
+Cell *execute(Node *u)	/* execute a node of the parse tree */
+{
+	Cell *(*proc)(Node **, int);
+	Cell *x;
+	Node *a;
+
+	if (u == NULL)
+		return(True);
+	for (a = u; ; a = a->nnext) {
+		curnode = a;
+		if (isvalue(a)) {
+			x = (Cell *) (a->narg[0]);
+			if (isfld(x) && !donefld)
+				fldbld();
+			else if (isrec(x) && !donerec)
+				recbld();
+			return(x);
+		}
+		if (notlegal(a->nobj))	/* probably a Cell* but too risky to print */
+			FATAL("illegal statement");
+		proc = proctab[a->nobj-FIRSTTOKEN];
+		x = (*proc)(a->narg, a->nobj);
+		if (isfld(x) && !donefld)
+			fldbld();
+		else if (isrec(x) && !donerec)
+			recbld();
+		if (isexpr(a))
+			return(x);
+		if (isjump(x))
+			return(x);
+		if (a->nnext == NULL)
+			return(x);
+		tempfree(x);
+	}
+}
+
+
+Cell *program(Node **a, int n)	/* execute an awk program */
+{				/* a[0] = BEGIN, a[1] = body, a[2] = END */
+	Cell *x;
+
+	if (setjmp(env) != 0)
+		goto ex;
+	if (a[0]) {		/* BEGIN */
+		x = execute(a[0]);
+		if (isexit(x))
+			return(True);
+		if (isjump(x))
+			FATAL("illegal break, continue, next or nextfile from BEGIN");
+		tempfree(x);
+	}
+	if (a[1] || a[2])
+		while (getrec(&record, &recsize, 1) > 0) {
+			x = execute(a[1]);
+			if (isexit(x))
+				break;
+			tempfree(x);
+		}
+  ex:
+	if (setjmp(env) != 0)	/* handles exit within END */
+		goto ex1;
+	if (a[2]) {		/* END */
+		x = execute(a[2]);
+		if (isbreak(x) || isnext(x) || iscont(x))
+			FATAL("illegal break, continue, next or nextfile from END");
+		tempfree(x);
+	}
+  ex1:
+	return(True);
+}
+
+struct Frame {	/* stack frame for awk function calls */
+	int nargs;	/* number of arguments in this call */
+	Cell *fcncell;	/* pointer to Cell for function */
+	Cell **args;	/* pointer to array of arguments after execute */
+	Cell *retval;	/* return value */
+};
+
+#define	NARGS	50	/* max args in a call */
+
+struct Frame *frame = NULL;	/* base of stack frames; dynamically allocated */
+int	nframe = 0;		/* number of frames allocated */
+struct Frame *fp = NULL;	/* frame pointer. bottom level unused */
+
+Cell *call(Node **a, int n)	/* function call.  very kludgy and fragile */
+{
+	static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE };
+	int i, ncall, ndef;
+	Node *x;
+	Cell *args[NARGS], *oargs[NARGS];	/* BUG: fixed size arrays */
+	Cell *y, *z, *fcn;
+	char *s;
+
+	fcn = execute(a[0]);	/* the function itself */
+	s = fcn->nval;
+	if (!isfcn(fcn))
+		FATAL("calling undefined function %s", s);
+	if (frame == NULL) {
+		fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame));
+		if (frame == NULL)
+			FATAL("out of space for stack frames calling %s", s);
+	}
+	for (ncall = 0, x = a[1]; x != NULL; x = x->nnext)	/* args in call */
+		ncall++;
+	ndef = (int) fcn->fval;			/* args in defn */
+	   dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) );
+	if (ncall > ndef)
+		WARNING("function %s called with %d args, uses only %d",
+			s, ncall, ndef);
+	if (ncall + ndef > NARGS)
+		FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
+	for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) {	/* get call args */
+		   dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) );
+		y = execute(x);
+		oargs[i] = y;
+		   dprintf( ("args[%d]: %s %f <%s>, t=%o\n",
+			   i, y->nval, y->fval, isarr(y) ? "(array)" : y->sval, y->tval) );
+		if (isfcn(y))
+			FATAL("can't use function %s as argument in %s", y->nval, s);
+		if (isarr(y))
+			args[i] = y;	/* arrays by ref */
+		else
+			args[i] = copycell(y);
+		tempfree(y);
+	}
+	for ( ; i < ndef; i++) {	/* add null args for ones not provided */
+		args[i] = gettemp();
+		*args[i] = newcopycell;
+	}
+	fp++;	/* now ok to up frame */
+	if (fp >= frame + nframe) {
+		int dfp = fp - frame;	/* old index */
+		frame = (struct Frame *)
+			realloc((char *) frame, (nframe += 100) * sizeof(struct Frame));
+		if (frame == NULL)
+			FATAL("out of space for stack frames in %s", s);
+		fp = frame + dfp;
+	}
+	fp->fcncell = fcn;
+	fp->args = args;
+	fp->nargs = ndef;	/* number defined with (excess are locals) */
+	fp->retval = gettemp();
+
+	   dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+	y = execute((Node *)(fcn->sval));	/* execute body */
+	   dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+
+	for (i = 0; i < ndef; i++) {
+		Cell *t = fp->args[i];
+		if (isarr(t)) {
+			if (t->csub == CCOPY) {
+				if (i >= ncall) {
+					freesymtab(t);
+					t->csub = CTEMP;
+					tempfree(t);
+				} else {
+					oargs[i]->tval = t->tval;
+					oargs[i]->tval &= ~(STR|NUM|DONTFREE);
+					oargs[i]->sval = t->sval;
+					tempfree(t);
+				}
+			}
+		} else if (t != y) {	/* kludge to prevent freeing twice */
+			t->csub = CTEMP;
+			tempfree(t);
+		}
+	}
+	tempfree(fcn);
+	if (isexit(y) || isnext(y))
+		return y;
+	tempfree(y);		/* this can free twice! */
+	z = fp->retval;			/* return value */
+	   dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) );
+	fp--;
+	return(z);
+}
+
+Cell *copycell(Cell *x)	/* make a copy of a cell in a temp */
+{
+	Cell *y;
+
+	y = gettemp();
+	y->csub = CCOPY;	/* prevents freeing until call is over */
+	y->nval = x->nval;	/* BUG? */
+	if (isstr(x))
+		y->sval = tostring(x->sval);
+	y->fval = x->fval;
+	y->tval = x->tval & ~(CON|FLD|REC|DONTFREE);	/* copy is not constant or field */
+							/* is DONTFREE right? */
+	return y;
+}
+
+Cell *arg(Node **a, int n)	/* nth argument of a function */
+{
+
+	n = ptoi(a[0]);	/* argument number, counting from 0 */
+	   dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) );
+	if (n+1 > fp->nargs)
+		FATAL("argument #%d of function %s was not supplied",
+			n+1, fp->fcncell->nval);
+	return fp->args[n];
+}
+
+Cell *jump(Node **a, int n)	/* break, continue, next, nextfile, return */
+{
+	Cell *y;
+
+	switch (n) {
+	case EXIT:
+		if (a[0] != NULL) {
+			y = execute(a[0]);
+			errorflag = (int) getfval(y);
+			tempfree(y);
+		}
+		longjmp(env, 1);
+	case RETURN:
+		if (a[0] != NULL) {
+			y = execute(a[0]);
+			if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+				setsval(fp->retval, getsval(y));
+				fp->retval->fval = getfval(y);
+				fp->retval->tval |= NUM;
+			}
+			else if (y->tval & STR)
+				setsval(fp->retval, getsval(y));
+			else if (y->tval & NUM)
+				setfval(fp->retval, getfval(y));
+			else		/* can't happen */
+				FATAL("bad type variable %d", y->tval);
+			tempfree(y);
+		}
+		return(jret);
+	case NEXT:
+		return(jnext);
+	case NEXTFILE:
+		nextfile();
+		return(jnextfile);
+	case BREAK:
+		return(jbreak);
+	case CONTINUE:
+		return(jcont);
+	default:	/* can't happen */
+		FATAL("illegal jump type %d", n);
+	}
+	return 0;	/* not reached */
+}
+
+Cell *getline(Node **a, int n)	/* get next line from specific input */
+{		/* a[0] is variable, a[1] is operator, a[2] is filename */
+	Cell *r, *x;
+	extern Cell **fldtab;
+	FILE *fp;
+	char *buf;
+	int bufsize = recsize;
+	int mode;
+
+	if ((buf = (char *) malloc(bufsize)) == NULL)
+		FATAL("out of memory in getline");
+
+	fflush(stdout);	/* in case someone is waiting for a prompt */
+	r = gettemp();
+	if (a[1] != NULL) {		/* getline < file */
+		x = execute(a[2]);		/* filename */
+		mode = ptoi(a[1]);
+		if (mode == '|')		/* input pipe */
+			mode = LE;	/* arbitrary flag */
+		fp = openfile(mode, getsval(x));
+		tempfree(x);
+		if (fp == NULL)
+			n = -1;
+		else
+			n = readrec(&buf, &bufsize, fp);
+		if (n <= 0) {
+			;
+		} else if (a[0] != NULL) {	/* getline var <file */
+			x = execute(a[0]);
+			setsval(x, buf);
+			tempfree(x);
+		} else {			/* getline <file */
+			setsval(fldtab[0], buf);
+			if (is_number(fldtab[0]->sval)) {
+				fldtab[0]->fval = atof(fldtab[0]->sval);
+				fldtab[0]->tval |= NUM;
+			}
+		}
+	} else {			/* bare getline; use current input */
+		if (a[0] == NULL)	/* getline */
+			n = getrec(&record, &recsize, 1);
+		else {			/* getline var */
+			n = getrec(&buf, &bufsize, 0);
+			x = execute(a[0]);
+			setsval(x, buf);
+			tempfree(x);
+		}
+	}
+	setfval(r, (Awkfloat) n);
+	free(buf);
+	return r;
+}
+
+Cell *getnf(Node **a, int n)	/* get NF */
+{
+	if (donefld == 0)
+		fldbld();
+	return (Cell *) a[0];
+}
+
+Cell *array(Node **a, int n)	/* a[0] is symtab, a[1] is list of subscripts */
+{
+	Cell *x, *y, *z;
+	char *s;
+	Node *np;
+	char *buf;
+	int bufsz = recsize;
+	int nsub = strlen(*SUBSEP);
+
+	if ((buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of memory in array");
+
+	x = execute(a[0]);	/* Cell* for symbol table */
+	buf[0] = 0;
+	for (np = a[1]; np; np = np->nnext) {
+		y = execute(np);	/* subscript */
+		s = getsval(y);
+		if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+			FATAL("out of memory for %s[%s...]", x->nval, buf);
+		strcat(buf, s);
+		if (np->nnext)
+			strcat(buf, *SUBSEP);
+		tempfree(y);
+	}
+	if (!isarr(x)) {
+		   dprintf( ("making %s into an array\n", x->nval) );
+		if (freeable(x))
+			xfree(x->sval);
+		x->tval &= ~(STR|NUM|DONTFREE);
+		x->tval |= ARR;
+		x->sval = (char *) makesymtab(NSYMTAB);
+	}
+	z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
+	z->ctype = OCELL;
+	z->csub = CVAR;
+	tempfree(x);
+	free(buf);
+	return(z);
+}
+
+Cell *awkdelete(Node **a, int n)	/* a[0] is symtab, a[1] is list of subscripts */
+{
+	Cell *x, *y;
+	Node *np;
+	char *s;
+	int nsub = strlen(*SUBSEP);
+
+	x = execute(a[0]);	/* Cell* for symbol table */
+	if (!isarr(x))
+		return True;
+	if (a[1] == 0) {	/* delete the elements, not the table */
+		freesymtab(x);
+		x->tval &= ~STR;
+		x->tval |= ARR;
+		x->sval = (char *) makesymtab(NSYMTAB);
+	} else {
+		int bufsz = recsize;
+		char *buf;
+		if ((buf = (char *) malloc(bufsz)) == NULL)
+			FATAL("out of memory in adelete");
+		buf[0] = 0;
+		for (np = a[1]; np; np = np->nnext) {
+			y = execute(np);	/* subscript */
+			s = getsval(y);
+			if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+				FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+			strcat(buf, s);	
+			if (np->nnext)
+				strcat(buf, *SUBSEP);
+			tempfree(y);
+		}
+		freeelem(x, buf);
+		free(buf);
+	}
+	tempfree(x);
+	return True;
+}
+
+Cell *intest(Node **a, int n)	/* a[0] is index (list), a[1] is symtab */
+{
+	Cell *x, *ap, *k;
+	Node *p;
+	char *buf;
+	char *s;
+	int bufsz = recsize;
+	int nsub = strlen(*SUBSEP);
+
+	ap = execute(a[1]);	/* array name */
+	if (!isarr(ap)) {
+		   dprintf( ("making %s into an array\n", ap->nval) );
+		if (freeable(ap))
+			xfree(ap->sval);
+		ap->tval &= ~(STR|NUM|DONTFREE);
+		ap->tval |= ARR;
+		ap->sval = (char *) makesymtab(NSYMTAB);
+	}
+	if ((buf = (char *) malloc(bufsz)) == NULL) {
+		FATAL("out of memory in intest");
+	}
+	buf[0] = 0;
+	for (p = a[0]; p; p = p->nnext) {
+		x = execute(p);	/* expr */
+		s = getsval(x);
+		if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+			FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+		strcat(buf, s);
+		tempfree(x);
+		if (p->nnext)
+			strcat(buf, *SUBSEP);
+	}
+	k = lookup(buf, (Array *) ap->sval);
+	tempfree(ap);
+	free(buf);
+	if (k == NULL)
+		return(False);
+	else
+		return(True);
+}
+
+
+Cell *matchop(Node **a, int n)	/* ~ and match() */
+{
+	Cell *x, *y;
+	char *s, *t;
+	int i;
+	fa *pfa;
+	int (*mf)(fa *, char *) = match, mode = 0;
+
+	if (n == MATCHFCN) {
+		mf = pmatch;
+		mode = 1;
+	}
+	x = execute(a[1]);	/* a[1] = target text */
+	s = getsval(x);
+	if (a[0] == 0)		/* a[1] == 0: already-compiled reg expr */
+		i = (*mf)((fa *) a[2], s);
+	else {
+		y = execute(a[2]);	/* a[2] = regular expr */
+		t = getsval(y);
+		pfa = makedfa(t, mode);
+		i = (*mf)(pfa, s);
+		tempfree(y);
+	}
+	tempfree(x);
+	if (n == MATCHFCN) {
+		int start = patbeg - s + 1;
+		if (patlen < 0)
+			start = 0;
+		setfval(rstartloc, (Awkfloat) start);
+		setfval(rlengthloc, (Awkfloat) patlen);
+		x = gettemp();
+		x->tval = NUM;
+		x->fval = start;
+		return x;
+	} else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
+		return(True);
+	else
+		return(False);
+}
+
+
+Cell *boolop(Node **a, int n)	/* a[0] || a[1], a[0] && a[1], !a[0] */
+{
+	Cell *x, *y;
+	int i;
+
+	x = execute(a[0]);
+	i = istrue(x);
+	tempfree(x);
+	switch (n) {
+	case BOR:
+		if (i) return(True);
+		y = execute(a[1]);
+		i = istrue(y);
+		tempfree(y);
+		if (i) return(True);
+		else return(False);
+	case AND:
+		if ( !i ) return(False);
+		y = execute(a[1]);
+		i = istrue(y);
+		tempfree(y);
+		if (i) return(True);
+		else return(False);
+	case NOT:
+		if (i) return(False);
+		else return(True);
+	default:	/* can't happen */
+		FATAL("unknown boolean operator %d", n);
+	}
+	return 0;	/*NOTREACHED*/
+}
+
+Cell *relop(Node **a, int n)	/* a[0 < a[1], etc. */
+{
+	int i;
+	Cell *x, *y;
+	Awkfloat j;
+
+	x = execute(a[0]);
+	y = execute(a[1]);
+	if (x->tval&NUM && y->tval&NUM) {
+		j = x->fval - y->fval;
+		i = j<0? -1: (j>0? 1: 0);
+	} else {
+		i = strcmp(getsval(x), getsval(y));
+	}
+	tempfree(x);
+	tempfree(y);
+	switch (n) {
+	case LT:	if (i<0) return(True);
+			else return(False);
+	case LE:	if (i<=0) return(True);
+			else return(False);
+	case NE:	if (i!=0) return(True);
+			else return(False);
+	case EQ:	if (i == 0) return(True);
+			else return(False);
+	case GE:	if (i>=0) return(True);
+			else return(False);
+	case GT:	if (i>0) return(True);
+			else return(False);
+	default:	/* can't happen */
+		FATAL("unknown relational operator %d", n);
+	}
+	return 0;	/*NOTREACHED*/
+}
+
+void tfree(Cell *a)	/* free a tempcell */
+{
+	if (freeable(a)) {
+		   dprintf( ("freeing %s %s %o\n", a->nval, a->sval, a->tval) );
+		xfree(a->sval);
+	}
+	if (a == tmps)
+		FATAL("tempcell list is curdled");
+	a->cnext = tmps;
+	tmps = a;
+}
+
+Cell *gettemp(void)	/* get a tempcell */
+{	int i;
+	Cell *x;
+
+	if (!tmps) {
+		tmps = (Cell *) calloc(100, sizeof(Cell));
+		if (!tmps)
+			FATAL("out of space for temporaries");
+		for(i = 1; i < 100; i++)
+			tmps[i-1].cnext = &tmps[i];
+		tmps[i-1].cnext = 0;
+	}
+	x = tmps;
+	tmps = x->cnext;
+	*x = tempcell;
+	return(x);
+}
+
+Cell *indirect(Node **a, int n)	/* $( a[0] ) */
+{
+	Cell *x;
+	int m;
+	char *s;
+
+	x = execute(a[0]);
+	m = (int) getfval(x);
+	if (m == 0 && !is_number(s = getsval(x)))	/* suspicion! */
+		FATAL("illegal field $(%s), name \"%s\"", s, x->nval);
+		/* BUG: can x->nval ever be null??? */
+	tempfree(x);
+	x = fieldadr(m);
+	x->ctype = OCELL;	/* BUG?  why are these needed? */
+	x->csub = CFLD;
+	return(x);
+}
+
+Cell *substr(Node **a, int nnn)		/* substr(a[0], a[1], a[2]) */
+{
+	int k, m, n;
+	char *s;
+	int temp;
+	Cell *x, *y, *z = 0;
+
+	x = execute(a[0]);
+	y = execute(a[1]);
+	if (a[2] != 0)
+		z = execute(a[2]);
+	s = getsval(x);
+	k = strlen(s) + 1;
+	if (k <= 1) {
+		tempfree(x);
+		tempfree(y);
+		if (a[2] != 0) {
+			tempfree(z);
+		}
+		x = gettemp();
+		setsval(x, "");
+		return(x);
+	}
+	m = (int) getfval(y);
+	if (m <= 0)
+		m = 1;
+	else if (m > k)
+		m = k;
+	tempfree(y);
+	if (a[2] != 0) {
+		n = (int) getfval(z);
+		tempfree(z);
+	} else
+		n = k - 1;
+	if (n < 0)
+		n = 0;
+	else if (n > k - m)
+		n = k - m;
+	   dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) );
+	y = gettemp();
+	temp = s[n+m-1];	/* with thanks to John Linderman */
+	s[n+m-1] = '\0';
+	setsval(y, s + m - 1);
+	s[n+m-1] = temp;
+	tempfree(x);
+	return(y);
+}
+
+Cell *sindex(Node **a, int nnn)		/* index(a[0], a[1]) */
+{
+	Cell *x, *y, *z;
+	char *s1, *s2, *p1, *p2, *q;
+	Awkfloat v = 0.0;
+
+	x = execute(a[0]);
+	s1 = getsval(x);
+	y = execute(a[1]);
+	s2 = getsval(y);
+
+	z = gettemp();
+	for (p1 = s1; *p1 != '\0'; p1++) {
+		for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++)
+			;
+		if (*p2 == '\0') {
+			v = (Awkfloat) (p1 - s1 + 1);	/* origin 1 */
+			break;
+		}
+	}
+	tempfree(x);
+	tempfree(y);
+	setfval(z, v);
+	return(z);
+}
+
+#define	MAXNUMSIZE	50
+
+int format(char **pbuf, int *pbufsize, char *s, Node *a)	/* printf-like conversions */
+{
+	char *fmt;
+	char *p, *t, *os;
+	Cell *x;
+	int flag = 0, n;
+	int fmtwd; /* format width */
+	int fmtsz = recsize;
+	char *buf = *pbuf;
+	int bufsize = *pbufsize;
+
+	os = s;
+	p = buf;
+	if ((fmt = (char *) malloc(fmtsz)) == NULL)
+		FATAL("out of memory in format()");
+	while (*s) {
+		adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format");
+		if (*s != '%') {
+			*p++ = *s++;
+			continue;
+		}
+		if (*(s+1) == '%') {
+			*p++ = '%';
+			s += 2;
+			continue;
+		}
+		/* have to be real careful in case this is a huge number, eg, %100000d */
+		fmtwd = atoi(s+1);
+		if (fmtwd < 0)
+			fmtwd = -fmtwd;
+		adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+		for (t = fmt; (*t++ = *s) != '\0'; s++) {
+			if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, 0))
+				FATAL("format item %.30s... ran format() out of memory", os);
+			if (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L')
+				break;	/* the ansi panoply */
+			if (*s == '*') {
+				x = execute(a);
+				a = a->nnext;
+				sprintf(t-1, "%d", fmtwd=(int) getfval(x));
+				if (fmtwd < 0)
+					fmtwd = -fmtwd;
+				adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+				t = fmt + strlen(fmt);
+				tempfree(x);
+			}
+		}
+		*t = '\0';
+		if (fmtwd < 0)
+			fmtwd = -fmtwd;
+		adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+
+		switch (*s) {
+		case 'f': case 'e': case 'g': case 'E': case 'G':
+			flag = 1;
+			break;
+		case 'd': case 'i':
+			flag = 2;
+			if(*(s-1) == 'l') break;
+			*(t-1) = 'l';
+			*t = 'd';
+			*++t = '\0';
+			break;
+		case 'o': case 'x': case 'X': case 'u':
+			flag = *(s-1) == 'l' ? 2 : 3;
+			break;
+		case 's':
+			flag = 4;
+			break;
+		case 'c':
+			flag = 5;
+			break;
+		default:
+			WARNING("weird printf conversion %s", fmt);
+			flag = 0;
+			break;
+		}
+		if (a == NULL)
+			FATAL("not enough args in printf(%s)", os);
+		x = execute(a);
+		a = a->nnext;
+		n = MAXNUMSIZE;
+		if (fmtwd > n)
+			n = fmtwd;
+		adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format");
+		switch (flag) {
+		case 0:	sprintf(p, "%s", fmt);	/* unknown, so dump it too */
+			t = getsval(x);
+			n = strlen(t);
+			if (fmtwd > n)
+				n = fmtwd;
+			adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format");
+			p += strlen(p);
+			sprintf(p, "%s", t);
+			break;
+		case 1:	sprintf(p, fmt, getfval(x)); break;
+		case 2:	sprintf(p, fmt, (long) getfval(x)); break;
+		case 3:	sprintf(p, fmt, (int) getfval(x)); break;
+		case 4:
+			t = getsval(x);
+			n = strlen(t);
+			if (fmtwd > n)
+				n = fmtwd;
+			if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, 0))
+				FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t);
+			sprintf(p, fmt, t);
+			break;
+		case 5:
+			if (isnum(x)) {
+				if (getfval(x))
+					sprintf(p, fmt, (int) getfval(x));
+				else
+					*p++ = '\0';
+			} else
+				sprintf(p, fmt, getsval(x)[0]);
+			break;
+		}
+		tempfree(x);
+		p += strlen(p);
+		s++;
+	}
+	*p = '\0';
+	free(fmt);
+	for ( ; a; a = a->nnext)		/* evaluate any remaining args */
+		execute(a);
+	*pbuf = buf;
+	*pbufsize = bufsize;
+	return p - buf;
+}
+
+Cell *awksprintf(Node **a, int n)		/* sprintf(a[0]) */
+{
+	Cell *x;
+	Node *y;
+	char *buf;
+	int bufsz=3*recsize;
+
+	if ((buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of memory in awksprintf");
+	y = a[0]->nnext;
+	x = execute(a[0]);
+	if (format(&buf, &bufsz, getsval(x), y) == -1)
+		FATAL("sprintf string %.30s... too long.  can't happen.", buf);
+	tempfree(x);
+	x = gettemp();
+	x->sval = buf;
+	x->tval = STR;
+	return(x);
+}
+
+Cell *awkprintf(Node **a, int n)		/* printf */
+{	/* a[0] is list of args, starting with format string */
+	/* a[1] is redirection operator, a[2] is redirection file */
+	FILE *fp;
+	Cell *x;
+	Node *y;
+	char *buf;
+	int len;
+	int bufsz=3*recsize;
+
+	if ((buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of memory in awkprintf");
+	y = a[0]->nnext;
+	x = execute(a[0]);
+	if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
+		FATAL("printf string %.30s... too long.  can't happen.", buf);
+	tempfree(x);
+	if (a[1] == NULL) {
+		/* fputs(buf, stdout); */
+		fwrite(buf, len, 1, stdout);
+		if (ferror(stdout))
+			FATAL("write error on stdout");
+	} else {
+		fp = redirect(ptoi(a[1]), a[2]);
+		/* fputs(buf, fp); */
+		fwrite(buf, len, 1, fp);
+		fflush(fp);
+		if (ferror(fp))
+			FATAL("write error on %s", filename(fp));
+	}
+	free(buf);
+	return(True);
+}
+
+Cell *arith(Node **a, int n)	/* a[0] + a[1], etc.  also -a[0] */
+{
+	Awkfloat i, j = 0;
+	double v;
+	Cell *x, *y, *z;
+
+	x = execute(a[0]);
+	i = getfval(x);
+	tempfree(x);
+	if (n != UMINUS) {
+		y = execute(a[1]);
+		j = getfval(y);
+		tempfree(y);
+	}
+	z = gettemp();
+	switch (n) {
+	case ADD:
+		i += j;
+		break;
+	case MINUS:
+		i -= j;
+		break;
+	case MULT:
+		i *= j;
+		break;
+	case DIVIDE:
+		if (j == 0)
+			FATAL("division by zero");
+		i /= j;
+		break;
+	case MOD:
+		if (j == 0)
+			FATAL("division by zero in mod");
+		modf(i/j, &v);
+		i = i - j * v;
+		break;
+	case UMINUS:
+		i = -i;
+		break;
+	case POWER:
+		if (j >= 0 && modf(j, &v) == 0.0)	/* pos integer exponent */
+			i = ipow(i, (int) j);
+		else
+			i = errcheck(pow(i, j), "pow");
+		break;
+	default:	/* can't happen */
+		FATAL("illegal arithmetic operator %d", n);
+	}
+	setfval(z, i);
+	return(z);
+}
+
+double ipow(double x, int n)	/* x**n.  ought to be done by pow, but isn't always */
+{
+	double v;
+
+	if (n <= 0)
+		return 1;
+	v = ipow(x, n/2);
+	if (n % 2 == 0)
+		return v * v;
+	else
+		return x * v * v;
+}
+
+Cell *incrdecr(Node **a, int n)		/* a[0]++, etc. */
+{
+	Cell *x, *z;
+	int k;
+	Awkfloat xf;
+
+	x = execute(a[0]);
+	xf = getfval(x);
+	k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
+	if (n == PREINCR || n == PREDECR) {
+		setfval(x, xf + k);
+		return(x);
+	}
+	z = gettemp();
+	setfval(z, xf);
+	setfval(x, xf + k);
+	tempfree(x);
+	return(z);
+}
+
+Cell *assign(Node **a, int n)	/* a[0] = a[1], a[0] += a[1], etc. */
+{		/* this is subtle; don't muck with it. */
+	Cell *x, *y;
+	Awkfloat xf, yf;
+	double v;
+
+	y = execute(a[1]);
+	x = execute(a[0]);
+	if (n == ASSIGN) {	/* ordinary assignment */
+		if (x == y && !(x->tval & (FLD|REC)))	/* self-assignment: */
+			;		/* leave alone unless it's a field */
+		else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+			setsval(x, getsval(y));
+			x->fval = getfval(y);
+			x->tval |= NUM;
+		}
+		else if (isstr(y))
+			setsval(x, getsval(y));
+		else if (isnum(y))
+			setfval(x, getfval(y));
+		else
+			funnyvar(y, "read value of");
+		tempfree(y);
+		return(x);
+	}
+	xf = getfval(x);
+	yf = getfval(y);
+	switch (n) {
+	case ADDEQ:
+		xf += yf;
+		break;
+	case SUBEQ:
+		xf -= yf;
+		break;
+	case MULTEQ:
+		xf *= yf;
+		break;
+	case DIVEQ:
+		if (yf == 0)
+			FATAL("division by zero in /=");
+		xf /= yf;
+		break;
+	case MODEQ:
+		if (yf == 0)
+			FATAL("division by zero in %%=");
+		modf(xf/yf, &v);
+		xf = xf - yf * v;
+		break;
+	case POWEQ:
+		if (yf >= 0 && modf(yf, &v) == 0.0)	/* pos integer exponent */
+			xf = ipow(xf, (int) yf);
+		else
+			xf = errcheck(pow(xf, yf), "pow");
+		break;
+	default:
+		FATAL("illegal assignment operator %d", n);
+		break;
+	}
+	tempfree(y);
+	setfval(x, xf);
+	return(x);
+}
+
+Cell *cat(Node **a, int q)	/* a[0] cat a[1] */
+{
+	Cell *x, *y, *z;
+	int n1, n2;
+	char *s;
+
+	x = execute(a[0]);
+	y = execute(a[1]);
+	getsval(x);
+	getsval(y);
+	n1 = strlen(x->sval);
+	n2 = strlen(y->sval);
+	s = (char *) malloc(n1 + n2 + 1);
+	if (s == NULL)
+		FATAL("out of space concatenating %.15s... and %.15s...",
+			x->sval, y->sval);
+	strcpy(s, x->sval);
+	strcpy(s+n1, y->sval);
+	tempfree(y);
+	z = gettemp();
+	z->sval = s;
+	z->tval = STR;
+	tempfree(x);
+	return(z);
+}
+
+Cell *pastat(Node **a, int n)	/* a[0] { a[1] } */
+{
+	Cell *x;
+
+	if (a[0] == 0)
+		x = execute(a[1]);
+	else {
+		x = execute(a[0]);
+		if (istrue(x)) {
+			tempfree(x);
+			x = execute(a[1]);
+		}
+	}
+	return x;
+}
+
+Cell *dopa2(Node **a, int n)	/* a[0], a[1] { a[2] } */
+{
+	Cell *x;
+	int pair;
+
+	pair = ptoi(a[3]);
+	if (pairstack[pair] == 0) {
+		x = execute(a[0]);
+		if (istrue(x))
+			pairstack[pair] = 1;
+		tempfree(x);
+	}
+	if (pairstack[pair] == 1) {
+		x = execute(a[1]);
+		if (istrue(x))
+			pairstack[pair] = 0;
+		tempfree(x);
+		x = execute(a[2]);
+		return(x);
+	}
+	return(False);
+}
+
+Cell *split(Node **a, int nnn)	/* split(a[0], a[1], a[2]); a[3] is type */
+{
+	Cell *x = 0, *y, *ap;
+	char *s;
+	int sep;
+	char *t, temp, num[50], *fs = 0;
+	int n, tempstat, arg3type;
+
+	y = execute(a[0]);	/* source string */
+	s = getsval(y);
+	arg3type = ptoi(a[3]);
+	if (a[2] == 0)		/* fs string */
+		fs = *FS;
+	else if (arg3type == STRING) {	/* split(str,arr,"string") */
+		x = execute(a[2]);
+		fs = getsval(x);
+	} else if (arg3type == REGEXPR)
+		fs = "(regexpr)";	/* split(str,arr,/regexpr/) */
+	else
+		FATAL("illegal type of split");
+	sep = *fs;
+	ap = execute(a[1]);	/* array name */
+	n = y->tval;
+	y->tval |= DONTFREE;	/* split(a[x], a); */
+	freesymtab(ap);
+	y->tval = n;
+	   dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, ap->nval, fs) );
+	ap->tval &= ~STR;
+	ap->tval |= ARR;
+	ap->sval = (char *) makesymtab(NSYMTAB);
+
+	n = 0;
+	if ((*s != '\0' && strlen(fs) > 1) || arg3type == REGEXPR) {	/* reg expr */
+		fa *pfa;
+		if (arg3type == REGEXPR) {	/* it's ready already */
+			pfa = (fa *) a[2];
+		} else {
+			pfa = makedfa(fs, 1);
+		}
+		if (nematch(pfa,s)) {
+			tempstat = pfa->initstat;
+			pfa->initstat = 2;
+			do {
+				n++;
+				sprintf(num, "%d", n);
+				temp = *patbeg;
+				*patbeg = '\0';
+				if (is_number(s))
+					setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
+				else
+					setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
+				*patbeg = temp;
+				s = patbeg + patlen;
+				if (*(patbeg+patlen-1) == 0 || *s == 0) {
+					n++;
+					sprintf(num, "%d", n);
+					setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
+					pfa->initstat = tempstat;
+					goto spdone;
+				}
+			} while (nematch(pfa,s));
+		}
+		n++;
+		sprintf(num, "%d", n);
+		if (is_number(s))
+			setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
+		else
+			setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
+  spdone:
+		pfa = NULL;
+	} else if (sep == ' ') {
+		for (n = 0; ; ) {
+			while (*s == ' ' || *s == '\t' || *s == '\n')
+				s++;
+			if (*s == 0)
+				break;
+			n++;
+			t = s;
+			do
+				s++;
+			while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0');
+			temp = *s;
+			*s = '\0';
+			sprintf(num, "%d", n);
+			if (is_number(t))
+				setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+			else
+				setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+			*s = temp;
+			if (*s != 0)
+				s++;
+		}
+	} else if (sep == 0) {	/* new: split(s, a, "") => 1 char/elem */
+		for (n = 0; *s != 0; s++) {
+			char buf[2];
+			n++;
+			sprintf(num, "%d", n);
+			buf[0] = *s;
+			buf[1] = 0;
+			if (isdigit((uschar)buf[0]))
+				setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
+			else
+				setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
+		}
+	} else if (*s != 0) {
+		for (;;) {
+			n++;
+			t = s;
+			while (*s != sep && *s != '\n' && *s != '\0')
+				s++;
+			temp = *s;
+			*s = '\0';
+			sprintf(num, "%d", n);
+			if (is_number(t))
+				setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+			else
+				setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+			*s = temp;
+			if (*s++ == 0)
+				break;
+		}
+	}
+	tempfree(ap);
+	tempfree(y);
+	if (a[2] != 0 && arg3type == STRING) {
+		tempfree(x);
+	}
+	x = gettemp();
+	x->tval = NUM;
+	x->fval = n;
+	return(x);
+}
+
+Cell *condexpr(Node **a, int n)	/* a[0] ? a[1] : a[2] */
+{
+	Cell *x;
+
+	x = execute(a[0]);
+	if (istrue(x)) {
+		tempfree(x);
+		x = execute(a[1]);
+	} else {
+		tempfree(x);
+		x = execute(a[2]);
+	}
+	return(x);
+}
+
+Cell *ifstat(Node **a, int n)	/* if (a[0]) a[1]; else a[2] */
+{
+	Cell *x;
+
+	x = execute(a[0]);
+	if (istrue(x)) {
+		tempfree(x);
+		x = execute(a[1]);
+	} else if (a[2] != 0) {
+		tempfree(x);
+		x = execute(a[2]);
+	}
+	return(x);
+}
+
+Cell *whilestat(Node **a, int n)	/* while (a[0]) a[1] */
+{
+	Cell *x;
+
+	for (;;) {
+		x = execute(a[0]);
+		if (!istrue(x))
+			return(x);
+		tempfree(x);
+		x = execute(a[1]);
+		if (isbreak(x)) {
+			x = True;
+			return(x);
+		}
+		if (isnext(x) || isexit(x) || isret(x))
+			return(x);
+		tempfree(x);
+	}
+}
+
+Cell *dostat(Node **a, int n)	/* do a[0]; while(a[1]) */
+{
+	Cell *x;
+
+	for (;;) {
+		x = execute(a[0]);
+		if (isbreak(x))
+			return True;
+		if (isnext(x) || isexit(x) || isret(x))
+			return(x);
+		tempfree(x);
+		x = execute(a[1]);
+		if (!istrue(x))
+			return(x);
+		tempfree(x);
+	}
+}
+
+Cell *forstat(Node **a, int n)	/* for (a[0]; a[1]; a[2]) a[3] */
+{
+	Cell *x;
+
+	x = execute(a[0]);
+	tempfree(x);
+	for (;;) {
+		if (a[1]!=0) {
+			x = execute(a[1]);
+			if (!istrue(x)) return(x);
+			else tempfree(x);
+		}
+		x = execute(a[3]);
+		if (isbreak(x))		/* turn off break */
+			return True;
+		if (isnext(x) || isexit(x) || isret(x))
+			return(x);
+		tempfree(x);
+		x = execute(a[2]);
+		tempfree(x);
+	}
+}
+
+Cell *instat(Node **a, int n)	/* for (a[0] in a[1]) a[2] */
+{
+	Cell *x, *vp, *arrayp, *cp, *ncp;
+	Array *tp;
+	int i;
+
+	vp = execute(a[0]);
+	arrayp = execute(a[1]);
+	if (!isarr(arrayp)) {
+		return True;
+	}
+	tp = (Array *) arrayp->sval;
+	tempfree(arrayp);
+	for (i = 0; i < tp->size; i++) {	/* this routine knows too much */
+		for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
+			setsval(vp, cp->nval);
+			ncp = cp->cnext;
+			x = execute(a[2]);
+			if (isbreak(x)) {
+				tempfree(vp);
+				return True;
+			}
+			if (isnext(x) || isexit(x) || isret(x)) {
+				tempfree(vp);
+				return(x);
+			}
+			tempfree(x);
+		}
+	}
+	return True;
+}
+
+Cell *bltin(Node **a, int n)	/* builtin functions. a[0] is type, a[1] is arg list */
+{
+	Cell *x, *y;
+	Awkfloat u;
+	int t;
+	char *p, *buf;
+	Node *nextarg;
+	FILE *fp;
+
+	t = ptoi(a[0]);
+	x = execute(a[1]);
+	nextarg = a[1]->nnext;
+	switch (t) {
+	case FLENGTH:
+		u = strlen(getsval(x)); break;
+	case FLOG:
+		u = errcheck(log(getfval(x)), "log"); break;
+	case FINT:
+		modf(getfval(x), &u); break;
+	case FEXP:
+		u = errcheck(exp(getfval(x)), "exp"); break;
+	case FSQRT:
+		u = errcheck(sqrt(getfval(x)), "sqrt"); break;
+	case FSIN:
+		u = sin(getfval(x)); break;
+	case FCOS:
+		u = cos(getfval(x)); break;
+	case FATAN:
+		if (nextarg == 0) {
+			WARNING("atan2 requires two arguments; returning 1.0");
+			u = 1.0;
+		} else {
+			y = execute(a[1]->nnext);
+			u = atan2(getfval(x), getfval(y));
+			tempfree(y);
+			nextarg = nextarg->nnext;
+		}
+		break;
+	case FSYSTEM:
+		fflush(stdout);		/* in case something is buffered already */
+		u = (Awkfloat) system(getsval(x)) / 256;   /* 256 is unix-dep */
+		break;
+	case FRAND:
+		/* in principle, rand() returns something in 0..RAND_MAX */
+		u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
+		break;
+	case FSRAND:
+		if (isrec(x))	/* no argument provided */
+			u = time((time_t *)0);
+		else
+			u = getfval(x);
+		srand((unsigned int) u);
+		break;
+	case FTOUPPER:
+	case FTOLOWER:
+		buf = tostring(getsval(x));
+		if (t == FTOUPPER) {
+			for (p = buf; *p; p++)
+				if (islower((uschar) *p))
+					*p = toupper(*p);
+		} else {
+			for (p = buf; *p; p++)
+				if (isupper((uschar) *p))
+					*p = tolower(*p);
+		}
+		tempfree(x);
+		x = gettemp();
+		setsval(x, buf);
+		free(buf);
+		return x;
+	case FFLUSH:
+		if ((fp = openfile(FFLUSH, getsval(x))) == NULL)
+			u = EOF;
+		else
+			u = fflush(fp);
+		break;
+	default:	/* can't happen */
+		FATAL("illegal function type %d", t);
+		break;
+	}
+	tempfree(x);
+	x = gettemp();
+	setfval(x, u);
+	if (nextarg != 0) {
+		WARNING("warning: function has too many arguments");
+		for ( ; nextarg; nextarg = nextarg->nnext)
+			execute(nextarg);
+	}
+	return(x);
+}
+
+Cell *printstat(Node **a, int n)	/* print a[0] */
+{
+	Node *x;
+	Cell *y;
+	FILE *fp;
+
+	if (a[1] == 0)	/* a[1] is redirection operator, a[2] is file */
+		fp = stdout;
+	else
+		fp = redirect(ptoi(a[1]), a[2]);
+	for (x = a[0]; x != NULL; x = x->nnext) {
+		y = execute(x);
+		fputs(getsval(y), fp);
+		tempfree(y);
+		if (x->nnext == NULL)
+			fputs(*ORS, fp);
+		else
+			fputs(*OFS, fp);
+	}
+	if (a[1] != 0)
+		fflush(fp);
+	if (ferror(fp))
+		FATAL("write error on %s", filename(fp));
+	return(True);
+}
+
+Cell *nullproc(Node **a, int n)
+{
+	n = n;
+	a = a;
+	return 0;
+}
+
+
+FILE *redirect(int a, Node *b)	/* set up all i/o redirections */
+{
+	FILE *fp;
+	Cell *x;
+	char *fname;
+
+	x = execute(b);
+	fname = getsval(x);
+	fp = openfile(a, fname);
+	if (fp == NULL)
+		FATAL("can't open file %s", fname);
+	tempfree(x);
+	return fp;
+}
+
+struct files {
+	FILE	*fp;
+	char	*fname;
+	int	mode;	/* '|', 'a', 'w' => LE/LT, GT */
+} files[FOPEN_MAX] ={
+	{ NULL,  "/dev/stdin",  LT },	/* watch out: don't free this! */
+	{ NULL, "/dev/stdout", GT },
+	{ NULL, "/dev/stderr", GT }
+};
+
+void stdinit(void)	/* in case stdin, etc., are not constants */
+{
+	files[0].fp = stdin;
+	files[1].fp = stdout;
+	files[2].fp = stderr;
+}
+
+FILE *openfile(int a, char *us)
+{
+	char *s = us;
+	int i, m;
+	FILE *fp = 0;
+
+	if (*s == '\0')
+		FATAL("null file name in print or getline");
+	for (i=0; i < FOPEN_MAX; i++)
+		if (files[i].fname && strcmp(s, files[i].fname) == 0) {
+			if (a == files[i].mode || (a==APPEND && files[i].mode==GT))
+				return files[i].fp;
+			if (a == FFLUSH)
+				return files[i].fp;
+		}
+	if (a == FFLUSH)	/* didn't find it, so don't create it! */
+		return NULL;
+
+	for (i=0; i < FOPEN_MAX; i++)
+		if (files[i].fp == 0)
+			break;
+	if (i >= FOPEN_MAX)
+		FATAL("%s makes too many open files", s);
+	fflush(stdout);	/* force a semblance of order */
+	m = a;
+	if (a == GT) {
+		fp = fopen(s, "w");
+	} else if (a == APPEND) {
+		fp = fopen(s, "a");
+		m = GT;	/* so can mix > and >> */
+	} else if (a == '|') {	/* output pipe */
+		fp = popen(s, "w");
+	} else if (a == LE) {	/* input pipe */
+		fp = popen(s, "r");
+	} else if (a == LT) {	/* getline <file */
+		fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r");	/* "-" is stdin */
+	} else	/* can't happen */
+		FATAL("illegal redirection %d", a);
+	if (fp != NULL) {
+		files[i].fname = tostring(s);
+		files[i].fp = fp;
+		files[i].mode = m;
+	}
+	return fp;
+}
+
+char *filename(FILE *fp)
+{
+	int i;
+
+	for (i = 0; i < FOPEN_MAX; i++)
+		if (fp == files[i].fp)
+			return files[i].fname;
+	return "?";
+}
+
+Cell *closefile(Node **a, int n)
+{
+	Cell *x;
+	int i, stat;
+
+	n = n;
+	x = execute(a[0]);
+	getsval(x);
+	stat = -1;
+	for (i = 0; i < FOPEN_MAX; i++) {
+		if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
+			if (ferror(files[i].fp))
+				WARNING( "i/o error occurred on %s", files[i].fname );
+			if (files[i].mode == '|' || files[i].mode == LE)
+				stat = pclose(files[i].fp);
+			else
+				stat = fclose(files[i].fp);
+			if (stat == EOF)
+				WARNING( "i/o error occurred closing %s", files[i].fname );
+			if (i > 2)	/* don't do /dev/std... */
+				xfree(files[i].fname);
+			files[i].fname = NULL;	/* watch out for ref thru this */
+			files[i].fp = NULL;
+		}
+	}
+	tempfree(x);
+	x = gettemp();
+	setfval(x, (Awkfloat) stat);
+	return(x);
+}
+
+void closeall(void)
+{
+	int i, stat;
+
+	for (i = 0; i < FOPEN_MAX; i++) {
+		if (files[i].fp) {
+			if (ferror(files[i].fp))
+				WARNING( "i/o error occurred on %s", files[i].fname );
+			if (files[i].mode == '|' || files[i].mode == LE)
+				stat = pclose(files[i].fp);
+			else
+				stat = fclose(files[i].fp);
+			if (stat == EOF && i != 0)
+				WARNING( "i/o error occurred while closing %s", files[i].fname );
+		}
+	}
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr);
+
+Cell *sub(Node **a, int nnn)	/* substitute command */
+{
+	char *sptr, *pb, *q;
+	Cell *x, *y, *result;
+	char *t, *buf;
+	fa *pfa;
+	int bufsz = recsize;
+
+	if ((buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of memory in sub");
+	x = execute(a[3]);	/* target string */
+	t = getsval(x);
+	if (a[0] == 0)		/* 0 => a[1] is already-compiled regexpr */
+		pfa = (fa *) a[1];	/* regular expression */
+	else {
+		y = execute(a[1]);
+		pfa = makedfa(getsval(y), 1);
+		tempfree(y);
+	}
+	y = execute(a[2]);	/* replacement string */
+	result = False;
+	if (pmatch(pfa, t)) {
+		sptr = t;
+		adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub");
+		pb = buf;
+		while (sptr < patbeg)
+			*pb++ = *sptr++;
+		sptr = getsval(y);
+		while (*sptr != 0) {
+			adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub");
+			if (*sptr == '\\') {
+				backsub(&pb, &sptr);
+			} else if (*sptr == '&') {
+				sptr++;
+				adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub");
+				for (q = patbeg; q < patbeg+patlen; )
+					*pb++ = *q++;
+			} else
+				*pb++ = *sptr++;
+		}
+		*pb = '\0';
+		if (pb > buf + bufsz)
+			FATAL("sub result1 %.30s too big; can't happen", buf);
+		sptr = patbeg + patlen;
+		if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
+			adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub");
+			while ((*pb++ = *sptr++) != 0)
+				;
+		}
+		if (pb > buf + bufsz)
+			FATAL("sub result2 %.30s too big; can't happen", buf);
+		setsval(x, buf);	/* BUG: should be able to avoid copy */
+		result = True;;
+	}
+	tempfree(x);
+	tempfree(y);
+	free(buf);
+	return result;
+}
+
+Cell *gsub(Node **a, int nnn)	/* global substitute */
+{
+	Cell *x, *y;
+	char *rptr, *sptr, *t, *pb, *q;
+	char *buf;
+	fa *pfa;
+	int mflag, tempstat, num;
+	int bufsz = recsize;
+
+	if ((buf = (char *) malloc(bufsz)) == NULL)
+		FATAL("out of memory in gsub");
+	mflag = 0;	/* if mflag == 0, can replace empty string */
+	num = 0;
+	x = execute(a[3]);	/* target string */
+	t = getsval(x);
+	if (a[0] == 0)		/* 0 => a[1] is already-compiled regexpr */
+		pfa = (fa *) a[1];	/* regular expression */
+	else {
+		y = execute(a[1]);
+		pfa = makedfa(getsval(y), 1);
+		tempfree(y);
+	}
+	y = execute(a[2]);	/* replacement string */
+	if (pmatch(pfa, t)) {
+		tempstat = pfa->initstat;
+		pfa->initstat = 2;
+		pb = buf;
+		rptr = getsval(y);
+		do {
+			if (patlen == 0 && *patbeg != 0) {	/* matched empty string */
+				if (mflag == 0) {	/* can replace empty */
+					num++;
+					sptr = rptr;
+					while (*sptr != 0) {
+						adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+						if (*sptr == '\\') {
+							backsub(&pb, &sptr);
+						} else if (*sptr == '&') {
+							sptr++;
+							adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+							for (q = patbeg; q < patbeg+patlen; )
+								*pb++ = *q++;
+						} else
+							*pb++ = *sptr++;
+					}
+				}
+				if (*t == 0)	/* at end */
+					goto done;
+				adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub");
+				*pb++ = *t++;
+				if (pb > buf + bufsz)	/* BUG: not sure of this test */
+					FATAL("gsub result0 %.30s too big; can't happen", buf);
+				mflag = 0;
+			}
+			else {	/* matched nonempty string */
+				num++;
+				sptr = t;
+				adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub");
+				while (sptr < patbeg)
+					*pb++ = *sptr++;
+				sptr = rptr;
+				while (*sptr != 0) {
+					adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+					if (*sptr == '\\') {
+						backsub(&pb, &sptr);
+					} else if (*sptr == '&') {
+						sptr++;
+						adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+						for (q = patbeg; q < patbeg+patlen; )
+							*pb++ = *q++;
+					} else
+						*pb++ = *sptr++;
+				}
+				t = patbeg + patlen;
+				if (patlen == 0 || *t == 0 || *(t-1) == 0)
+					goto done;
+				if (pb > buf + bufsz)
+					FATAL("gsub result1 %.30s too big; can't happen", buf);
+				mflag = 1;
+			}
+		} while (pmatch(pfa,t));
+		sptr = t;
+		adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub");
+		while ((*pb++ = *sptr++) != 0)
+			;
+	done:	if (pb > buf + bufsz)
+			FATAL("gsub result2 %.30s too big; can't happen", buf);
+		*pb = '\0';
+		setsval(x, buf);	/* BUG: should be able to avoid copy + free */
+		pfa->initstat = tempstat;
+	}
+	tempfree(x);
+	tempfree(y);
+	x = gettemp();
+	x->tval = NUM;
+	x->fval = num;
+	free(buf);
+	return(x);
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr)	/* handle \\& variations */
+{						/* sptr[0] == '\\' */
+	char *pb = *pb_ptr, *sptr = *sptr_ptr;
+
+	if (sptr[1] == '\\') {
+		if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
+			*pb++ = '\\';
+			*pb++ = '&';
+			sptr += 4;
+		} else if (sptr[2] == '&') {	/* \\& -> \ + matched */
+			*pb++ = '\\';
+			sptr += 2;
+		} else {			/* \\x -> \\x */
+			*pb++ = *sptr++;
+			*pb++ = *sptr++;
+		}
+	} else if (sptr[1] == '&') {	/* literal & */
+		sptr++;
+		*pb++ = *sptr++;
+	} else				/* literal \ */
+		*pb++ = *sptr++;
+
+	*pb_ptr = pb;
+	*sptr_ptr = sptr;
+}
--- /dev/null
+++ b/utils/awk/tran.c
@@ -1,0 +1,437 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define	DEBUG
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "ytab.h"
+
+#define	FULLTAB	2	/* rehash when table gets this x full */
+#define	GROWTAB 4	/* grow table by this factor */
+
+Array	*symtab;	/* main symbol table */
+
+char	**FS;		/* initial field sep */
+char	**RS;		/* initial record sep */
+char	**OFS;		/* output field sep */
+char	**ORS;		/* output record sep */
+char	**OFMT;		/* output format for numbers */
+char	**CONVFMT;	/* format for conversions in getsval */
+Awkfloat *NF;		/* number of fields in current record */
+Awkfloat *NR;		/* number of current record */
+Awkfloat *FNR;		/* number of current record in current file */
+char	**FILENAME;	/* current filename argument */
+Awkfloat *ARGC;		/* number of arguments from command line */
+char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
+Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
+Awkfloat *RLENGTH;	/* length of same */
+
+Cell	*nrloc;		/* NR */
+Cell	*nfloc;		/* NF */
+Cell	*fnrloc;	/* FNR */
+Array	*ARGVtab;	/* symbol table containing ARGV[...] */
+Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
+Cell	*rstartloc;	/* RSTART */
+Cell	*rlengthloc;	/* RLENGTH */
+Cell	*symtabloc;	/* SYMTAB */
+
+Cell	*nullloc;	/* a guaranteed empty cell */
+Node	*nullnode;	/* zero&null, converted into a node for comparisons */
+Cell	*literal0;
+
+extern Cell **fldtab;
+
+void syminit(void)	/* initialize symbol table with builtin vars */
+{
+	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
+	/* this is used for if(x)... tests: */
+	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
+	nullnode = celltonode(nullloc, CCON);
+
+	FS = &setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab)->sval;
+	RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+	OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
+	ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
+	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
+	NF = &nfloc->fval;
+	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
+	NR = &nrloc->fval;
+	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
+	FNR = &fnrloc->fval;
+	SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
+	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
+	RSTART = &rstartloc->fval;
+	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
+	RLENGTH = &rlengthloc->fval;
+	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
+	symtabloc->sval = (char *) symtab;
+}
+
+void arginit(int ac, char **av)	/* set up ARGV and ARGC */
+{
+	Cell *cp;
+	int i;
+	char temp[50];
+
+	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
+	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
+	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
+	cp->sval = (char *) ARGVtab;
+	for (i = 0; i < ac; i++) {
+		sprintf(temp, "%d", i);
+		if (is_number(*av))
+			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
+		else
+			setsymtab(temp, *av, 0.0, STR, ARGVtab);
+		av++;
+	}
+}
+
+void envinit(char **envp)	/* set up ENVIRON variable */
+{
+	Cell *cp;
+	char *p;
+
+	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
+	ENVtab = makesymtab(NSYMTAB);
+	cp->sval = (char *) ENVtab;
+	for ( ; *envp; envp++) {
+		if ((p = strchr(*envp, '=')) == NULL)
+			continue;
+		if( p == *envp ) /* no left hand side name in env string */
+			continue;
+		*p++ = 0;	/* split into two strings at = */
+		if (is_number(p))
+			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
+		else
+			setsymtab(*envp, p, 0.0, STR, ENVtab);
+		p[-1] = '=';	/* restore in case env is passed down to a shell */
+	}
+}
+
+Array *makesymtab(int n)	/* make a new symbol table */
+{
+	Array *ap;
+	Cell **tp;
+
+	ap = (Array *) malloc(sizeof(Array));
+	tp = (Cell **) calloc(n, sizeof(Cell *));
+	if (ap == NULL || tp == NULL)
+		FATAL("out of space in makesymtab");
+	ap->nelem = 0;
+	ap->size = n;
+	ap->tab = tp;
+	return(ap);
+}
+
+void freesymtab(Cell *ap)	/* free a symbol table */
+{
+	Cell *cp, *temp;
+	Array *tp;
+	int i;
+
+	if (!isarr(ap))
+		return;
+	tp = (Array *) ap->sval;
+	if (tp == NULL)
+		return;
+	for (i = 0; i < tp->size; i++) {
+		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
+			xfree(cp->nval);
+			if (freeable(cp))
+				xfree(cp->sval);
+			temp = cp->cnext;	/* avoids freeing then using */
+			free(cp); 
+		}
+		tp->tab[i] = 0;
+	}
+	free(tp->tab);
+	free(tp);
+}
+
+void freeelem(Cell *ap, char *s)	/* free elem s from ap (i.e., ap["s"] */
+{
+	Array *tp;
+	Cell *p, *prev = NULL;
+	int h;
+	
+	tp = (Array *) ap->sval;
+	h = hash(s, tp->size);
+	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
+		if (strcmp(s, p->nval) == 0) {
+			if (prev == NULL)	/* 1st one */
+				tp->tab[h] = p->cnext;
+			else			/* middle somewhere */
+				prev->cnext = p->cnext;
+			if (freeable(p))
+				xfree(p->sval);
+			free(p->nval);
+			free(p);
+			tp->nelem--;
+			return;
+		}
+}
+
+Cell *setsymtab(char *n, char *s, Awkfloat f, unsigned t, Array *tp)
+{
+	int h;
+	Cell *p;
+
+	if (n != NULL && (p = lookup(n, tp)) != NULL) {
+		   dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
+			p, p->nval, p->sval, p->fval, p->tval) );
+		return(p);
+	}
+	p = (Cell *) malloc(sizeof(Cell));
+	if (p == NULL)
+		FATAL("out of space for symbol table at %s", n);
+	p->nval = tostring(n);
+	p->sval = s ? tostring(s) : tostring("");
+	p->fval = f;
+	p->tval = t;
+	p->csub = CUNK;
+	p->ctype = OCELL;
+	tp->nelem++;
+	if (tp->nelem > FULLTAB * tp->size)
+		rehash(tp);
+	h = hash(n, tp->size);
+	p->cnext = tp->tab[h];
+	tp->tab[h] = p;
+	   dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
+		p, p->nval, p->sval, p->fval, p->tval) );
+	return(p);
+}
+
+int hash(char *s, int n)	/* form hash value for string s */
+{
+	unsigned hashval;
+
+	for (hashval = 0; *s != '\0'; s++)
+		hashval = (*s + 31 * hashval);
+	return hashval % n;
+}
+
+void rehash(Array *tp)	/* rehash items in small table into big one */
+{
+	int i, nh, nsz;
+	Cell *cp, *op, **np;
+
+	nsz = GROWTAB * tp->size;
+	np = (Cell **) calloc(nsz, sizeof(Cell *));
+	if (np == NULL)		/* can't do it, but can keep running. */
+		return;		/* someone else will run out later. */
+	for (i = 0; i < tp->size; i++) {
+		for (cp = tp->tab[i]; cp; cp = op) {
+			op = cp->cnext;
+			nh = hash(cp->nval, nsz);
+			cp->cnext = np[nh];
+			np[nh] = cp;
+		}
+	}
+	free(tp->tab);
+	tp->tab = np;
+	tp->size = nsz;
+}
+
+Cell *lookup(char *s, Array *tp)	/* look for s in tp */
+{
+	Cell *p;
+	int h;
+
+	h = hash(s, tp->size);
+	for (p = tp->tab[h]; p != NULL; p = p->cnext)
+		if (strcmp(s, p->nval) == 0)
+			return(p);	/* found it */
+	return(NULL);			/* not found */
+}
+
+Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
+{
+	int fldno;
+
+	if ((vp->tval & (NUM | STR)) == 0) 
+		funnyvar(vp, "assign to");
+	if (isfld(vp)) {
+		donerec = 0;	/* mark $0 invalid */
+		fldno = atoi(vp->nval);
+		if (fldno > *NF)
+			newfld(fldno);
+		   dprintf( ("setting field %d to %g\n", fldno, f) );
+	} else if (isrec(vp)) {
+		donefld = 0;	/* mark $1... invalid */
+		donerec = 1;
+	}
+	if (freeable(vp))
+		xfree(vp->sval); /* free any previous string */
+	vp->tval &= ~STR;	/* mark string invalid */
+	vp->tval |= NUM;	/* mark number ok */
+	   dprintf( ("setfval %p: %s = %g, t=%o\n", vp, vp->nval, f, vp->tval) );
+	return vp->fval = f;
+}
+
+void funnyvar(Cell *vp, char *rw)
+{
+	if (isarr(vp))
+		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
+	if (vp->tval & FCN)
+		FATAL("can't %s %s; it's a function.", rw, vp->nval);
+	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
+		vp, vp->nval, vp->sval, vp->fval, vp->tval);
+}
+
+char *setsval(Cell *vp, char *s)	/* set string val of a Cell */
+{
+	char *t;
+	int fldno;
+
+	   dprintf( ("starting setsval %p: %s = \"%s\", t=%o\n", vp, vp->nval, s, vp->tval) );
+	if ((vp->tval & (NUM | STR)) == 0)
+		funnyvar(vp, "assign to");
+	if (isfld(vp)) {
+		donerec = 0;	/* mark $0 invalid */
+		fldno = atoi(vp->nval);
+		if (fldno > *NF)
+			newfld(fldno);
+		   dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
+	} else if (isrec(vp)) {
+		donefld = 0;	/* mark $1... invalid */
+		donerec = 1;
+	}
+	t = tostring(s);	/* in case it's self-assign */
+	vp->tval &= ~NUM;
+	vp->tval |= STR;
+	if (freeable(vp))
+		xfree(vp->sval);
+	vp->tval &= ~DONTFREE;
+	   dprintf( ("setsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, t,t, vp->tval) );
+	return(vp->sval = t);
+}
+
+Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
+{
+	if ((vp->tval & (NUM | STR)) == 0)
+		funnyvar(vp, "read value of");
+	if (isfld(vp) && donefld == 0)
+		fldbld();
+	else if (isrec(vp) && donerec == 0)
+		recbld();
+	if (!isnum(vp)) {	/* not a number */
+		vp->fval = atof(vp->sval);	/* best guess */
+		if (is_number(vp->sval) && !(vp->tval&CON))
+			vp->tval |= NUM;	/* make NUM only sparingly */
+	}
+	   dprintf( ("getfval %p: %s = %g, t=%o\n", vp, vp->nval, vp->fval, vp->tval) );
+	return(vp->fval);
+}
+
+char *getsval(Cell *vp)	/* get string val of a Cell */
+{
+	char s[100];	/* BUG: unchecked */
+	double dtemp;
+
+	if ((vp->tval & (NUM | STR)) == 0)
+		funnyvar(vp, "read value of");
+	if (isfld(vp) && donefld == 0)
+		fldbld();
+	else if (isrec(vp) && donerec == 0)
+		recbld();
+	if (isstr(vp) == 0) {
+		if (freeable(vp))
+			xfree(vp->sval);
+		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */
+			sprintf(s, "%.30g", vp->fval);
+		else
+			sprintf(s, *CONVFMT, vp->fval);
+		vp->sval = tostring(s);
+		vp->tval &= ~DONTFREE;
+		vp->tval |= STR;
+	}
+	   dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, vp->sval, vp->sval, vp->tval) );
+	return(vp->sval);
+}
+
+char *tostring(char *s)	/* make a copy of string s */
+{
+	char *p;
+
+	p = (char *) malloc(strlen(s)+1);
+	if (p == NULL)
+		FATAL("out of space in tostring on %s", s);
+	strcpy(p, s);
+	return(p);
+}
+
+char *qstring(char *is, int delim)	/* collect string up to next delim */
+{
+	char *os = is;
+	int c, n;
+	uschar *s = (uschar *) is;
+	uschar *buf, *bp;
+
+	if ((buf = (uschar *) malloc(strlen(s)+3)) == NULL)
+		FATAL( "out of space in qstring(%s)", s);
+	for (bp = buf; (c = *s) != delim; s++) {
+		if (c == '\n')
+			SYNTAX( "newline in string %.20s...", os );
+		else if (c != '\\')
+			*bp++ = c;
+		else {	/* \something */
+			c = *++s;
+			if (c == 0) {	/* \ at end */
+				*bp++ = '\\';
+				break;	/* for loop */
+			}	
+			switch (c) {
+			case '\\':	*bp++ = '\\'; break;
+			case 'n':	*bp++ = '\n'; break;
+			case 't':	*bp++ = '\t'; break;
+			case 'b':	*bp++ = '\b'; break;
+			case 'f':	*bp++ = '\f'; break;
+			case 'r':	*bp++ = '\r'; break;
+			default:
+				if (!isdigit(c)) {
+					*bp++ = c;
+					break;
+				}
+				n = c - '0';
+				if (isdigit(s[1])) {
+					n = 8 * n + *++s - '0';
+					if (isdigit(s[1]))
+						n = 8 * n + *++s - '0';
+				}
+				*bp++ = n;
+				break;
+			}
+		}
+	}
+	*bp++ = 0;
+	return (char *) buf;
+}
--- /dev/null
+++ b/utils/awk/ytab.c
@@ -1,0 +1,1623 @@
+
+#line	26	"/n/bopp/v7/bwk/awk/awkgram.y"
+#include <stdio.h>
+#include <string.h>
+#include "awk.h"
+
+void checkdup(Node *list, Cell *item);
+int yywrap(void) { return(1); }
+
+Node	*beginloc = 0;
+Node	*endloc = 0;
+int	infunc	= 0;	/* = 1 if in arglist or body of func */
+int	inloop	= 0;	/* = 1 if in while, for, do */
+char	*curfname = 0;	/* current function name */
+Node	*arglist = 0;	/* list of args for current function */
+
+#line	41	"/n/bopp/v7/bwk/awk/awkgram.y"
+typedef union  {
+	Node	*p;
+	Cell	*cp;
+	int	i;
+	char	*s;
+} YYSTYPE;
+extern	int	yyerrflag;
+#ifndef	YYMAXDEPTH
+#define	YYMAXDEPTH	150
+#endif
+YYSTYPE	yylval;
+YYSTYPE	yyval;
+#define	FIRSTTOKEN	57346
+#define	PROGRAM	57347
+#define	PASTAT	57348
+#define	PASTAT2	57349
+#define	XBEGIN	57350
+#define	XEND	57351
+#define	NL	57352
+#define	ARRAY	57353
+#define	MATCH	57354
+#define	NOTMATCH	57355
+#define	MATCHOP	57356
+#define	FINAL	57357
+#define	DOT	57358
+#define	ALL	57359
+#define	CCL	57360
+#define	NCCL	57361
+#define	CHAR	57362
+#define	OR	57363
+#define	STAR	57364
+#define	QUEST	57365
+#define	PLUS	57366
+#define	AND	57367
+#define	BOR	57368
+#define	APPEND	57369
+#define	EQ	57370
+#define	GE	57371
+#define	GT	57372
+#define	LE	57373
+#define	LT	57374
+#define	NE	57375
+#define	IN	57376
+#define	ARG	57377
+#define	BLTIN	57378
+#define	BREAK	57379
+#define	CLOSE	57380
+#define	CONTINUE	57381
+#define	DELETE	57382
+#define	DO	57383
+#define	EXIT	57384
+#define	FOR	57385
+#define	FUNC	57386
+#define	SUB	57387
+#define	GSUB	57388
+#define	IF	57389
+#define	INDEX	57390
+#define	LSUBSTR	57391
+#define	MATCHFCN	57392
+#define	NEXT	57393
+#define	NEXTFILE	57394
+#define	ADD	57395
+#define	MINUS	57396
+#define	MULT	57397
+#define	DIVIDE	57398
+#define	MOD	57399
+#define	ASSIGN	57400
+#define	ASGNOP	57401
+#define	ADDEQ	57402
+#define	SUBEQ	57403
+#define	MULTEQ	57404
+#define	DIVEQ	57405
+#define	MODEQ	57406
+#define	POWEQ	57407
+#define	PRINT	57408
+#define	PRINTF	57409
+#define	SPRINTF	57410
+#define	ELSE	57411
+#define	INTEST	57412
+#define	CONDEXPR	57413
+#define	POSTINCR	57414
+#define	PREINCR	57415
+#define	POSTDECR	57416
+#define	PREDECR	57417
+#define	VAR	57418
+#define	IVAR	57419
+#define	VARNF	57420
+#define	CALL	57421
+#define	NUMBER	57422
+#define	STRING	57423
+#define	REGEXPR	57424
+#define	GETLINE	57425
+#define	RETURN	57426
+#define	SPLIT	57427
+#define	SUBSTR	57428
+#define	WHILE	57429
+#define	CAT	57430
+#define	NOT	57431
+#define	UMINUS	57432
+#define	POWER	57433
+#define	DECR	57434
+#define	INCR	57435
+#define	INDIRECT	57436
+#define	LASTTOKEN	57437
+#define YYEOFCODE 1
+#define YYERRCODE 2
+
+#line	445	"/n/bopp/v7/bwk/awk/awkgram.y"
+
+
+void setfname(Cell *p)
+{
+	if (isarr(p))
+		SYNTAX("%s is an array, not a function", p->nval);
+	else if (isfcn(p))
+		SYNTAX("you can't define function %s more than once", p->nval);
+	curfname = p->nval;
+}
+
+int constnode(Node *p)
+{
+	return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
+}
+
+char *strnode(Node *p)
+{
+	return ((Cell *)(p->narg[0]))->sval;
+}
+
+Node *notnull(Node *n)
+{
+	switch (n->nobj) {
+	case LE: case LT: case EQ: case NE: case GT: case GE:
+	case BOR: case AND: case NOT:
+		return n;
+	default:
+		return op2(NE, n, nullnode);
+	}
+}
+
+void checkdup(Node *vl, Cell *cp)	/* check if name already in list */
+{
+	char *s = cp->nval;
+	for ( ; vl; vl = vl->nnext) {
+		if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
+			SYNTAX("duplicate argument %s", s);
+			break;
+		}
+	}
+}
+short	yyexca[] =
+{-1, 0,
+	1, 28,
+	8, 28,
+	9, 28,
+	12, 28,
+	13, 28,
+	16, 28,
+	45, 28,
+	46, 28,
+	48, 28,
+	54, 28,
+	55, 28,
+	56, 28,
+	58, 28,
+	60, 28,
+	78, 28,
+	86, 28,
+	87, 28,
+	88, 28,
+	89, 28,
+	90, 28,
+	91, 28,
+	95, 28,
+	97, 28,
+	98, 28,
+	101, 28,
+	102, 28,
+	105, 28,
+	108, 28,
+	109, 28,
+	110, 28,
+	-2, 0,
+-1, 1,
+	1, -1,
+	-2, 0,
+-1, 157,
+	15, 30,
+	-2, 0,
+-1, 176,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 63,
+-1, 177,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 64,
+-1, 178,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 65,
+-1, 179,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 66,
+-1, 180,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 67,
+-1, 181,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 68,
+-1, 183,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 70,
+-1, 289,
+	24, 0,
+	44, 0,
+	-2, 53,
+-1, 333,
+	17, 30,
+	-2, 0,
+-1, 355,
+	17, 30,
+	-2, 0,
+};
+#define	YYNPROD	185
+#define	YYPRIVATE 57344
+#define	YYLAST	4177
+short	yyact[] =
+{
+  17, 277, 138,  66, 243, 228, 253,  54,  24,  43,
+ 125, 112, 200,  43, 103, 104, 100, 139, 102, 155,
+ 308, 185, 215, 249, 100, 253, 100, 100, 100, 107,
+ 105, 100, 122, 123, 124, 223, 107, 206,  43,  82,
+ 162,  43,  83, 103, 104,  10, 113, 314,   9, 252,
+  42,  22,  44, 244,  42,  22,  44, 103, 104, 134,
+ 142, 113, 146, 190, 278, 352, 149, 150, 152, 153,
+ 148, 276, 316, 163,  23, 100, 351, 350,  23,  42,
+  62,  44,  42,  22,  44,  11, 156, 168, 169,  85,
+ 253,  51, 321,  79,  80, 232, 190,  86, 135, 133,
+ 100, 318, 182, 320, 269, 258,  23, 100, 100, 100,
+ 100, 100, 100, 100, 108, 109, 110, 111, 233, 275,
+ 112, 234, 190, 110, 111,  43, 100, 112, 335, 190,
+ 190,  11, 203, 205, 190, 324, 278, 190, 190, 212,
+ 284, 190, 211, 265, 260, 190, 100, 259, 221,   3,
+ 141, 188, 100,  16, 226, 140, 331,   6, 156, 141,
+ 219, 230,   7, 100, 310,   6,  42, 170,  44, 167,
+   7, 158, 100, 157, 100, 131, 100, 100, 100, 100,
+ 100, 100, 100, 130, 100,  48, 251, 100, 100, 129,
+  49, 128, 236, 127, 100, 126, 120, 119,  52,  16,
+ 190,  19, 100, 312, 141, 274, 218, 100, 143, 100,
+ 100, 100,   4, 154, 100, 100, 217, 271, 144, 132,
+ 317,  50, 347, 361, 364, 270,   1, 115,  72,  40,
+ 224,   5, 100, 100, 100, 100, 163,  58, 163, 163,
+ 163, 163,  20,  67, 163, 222, 100, 293,  61, 288,
+ 294,  60, 238, 248,  81, 100, 100, 292,  96,   8,
+ 239, 159, 160,   2,   0,   0, 114,   0, 116, 117,
+ 118, 300, 301, 121, 164,   0, 282,   0, 285, 286,
+ 287, 289,   0, 100, 291,   0, 100, 100, 100,   0,
+ 100,   0, 100, 156,   0, 309,   0, 100,   0, 100,
+ 100,   0,   0, 100,   0, 100, 100, 100,   0,   0,
+   0,   0,   0, 334, 313, 165, 163,  96,   0,   0,
+   0,   0,   0,   0,   0,   0,   0, 341, 156, 342,
+ 333,   0, 340, 100,   0,   0,   0, 230, 100, 346,
+ 100,   0, 116,   0, 100, 100, 348,   0, 356,  96,
+ 194, 195, 196, 197, 198, 199, 337, 359,   0, 230,
+   0, 360, 362, 156,   0, 355,   0,   0, 207,   0,
+   0,   0, 238,   0,   0, 238, 238, 238,   0, 238,
+ 239, 238,   0, 239, 239, 239,   0, 239,  96, 239,
+   0,   0,  21,   0,  96,   0,   0,   0, 338,   0,
+   0,   0,   0,   0, 257, 242,   0,   0,  55,   0,
+   0,   0,   0,   0,  96,   0,  96,   0,  96,  96,
+  96,  96,  96,  96,  96,   0,  96, 238,   0,  96,
+  96,   0,   0,   0,   0, 239, 256, 164,   0, 164,
+ 164, 164, 164,   0,  96, 164,   0,   0,   0, 261,
+   0,  96,  96,  96,   0,   0,  96,  96,   0,   0,
+   0,   0,   0, 137,   0,   0, 166,   0,   0,   0,
+ 147,   0,   0,   0,  96, 279, 280, 281, 165,   0,
+ 165, 165, 165, 165,   0,   0, 165, 184,  96,   0,
+   0,   0,   0,   0,   0,   0,   0,  96,  96,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  74,   0,
+ 189, 191,   0,  15,   0,   0,   0, 164,   0,   0,
+   0,   0,   0,   0,   0, 242,   0, 106, 242, 242,
+ 242,   0, 242,   0, 242,   0,   0,   0,   0,  96,
+   0,  96,  96, 137,   0,  96,   0,  96,  96,  96,
+ 229,   0,   0, 220,   0,   0,   0, 137, 165,  15,
+   0,  15,   0, 227,   0, 235,   0,   0, 145,   0,
+   0,   0,   0,   0, 151,  96,   0, 137, 137,   0,
+ 242,   0,  96,   0,   0,   0,  96,  96,   0,   0,
+   0,   0,   0, 171, 173, 175, 176, 177, 178, 179,
+ 180, 181, 183,   0,   0,   0,   0,   0,   0,   0,
+ 186, 187,   0, 262, 263, 264,   0, 266, 267, 268,
+   0,   0, 201,   0,   0,   0,   0,   0, 201, 201,
+   0, 273,   0,   0, 290, 208, 209, 210, 201, 213,
+ 214, 189,   0,   0,   0, 297,   0,   0,   0, 101,
+   0,   0,   0, 295,   0,   0,   0, 303,   0,   0,
+ 240,   0,   0,   0,   0,   0,   0, 231,   0, 311,
+ 106,  98,  97,   0,   0,   0, 245,   0,   0, 137,
+ 241,  43,  28,   0,  30,   0,   0,   0,   0,   0,
+   0,  46,  47,   0,  34,   0,  35,   0, 254,   0,
+ 255,   0,   0,   0,   0,   0,   0, 336, 323, 326,
+ 328, 329,   0,   0,  38,   0,   0,   0, 189,   0,
+   0,   0,  42,  22,  44,  29,  36,  39,   0, 237,
+ 315,  33,   0,  37,  41,   0,   0,  27,  26,   0,
+   0,  99,   0,   0,  31,  32,  23,   0,   0,   0,
+   0,   0,   0, 201,   0,   0, 357, 137,   0, 296,
+   0,   0,   0,   0,   0,   0, 298,   0,   0,   0,
+ 363, 299, 302, 365,   0, 304, 305, 306,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0, 106,   0,   0,   0,   0,   0,  75,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  16,  18,
+   0,  68,  45,   0, 358,   0,   0,   0, 332,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0, 339,   0,   0,   0,   0,   0, 343,   0,
+ 344,  43,  28,  56,  30,  57,  73,  69,  59,  70,
+   0,  46,  47,  71,  34,   0,  35,  63,  64,   0,
+   0,   0,   0,   0,   0,   0,   0,  75,   0,   0,
+   0,   0,  77,  78,  38,  53,   0,  16,  18,   0,
+  68,  45,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  65,  37,  41,  76,   0,  27,  26,   0,
+   0,  25,   0,   0,  31,  32,  23,   0,   0,   0,
+  43,  28,  56,  30,  57,  73,  69,  59,  70,   0,
+  46,  47,  71,  34,   0,  35,  63,  64,   0,   0,
+   0,   0,   0,   0,  75,   0,   0,   0,   0,   0,
+   0,  77,  78,  38,  16,  18,   0,  68,  45,   0,
+ 307,  42,  22,  44,  29,  36,  39,   0,   0,   0,
+  33,  65,  37,  41,  76,   0,  27,  26,   0,   0,
+  25,   0,   0,  31,  32,  23,   0,  43,  28,  56,
+  30,  57,  73,  69,  59,  70,   0,  46,  47,  71,
+  34,   0,  35,  63,  64,   0,   0,   0,   0,   0,
+   0,   0,   0,  75,   0,   0,   0,   0,  77,  78,
+  38, 272,   0,  16,  18,   0,  68,  45,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,  65,  37,
+  41,  76,   0,  27,  26,   0,   0,  25,   0,   0,
+  31,  32,  23,   0,   0,   0,  43,  28,  56,  30,
+  57,  73,  69,  59,  70,   0,  46,  47,  71,  34,
+   0,  35,  63,  64,   0,   0,   0,   0,   0,   0,
+  75,   0,   0,   0,   0,   0,   0,  77,  78,  38,
+  16,  18,   0,  68,  45,   0, 247,  42,  22,  44,
+  29,  36,  39,   0,   0,   0,  33,  65,  37,  41,
+  76,   0,  27,  26,   0,   0,  25,   0,   0,  31,
+  32,  23,   0,  43,  28,  56,  30,  57,  73,  69,
+  59,  70,   0,  46,  47,  71,  34,   0,  35,  63,
+  64,   0,   0,   0,   0,   0,   0,  75,   0,   0,
+   0,   0,   0,   0,  77,  78,  38,  16,  18,   0,
+  68,  45,   0, 246,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33,  65,  37,  41,  76,   0,  27,
+  26,   0,   0,  25,   0,   0,  31,  32,  23,   0,
+  43,  28,  56,  30,  57,  73,  69,  59,  70,   0,
+  46,  47,  71,  34,   0,  35,  63,  64,   0,   0,
+   0,   0,   0,   0,  75,   0,   0,   0,   0,   0,
+   0,  77,  78,  38,  16,  18,   0,  68,  45,   0,
+ 225,  42,  22,  44,  29,  36,  39,   0,   0,   0,
+  33,  65,  37,  41,  76,   0,  27,  26,   0,   0,
+  25,   0,   0,  31,  32,  23,   0,  43,  28,  56,
+  30,  57,  73,  69,  59,  70,   0,  46,  47,  71,
+  34,   0,  35,  63,  64,   0,   0,   0,   0,   0,
+   0,  75,   0,   0,   0,   0,   0,   0,  77,  78,
+  38,  16,  18,   0,  68,  45,   0, 216,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,  65,  37,
+  41,  76,   0,  27,  26,   0,   0,  25,   0,   0,
+  31,  32,  23,   0,  43,  28,  56,  30,  57,  73,
+  69,  59,  70,   0,  46,  47,  71,  34,   0,  35,
+  63,  64,   0,   0,   0,   0,   0,   0,  75,   0,
+   0,   0,   0,   0,   0,  77,  78,  38,  16,  18,
+   0,  68,  45,   0, 136,  42,  22,  44,  29,  36,
+  39,   0,   0,   0,  33,  65,  37,  41,  76,   0,
+  27,  26,   0,   0,  25,   0,   0,  31,  32,  23,
+   0,  43,  28,  56,  30,  57,  73,  69,  59,  70,
+   0,  46,  47,  71,  34,   0,  35,  63,  64,   0,
+   0,   0,   0,   0,   0,  75,   0,   0,   0,   0,
+   0,   0,  77,  78,  38,  16,  18,   0,  68,  45,
+   0,   0,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  65,  37,  41,  76,   0,  27,  26,   0,
+   0,  25,   0,   0,  31,  32,  23,   0,  43,  28,
+  56,  30,  57,  73,  69,  59,  70,   0,  46,  47,
+  71,  34,   0,  35,  63,  64,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  77,
+  78,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,   0,   0,  33,  65,
+  37,  41,  76,   0,  27,  26,   0,   0,  25,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 330,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 327,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 325,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 141,   0,   0, 101,  95, 140,
+   0,   0,   0,   0,   0,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23, 190,   0, 101,  95,   0,
+   0, 192,   0,   0,   0,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 354,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 353,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 349,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23, 101,  95, 345,   0,   0,
+   0,   0,   0,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 322,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 319,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 278,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26,   0, 190,  99, 101,  95,
+  31,  32,  23,   0,   0,   0,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0, 192,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84, 250,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,   0,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39, 101,  95,   0,
+  33,   0,  37,  41,   0,   0,  27,  26,  93,   0,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,   0,
+   0,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,  75,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,  18,   0,   0,  45,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,   0,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23,  43,  28,   0,  30,   0,
+  73,   0,   0,   0,   0,  46,  47,   0,  34,   0,
+  35,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  77,  78,  38,   0,
+   0, 101,   0,   0,   0,   0,  42,  22,  44,  29,
+  36,  39, 240,   0,   0,  33,   0,  37,  41,   0,
+   0,  27,  26,  98,  97,  25,   0,   0,  31,  32,
+  23,   0, 241,  43,  28,   0,  30,   0,   0,   0,
+   0,   0,   0,  46,  47,   0,  34,   0,  35,   0,
+   0,   0,   0,   0,  12,  13,   0,   0,  16,  18,
+   0,   0,  45,   0,   0,   0,  38,   0,   0,   0,
+   0,   0,   0,   0,  42,  22,  44,  29,  36,  39,
+   0, 237,   0,  33,   0,  37,  41,   0,   0,  27,
+  26,  43,  28,  99,  30,   0,  31,  32,  23,   0,
+  14,  46,  47,   0,  34,   0,  35,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,  38,   0,   0, 101,   0,   0,
+   0,   0,  42,  22,  44,  29,  36,  39, 240,   0,
+   0,  33,   0,  37,  41,   0,   0,  27,  26,  98,
+   0,  25,   0,   0,  31,  32,  23,   0, 241,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0, 141,   0,   0,  18,   0, 140,  45,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0, 253,   0,  33,
+  18,  37,  41,  45,   0,  27,  26,  43,  28,  99,
+  30,   0,  31,  32,  23,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  43,  28,   0,  30,   0,   0,   0,   0,
+  38,   0,  46,  47,   0,  34,   0,  35,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,   0,  37,
+  41,   0,   0,  27,  26,  38,   0,  25, 101,   0,
+  31,  32,  23,  42,  22,  44,  29,  36,  39, 240,
+   0,   0,  33,   0,  37,  41,   0,   0,  27,  26,
+   0,   0,  25,   0,   0,  31,  32,  23,   0, 241,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0, 174,   0,   0, 283,   0,   0,  45,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0, 172,   0,
+  33, 283,  37,  41,  45,   0,  27,  26,  43,  28,
+  99,  30,   0,  31,  32,  23,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  43,  28,   0,  30,   0,   0,   0,
+   0,  38,   0,  46,  47,   0,  34,   0,  35,  42,
+  22,  44,  29,  36,  39,   0, 253,   0,  33, 283,
+  37,  41,  45,   0,  27,  26,  38,   0,  25,   0,
+   0,  31,  32,  23,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33,  18,  37,  41,  45, 204,  27,
+  26,  43,  28,  25,  30,   0,  31,  32,  23,   0,
+   0,  46,  47,   0,  34,   0,  35,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  43,  28,   0,  30,
+   0,   0,   0,   0,  38,   0,  46,  47,   0,  34,
+   0,  35,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  18,  37,  41,  45, 202,  27,  26,  38,
+   0,  25,   0,   0,  31,  32,  23,  42,  22,  44,
+  29,  36,  39,   0, 174,   0,  33,  18,  37,  41,
+  45,   0,  27,  26,  43,  28,  25,  30,   0,  31,
+  32,  23,   0,   0,  46,  47,   0,  34,   0,  35,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  43,
+  28,   0,  30,   0,   0,   0,   0,  38,   0,  46,
+  47,   0,  34,   0,  35,  42,  22,  44,  29,  36,
+  39,   0, 172,   0,  33,  18,  37,  41,  45,   0,
+  27,  26,  38,   0,  25,   0,   0,  31,  32,  23,
+  42,  22,  44,  29,  36,  39,   0,   0,   0,  33,
+  18,  37,  41,  45,   0,  27,  26,  43,  28,  25,
+  30,   0,  31,  32,  23,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  43,  28,   0,  30,   0,   0,   0,   0,
+  38,   0,  46,  47,   0,  34,   0,  35,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33, 283,  37,
+  41,  45,   0,  27,  26,  38,   0,  25,   0,   0,
+  31,  32,  23,  42,  22,  44,  29,  36,  39,   0,
+   0,   0,  33, 101,  37,  41,   0,   0,  27,  26,
+  43,  28,  25,  30,   0,  31,  32,  23,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,  43,  28,   0,  30,   0,
+   0,   0,   0,  38,   0,  46,  47,   0,  34,   0,
+  35,  42,  22,  44,  29,  36,  39,   0,   0, 193,
+  33, 161,  37,  41,  45,   0,  27,  26,  38,   0,
+  25,   0,   0,  31,  32,  23,  42,  22,  44,  29,
+  36,  39,   0,   0,   0,  33, 101,  37,  41,  45,
+   0,  27,  26,  43,  28,  99,  30,   0,  31,  32,
+  23,   0,   0,  46,  47,   0,  34,   0,  35,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  43,  28,
+   0,  30,   0,   0,   0,   0,  38,   0,  46,  47,
+   0,  34,   0,  35,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33, 101,  37,  41,   0,   0,  27,
+  26,  38,   0,  25,   0,   0,  31,  32,  23,  42,
+  22,  44,  29,  36,  39,   0,   0,   0,  33, 101,
+  37,  41,   0,   0,  27,  26,  43,  28,  25,  30,
+   0,  31,  32,  23,   0,   0,  46,  47,   0,  34,
+   0,  35,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  43,  28,   0,  30,   0,   0,   0,   0,  38,
+   0,  46,  47,   0,  34,   0,  35,  42,  22,  44,
+  29,  36,  39,   0,   0,   0,  33,   0,  37,  41,
+   0,   0,  27,  26,  38,   0,  99,   0,   0,  31,
+  32,  23,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,   0,   0,  37,  41,   0,   0,  27,  26,   0,
+   0,  99,   0,   0,  31,  32,  23
+};
+short	yypact[] =
+{
+ 147,-1000,-1000,-1000,3266, 175,-1000,-1000, 155,-1000,
+ 187, 865, 141, 141, -47,2905,-1000, -51,3817,-1000,
+  13,  42,-1000,4041,-1000,3983,4041,4041, 184, 183,
+4041, -36, -36, -32, 182, 180,-1000, 178, 176,-1000,
+ 170, 162,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+3266, 865,3817,-1000,1336,-1000, 140, 140, 198,3392,
+-1000,1403, 865, 140, 140,3392, 140,-1000, 194,-1000,
+ 160, 158,3958,  -7,2905,-1000, 156,-1000,-1000, 865,
+ 865, 154,-1000,-1000,3817,3792,3734,3817,3817,3817,
+3817,3817,3817,3817,  -7, -74,  13,-1000,-1000,4041,
+ -94,3817,3817,-1000,-1000, 134,1904,3900,4041,4041,
+4041,4041,4041,3817,-1000,-1000, -96, -96, -96,3709,
+3651,  13,-1000,-1000,  -5,4041,3817,3817,3817,3817,
+3817,3817, -70,-1000,1269, 141,-1000,-1000,-1000, 196,
+ 194,-1000,-1000,-1000,1403,1804,-1000, -44,1202,-1000,
+-1000,1804,-1000,-1000,1403,-1000, 196,3140,3817,  81,
+ 189,3817,3208, -65,-1000,  13,  34,3817,1135,1068,
+ -63,2815,-1000,2995,-1000,3074,4066,4066,4066,4066,
+4066,4066,-1000,4066,-1000, -36,2725,2905,   5,3417,
+-1000,3417,-1000,4041, -96,  20,  20, -96, -96, -96,
+  85,2905,-1000, 130,-1000, 127,4041,  13,2635,2635,
+2635, 126, 189,2635,2635,  88,-1000, 865,-1000,-1000,
+-1000,-1000,1001,-1000, 195,-1000,-1000,-1000, 104,  27,
+-1000,2542,4041,4041,4041,3626, 123,3875,3568,3543,
+3875,  -7,  13,3875,3817,2542,-1000,-1000, 119,-1000,
+3817,-1000,  -7,-1000,2905,2905,  13,3417,-1000,-1000,
+-1000,  13,3417,3417,  80,-1000,3417,3417,3417,-1000,
+ 932, -79,-1000,-1000,-1000, 149,  -7, 193,-1000,  13,
+  13,  13,3208,3817,   3, 636,3334,3485,-1000,4066,
+-1000,3208,  52, 193, 193,  15,2905,-1000,2905,2452,
+  86,  75,2362, 118,1703,1603,1503,-1000, 143,3817,
+ 194,  47,-1000, 111,  -7,3875,-1000, 141,-1000,-1000,
+-1000,-1000,-1000,3417,-1000,-1000,  -4,-1000,  -4,3417,
+-1000,3817,2272,3140, 193,   3,-1000,3208, 865,2174,
+  60,  59,  48,2084,1994, 194,  47,1403, 796,-1000,
+-1000,-1000,-1000,-1000, 140,3140, 193,-1000,-1000,-1000,
+  47,1403, 193,-1000,1403,-1000
+};
+short	yypgo[] =
+{
+   0, 263, 508,  40,  30, 262,  12, 261, 242, 201,
+  45,  48, 259,   8,   3,   5, 408,   7,   0, 392,
+ 254, 253, 251, 248, 245, 243, 237,   2, 231, 212,
+  80, 230,   1, 404,  17,  19,  97,  89, 229, 228,
+ 226, 224, 223, 222, 220, 219, 218, 217, 213
+};
+short	yyr1[] =
+{
+   0,  40,  40,  36,  36,  37,  37,  33,  33,  26,
+  26,  24,  24,  41,  22,  42,  22,  43,  22,  20,
+  20,  23,  30,  30,  34,  34,  35,  35,  29,  29,
+  15,  15,   1,   1,  10,  11,  11,  11,  11,  11,
+  11,  11,  44,  11,  12,  12,   6,   6,   3,   3,
+   3,   3,   3,   3,   3,   3,   3,   3,   3,   2,
+   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+   2,   2,   2,   2,   2,   2,   2,   2,   4,   4,
+   5,   5,   7,   7,   7,  39,  39,  28,  28,  28,
+  28,  31,  31,   9,   9,  45,  13,  32,  32,  14,
+  14,  14,  14,  14,  14,  14,  14,  27,  27,  16,
+  16,  46,  47,  16,  16,  16,  16,  16,  16,  16,
+  16,  16,  16,  16,  16,  48,  16,  16,  17,  17,
+  38,  38,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,  18,  18,  18,  18,  21,  21,
+  21,  19,  19,  19,  25
+};
+short	yyr2[] =
+{
+   0,   1,   1,   1,   2,   1,   2,   1,   2,   1,
+   2,   1,   2,   0,  12,   0,  10,   0,   8,   1,
+   1,   4,   1,   2,   1,   2,   0,   1,   0,   1,
+   0,   1,   1,   3,   1,   1,   4,   3,   6,   3,
+   4,   4,   0,   9,   1,   3,   1,   3,   3,   5,
+   3,   3,   3,   3,   3,   5,   2,   1,   1,   3,
+   5,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+   3,   3,   5,   4,   3,   2,   1,   1,   3,   3,
+   1,   3,   0,   1,   3,   1,   1,   1,   1,   2,
+   2,   1,   2,   1,   2,   0,   4,   1,   2,   4,
+   4,   4,   2,   5,   2,   1,   1,   1,   2,   2,
+   2,   0,   0,   9,   3,   2,   1,   4,   2,   3,
+   2,   2,   3,   2,   2,   0,   3,   2,   1,   2,
+   1,   1,   4,   3,   3,   3,   3,   3,   3,   2,
+   2,   2,   3,   4,   1,   3,   4,   2,   2,   2,
+   2,   2,   4,   3,   2,   1,   6,   6,   3,   6,
+   6,   1,   8,   8,   6,   4,   1,   6,   6,   8,
+   8,   8,   6,   1,   1,   4,   1,   2,   0,   1,
+   3,   1,   1,   1,   4
+};
+short	yychk[] =
+{
+-1000, -40,  -1,   2, -29, -28,  10,  15, -12, -11,
+ -10, -30,   8,   9,  54,  -2,  12, -18,  13,  -9,
+  -8, -19,  87, 110, -13, 105, 102, 101,  46,  89,
+  48, 108, 109,  95,  58,  60,  90,  97,  78,  91,
+ -38,  98,  86,  45,  88,  16,  55,  56,  10,  15,
+ -29, -30,  11,  10, -17, -16,  47,  49, -26,  52,
+ -22, -23, -30,  61,  62,  96, -14, -25,  15,  51,
+  53,  57, -39,  50,  -2,   2,  99,  76,  77, -30,
+ -30, -20,  86,  89,  93, -37, -36,  38,  39,  40,
+  41,  42,  43,  24,  44,  14,  -8,  36,  35, 105,
+ -18,  13,  69, 108, 109,  -4,  -2,  16, 101, 102,
+ 103, 104, 107,  19,  -8,  -9,  -8,  -8,  -8,  13,
+  13,  -8, -18, -18, -18,  42,  13,  13,  13,  13,
+  13,  13, -45, -11, -17, -10,  18, -16, -27, -34,
+  15,  10, -27,  10, -46,  -2, -27, -16, -17, -27,
+ -27,  -2, -27, -27, -48, -35, -34,  13,  13,  -7,
+  -5,  13,  -3, -18,  -9,  -8, -19,  13, -17, -17,
+  13,  -2,  10,  -2,  10,  -2,  -2,  -2,  -2,  -2,
+  -2,  -2, -13,  -2, -19,  95,  -2,  -2,  17, -33,
+  11, -33,  17,  69,  -8,  -8,  -8,  -8,  -8,  -8,
+  -6,  -2,  17,  -6,  17,  -6,  42,  -8,  -2,  -2,
+  -2,  -6, -13,  -2,  -2,  92,  18, -30,  10, -35,
+ -16, -27, -24,  79, -31,  18, -27, -16, -15, -19,
+ -14,  -2,  14,  37,  40, -33,  -4,  93, -37, -36,
+  24,  44,  -8,  69,  19,  -2,  18,  18, -21,  86,
+  94, -18,  44,  10,  -2,  -2,  -8, -33,  20,  17,
+  17,  -8, -33, -33, -33,  17, -33, -33, -33,  16,
+ -17, -47,  10, -16,  10,  15,  44, -32,  17,  -8,
+  -8,  -8,  -3,  13,  17,  -3,  -3,  -3, -13,  -3,
+ -19,  -3,  -6, -32, -32, -33,  -2, -19,  -2,  -2,
+ -13, -13,  -2, -19,  -2,  -2,  -2,  18,  99, -35,
+  15, -19,  10,  -4,  44,  94,  20, -44,  86,  17,
+  17,  17,  17, -33,  17,  17, -33,  17, -33, -33,
+  17,  13,  -2, -35, -32,  17, -19,  -3, -30,  -2,
+ -13, -18, -18,  -2,  -2,  15, -15, -43, -17,  17,
+  17,  17,  17,  17,  17, -35, -32, -16,  18, -27,
+ -15, -42, -32, -16, -41, -16
+};
+short	yydef[] =
+{
+  -2,  -2,   1,   2,  32,  29,  87,  88,  28,  44,
+  35,   0,   0,   0,   0,  34,  22, 173,   0,  76,
+  77, 174, 176,   0,  93,   0,   0,   0, 144,   0,
+   0,   0,   0, 155,   0,   0, 161,   0,   0, 166,
+   0,   0, 181, 182, 183,  95, 130, 131,  89,  90,
+  33,   0,   0,  23,   0, 128,   0,   0, 111,   0,
+ 116,   0,   0,   0,   0,   0,   0, 125,  26,   9,
+   0,   0,  82,   0, 105, 106,   0,  85,  86,   0,
+   0,   0,  19,  20,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  75,   5,   3,   0,
+ 173,   0,   0, 150, 151,   0,   0,   0,   0,   0,
+   0,   0,   0,   0, 177,  94, 141, 139, 140,   0,
+   0, 147, 148, 149, 154,   0,   0,   0,   0,   0,
+   0,   0,   0,  45,   0,  37,  39, 129, 109, 107,
+  26,  24, 110,  10,   0,   0, 115, 118,   0, 120,
+ 121,   0, 123, 124,   0, 127,  27,  -2,   0, 102,
+  83,   0,  80, 173,  57,  58, 104,   0,   0,   0,
+ 178,   0,   6,  61,   4,  62,  -2,  -2,  -2,  -2,
+  -2,  -2,  69,  -2,  71,  74,   0,  59,   0,   0,
+   7,   0, 158,   0, 136, 133, 134, 135, 137, 138,
+   0,  46, 142,   0, 145,   0,   0, 153,   0,   0,
+   0,   0,  93,   0,   0,   0,  36,   0,  25, 108,
+ 112, 114,   0,  11, 119,  91, 122, 126,   0, 174,
+  31,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  56,   0,   0,   0,  40,  41,   0, 179,
+   0,  73,   0,   8,  79,  78, 132,   0, 175, 143,
+ 146, 152,   0,   0,   0, 165,   0,   0,   0,  96,
+   0,   0,  12, 117,  92,  26,   0,  21,  97,  99,
+ 100, 101,  81,   0,  84,   0,  50,  51,  52,  -2,
+  54,  48,   0, 184,  42,   0,  60,  72,  47,   0,
+  93,  93,   0,   0,   0,   0,   0,  38,   0,   0,
+  26,   0,  98,   0,   0,   0, 103,   0, 180, 156,
+ 157, 159, 160,   0, 164, 167,   0, 168,   0,   0,
+ 172,   0,   0,  -2,  17,   0,  55,  49,   0,   0,
+  93,   0,   0,   0,   0,  26,   0,   0,   0, 162,
+ 163, 169, 170, 171,   0,  -2,  15,  18,  43, 113,
+   0,   0,  13,  16,   0,  14
+};
+short	yytok1[] =
+{
+   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0, 104,   0,   0,
+  13,  17, 103, 101,  11, 102,   0,  16,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  94,  15,
+   0,   0,   0,  93,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  19,   0,  20,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  12,  14,  18
+};
+short	yytok2[] =
+{
+   2,   3,   4,   5,   6,   7,   8,   9,  10,  21,
+  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
+  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
+  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
+  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
+  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
+  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,
+  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,
+  92,  95,  96,  97,  98,  99, 100, 105, 106, 107,
+ 108, 109, 110, 111
+};
+long	yytok3[] =
+{
+   0
+};
+#define YYFLAG 		-1000
+#define YYERROR		goto yyerrlab
+#define YYACCEPT	return(0)
+#define YYABORT		return(1)
+#define	yyclearin	yychar = -1
+#define	yyerrok		yyerrflag = 0
+
+#ifdef	yydebug
+#include	"y.debug"
+#else
+#define	yydebug		0
+char*	yytoknames[1];		/* for debugging */
+char*	yystates[1];		/* for debugging */
+#endif
+
+/*	parser for yacc output	*/
+
+int	yynerrs = 0;		/* number of errors */
+int	yyerrflag = 0;		/* error recovery flag */
+
+extern	int	fprint(int, char*, ...);
+extern	int	sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+	static char x[10];
+
+	if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+	if(yytoknames[yyc-1])
+		return yytoknames[yyc-1];
+	sprintf(x, "<%d>", yyc);
+	return x;
+}
+
+char*
+yystatname(int yys)
+{
+	static char x[10];
+
+	if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+	if(yystates[yys])
+		return yystates[yys];
+	sprintf(x, "<%d>\n", yys);
+	return x;
+}
+
+long
+yylex1(void)
+{
+	long yychar;
+	long *t3p;
+	int c;
+
+	yychar = yylex();
+	if(yychar <= 0) {
+		c = yytok1[0];
+		goto out;
+	}
+	if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+		c = yytok1[yychar];
+		goto out;
+	}
+	if(yychar >= YYPRIVATE)
+		if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+			c = yytok2[yychar-YYPRIVATE];
+			goto out;
+		}
+	for(t3p=yytok3;; t3p+=2) {
+		c = t3p[0];
+		if(c == yychar) {
+			c = t3p[1];
+			goto out;
+		}
+		if(c == 0)
+			break;
+	}
+	c = 0;
+
+out:
+	if(c == 0)
+		c = yytok2[1];	/* unknown char */
+	if(yydebug >= 3)
+		printf("lex %.4lX %s\n", yychar, yytokname(c));
+	return c;
+}
+
+int
+yyparse(void)
+{
+	struct
+	{
+		YYSTYPE	yyv;
+		int	yys;
+	} yys[YYMAXDEPTH], *yyp, *yypt;
+	short *yyxi;
+	int yyj, yym, yystate, yyn, yyg;
+	YYSTYPE save1, save2;
+	int save3, save4;
+	long yychar;
+
+	save1 = yylval;
+	save2 = yyval;
+	save3 = yynerrs;
+	save4 = yyerrflag;
+
+	yystate = 0;
+	yychar = -1;
+	yynerrs = 0;
+	yyerrflag = 0;
+	yyp = &yys[-1];
+	goto yystack;
+
+ret0:
+	yyn = 0;
+	goto ret;
+
+ret1:
+	yyn = 1;
+	goto ret;
+
+ret:
+	yylval = save1;
+	yyval = save2;
+	yynerrs = save3;
+	yyerrflag = save4;
+	return yyn;
+
+yystack:
+	/* put a state and value onto the stack */
+	if(yydebug >= 4)
+		printf("char %s in %s", yytokname(yychar), yystatname(yystate));
+
+	yyp++;
+	if(yyp >= &yys[YYMAXDEPTH]) { 
+		yyerror("yacc stack overflow"); 
+		goto ret1; 
+	}
+	yyp->yys = yystate;
+	yyp->yyv = yyval;
+
+yynewstate:
+	yyn = yypact[yystate];
+	if(yyn <= YYFLAG)
+		goto yydefault; /* simple state */
+	if(yychar < 0)
+		yychar = yylex1();
+	yyn += yychar;
+	if(yyn < 0 || yyn >= YYLAST)
+		goto yydefault;
+	yyn = yyact[yyn];
+	if(yychk[yyn] == yychar) { /* valid shift */
+		yychar = -1;
+		yyval = yylval;
+		yystate = yyn;
+		if(yyerrflag > 0)
+			yyerrflag--;
+		goto yystack;
+	}
+
+yydefault:
+	/* default state action */
+	yyn = yydef[yystate];
+	if(yyn == -2) {
+		if(yychar < 0)
+			yychar = yylex1();
+
+		/* look through exception table */
+		for(yyxi=yyexca;; yyxi+=2)
+			if(yyxi[0] == -1 && yyxi[1] == yystate)
+				break;
+		for(yyxi += 2;; yyxi += 2) {
+			yyn = yyxi[0];
+			if(yyn < 0 || yyn == yychar)
+				break;
+		}
+		yyn = yyxi[1];
+		if(yyn < 0)
+			goto ret0;
+	}
+	if(yyn == 0) {
+		/* error ... attempt to resume parsing */
+		switch(yyerrflag) {
+		case 0:   /* brand new error */
+			yyerror("syntax error");
+			if(yydebug >= 1) {
+				printf("%s", yystatname(yystate));
+				printf("saw %s\n", yytokname(yychar));
+			}
+yyerrlab:
+			yynerrs++;
+
+		case 1:
+		case 2: /* incompletely recovered error ... try again */
+			yyerrflag = 3;
+
+			/* find a state where "error" is a legal shift action */
+			while(yyp >= yys) {
+				yyn = yypact[yyp->yys] + YYERRCODE;
+				if(yyn >= 0 && yyn < YYLAST) {
+					yystate = yyact[yyn];  /* simulate a shift of "error" */
+					if(yychk[yystate] == YYERRCODE)
+						goto yystack;
+				}
+
+				/* the current yyp has no shift onn "error", pop stack */
+				if(yydebug >= 2)
+					printf("error recovery pops state %d, uncovers %d\n",
+						yyp->yys, (yyp-1)->yys );
+				yyp--;
+			}
+			/* there is no state on the stack with an error shift ... abort */
+			goto ret1;
+
+		case 3:  /* no shift yet; clobber input char */
+			if(yydebug >= YYEOFCODE)
+				printf("error recovery discards %s\n", yytokname(yychar));
+			if(yychar == YYEOFCODE)
+				goto ret1;
+			yychar = -1;
+			goto yynewstate;   /* try again in the same state */
+		}
+	}
+
+	/* reduction by production yyn */
+	if(yydebug >= 2)
+		printf("reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+	yypt = yyp;
+	yyp -= yyr2[yyn];
+	yyval = (yyp+1)->yyv;
+	yym = yyn;
+
+	/* consult goto table to find next state */
+	yyn = yyr1[yyn];
+	yyg = yypgo[yyn];
+	yyj = yyg + yyp->yys + 1;
+
+	if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+		yystate = yyact[yyg];
+	switch(yym) {
+		
+case 1:
+#line	98	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (errorflag==0)
+			winner = (Node *)stat3(PROGRAM, beginloc, yypt[-0].yyv.p, endloc); } break;
+case 2:
+#line	100	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyclearin; bracecheck(); SYNTAX("bailing out"); } break;
+case 13:
+#line	124	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 14:
+#line	125	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat4(FOR, yypt[-9].yyv.p, notnull(yypt[-6].yyv.p), yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 15:
+#line	126	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 16:
+#line	127	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat4(FOR, yypt[-7].yyv.p, NIL, yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 17:
+#line	128	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 18:
+#line	129	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat3(IN, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), yypt[-0].yyv.p); } break;
+case 19:
+#line	133	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ setfname(yypt[-0].yyv.cp); } break;
+case 20:
+#line	134	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ setfname(yypt[-0].yyv.cp); } break;
+case 21:
+#line	138	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-1].yyv.p); } break;
+case 26:
+#line	150	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.i = 0; } break;
+case 28:
+#line	155	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.i = 0; } break;
+case 30:
+#line	161	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 32:
+#line	166	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 33:
+#line	167	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 34:
+#line	171	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-0].yyv.p); } break;
+case 35:
+#line	175	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, yypt[-0].yyv.p, stat2(PRINT, rectonode(), NIL)); } break;
+case 36:
+#line	176	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 37:
+#line	177	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = pa2stat(yypt[-2].yyv.p, yypt[-0].yyv.p, stat2(PRINT, rectonode(), NIL)); } break;
+case 38:
+#line	178	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = pa2stat(yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 39:
+#line	179	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, NIL, yypt[-1].yyv.p); } break;
+case 40:
+#line	181	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ beginloc = linkum(beginloc, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 41:
+#line	183	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ endloc = linkum(endloc, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 42:
+#line	184	"/n/bopp/v7/bwk/awk/awkgram.y"
+{infunc++;} break;
+case 43:
+#line	185	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ infunc--; curfname=0; defn((Cell *)yypt[-7].yyv.p, yypt[-5].yyv.p, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 45:
+#line	190	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 47:
+#line	195	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 48:
+#line	199	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 49:
+#line	201	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(CONDEXPR, notnull(yypt[-4].yyv.p), yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 50:
+#line	203	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BOR, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 51:
+#line	205	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(AND, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 52:
+#line	206	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 53:
+#line	208	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-0].yyv.p))
+			yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(strnode(yypt[-0].yyv.p), 0));
+		  else
+			yyval.p = op3(yypt[-1].yyv.i, (Node *)1, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 54:
+#line	212	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-2].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 55:
+#line	213	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-3].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 56:
+#line	214	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CAT, yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 59:
+#line	220	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 60:
+#line	222	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(CONDEXPR, notnull(yypt[-4].yyv.p), yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 61:
+#line	224	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BOR, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 62:
+#line	226	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(AND, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 63:
+#line	227	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 64:
+#line	228	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 65:
+#line	229	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 66:
+#line	230	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 67:
+#line	231	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 68:
+#line	232	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 69:
+#line	233	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 70:
+#line	235	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-0].yyv.p))
+			yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(strnode(yypt[-0].yyv.p), 0));
+		  else
+			yyval.p = op3(yypt[-1].yyv.i, (Node *)1, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 71:
+#line	239	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-2].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 72:
+#line	240	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-3].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 73:
+#line	241	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else yyval.p = op3(GETLINE, yypt[-0].yyv.p, itonp(yypt[-2].yyv.i), yypt[-3].yyv.p); } break;
+case 74:
+#line	244	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else yyval.p = op3(GETLINE, (Node*)0, itonp(yypt[-1].yyv.i), yypt[-2].yyv.p); } break;
+case 75:
+#line	247	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CAT, yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 78:
+#line	253	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 79:
+#line	254	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 81:
+#line	259	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 82:
+#line	263	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = rectonode(); } break;
+case 84:
+#line	265	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 93:
+#line	282	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(MATCH, NIL, rectonode(), (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 94:
+#line	283	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(NOT, notnull(yypt[-0].yyv.p)); } break;
+case 95:
+#line	287	"/n/bopp/v7/bwk/awk/awkgram.y"
+{startreg();} break;
+case 96:
+#line	287	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.s = yypt[-1].yyv.s; } break;
+case 99:
+#line	295	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("print | is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 100:
+#line	298	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			if (safe) SYNTAX("print >> is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 101:
+#line	301	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			if (safe) SYNTAX("print > is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 102:
+#line	304	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(yypt[-1].yyv.i, yypt[-0].yyv.p, NIL, NIL); } break;
+case 103:
+#line	305	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DELETE, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p); } break;
+case 104:
+#line	306	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DELETE, makearr(yypt[-0].yyv.p), 0); } break;
+case 105:
+#line	307	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = exptostat(yypt[-0].yyv.p); } break;
+case 106:
+#line	308	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyclearin; SYNTAX("illegal statement"); } break;
+case 109:
+#line	317	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (!inloop) SYNTAX("break illegal outside of loops");
+				  yyval.p = stat1(BREAK, NIL); } break;
+case 110:
+#line	319	"/n/bopp/v7/bwk/awk/awkgram.y"
+{  if (!inloop) SYNTAX("continue illegal outside of loops");
+				  yyval.p = stat1(CONTINUE, NIL); } break;
+case 111:
+#line	321	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 112:
+#line	321	"/n/bopp/v7/bwk/awk/awkgram.y"
+{--inloop;} break;
+case 113:
+#line	322	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DO, yypt[-6].yyv.p, notnull(yypt[-2].yyv.p)); } break;
+case 114:
+#line	323	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(EXIT, yypt[-1].yyv.p); } break;
+case 115:
+#line	324	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(EXIT, NIL); } break;
+case 117:
+#line	326	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(IF, yypt[-3].yyv.p, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 118:
+#line	327	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(IF, yypt[-1].yyv.p, yypt[-0].yyv.p, NIL); } break;
+case 119:
+#line	328	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 120:
+#line	329	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (infunc)
+				SYNTAX("next is illegal inside a function");
+			  yyval.p = stat1(NEXT, NIL); } break;
+case 121:
+#line	332	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (infunc)
+				SYNTAX("nextfile is illegal inside a function");
+			  yyval.p = stat1(NEXTFILE, NIL); } break;
+case 122:
+#line	335	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(RETURN, yypt[-1].yyv.p); } break;
+case 123:
+#line	336	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(RETURN, NIL); } break;
+case 125:
+#line	338	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 126:
+#line	338	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat2(WHILE, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 127:
+#line	339	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 129:
+#line	344	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 132:
+#line	352	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(DIVEQ, yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 133:
+#line	353	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(ADD, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 134:
+#line	354	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MINUS, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 135:
+#line	355	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MULT, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 136:
+#line	356	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(DIVIDE, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 137:
+#line	357	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MOD, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 138:
+#line	358	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(POWER, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 139:
+#line	359	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(UMINUS, yypt[-0].yyv.p); } break;
+case 140:
+#line	360	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-0].yyv.p; } break;
+case 141:
+#line	361	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(NOT, notnull(yypt[-0].yyv.p)); } break;
+case 142:
+#line	362	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-2].yyv.i), rectonode()); } break;
+case 143:
+#line	363	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-3].yyv.i), yypt[-1].yyv.p); } break;
+case 144:
+#line	364	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-0].yyv.i), rectonode()); } break;
+case 145:
+#line	365	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CALL, celltonode(yypt[-2].yyv.cp,CVAR), NIL); } break;
+case 146:
+#line	366	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CALL, celltonode(yypt[-3].yyv.cp,CVAR), yypt[-1].yyv.p); } break;
+case 147:
+#line	367	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(CLOSE, yypt[-0].yyv.p); } break;
+case 148:
+#line	368	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(PREDECR, yypt[-0].yyv.p); } break;
+case 149:
+#line	369	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(PREINCR, yypt[-0].yyv.p); } break;
+case 150:
+#line	370	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(POSTDECR, yypt[-1].yyv.p); } break;
+case 151:
+#line	371	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(POSTINCR, yypt[-1].yyv.p); } break;
+case 152:
+#line	372	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 153:
+#line	373	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, NIL, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 154:
+#line	374	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, yypt[-0].yyv.p, NIL, NIL); } break;
+case 155:
+#line	375	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, NIL, NIL, NIL); } break;
+case 156:
+#line	377	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INDEX, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 157:
+#line	379	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ SYNTAX("index() doesn't permit regular expressions");
+		  yyval.p = op2(INDEX, yypt[-3].yyv.p, (Node*)yypt[-1].yyv.s); } break;
+case 158:
+#line	381	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 159:
+#line	383	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(MATCHFCN, NIL, yypt[-3].yyv.p, (Node*)makedfa(yypt[-1].yyv.s, 1)); } break;
+case 160:
+#line	385	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-1].yyv.p))
+			yyval.p = op3(MATCHFCN, NIL, yypt[-3].yyv.p, (Node*)makedfa(strnode(yypt[-1].yyv.p), 1));
+		  else
+			yyval.p = op3(MATCHFCN, (Node *)1, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 161:
+#line	389	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CCON); } break;
+case 162:
+#line	391	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p, (Node*)STRING); } break;
+case 163:
+#line	393	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), (Node*)makedfa(yypt[-1].yyv.s, 1), (Node *)REGEXPR); } break;
+case 164:
+#line	395	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-3].yyv.p, makearr(yypt[-1].yyv.p), NIL, (Node*)STRING); } break;
+case 165:
+#line	396	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(yypt[-3].yyv.i, yypt[-1].yyv.p); } break;
+case 166:
+#line	397	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CCON); } break;
+case 167:
+#line	399	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(yypt[-5].yyv.i, NIL, (Node*)makedfa(yypt[-3].yyv.s, 1), yypt[-1].yyv.p, rectonode()); } break;
+case 168:
+#line	401	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-3].yyv.p))
+			yyval.p = op4(yypt[-5].yyv.i, NIL, (Node*)makedfa(strnode(yypt[-3].yyv.p), 1), yypt[-1].yyv.p, rectonode());
+		  else
+			yyval.p = op4(yypt[-5].yyv.i, (Node *)1, yypt[-3].yyv.p, yypt[-1].yyv.p, rectonode()); } break;
+case 169:
+#line	406	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(yypt[-7].yyv.i, NIL, (Node*)makedfa(yypt[-5].yyv.s, 1), yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 170:
+#line	408	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-5].yyv.p))
+			yyval.p = op4(yypt[-7].yyv.i, NIL, (Node*)makedfa(strnode(yypt[-5].yyv.p), 1), yypt[-3].yyv.p, yypt[-1].yyv.p);
+		  else
+			yyval.p = op4(yypt[-7].yyv.i, (Node *)1, yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 171:
+#line	413	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(SUBSTR, yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 172:
+#line	415	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(SUBSTR, yypt[-3].yyv.p, yypt[-1].yyv.p, NIL); } break;
+case 175:
+#line	421	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(ARRAY, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p); } break;
+case 176:
+#line	422	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(INDIRECT, celltonode(yypt[-0].yyv.cp, CVAR)); } break;
+case 177:
+#line	423	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(INDIRECT, yypt[-0].yyv.p); } break;
+case 178:
+#line	427	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ arglist = yyval.p = 0; } break;
+case 179:
+#line	428	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ arglist = yyval.p = celltonode(yypt[-0].yyv.cp,CVAR); } break;
+case 180:
+#line	429	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			checkdup(yypt[-2].yyv.p, yypt[-0].yyv.cp);
+			arglist = yyval.p = linkum(yypt[-2].yyv.p,celltonode(yypt[-0].yyv.cp,CVAR)); } break;
+case 181:
+#line	435	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CVAR); } break;
+case 182:
+#line	436	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(ARG, itonp(yypt[-0].yyv.i)); } break;
+case 183:
+#line	437	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(VARNF, (Node *) yypt[-0].yyv.cp); } break;
+case 184:
+#line	442	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-1].yyv.p); } break;
+	}
+	goto yystack;  /* stack new state and value */
+}
--- /dev/null
+++ b/utils/awk/ytab.h
@@ -1,0 +1,100 @@
+
+typedef union  {
+	Node	*p;
+	Cell	*cp;
+	int	i;
+	char	*s;
+}	YYSTYPE;
+extern	YYSTYPE	yylval;
+#define	FIRSTTOKEN	57346
+#define	PROGRAM	57347
+#define	PASTAT	57348
+#define	PASTAT2	57349
+#define	XBEGIN	57350
+#define	XEND	57351
+#define	NL	57352
+#define	ARRAY	57353
+#define	MATCH	57354
+#define	NOTMATCH	57355
+#define	MATCHOP	57356
+#define	FINAL	57357
+#define	DOT	57358
+#define	ALL	57359
+#define	CCL	57360
+#define	NCCL	57361
+#define	CHAR	57362
+#define	OR	57363
+#define	STAR	57364
+#define	QUEST	57365
+#define	PLUS	57366
+#define	AND	57367
+#define	BOR	57368
+#define	APPEND	57369
+#define	EQ	57370
+#define	GE	57371
+#define	GT	57372
+#define	LE	57373
+#define	LT	57374
+#define	NE	57375
+#define	IN	57376
+#define	ARG	57377
+#define	BLTIN	57378
+#define	BREAK	57379
+#define	CLOSE	57380
+#define	CONTINUE	57381
+#define	DELETE	57382
+#define	DO	57383
+#define	EXIT	57384
+#define	FOR	57385
+#define	FUNC	57386
+#define	SUB	57387
+#define	GSUB	57388
+#define	IF	57389
+#define	INDEX	57390
+#define	LSUBSTR	57391
+#define	MATCHFCN	57392
+#define	NEXT	57393
+#define	NEXTFILE	57394
+#define	ADD	57395
+#define	MINUS	57396
+#define	MULT	57397
+#define	DIVIDE	57398
+#define	MOD	57399
+#define	ASSIGN	57400
+#define	ASGNOP	57401
+#define	ADDEQ	57402
+#define	SUBEQ	57403
+#define	MULTEQ	57404
+#define	DIVEQ	57405
+#define	MODEQ	57406
+#define	POWEQ	57407
+#define	PRINT	57408
+#define	PRINTF	57409
+#define	SPRINTF	57410
+#define	ELSE	57411
+#define	INTEST	57412
+#define	CONDEXPR	57413
+#define	POSTINCR	57414
+#define	PREINCR	57415
+#define	POSTDECR	57416
+#define	PREDECR	57417
+#define	VAR	57418
+#define	IVAR	57419
+#define	VARNF	57420
+#define	CALL	57421
+#define	NUMBER	57422
+#define	STRING	57423
+#define	REGEXPR	57424
+#define	GETLINE	57425
+#define	RETURN	57426
+#define	SPLIT	57427
+#define	SUBSTR	57428
+#define	WHILE	57429
+#define	CAT	57430
+#define	NOT	57431
+#define	UMINUS	57432
+#define	POWER	57433
+#define	DECR	57434
+#define	INCR	57435
+#define	INDIRECT	57436
+#define	LASTTOKEN	57437
--- /dev/null
+++ b/utils/awk/ytabc.bak
@@ -1,0 +1,1623 @@
+
+#line	26	"/n/bopp/v7/bwk/awk/awkgram.y"
+#include <stdio.h>
+#include <string.h>
+#include "awk.h"
+
+void checkdup(Node *list, Cell *item);
+int yywrap(void) { return(1); }
+
+Node	*beginloc = 0;
+Node	*endloc = 0;
+int	infunc	= 0;	/* = 1 if in arglist or body of func */
+int	inloop	= 0;	/* = 1 if in while, for, do */
+char	*curfname = 0;	/* current function name */
+Node	*arglist = 0;	/* list of args for current function */
+
+#line	41	"/n/bopp/v7/bwk/awk/awkgram.y"
+typedef union  {
+	Node	*p;
+	Cell	*cp;
+	int	i;
+	char	*s;
+} YYSTYPE;
+extern	int	yyerrflag;
+#ifndef	YYMAXDEPTH
+#define	YYMAXDEPTH	150
+#endif
+YYSTYPE	yylval;
+YYSTYPE	yyval;
+#define	FIRSTTOKEN	57346
+#define	PROGRAM	57347
+#define	PASTAT	57348
+#define	PASTAT2	57349
+#define	XBEGIN	57350
+#define	XEND	57351
+#define	NL	57352
+#define	ARRAY	57353
+#define	MATCH	57354
+#define	NOTMATCH	57355
+#define	MATCHOP	57356
+#define	FINAL	57357
+#define	DOT	57358
+#define	ALL	57359
+#define	CCL	57360
+#define	NCCL	57361
+#define	CHAR	57362
+#define	OR	57363
+#define	STAR	57364
+#define	QUEST	57365
+#define	PLUS	57366
+#define	AND	57367
+#define	BOR	57368
+#define	APPEND	57369
+#define	EQ	57370
+#define	GE	57371
+#define	GT	57372
+#define	LE	57373
+#define	LT	57374
+#define	NE	57375
+#define	IN	57376
+#define	ARG	57377
+#define	BLTIN	57378
+#define	BREAK	57379
+#define	CLOSE	57380
+#define	CONTINUE	57381
+#define	DELETE	57382
+#define	DO	57383
+#define	EXIT	57384
+#define	FOR	57385
+#define	FUNC	57386
+#define	SUB	57387
+#define	GSUB	57388
+#define	IF	57389
+#define	INDEX	57390
+#define	LSUBSTR	57391
+#define	MATCHFCN	57392
+#define	NEXT	57393
+#define	NEXTFILE	57394
+#define	ADD	57395
+#define	MINUS	57396
+#define	MULT	57397
+#define	DIVIDE	57398
+#define	MOD	57399
+#define	ASSIGN	57400
+#define	ASGNOP	57401
+#define	ADDEQ	57402
+#define	SUBEQ	57403
+#define	MULTEQ	57404
+#define	DIVEQ	57405
+#define	MODEQ	57406
+#define	POWEQ	57407
+#define	PRINT	57408
+#define	PRINTF	57409
+#define	SPRINTF	57410
+#define	ELSE	57411
+#define	INTEST	57412
+#define	CONDEXPR	57413
+#define	POSTINCR	57414
+#define	PREINCR	57415
+#define	POSTDECR	57416
+#define	PREDECR	57417
+#define	VAR	57418
+#define	IVAR	57419
+#define	VARNF	57420
+#define	CALL	57421
+#define	NUMBER	57422
+#define	STRING	57423
+#define	REGEXPR	57424
+#define	GETLINE	57425
+#define	RETURN	57426
+#define	SPLIT	57427
+#define	SUBSTR	57428
+#define	WHILE	57429
+#define	CAT	57430
+#define	NOT	57431
+#define	UMINUS	57432
+#define	POWER	57433
+#define	DECR	57434
+#define	INCR	57435
+#define	INDIRECT	57436
+#define	LASTTOKEN	57437
+#define YYEOFCODE 1
+#define YYERRCODE 2
+
+#line	445	"/n/bopp/v7/bwk/awk/awkgram.y"
+
+
+void setfname(Cell *p)
+{
+	if (isarr(p))
+		SYNTAX("%s is an array, not a function", p->nval);
+	else if (isfcn(p))
+		SYNTAX("you can't define function %s more than once", p->nval);
+	curfname = p->nval;
+}
+
+int constnode(Node *p)
+{
+	return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
+}
+
+char *strnode(Node *p)
+{
+	return ((Cell *)(p->narg[0]))->sval;
+}
+
+Node *notnull(Node *n)
+{
+	switch (n->nobj) {
+	case LE: case LT: case EQ: case NE: case GT: case GE:
+	case BOR: case AND: case NOT:
+		return n;
+	default:
+		return op2(NE, n, nullnode);
+	}
+}
+
+void checkdup(Node *vl, Cell *cp)	/* check if name already in list */
+{
+	char *s = cp->nval;
+	for ( ; vl; vl = vl->nnext) {
+		if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
+			SYNTAX("duplicate argument %s", s);
+			break;
+		}
+	}
+}
+short	yyexca[] =
+{-1, 0,
+	1, 28,
+	8, 28,
+	9, 28,
+	12, 28,
+	13, 28,
+	16, 28,
+	45, 28,
+	46, 28,
+	48, 28,
+	54, 28,
+	55, 28,
+	56, 28,
+	58, 28,
+	60, 28,
+	78, 28,
+	86, 28,
+	87, 28,
+	88, 28,
+	89, 28,
+	90, 28,
+	91, 28,
+	95, 28,
+	97, 28,
+	98, 28,
+	101, 28,
+	102, 28,
+	105, 28,
+	108, 28,
+	109, 28,
+	110, 28,
+	-2, 0,
+-1, 1,
+	1, -1,
+	-2, 0,
+-1, 157,
+	15, 30,
+	-2, 0,
+-1, 176,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 63,
+-1, 177,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 64,
+-1, 178,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 65,
+-1, 179,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 66,
+-1, 180,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 67,
+-1, 181,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 68,
+-1, 183,
+	14, 0,
+	24, 0,
+	38, 0,
+	39, 0,
+	40, 0,
+	41, 0,
+	42, 0,
+	43, 0,
+	44, 0,
+	-2, 70,
+-1, 289,
+	24, 0,
+	44, 0,
+	-2, 53,
+-1, 333,
+	17, 30,
+	-2, 0,
+-1, 355,
+	17, 30,
+	-2, 0,
+};
+#define	YYNPROD	185
+#define	YYPRIVATE 57344
+#define	YYLAST	4177
+short	yyact[] =
+{
+  17, 277, 138,  66, 243, 228, 253,  54,  24,  43,
+ 125, 112, 200,  43, 103, 104, 100, 139, 102, 155,
+ 308, 185, 215, 249, 100, 253, 100, 100, 100, 107,
+ 105, 100, 122, 123, 124, 223, 107, 206,  43,  82,
+ 162,  43,  83, 103, 104,  10, 113, 314,   9, 252,
+  42,  22,  44, 244,  42,  22,  44, 103, 104, 134,
+ 142, 113, 146, 190, 278, 352, 149, 150, 152, 153,
+ 148, 276, 316, 163,  23, 100, 351, 350,  23,  42,
+  62,  44,  42,  22,  44,  11, 156, 168, 169,  85,
+ 253,  51, 321,  79,  80, 232, 190,  86, 135, 133,
+ 100, 318, 182, 320, 269, 258,  23, 100, 100, 100,
+ 100, 100, 100, 100, 108, 109, 110, 111, 233, 275,
+ 112, 234, 190, 110, 111,  43, 100, 112, 335, 190,
+ 190,  11, 203, 205, 190, 324, 278, 190, 190, 212,
+ 284, 190, 211, 265, 260, 190, 100, 259, 221,   3,
+ 141, 188, 100,  16, 226, 140, 331,   6, 156, 141,
+ 219, 230,   7, 100, 310,   6,  42, 170,  44, 167,
+   7, 158, 100, 157, 100, 131, 100, 100, 100, 100,
+ 100, 100, 100, 130, 100,  48, 251, 100, 100, 129,
+  49, 128, 236, 127, 100, 126, 120, 119,  52,  16,
+ 190,  19, 100, 312, 141, 274, 218, 100, 143, 100,
+ 100, 100,   4, 154, 100, 100, 217, 271, 144, 132,
+ 317,  50, 347, 361, 364, 270,   1, 115,  72,  40,
+ 224,   5, 100, 100, 100, 100, 163,  58, 163, 163,
+ 163, 163,  20,  67, 163, 222, 100, 293,  61, 288,
+ 294,  60, 238, 248,  81, 100, 100, 292,  96,   8,
+ 239, 159, 160,   2,   0,   0, 114,   0, 116, 117,
+ 118, 300, 301, 121, 164,   0, 282,   0, 285, 286,
+ 287, 289,   0, 100, 291,   0, 100, 100, 100,   0,
+ 100,   0, 100, 156,   0, 309,   0, 100,   0, 100,
+ 100,   0,   0, 100,   0, 100, 100, 100,   0,   0,
+   0,   0,   0, 334, 313, 165, 163,  96,   0,   0,
+   0,   0,   0,   0,   0,   0,   0, 341, 156, 342,
+ 333,   0, 340, 100,   0,   0,   0, 230, 100, 346,
+ 100,   0, 116,   0, 100, 100, 348,   0, 356,  96,
+ 194, 195, 196, 197, 198, 199, 337, 359,   0, 230,
+   0, 360, 362, 156,   0, 355,   0,   0, 207,   0,
+   0,   0, 238,   0,   0, 238, 238, 238,   0, 238,
+ 239, 238,   0, 239, 239, 239,   0, 239,  96, 239,
+   0,   0,  21,   0,  96,   0,   0,   0, 338,   0,
+   0,   0,   0,   0, 257, 242,   0,   0,  55,   0,
+   0,   0,   0,   0,  96,   0,  96,   0,  96,  96,
+  96,  96,  96,  96,  96,   0,  96, 238,   0,  96,
+  96,   0,   0,   0,   0, 239, 256, 164,   0, 164,
+ 164, 164, 164,   0,  96, 164,   0,   0,   0, 261,
+   0,  96,  96,  96,   0,   0,  96,  96,   0,   0,
+   0,   0,   0, 137,   0,   0, 166,   0,   0,   0,
+ 147,   0,   0,   0,  96, 279, 280, 281, 165,   0,
+ 165, 165, 165, 165,   0,   0, 165, 184,  96,   0,
+   0,   0,   0,   0,   0,   0,   0,  96,  96,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  74,   0,
+ 189, 191,   0,  15,   0,   0,   0, 164,   0,   0,
+   0,   0,   0,   0,   0, 242,   0, 106, 242, 242,
+ 242,   0, 242,   0, 242,   0,   0,   0,   0,  96,
+   0,  96,  96, 137,   0,  96,   0,  96,  96,  96,
+ 229,   0,   0, 220,   0,   0,   0, 137, 165,  15,
+   0,  15,   0, 227,   0, 235,   0,   0, 145,   0,
+   0,   0,   0,   0, 151,  96,   0, 137, 137,   0,
+ 242,   0,  96,   0,   0,   0,  96,  96,   0,   0,
+   0,   0,   0, 171, 173, 175, 176, 177, 178, 179,
+ 180, 181, 183,   0,   0,   0,   0,   0,   0,   0,
+ 186, 187,   0, 262, 263, 264,   0, 266, 267, 268,
+   0,   0, 201,   0,   0,   0,   0,   0, 201, 201,
+   0, 273,   0,   0, 290, 208, 209, 210, 201, 213,
+ 214, 189,   0,   0,   0, 297,   0,   0,   0, 101,
+   0,   0,   0, 295,   0,   0,   0, 303,   0,   0,
+ 240,   0,   0,   0,   0,   0,   0, 231,   0, 311,
+ 106,  98,  97,   0,   0,   0, 245,   0,   0, 137,
+ 241,  43,  28,   0,  30,   0,   0,   0,   0,   0,
+   0,  46,  47,   0,  34,   0,  35,   0, 254,   0,
+ 255,   0,   0,   0,   0,   0,   0, 336, 323, 326,
+ 328, 329,   0,   0,  38,   0,   0,   0, 189,   0,
+   0,   0,  42,  22,  44,  29,  36,  39,   0, 237,
+ 315,  33,   0,  37,  41,   0,   0,  27,  26,   0,
+   0,  99,   0,   0,  31,  32,  23,   0,   0,   0,
+   0,   0,   0, 201,   0,   0, 357, 137,   0, 296,
+   0,   0,   0,   0,   0,   0, 298,   0,   0,   0,
+ 363, 299, 302, 365,   0, 304, 305, 306,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0, 106,   0,   0,   0,   0,   0,  75,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  16,  18,
+   0,  68,  45,   0, 358,   0,   0,   0, 332,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0, 339,   0,   0,   0,   0,   0, 343,   0,
+ 344,  43,  28,  56,  30,  57,  73,  69,  59,  70,
+   0,  46,  47,  71,  34,   0,  35,  63,  64,   0,
+   0,   0,   0,   0,   0,   0,   0,  75,   0,   0,
+   0,   0,  77,  78,  38,  53,   0,  16,  18,   0,
+  68,  45,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  65,  37,  41,  76,   0,  27,  26,   0,
+   0,  25,   0,   0,  31,  32,  23,   0,   0,   0,
+  43,  28,  56,  30,  57,  73,  69,  59,  70,   0,
+  46,  47,  71,  34,   0,  35,  63,  64,   0,   0,
+   0,   0,   0,   0,  75,   0,   0,   0,   0,   0,
+   0,  77,  78,  38,  16,  18,   0,  68,  45,   0,
+ 307,  42,  22,  44,  29,  36,  39,   0,   0,   0,
+  33,  65,  37,  41,  76,   0,  27,  26,   0,   0,
+  25,   0,   0,  31,  32,  23,   0,  43,  28,  56,
+  30,  57,  73,  69,  59,  70,   0,  46,  47,  71,
+  34,   0,  35,  63,  64,   0,   0,   0,   0,   0,
+   0,   0,   0,  75,   0,   0,   0,   0,  77,  78,
+  38, 272,   0,  16,  18,   0,  68,  45,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,  65,  37,
+  41,  76,   0,  27,  26,   0,   0,  25,   0,   0,
+  31,  32,  23,   0,   0,   0,  43,  28,  56,  30,
+  57,  73,  69,  59,  70,   0,  46,  47,  71,  34,
+   0,  35,  63,  64,   0,   0,   0,   0,   0,   0,
+  75,   0,   0,   0,   0,   0,   0,  77,  78,  38,
+  16,  18,   0,  68,  45,   0, 247,  42,  22,  44,
+  29,  36,  39,   0,   0,   0,  33,  65,  37,  41,
+  76,   0,  27,  26,   0,   0,  25,   0,   0,  31,
+  32,  23,   0,  43,  28,  56,  30,  57,  73,  69,
+  59,  70,   0,  46,  47,  71,  34,   0,  35,  63,
+  64,   0,   0,   0,   0,   0,   0,  75,   0,   0,
+   0,   0,   0,   0,  77,  78,  38,  16,  18,   0,
+  68,  45,   0, 246,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33,  65,  37,  41,  76,   0,  27,
+  26,   0,   0,  25,   0,   0,  31,  32,  23,   0,
+  43,  28,  56,  30,  57,  73,  69,  59,  70,   0,
+  46,  47,  71,  34,   0,  35,  63,  64,   0,   0,
+   0,   0,   0,   0,  75,   0,   0,   0,   0,   0,
+   0,  77,  78,  38,  16,  18,   0,  68,  45,   0,
+ 225,  42,  22,  44,  29,  36,  39,   0,   0,   0,
+  33,  65,  37,  41,  76,   0,  27,  26,   0,   0,
+  25,   0,   0,  31,  32,  23,   0,  43,  28,  56,
+  30,  57,  73,  69,  59,  70,   0,  46,  47,  71,
+  34,   0,  35,  63,  64,   0,   0,   0,   0,   0,
+   0,  75,   0,   0,   0,   0,   0,   0,  77,  78,
+  38,  16,  18,   0,  68,  45,   0, 216,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,  65,  37,
+  41,  76,   0,  27,  26,   0,   0,  25,   0,   0,
+  31,  32,  23,   0,  43,  28,  56,  30,  57,  73,
+  69,  59,  70,   0,  46,  47,  71,  34,   0,  35,
+  63,  64,   0,   0,   0,   0,   0,   0,  75,   0,
+   0,   0,   0,   0,   0,  77,  78,  38,  16,  18,
+   0,  68,  45,   0, 136,  42,  22,  44,  29,  36,
+  39,   0,   0,   0,  33,  65,  37,  41,  76,   0,
+  27,  26,   0,   0,  25,   0,   0,  31,  32,  23,
+   0,  43,  28,  56,  30,  57,  73,  69,  59,  70,
+   0,  46,  47,  71,  34,   0,  35,  63,  64,   0,
+   0,   0,   0,   0,   0,  75,   0,   0,   0,   0,
+   0,   0,  77,  78,  38,  16,  18,   0,  68,  45,
+   0,   0,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  65,  37,  41,  76,   0,  27,  26,   0,
+   0,  25,   0,   0,  31,  32,  23,   0,  43,  28,
+  56,  30,  57,  73,  69,  59,  70,   0,  46,  47,
+  71,  34,   0,  35,  63,  64,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  77,
+  78,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,   0,   0,  33,  65,
+  37,  41,  76,   0,  27,  26,   0,   0,  25,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 330,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 327,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 190,   0, 101,  95,   0,   0,
+ 325,   0,   0,   0,   0,   0,   0,  93,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  98,  97,
+   0,  87,  88,  89,  90,  91,  92,  94,  43,  28,
+   0,  30,   0,   0,   0,   0,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  38,   0,   0,   0,   0,   0,   0,   0,  42,
+  22,  44,  29,  36,  39,   0,  84,   0,  33,   0,
+  37,  41,   0,   0,  27,  26,   0,   0,  99,   0,
+   0,  31,  32,  23, 141,   0,   0, 101,  95, 140,
+   0,   0,   0,   0,   0,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23, 190,   0, 101,  95,   0,
+   0, 192,   0,   0,   0,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 354,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 353,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26, 101,  95,  99,
+   0, 349,  31,  32,  23,   0,   0,   0,  93,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+  97,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,  84,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23, 101,  95, 345,   0,   0,
+   0,   0,   0,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 322,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 319,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26, 101,  95,  99,   0, 278,
+  31,  32,  23,   0,   0,   0,  93,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,  98,  97,   0,
+  87,  88,  89,  90,  91,  92,  94,  43,  28,   0,
+  30,   0,   0,   0,   0,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  38,   0,   0,   0,   0,   0,   0,   0,  42,  22,
+  44,  29,  36,  39,   0,  84,   0,  33,   0,  37,
+  41,   0,   0,  27,  26,   0, 190,  99, 101,  95,
+  31,  32,  23,   0,   0,   0,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0, 192,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84, 250,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,  97,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0,  84,   0,
+  33,   0,  37,  41,   0,   0,  27,  26, 101,  95,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,  93,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  98,   0,   0,  87,  88,  89,  90,  91,  92,  94,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39, 101,  95,   0,
+  33,   0,  37,  41,   0,   0,  27,  26,  93,   0,
+  99,   0,   0,  31,  32,  23,   0,   0,   0,   0,
+   0,   0,  87,  88,  89,  90,  91,  92,  94,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0,  75,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  38,  18,   0,   0,  45,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0,   0,   0,  33,
+   0,  37,  41,   0,   0,  27,  26,   0,   0,  99,
+   0,   0,  31,  32,  23,  43,  28,   0,  30,   0,
+  73,   0,   0,   0,   0,  46,  47,   0,  34,   0,
+  35,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  77,  78,  38,   0,
+   0, 101,   0,   0,   0,   0,  42,  22,  44,  29,
+  36,  39, 240,   0,   0,  33,   0,  37,  41,   0,
+   0,  27,  26,  98,  97,  25,   0,   0,  31,  32,
+  23,   0, 241,  43,  28,   0,  30,   0,   0,   0,
+   0,   0,   0,  46,  47,   0,  34,   0,  35,   0,
+   0,   0,   0,   0,  12,  13,   0,   0,  16,  18,
+   0,   0,  45,   0,   0,   0,  38,   0,   0,   0,
+   0,   0,   0,   0,  42,  22,  44,  29,  36,  39,
+   0, 237,   0,  33,   0,  37,  41,   0,   0,  27,
+  26,  43,  28,  99,  30,   0,  31,  32,  23,   0,
+  14,  46,  47,   0,  34,   0,  35,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,  38,   0,   0, 101,   0,   0,
+   0,   0,  42,  22,  44,  29,  36,  39, 240,   0,
+   0,  33,   0,  37,  41,   0,   0,  27,  26,  98,
+   0,  25,   0,   0,  31,  32,  23,   0, 241,  43,
+  28,   0,  30,   0,   0,   0,   0,   0,   0,  46,
+  47,   0,  34,   0,  35,   0,   0,   0,   0,   0,
+   0,   0, 141,   0,   0,  18,   0, 140,  45,   0,
+   0,   0,  38,   0,   0,   0,   0,   0,   0,   0,
+  42,  22,  44,  29,  36,  39,   0, 253,   0,  33,
+  18,  37,  41,  45,   0,  27,  26,  43,  28,  99,
+  30,   0,  31,  32,  23,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  43,  28,   0,  30,   0,   0,   0,   0,
+  38,   0,  46,  47,   0,  34,   0,  35,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33,   0,  37,
+  41,   0,   0,  27,  26,  38,   0,  25, 101,   0,
+  31,  32,  23,  42,  22,  44,  29,  36,  39, 240,
+   0,   0,  33,   0,  37,  41,   0,   0,  27,  26,
+   0,   0,  25,   0,   0,  31,  32,  23,   0, 241,
+  43,  28,   0,  30,   0,   0,   0,   0,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0, 174,   0,   0, 283,   0,   0,  45,
+   0,   0,   0,  38,   0,   0,   0,   0,   0,   0,
+   0,  42,  22,  44,  29,  36,  39,   0, 172,   0,
+  33, 283,  37,  41,  45,   0,  27,  26,  43,  28,
+  99,  30,   0,  31,  32,  23,   0,   0,  46,  47,
+   0,  34,   0,  35,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  43,  28,   0,  30,   0,   0,   0,
+   0,  38,   0,  46,  47,   0,  34,   0,  35,  42,
+  22,  44,  29,  36,  39,   0, 253,   0,  33, 283,
+  37,  41,  45,   0,  27,  26,  38,   0,  25,   0,
+   0,  31,  32,  23,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33,  18,  37,  41,  45, 204,  27,
+  26,  43,  28,  25,  30,   0,  31,  32,  23,   0,
+   0,  46,  47,   0,  34,   0,  35,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  43,  28,   0,  30,
+   0,   0,   0,   0,  38,   0,  46,  47,   0,  34,
+   0,  35,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,  33,  18,  37,  41,  45, 202,  27,  26,  38,
+   0,  25,   0,   0,  31,  32,  23,  42,  22,  44,
+  29,  36,  39,   0, 174,   0,  33,  18,  37,  41,
+  45,   0,  27,  26,  43,  28,  25,  30,   0,  31,
+  32,  23,   0,   0,  46,  47,   0,  34,   0,  35,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,  43,
+  28,   0,  30,   0,   0,   0,   0,  38,   0,  46,
+  47,   0,  34,   0,  35,  42,  22,  44,  29,  36,
+  39,   0, 172,   0,  33,  18,  37,  41,  45,   0,
+  27,  26,  38,   0,  25,   0,   0,  31,  32,  23,
+  42,  22,  44,  29,  36,  39,   0,   0,   0,  33,
+  18,  37,  41,  45,   0,  27,  26,  43,  28,  25,
+  30,   0,  31,  32,  23,   0,   0,  46,  47,   0,
+  34,   0,  35,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  43,  28,   0,  30,   0,   0,   0,   0,
+  38,   0,  46,  47,   0,  34,   0,  35,  42,  22,
+  44,  29,  36,  39,   0,   0,   0,  33, 283,  37,
+  41,  45,   0,  27,  26,  38,   0,  25,   0,   0,
+  31,  32,  23,  42,  22,  44,  29,  36,  39,   0,
+   0,   0,  33, 101,  37,  41,   0,   0,  27,  26,
+  43,  28,  25,  30,   0,  31,  32,  23,   0,   0,
+  46,  47,   0,  34,   0,  35,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,  43,  28,   0,  30,   0,
+   0,   0,   0,  38,   0,  46,  47,   0,  34,   0,
+  35,  42,  22,  44,  29,  36,  39,   0,   0, 193,
+  33, 161,  37,  41,  45,   0,  27,  26,  38,   0,
+  25,   0,   0,  31,  32,  23,  42,  22,  44,  29,
+  36,  39,   0,   0,   0,  33, 101,  37,  41,  45,
+   0,  27,  26,  43,  28,  99,  30,   0,  31,  32,
+  23,   0,   0,  46,  47,   0,  34,   0,  35,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  43,  28,
+   0,  30,   0,   0,   0,   0,  38,   0,  46,  47,
+   0,  34,   0,  35,  42,  22,  44,  29,  36,  39,
+   0,   0,   0,  33, 101,  37,  41,   0,   0,  27,
+  26,  38,   0,  25,   0,   0,  31,  32,  23,  42,
+  22,  44,  29,  36,  39,   0,   0,   0,  33, 101,
+  37,  41,   0,   0,  27,  26,  43,  28,  25,  30,
+   0,  31,  32,  23,   0,   0,  46,  47,   0,  34,
+   0,  35,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  43,  28,   0,  30,   0,   0,   0,   0,  38,
+   0,  46,  47,   0,  34,   0,  35,  42,  22,  44,
+  29,  36,  39,   0,   0,   0,  33,   0,  37,  41,
+   0,   0,  27,  26,  38,   0,  99,   0,   0,  31,
+  32,  23,  42,  22,  44,  29,  36,  39,   0,   0,
+   0,   0,   0,  37,  41,   0,   0,  27,  26,   0,
+   0,  99,   0,   0,  31,  32,  23
+};
+short	yypact[] =
+{
+ 147,-1000,-1000,-1000,3266, 175,-1000,-1000, 155,-1000,
+ 187, 865, 141, 141, -47,2905,-1000, -51,3817,-1000,
+  13,  42,-1000,4041,-1000,3983,4041,4041, 184, 183,
+4041, -36, -36, -32, 182, 180,-1000, 178, 176,-1000,
+ 170, 162,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+3266, 865,3817,-1000,1336,-1000, 140, 140, 198,3392,
+-1000,1403, 865, 140, 140,3392, 140,-1000, 194,-1000,
+ 160, 158,3958,  -7,2905,-1000, 156,-1000,-1000, 865,
+ 865, 154,-1000,-1000,3817,3792,3734,3817,3817,3817,
+3817,3817,3817,3817,  -7, -74,  13,-1000,-1000,4041,
+ -94,3817,3817,-1000,-1000, 134,1904,3900,4041,4041,
+4041,4041,4041,3817,-1000,-1000, -96, -96, -96,3709,
+3651,  13,-1000,-1000,  -5,4041,3817,3817,3817,3817,
+3817,3817, -70,-1000,1269, 141,-1000,-1000,-1000, 196,
+ 194,-1000,-1000,-1000,1403,1804,-1000, -44,1202,-1000,
+-1000,1804,-1000,-1000,1403,-1000, 196,3140,3817,  81,
+ 189,3817,3208, -65,-1000,  13,  34,3817,1135,1068,
+ -63,2815,-1000,2995,-1000,3074,4066,4066,4066,4066,
+4066,4066,-1000,4066,-1000, -36,2725,2905,   5,3417,
+-1000,3417,-1000,4041, -96,  20,  20, -96, -96, -96,
+  85,2905,-1000, 130,-1000, 127,4041,  13,2635,2635,
+2635, 126, 189,2635,2635,  88,-1000, 865,-1000,-1000,
+-1000,-1000,1001,-1000, 195,-1000,-1000,-1000, 104,  27,
+-1000,2542,4041,4041,4041,3626, 123,3875,3568,3543,
+3875,  -7,  13,3875,3817,2542,-1000,-1000, 119,-1000,
+3817,-1000,  -7,-1000,2905,2905,  13,3417,-1000,-1000,
+-1000,  13,3417,3417,  80,-1000,3417,3417,3417,-1000,
+ 932, -79,-1000,-1000,-1000, 149,  -7, 193,-1000,  13,
+  13,  13,3208,3817,   3, 636,3334,3485,-1000,4066,
+-1000,3208,  52, 193, 193,  15,2905,-1000,2905,2452,
+  86,  75,2362, 118,1703,1603,1503,-1000, 143,3817,
+ 194,  47,-1000, 111,  -7,3875,-1000, 141,-1000,-1000,
+-1000,-1000,-1000,3417,-1000,-1000,  -4,-1000,  -4,3417,
+-1000,3817,2272,3140, 193,   3,-1000,3208, 865,2174,
+  60,  59,  48,2084,1994, 194,  47,1403, 796,-1000,
+-1000,-1000,-1000,-1000, 140,3140, 193,-1000,-1000,-1000,
+  47,1403, 193,-1000,1403,-1000
+};
+short	yypgo[] =
+{
+   0, 263, 508,  40,  30, 262,  12, 261, 242, 201,
+  45,  48, 259,   8,   3,   5, 408,   7,   0, 392,
+ 254, 253, 251, 248, 245, 243, 237,   2, 231, 212,
+  80, 230,   1, 404,  17,  19,  97,  89, 229, 228,
+ 226, 224, 223, 222, 220, 219, 218, 217, 213
+};
+short	yyr1[] =
+{
+   0,  40,  40,  36,  36,  37,  37,  33,  33,  26,
+  26,  24,  24,  41,  22,  42,  22,  43,  22,  20,
+  20,  23,  30,  30,  34,  34,  35,  35,  29,  29,
+  15,  15,   1,   1,  10,  11,  11,  11,  11,  11,
+  11,  11,  44,  11,  12,  12,   6,   6,   3,   3,
+   3,   3,   3,   3,   3,   3,   3,   3,   3,   2,
+   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+   2,   2,   2,   2,   2,   2,   2,   2,   4,   4,
+   5,   5,   7,   7,   7,  39,  39,  28,  28,  28,
+  28,  31,  31,   9,   9,  45,  13,  32,  32,  14,
+  14,  14,  14,  14,  14,  14,  14,  27,  27,  16,
+  16,  46,  47,  16,  16,  16,  16,  16,  16,  16,
+  16,  16,  16,  16,  16,  48,  16,  16,  17,  17,
+  38,  38,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,   8,   8,   8,   8,   8,   8,
+   8,   8,   8,   8,  18,  18,  18,  18,  21,  21,
+  21,  19,  19,  19,  25
+};
+short	yyr2[] =
+{
+   0,   1,   1,   1,   2,   1,   2,   1,   2,   1,
+   2,   1,   2,   0,  12,   0,  10,   0,   8,   1,
+   1,   4,   1,   2,   1,   2,   0,   1,   0,   1,
+   0,   1,   1,   3,   1,   1,   4,   3,   6,   3,
+   4,   4,   0,   9,   1,   3,   1,   3,   3,   5,
+   3,   3,   3,   3,   3,   5,   2,   1,   1,   3,
+   5,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+   3,   3,   5,   4,   3,   2,   1,   1,   3,   3,
+   1,   3,   0,   1,   3,   1,   1,   1,   1,   2,
+   2,   1,   2,   1,   2,   0,   4,   1,   2,   4,
+   4,   4,   2,   5,   2,   1,   1,   1,   2,   2,
+   2,   0,   0,   9,   3,   2,   1,   4,   2,   3,
+   2,   2,   3,   2,   2,   0,   3,   2,   1,   2,
+   1,   1,   4,   3,   3,   3,   3,   3,   3,   2,
+   2,   2,   3,   4,   1,   3,   4,   2,   2,   2,
+   2,   2,   4,   3,   2,   1,   6,   6,   3,   6,
+   6,   1,   8,   8,   6,   4,   1,   6,   6,   8,
+   8,   8,   6,   1,   1,   4,   1,   2,   0,   1,
+   3,   1,   1,   1,   4
+};
+short	yychk[] =
+{
+-1000, -40,  -1,   2, -29, -28,  10,  15, -12, -11,
+ -10, -30,   8,   9,  54,  -2,  12, -18,  13,  -9,
+  -8, -19,  87, 110, -13, 105, 102, 101,  46,  89,
+  48, 108, 109,  95,  58,  60,  90,  97,  78,  91,
+ -38,  98,  86,  45,  88,  16,  55,  56,  10,  15,
+ -29, -30,  11,  10, -17, -16,  47,  49, -26,  52,
+ -22, -23, -30,  61,  62,  96, -14, -25,  15,  51,
+  53,  57, -39,  50,  -2,   2,  99,  76,  77, -30,
+ -30, -20,  86,  89,  93, -37, -36,  38,  39,  40,
+  41,  42,  43,  24,  44,  14,  -8,  36,  35, 105,
+ -18,  13,  69, 108, 109,  -4,  -2,  16, 101, 102,
+ 103, 104, 107,  19,  -8,  -9,  -8,  -8,  -8,  13,
+  13,  -8, -18, -18, -18,  42,  13,  13,  13,  13,
+  13,  13, -45, -11, -17, -10,  18, -16, -27, -34,
+  15,  10, -27,  10, -46,  -2, -27, -16, -17, -27,
+ -27,  -2, -27, -27, -48, -35, -34,  13,  13,  -7,
+  -5,  13,  -3, -18,  -9,  -8, -19,  13, -17, -17,
+  13,  -2,  10,  -2,  10,  -2,  -2,  -2,  -2,  -2,
+  -2,  -2, -13,  -2, -19,  95,  -2,  -2,  17, -33,
+  11, -33,  17,  69,  -8,  -8,  -8,  -8,  -8,  -8,
+  -6,  -2,  17,  -6,  17,  -6,  42,  -8,  -2,  -2,
+  -2,  -6, -13,  -2,  -2,  92,  18, -30,  10, -35,
+ -16, -27, -24,  79, -31,  18, -27, -16, -15, -19,
+ -14,  -2,  14,  37,  40, -33,  -4,  93, -37, -36,
+  24,  44,  -8,  69,  19,  -2,  18,  18, -21,  86,
+  94, -18,  44,  10,  -2,  -2,  -8, -33,  20,  17,
+  17,  -8, -33, -33, -33,  17, -33, -33, -33,  16,
+ -17, -47,  10, -16,  10,  15,  44, -32,  17,  -8,
+  -8,  -8,  -3,  13,  17,  -3,  -3,  -3, -13,  -3,
+ -19,  -3,  -6, -32, -32, -33,  -2, -19,  -2,  -2,
+ -13, -13,  -2, -19,  -2,  -2,  -2,  18,  99, -35,
+  15, -19,  10,  -4,  44,  94,  20, -44,  86,  17,
+  17,  17,  17, -33,  17,  17, -33,  17, -33, -33,
+  17,  13,  -2, -35, -32,  17, -19,  -3, -30,  -2,
+ -13, -18, -18,  -2,  -2,  15, -15, -43, -17,  17,
+  17,  17,  17,  17,  17, -35, -32, -16,  18, -27,
+ -15, -42, -32, -16, -41, -16
+};
+short	yydef[] =
+{
+  -2,  -2,   1,   2,  32,  29,  87,  88,  28,  44,
+  35,   0,   0,   0,   0,  34,  22, 173,   0,  76,
+  77, 174, 176,   0,  93,   0,   0,   0, 144,   0,
+   0,   0,   0, 155,   0,   0, 161,   0,   0, 166,
+   0,   0, 181, 182, 183,  95, 130, 131,  89,  90,
+  33,   0,   0,  23,   0, 128,   0,   0, 111,   0,
+ 116,   0,   0,   0,   0,   0,   0, 125,  26,   9,
+   0,   0,  82,   0, 105, 106,   0,  85,  86,   0,
+   0,   0,  19,  20,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  75,   5,   3,   0,
+ 173,   0,   0, 150, 151,   0,   0,   0,   0,   0,
+   0,   0,   0,   0, 177,  94, 141, 139, 140,   0,
+   0, 147, 148, 149, 154,   0,   0,   0,   0,   0,
+   0,   0,   0,  45,   0,  37,  39, 129, 109, 107,
+  26,  24, 110,  10,   0,   0, 115, 118,   0, 120,
+ 121,   0, 123, 124,   0, 127,  27,  -2,   0, 102,
+  83,   0,  80, 173,  57,  58, 104,   0,   0,   0,
+ 178,   0,   6,  61,   4,  62,  -2,  -2,  -2,  -2,
+  -2,  -2,  69,  -2,  71,  74,   0,  59,   0,   0,
+   7,   0, 158,   0, 136, 133, 134, 135, 137, 138,
+   0,  46, 142,   0, 145,   0,   0, 153,   0,   0,
+   0,   0,  93,   0,   0,   0,  36,   0,  25, 108,
+ 112, 114,   0,  11, 119,  91, 122, 126,   0, 174,
+  31,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,  56,   0,   0,   0,  40,  41,   0, 179,
+   0,  73,   0,   8,  79,  78, 132,   0, 175, 143,
+ 146, 152,   0,   0,   0, 165,   0,   0,   0,  96,
+   0,   0,  12, 117,  92,  26,   0,  21,  97,  99,
+ 100, 101,  81,   0,  84,   0,  50,  51,  52,  -2,
+  54,  48,   0, 184,  42,   0,  60,  72,  47,   0,
+  93,  93,   0,   0,   0,   0,   0,  38,   0,   0,
+  26,   0,  98,   0,   0,   0, 103,   0, 180, 156,
+ 157, 159, 160,   0, 164, 167,   0, 168,   0,   0,
+ 172,   0,   0,  -2,  17,   0,  55,  49,   0,   0,
+  93,   0,   0,   0,   0,  26,   0,   0,   0, 162,
+ 163, 169, 170, 171,   0,  -2,  15,  18,  43, 113,
+   0,   0,  13,  16,   0,  14
+};
+short	yytok1[] =
+{
+   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0, 104,   0,   0,
+  13,  17, 103, 101,  11, 102,   0,  16,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  94,  15,
+   0,   0,   0,  93,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,  19,   0,  20,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,  12,  14,  18
+};
+short	yytok2[] =
+{
+   2,   3,   4,   5,   6,   7,   8,   9,  10,  21,
+  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
+  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
+  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
+  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
+  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
+  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,
+  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,
+  92,  95,  96,  97,  98,  99, 100, 105, 106, 107,
+ 108, 109, 110, 111
+};
+long	yytok3[] =
+{
+   0
+};
+#define YYFLAG 		-1000
+#define YYERROR		goto yyerrlab
+#define YYACCEPT	return(0)
+#define YYABORT		return(1)
+#define	yyclearin	yychar = -1
+#define	yyerrok		yyerrflag = 0
+
+#ifdef	yydebug
+#include	"y.debug"
+#else
+#define	yydebug		0
+char*	yytoknames[1];		/* for debugging */
+char*	yystates[1];		/* for debugging */
+#endif
+
+/*	parser for yacc output	*/
+
+int	yynerrs = 0;		/* number of errors */
+int	yyerrflag = 0;		/* error recovery flag */
+
+extern	int	fprint(int, char*, ...);
+extern	int	sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+	static char x[10];
+
+	if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+	if(yytoknames[yyc-1])
+		return yytoknames[yyc-1];
+	sprintf(x, "<%d>", yyc);
+	return x;
+}
+
+char*
+yystatname(int yys)
+{
+	static char x[10];
+
+	if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+	if(yystates[yys])
+		return yystates[yys];
+	sprintf(x, "<%d>\n", yys);
+	return x;
+}
+
+long
+yylex1(void)
+{
+	long yychar;
+	long *t3p;
+	int c;
+
+	yychar = yylex();
+	if(yychar <= 0) {
+		c = yytok1[0];
+		goto out;
+	}
+	if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+		c = yytok1[yychar];
+		goto out;
+	}
+	if(yychar >= YYPRIVATE)
+		if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+			c = yytok2[yychar-YYPRIVATE];
+			goto out;
+		}
+	for(t3p=yytok3;; t3p+=2) {
+		c = t3p[0];
+		if(c == yychar) {
+			c = t3p[1];
+			goto out;
+		}
+		if(c == 0)
+			break;
+	}
+	c = 0;
+
+out:
+	if(c == 0)
+		c = yytok2[1];	/* unknown char */
+	if(yydebug >= 3)
+		printf("lex %.4lX %s\n", yychar, yytokname(c));
+	return c;
+}
+
+int
+yyparse(void)
+{
+	struct
+	{
+		YYSTYPE	yyv;
+		int	yys;
+	} yys[YYMAXDEPTH], *yyp, *yypt;
+	short *yyxi;
+	int yyj, yym, yystate, yyn, yyg;
+	YYSTYPE save1, save2;
+	int save3, save4;
+	long yychar;
+
+	save1 = yylval;
+	save2 = yyval;
+	save3 = yynerrs;
+	save4 = yyerrflag;
+
+	yystate = 0;
+	yychar = -1;
+	yynerrs = 0;
+	yyerrflag = 0;
+	yyp = &yys[-1];
+	goto yystack;
+
+ret0:
+	yyn = 0;
+	goto ret;
+
+ret1:
+	yyn = 1;
+	goto ret;
+
+ret:
+	yylval = save1;
+	yyval = save2;
+	yynerrs = save3;
+	yyerrflag = save4;
+	return yyn;
+
+yystack:
+	/* put a state and value onto the stack */
+	if(yydebug >= 4)
+		printf("char %s in %s", yytokname(yychar), yystatname(yystate));
+
+	yyp++;
+	if(yyp >= &yys[YYMAXDEPTH]) { 
+		yyerror("yacc stack overflow"); 
+		goto ret1; 
+	}
+	yyp->yys = yystate;
+	yyp->yyv = yyval;
+
+yynewstate:
+	yyn = yypact[yystate];
+	if(yyn <= YYFLAG)
+		goto yydefault; /* simple state */
+	if(yychar < 0)
+		yychar = yylex1();
+	yyn += yychar;
+	if(yyn < 0 || yyn >= YYLAST)
+		goto yydefault;
+	yyn = yyact[yyn];
+	if(yychk[yyn] == yychar) { /* valid shift */
+		yychar = -1;
+		yyval = yylval;
+		yystate = yyn;
+		if(yyerrflag > 0)
+			yyerrflag--;
+		goto yystack;
+	}
+
+yydefault:
+	/* default state action */
+	yyn = yydef[yystate];
+	if(yyn == -2) {
+		if(yychar < 0)
+			yychar = yylex1();
+
+		/* look through exception table */
+		for(yyxi=yyexca;; yyxi+=2)
+			if(yyxi[0] == -1 && yyxi[1] == yystate)
+				break;
+		for(yyxi += 2;; yyxi += 2) {
+			yyn = yyxi[0];
+			if(yyn < 0 || yyn == yychar)
+				break;
+		}
+		yyn = yyxi[1];
+		if(yyn < 0)
+			goto ret0;
+	}
+	if(yyn == 0) {
+		/* error ... attempt to resume parsing */
+		switch(yyerrflag) {
+		case 0:   /* brand new error */
+			yyerror("syntax error");
+			if(yydebug >= 1) {
+				printf("%s", yystatname(yystate));
+				printf("saw %s\n", yytokname(yychar));
+			}
+yyerrlab:
+			yynerrs++;
+
+		case 1:
+		case 2: /* incompletely recovered error ... try again */
+			yyerrflag = 3;
+
+			/* find a state where "error" is a legal shift action */
+			while(yyp >= yys) {
+				yyn = yypact[yyp->yys] + YYERRCODE;
+				if(yyn >= 0 && yyn < YYLAST) {
+					yystate = yyact[yyn];  /* simulate a shift of "error" */
+					if(yychk[yystate] == YYERRCODE)
+						goto yystack;
+				}
+
+				/* the current yyp has no shift onn "error", pop stack */
+				if(yydebug >= 2)
+					printf("error recovery pops state %d, uncovers %d\n",
+						yyp->yys, (yyp-1)->yys );
+				yyp--;
+			}
+			/* there is no state on the stack with an error shift ... abort */
+			goto ret1;
+
+		case 3:  /* no shift yet; clobber input char */
+			if(yydebug >= YYEOFCODE)
+				printf("error recovery discards %s\n", yytokname(yychar));
+			if(yychar == YYEOFCODE)
+				goto ret1;
+			yychar = -1;
+			goto yynewstate;   /* try again in the same state */
+		}
+	}
+
+	/* reduction by production yyn */
+	if(yydebug >= 2)
+		printf("reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+	yypt = yyp;
+	yyp -= yyr2[yyn];
+	yyval = (yyp+1)->yyv;
+	yym = yyn;
+
+	/* consult goto table to find next state */
+	yyn = yyr1[yyn];
+	yyg = yypgo[yyn];
+	yyj = yyg + yyp->yys + 1;
+
+	if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+		yystate = yyact[yyg];
+	switch(yym) {
+		
+case 1:
+#line	98	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (errorflag==0)
+			winner = (Node *)stat3(PROGRAM, beginloc, yypt[-0].yyv.p, endloc); } break;
+case 2:
+#line	100	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyclearin; bracecheck(); SYNTAX("bailing out"); } break;
+case 13:
+#line	124	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 14:
+#line	125	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat4(FOR, yypt[-9].yyv.p, notnull(yypt[-6].yyv.p), yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 15:
+#line	126	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 16:
+#line	127	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat4(FOR, yypt[-7].yyv.p, NIL, yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 17:
+#line	128	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 18:
+#line	129	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat3(IN, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), yypt[-0].yyv.p); } break;
+case 19:
+#line	133	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ setfname(yypt[-0].yyv.cp); } break;
+case 20:
+#line	134	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ setfname(yypt[-0].yyv.cp); } break;
+case 21:
+#line	138	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-1].yyv.p); } break;
+case 26:
+#line	150	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.i = 0; } break;
+case 28:
+#line	155	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.i = 0; } break;
+case 30:
+#line	161	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 32:
+#line	166	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 33:
+#line	167	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 34:
+#line	171	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-0].yyv.p); } break;
+case 35:
+#line	175	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, yypt[-0].yyv.p, stat2(PRINT, rectonode(), NIL)); } break;
+case 36:
+#line	176	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 37:
+#line	177	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = pa2stat(yypt[-2].yyv.p, yypt[-0].yyv.p, stat2(PRINT, rectonode(), NIL)); } break;
+case 38:
+#line	178	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = pa2stat(yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 39:
+#line	179	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(PASTAT, NIL, yypt[-1].yyv.p); } break;
+case 40:
+#line	181	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ beginloc = linkum(beginloc, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 41:
+#line	183	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ endloc = linkum(endloc, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 42:
+#line	184	"/n/bopp/v7/bwk/awk/awkgram.y"
+{infunc++;} break;
+case 43:
+#line	185	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ infunc--; curfname=0; defn((Cell *)yypt[-7].yyv.p, yypt[-5].yyv.p, yypt[-1].yyv.p); yyval.p = 0; } break;
+case 45:
+#line	190	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 47:
+#line	195	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 48:
+#line	199	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 49:
+#line	201	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(CONDEXPR, notnull(yypt[-4].yyv.p), yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 50:
+#line	203	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BOR, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 51:
+#line	205	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(AND, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 52:
+#line	206	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 53:
+#line	208	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-0].yyv.p))
+			yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(strnode(yypt[-0].yyv.p), 0));
+		  else
+			yyval.p = op3(yypt[-1].yyv.i, (Node *)1, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 54:
+#line	212	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-2].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 55:
+#line	213	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-3].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 56:
+#line	214	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CAT, yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 59:
+#line	220	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 60:
+#line	222	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(CONDEXPR, notnull(yypt[-4].yyv.p), yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 61:
+#line	224	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BOR, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 62:
+#line	226	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(AND, notnull(yypt[-2].yyv.p), notnull(yypt[-0].yyv.p)); } break;
+case 63:
+#line	227	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 64:
+#line	228	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 65:
+#line	229	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 66:
+#line	230	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 67:
+#line	231	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 68:
+#line	232	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(yypt[-1].yyv.i, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 69:
+#line	233	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 70:
+#line	235	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-0].yyv.p))
+			yyval.p = op3(yypt[-1].yyv.i, NIL, yypt[-2].yyv.p, (Node*)makedfa(strnode(yypt[-0].yyv.p), 0));
+		  else
+			yyval.p = op3(yypt[-1].yyv.i, (Node *)1, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 71:
+#line	239	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-2].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 72:
+#line	240	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INTEST, yypt[-3].yyv.p, makearr(yypt[-0].yyv.p)); } break;
+case 73:
+#line	241	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else yyval.p = op3(GETLINE, yypt[-0].yyv.p, itonp(yypt[-2].yyv.i), yypt[-3].yyv.p); } break;
+case 74:
+#line	244	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("cmd | getline is unsafe");
+			else yyval.p = op3(GETLINE, (Node*)0, itonp(yypt[-1].yyv.i), yypt[-2].yyv.p); } break;
+case 75:
+#line	247	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CAT, yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 78:
+#line	253	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 79:
+#line	254	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 81:
+#line	259	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 82:
+#line	263	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = rectonode(); } break;
+case 84:
+#line	265	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 93:
+#line	282	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(MATCH, NIL, rectonode(), (Node*)makedfa(yypt[-0].yyv.s, 0)); } break;
+case 94:
+#line	283	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(NOT, notnull(yypt[-0].yyv.p)); } break;
+case 95:
+#line	287	"/n/bopp/v7/bwk/awk/awkgram.y"
+{startreg();} break;
+case 96:
+#line	287	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.s = yypt[-1].yyv.s; } break;
+case 99:
+#line	295	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ 
+			if (safe) SYNTAX("print | is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 100:
+#line	298	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			if (safe) SYNTAX("print >> is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 101:
+#line	301	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			if (safe) SYNTAX("print > is unsafe");
+			else yyval.p = stat3(yypt[-3].yyv.i, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 102:
+#line	304	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(yypt[-1].yyv.i, yypt[-0].yyv.p, NIL, NIL); } break;
+case 103:
+#line	305	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DELETE, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p); } break;
+case 104:
+#line	306	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DELETE, makearr(yypt[-0].yyv.p), 0); } break;
+case 105:
+#line	307	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = exptostat(yypt[-0].yyv.p); } break;
+case 106:
+#line	308	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyclearin; SYNTAX("illegal statement"); } break;
+case 109:
+#line	317	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (!inloop) SYNTAX("break illegal outside of loops");
+				  yyval.p = stat1(BREAK, NIL); } break;
+case 110:
+#line	319	"/n/bopp/v7/bwk/awk/awkgram.y"
+{  if (!inloop) SYNTAX("continue illegal outside of loops");
+				  yyval.p = stat1(CONTINUE, NIL); } break;
+case 111:
+#line	321	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 112:
+#line	321	"/n/bopp/v7/bwk/awk/awkgram.y"
+{--inloop;} break;
+case 113:
+#line	322	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat2(DO, yypt[-6].yyv.p, notnull(yypt[-2].yyv.p)); } break;
+case 114:
+#line	323	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(EXIT, yypt[-1].yyv.p); } break;
+case 115:
+#line	324	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(EXIT, NIL); } break;
+case 117:
+#line	326	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(IF, yypt[-3].yyv.p, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 118:
+#line	327	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat3(IF, yypt[-1].yyv.p, yypt[-0].yyv.p, NIL); } break;
+case 119:
+#line	328	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 120:
+#line	329	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (infunc)
+				SYNTAX("next is illegal inside a function");
+			  yyval.p = stat1(NEXT, NIL); } break;
+case 121:
+#line	332	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (infunc)
+				SYNTAX("nextfile is illegal inside a function");
+			  yyval.p = stat1(NEXTFILE, NIL); } break;
+case 122:
+#line	335	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(RETURN, yypt[-1].yyv.p); } break;
+case 123:
+#line	336	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = stat1(RETURN, NIL); } break;
+case 125:
+#line	338	"/n/bopp/v7/bwk/awk/awkgram.y"
+{inloop++;} break;
+case 126:
+#line	338	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ --inloop; yyval.p = stat2(WHILE, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 127:
+#line	339	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = 0; } break;
+case 129:
+#line	344	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = linkum(yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
+case 132:
+#line	352	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(DIVEQ, yypt[-3].yyv.p, yypt[-0].yyv.p); } break;
+case 133:
+#line	353	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(ADD, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 134:
+#line	354	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MINUS, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 135:
+#line	355	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MULT, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 136:
+#line	356	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(DIVIDE, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 137:
+#line	357	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(MOD, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 138:
+#line	358	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(POWER, yypt[-2].yyv.p, yypt[-0].yyv.p); } break;
+case 139:
+#line	359	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(UMINUS, yypt[-0].yyv.p); } break;
+case 140:
+#line	360	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-0].yyv.p; } break;
+case 141:
+#line	361	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(NOT, notnull(yypt[-0].yyv.p)); } break;
+case 142:
+#line	362	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-2].yyv.i), rectonode()); } break;
+case 143:
+#line	363	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-3].yyv.i), yypt[-1].yyv.p); } break;
+case 144:
+#line	364	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(BLTIN, itonp(yypt[-0].yyv.i), rectonode()); } break;
+case 145:
+#line	365	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CALL, celltonode(yypt[-2].yyv.cp,CVAR), NIL); } break;
+case 146:
+#line	366	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(CALL, celltonode(yypt[-3].yyv.cp,CVAR), yypt[-1].yyv.p); } break;
+case 147:
+#line	367	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(CLOSE, yypt[-0].yyv.p); } break;
+case 148:
+#line	368	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(PREDECR, yypt[-0].yyv.p); } break;
+case 149:
+#line	369	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(PREINCR, yypt[-0].yyv.p); } break;
+case 150:
+#line	370	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(POSTDECR, yypt[-1].yyv.p); } break;
+case 151:
+#line	371	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(POSTINCR, yypt[-1].yyv.p); } break;
+case 152:
+#line	372	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, yypt[-2].yyv.p, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 153:
+#line	373	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, NIL, itonp(yypt[-1].yyv.i), yypt[-0].yyv.p); } break;
+case 154:
+#line	374	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, yypt[-0].yyv.p, NIL, NIL); } break;
+case 155:
+#line	375	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(GETLINE, NIL, NIL, NIL); } break;
+case 156:
+#line	377	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(INDEX, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 157:
+#line	379	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ SYNTAX("index() doesn't permit regular expressions");
+		  yyval.p = op2(INDEX, yypt[-3].yyv.p, (Node*)yypt[-1].yyv.s); } break;
+case 158:
+#line	381	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = yypt[-1].yyv.p; } break;
+case 159:
+#line	383	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(MATCHFCN, NIL, yypt[-3].yyv.p, (Node*)makedfa(yypt[-1].yyv.s, 1)); } break;
+case 160:
+#line	385	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-1].yyv.p))
+			yyval.p = op3(MATCHFCN, NIL, yypt[-3].yyv.p, (Node*)makedfa(strnode(yypt[-1].yyv.p), 1));
+		  else
+			yyval.p = op3(MATCHFCN, (Node *)1, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 161:
+#line	389	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CCON); } break;
+case 162:
+#line	391	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p, (Node*)STRING); } break;
+case 163:
+#line	393	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-5].yyv.p, makearr(yypt[-3].yyv.p), (Node*)makedfa(yypt[-1].yyv.s, 1), (Node *)REGEXPR); } break;
+case 164:
+#line	395	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(SPLIT, yypt[-3].yyv.p, makearr(yypt[-1].yyv.p), NIL, (Node*)STRING); } break;
+case 165:
+#line	396	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(yypt[-3].yyv.i, yypt[-1].yyv.p); } break;
+case 166:
+#line	397	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CCON); } break;
+case 167:
+#line	399	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(yypt[-5].yyv.i, NIL, (Node*)makedfa(yypt[-3].yyv.s, 1), yypt[-1].yyv.p, rectonode()); } break;
+case 168:
+#line	401	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-3].yyv.p))
+			yyval.p = op4(yypt[-5].yyv.i, NIL, (Node*)makedfa(strnode(yypt[-3].yyv.p), 1), yypt[-1].yyv.p, rectonode());
+		  else
+			yyval.p = op4(yypt[-5].yyv.i, (Node *)1, yypt[-3].yyv.p, yypt[-1].yyv.p, rectonode()); } break;
+case 169:
+#line	406	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op4(yypt[-7].yyv.i, NIL, (Node*)makedfa(yypt[-5].yyv.s, 1), yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 170:
+#line	408	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ if (constnode(yypt[-5].yyv.p))
+			yyval.p = op4(yypt[-7].yyv.i, NIL, (Node*)makedfa(strnode(yypt[-5].yyv.p), 1), yypt[-3].yyv.p, yypt[-1].yyv.p);
+		  else
+			yyval.p = op4(yypt[-7].yyv.i, (Node *)1, yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 171:
+#line	413	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(SUBSTR, yypt[-5].yyv.p, yypt[-3].yyv.p, yypt[-1].yyv.p); } break;
+case 172:
+#line	415	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op3(SUBSTR, yypt[-3].yyv.p, yypt[-1].yyv.p, NIL); } break;
+case 175:
+#line	421	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op2(ARRAY, makearr(yypt[-3].yyv.p), yypt[-1].yyv.p); } break;
+case 176:
+#line	422	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(INDIRECT, celltonode(yypt[-0].yyv.cp, CVAR)); } break;
+case 177:
+#line	423	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(INDIRECT, yypt[-0].yyv.p); } break;
+case 178:
+#line	427	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ arglist = yyval.p = 0; } break;
+case 179:
+#line	428	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ arglist = yyval.p = celltonode(yypt[-0].yyv.cp,CVAR); } break;
+case 180:
+#line	429	"/n/bopp/v7/bwk/awk/awkgram.y"
+{
+			checkdup(yypt[-2].yyv.p, yypt[-0].yyv.cp);
+			arglist = yyval.p = linkum(yypt[-2].yyv.p,celltonode(yypt[-0].yyv.cp,CVAR)); } break;
+case 181:
+#line	435	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = celltonode(yypt[-0].yyv.cp, CVAR); } break;
+case 182:
+#line	436	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(ARG, itonp(yypt[-0].yyv.i)); } break;
+case 183:
+#line	437	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = op1(VARNF, (Node *) yypt[-0].yyv.cp); } break;
+case 184:
+#line	442	"/n/bopp/v7/bwk/awk/awkgram.y"
+{ yyval.p = notnull(yypt[-1].yyv.p); } break;
+	}
+	goto yystack;  /* stack new state and value */
+}
--- /dev/null
+++ b/utils/awk/ytabh.bak
@@ -1,0 +1,100 @@
+
+typedef union  {
+	Node	*p;
+	Cell	*cp;
+	int	i;
+	char	*s;
+}	YYSTYPE;
+extern	YYSTYPE	yylval;
+#define	FIRSTTOKEN	57346
+#define	PROGRAM	57347
+#define	PASTAT	57348
+#define	PASTAT2	57349
+#define	XBEGIN	57350
+#define	XEND	57351
+#define	NL	57352
+#define	ARRAY	57353
+#define	MATCH	57354
+#define	NOTMATCH	57355
+#define	MATCHOP	57356
+#define	FINAL	57357
+#define	DOT	57358
+#define	ALL	57359
+#define	CCL	57360
+#define	NCCL	57361
+#define	CHAR	57362
+#define	OR	57363
+#define	STAR	57364
+#define	QUEST	57365
+#define	PLUS	57366
+#define	AND	57367
+#define	BOR	57368
+#define	APPEND	57369
+#define	EQ	57370
+#define	GE	57371
+#define	GT	57372
+#define	LE	57373
+#define	LT	57374
+#define	NE	57375
+#define	IN	57376
+#define	ARG	57377
+#define	BLTIN	57378
+#define	BREAK	57379
+#define	CLOSE	57380
+#define	CONTINUE	57381
+#define	DELETE	57382
+#define	DO	57383
+#define	EXIT	57384
+#define	FOR	57385
+#define	FUNC	57386
+#define	SUB	57387
+#define	GSUB	57388
+#define	IF	57389
+#define	INDEX	57390
+#define	LSUBSTR	57391
+#define	MATCHFCN	57392
+#define	NEXT	57393
+#define	NEXTFILE	57394
+#define	ADD	57395
+#define	MINUS	57396
+#define	MULT	57397
+#define	DIVIDE	57398
+#define	MOD	57399
+#define	ASSIGN	57400
+#define	ASGNOP	57401
+#define	ADDEQ	57402
+#define	SUBEQ	57403
+#define	MULTEQ	57404
+#define	DIVEQ	57405
+#define	MODEQ	57406
+#define	POWEQ	57407
+#define	PRINT	57408
+#define	PRINTF	57409
+#define	SPRINTF	57410
+#define	ELSE	57411
+#define	INTEST	57412
+#define	CONDEXPR	57413
+#define	POSTINCR	57414
+#define	PREINCR	57415
+#define	POSTDECR	57416
+#define	PREDECR	57417
+#define	VAR	57418
+#define	IVAR	57419
+#define	VARNF	57420
+#define	CALL	57421
+#define	NUMBER	57422
+#define	STRING	57423
+#define	REGEXPR	57424
+#define	GETLINE	57425
+#define	RETURN	57426
+#define	SPLIT	57427
+#define	SUBSTR	57428
+#define	WHILE	57429
+#define	CAT	57430
+#define	NOT	57431
+#define	UMINUS	57432
+#define	POWER	57433
+#define	DECR	57434
+#define	INCR	57435
+#define	INDIRECT	57436
+#define	LASTTOKEN	57437
--- /dev/null
+++ b/utils/c2l/Nt.c
@@ -1,0 +1,131 @@
+#include	<windows.h>  
+#include	<lib9.h>
+
+#define	Windows	(1<<2)		/* hack - can't include cc.h because of clashes */
+#define	Chunk	(1*1024*1024)
+
+void*
+mysbrk(ulong size)
+{
+	void *v;
+	static int chunk;
+	static uchar *brk;
+
+	if(chunk < size) {
+		chunk = Chunk;
+		if(chunk < size)
+			chunk = Chunk + size;
+		brk = VirtualAlloc(NULL, chunk, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 	
+		if(brk == 0)
+			return (void*)-1;
+	}
+	v = brk;
+	chunk -= size;
+	brk += size;
+	return v;
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	fprint(2, "mywait called\n");
+	abort();
+	return 0;
+}
+
+int
+mydup(int f1, int f2)
+{
+	int ok;
+
+	ok = _dup2(f1, f2);
+	if(ok < 0)
+		return -1;
+	return f2;
+/*
+	fprint(2, "mydup called\n");
+	abort();
+	return 0;
+*/
+}
+
+int
+mypipe(int *fd)
+{
+	fprint(2, "mypipe called\n");
+	abort();
+	return 0;
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Windows;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return getcwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	fprint(2, "myexec called\n");
+	abort();
+	return 0;
+}
+
+int
+myfork(void)
+{
+	fprint(2, "myfork called\n");
+	abort();
+	return 0;
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(uint n)
+{
+	return mysbrk(n);
+}
+
+void*
+calloc(uint m, uint n)
+{
+	return mysbrk(m*n);
+}
+
+void*
+realloc(void *p, uint n)
+{
+	void *new;
+
+	new = malloc(n);
+	if(new && p)
+		memmove(new, p, n);
+	return new;
+}
+
+void
+free(void *p)
+{
+}
--- /dev/null
+++ b/utils/c2l/Plan9.c
@@ -1,0 +1,108 @@
+#include	"cc.h"
+
+void*
+mysbrk(ulong size)
+{
+	return sbrk(size);
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	int p;
+	Waitmsg *w;
+
+	if((w = wait()) == nil)
+		return -1;
+	else{
+		p = w->pid;
+		*s = 0;
+		if(w->msg[0])
+			*s = 1;
+		free(w);
+		return p;
+	}
+}
+
+int
+mydup(int f1, int f2)
+{
+	return dup(f1,f2);
+}
+
+int
+mypipe(int *fd)
+{
+	return pipe(fd);
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Plan9;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return getwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	return exec(path, argv);
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(ulong n)
+{
+	return alloc(n);
+}
+
+void*
+calloc(ulong m, ulong n)
+{
+	return alloc(m*n);
+}
+
+void*
+realloc(void*, ulong)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void
+free(void*)
+{
+}
+
+int
+myfork(void)
+{
+	return fork();
+}
+
+void
+setmalloctag(void*, ulong)
+{
+}
--- /dev/null
+++ b/utils/c2l/Posix.c
@@ -1,0 +1,94 @@
+#include	"cc.h"
+#include	<sys/wait.h>
+
+void*
+mysbrk(ulong size)
+{
+	return (void*)sbrk(size);
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	return wait(s);
+}
+
+int
+mydup(int f1, int f2)
+{
+	if(f2 < 0)
+		return dup(f1);
+	return dup2(f1,f2);
+}
+
+int
+mypipe(int *fd)
+{
+	return pipe(fd);
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Unix;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return (char*)getcwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	return execvp(path, argv);
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(size_t n)
+{
+	return alloc(n);
+}
+
+void*
+calloc(size_t m, size_t n)
+{
+	return alloc(m*n);
+}
+
+void*
+realloc(void *p, size_t n)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void
+free(void *p)
+{
+}
+
+int
+myfork(void)
+{
+	return fork();
+}
--- /dev/null
+++ b/utils/c2l/acid.c
@@ -1,0 +1,13 @@
+#include "cc.h"
+
+void
+acidtype(Type *t)
+{
+	USED(t);
+}
+
+void
+acidvar(Sym *s)
+{
+	USED(s);
+}
--- /dev/null
+++ b/utils/c2l/bits.c
@@ -1,0 +1,89 @@
+#include	"cc.h"
+
+Bits
+bor(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] | b.b[i];
+	return c;
+}
+
+Bits
+band(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] & b.b[i];
+	return c;
+}
+
+/*
+Bits
+bnot(Bits a)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = ~a.b[i];
+	return c;
+}
+*/
+
+int
+bany(Bits *a)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a->b[i])
+			return 1;
+	return 0;
+}
+
+int
+beq(Bits a, Bits b)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a.b[i] != b.b[i])
+			return 0;
+	return 1;
+}
+
+int
+bnum(Bits a)
+{
+	int i;
+	long b;
+
+	for(i=0; i<BITS; i++)
+		if(b = a.b[i])
+			return 32*i + bitno(b);
+	diag(Z, "bad in bnum");
+	return 0;
+}
+
+Bits
+blsh(uint n)
+{
+	Bits c;
+
+	c = zbits;
+	c.b[n/32] = 1L << (n%32);
+	return c;
+}
+
+int
+bset(Bits a, uint n)
+{
+	if(a.b[n/32] & (1L << (n%32)))
+		return 1;
+	return 0;
+}
--- /dev/null
+++ b/utils/c2l/c2l.c
@@ -1,0 +1,5116 @@
+#define EXTERN
+
+#include "cc.h"
+
+/*
+ *	locals, parameters, globals etc of the same name should work ok without having
+ *	to duplicate Syms because the details are on the containing Nodes
+ */
+
+#define SZ_CHAR	1
+#define SZ_SHORT	2
+#define SZ_INT	4
+#define SZ_LONG	4
+#define SZ_FLOAT	4
+#define SZ_IND	4
+#define SZ_VLONG	8
+#define SZ_DOUBLE	8
+
+char buf[128], mbuf[128];
+static Sym *sysop, *bioop, *libcop;
+static int again;
+
+#define	STAR	0x80
+#define	RET		0x80
+
+#define	LARR	(-1729)
+
+static void swalk(void);
+static int isdec(Node*);
+static int isconst(Node*, vlong);
+static int cktype(Node*, Node*, int, int);
+static void addnode(int, Node*);
+static int argpos(Node*, Node*);
+static void setdec(Sym*, Type*);
+static Type* tcp(Type*);
+static int isadt(Type*);
+static void aargs(Node*);
+static int iteq(Type*, Type*);
+static Node* arg(Node*, int);
+static void etgen2(Sym*);
+static Node* ckneg(Node*);
+static Sym* suename(Type*);
+static int isnil(Node*);
+static void sliceasgn(Node*);
+static Node* lastn(Node*);
+static char* hasm(void);
+static void prn(Node*, int);
+static int isfn(Type*);
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
+
+enum{
+	TCFD = 1,
+	TCFC = 2,
+	TCPC = 4,
+	TCAR = 8,
+	TCIN = 16,
+	TCGEN = TCFD|TCFC|TCPC|TCAR,
+	TCALL = TCFD|TCFC|TCPC|TCAR|TCIN,
+};
+
+enum{
+	SGLOB,
+	SPARM,
+	SAUTO,
+};
+
+typedef struct Scope Scope;
+
+struct Scope{
+	Node *n;
+	int	k;
+	Scope *nxt;
+};
+
+static void
+prtyp(Type *t, char *s, int nl)
+{
+	print("%s: ", s);
+	if(t == T){
+		print("nil");
+		if(nl)
+			print("\n");
+		return;
+	}
+	while(t != T){
+		print("%d(%d)[%x] ", t->etype, t->mark, (int)t);
+		if(isadt(t))
+			break;
+		t = t->link;
+	}
+	if(nl)
+		print("\n");
+}
+
+static Node*
+func(Node *n)
+{
+	while(n != Z && n->op != OFUNC)
+		n = n->left;
+	return n;
+}
+
+static void
+setmain(Node *n)
+{
+	inmain |= n->left->op == ONAME && strcmp(n->left->sym->name, "main") == 0;
+}
+
+static Node*
+protoname(Node *n)
+{
+	do
+		n = n->left;
+	while(n != Z && n->op != ONAME && n->op != ODOTDOT);
+	return n;
+}
+
+static Type*
+prototype(Node *n, Type *t)
+{
+	for( ; n != Z ; n = n->left){
+		switch(n->op){
+		case OARRAY:
+			t = typ(TARRAY, t);
+			t->width = 0;
+			break;
+		case OIND:
+			t = typ(TIND, t);
+			break;
+		case OFUNC:
+			t = typ(TFUNC, t);
+			t->down = fnproto(n);
+			break;
+		}
+	}
+	return t;
+}
+
+static Scope *scopes, *freescopes;
+
+static void
+pushdcl(Node *n, int c)
+{
+	Sym *s;
+
+	if(passes){
+		s = n->sym;
+		push1(s);
+		if(c != CAUTO || s->class != CSTATIC)
+			s->class = c;
+		s->type = n->type;
+	}
+}
+
+static void
+pushparams(Node *n)
+{
+	if(n == Z)
+		return;
+	if(passes){
+		if(n->op == OLIST){
+			pushparams(n->left);
+			pushparams(n->right);
+		}
+		else if(n->op == OPROTO){
+			n = protoname(n);
+			if(n != Z && n->op == ONAME)
+				pushdcl(n, CPARAM);
+		}
+		else if(n->op == ONAME){
+			addnode(OPROTO, n);
+			pushdcl(n, CPARAM);
+		}
+		else if(n->op != ODOTDOT)
+			diag(Z, "bad op in pushparams");
+	}
+}
+
+static void
+pushscope(Node *n, int k)
+{
+	Scope *s;
+
+	if(freescopes != nil){
+		s = freescopes;
+		freescopes = freescopes->nxt;
+	}
+	else
+		s = (Scope*)malloc(sizeof(Scope));
+	s->n = n;
+	s->k = k;
+	s->nxt = scopes;
+	scopes = s;
+	if(passes && (k == SPARM || k == SAUTO))
+		markdcl();
+	if(k == SPARM)
+		pushparams(n->right);
+}
+
+static void
+popscope(void)
+{
+	int k;
+	Scope *s;
+
+	s = scopes;
+	k = s->k;
+	scopes = scopes->nxt;
+	s->nxt = freescopes;
+	freescopes = s;
+	if(passes && (k == SPARM || k == SAUTO))
+		revertdcl();
+}
+
+static Node*
+curfn(void)
+{
+	Scope *s;
+
+	for(s = scopes; s != nil; s = s->nxt)
+		if(s->k == SPARM)
+			return s->n;
+	return Z;
+}
+
+static void
+marktype(Type *t, int tc)
+{
+	t->mark = tc;
+}
+
+static int
+marked(Type *t)
+{
+	return t == T ? 0 :  t->mark;
+}
+
+static Sym*
+decsym(Node *n)
+{
+	if(n == Z)
+		return S;
+	if(n->op == OFUNC){
+		if(n->left->op == ONAME)
+			return n->left->sym;
+		return S;
+	}
+	if(n->op == ODAS)
+		return n->left->sym;
+	return n->sym;
+}
+
+static void
+trep(Type *t1, Type *t)
+{
+	int l;
+	Sym *s;
+	Type *t2;
+
+	if(t1 != T){
+		l = t1->lineno;
+		s = t1->sym;
+		t2 = t1->down;
+		*t1 = *t;
+		t1->down = t2;
+		t1->sym = s;
+		t1->lineno = l;
+	}
+}
+
+static void
+tind(Node *n)
+{
+	if(n == Z)
+		return;
+	n = protoname(n);
+	if(n != Z && n->type != T){
+		n->type = tcp(n->type->link);
+		marktype(n->type, TCIN);
+	}
+}
+
+static void
+tcon(Node *n, Type *t)
+{
+	Type *tt;
+
+	if(n->garb)
+		return;
+	n->garb = 1;
+	again = 1;
+	switch(n->op){
+		case OCONST:
+			if(t->mark == TCFD && !isnil(n))
+				addnode(OFILDES, n);
+			n->type = t;
+			break;
+		case OCAST:
+			tcon(n->left, t);
+			*n = *n->left;
+			n->type = t;
+			break;
+		case ONAME:
+			n->sym->type = t;
+			n->type = t;
+			setdec(n->sym, t);
+			break;
+		case ODOT:
+		case ODOTIND:
+			trep(n->type, t);
+			n->type = t;
+			break;
+		case OARRIND:
+			tt = n->left->type;
+			if(tt != T)
+				tt->link = t;
+			n->type = t;
+			break;
+		case OFUNC:
+			n->left->type->link = t;
+			if(n->left->op == ONAME)
+				n->left->sym->type->link = t;
+			n->type = t;
+			break;
+	}
+}
+
+static Node*
+retval(Node *n)
+{
+	int i;
+	Type *t;
+	Node *a, *l, *cf;
+
+	cf = curfn();
+	t = cf->left->type->link;
+	if(t->mark&(TCPC|TCFC) && (n == Z || !(n->type->mark&(TCPC|TCFC)))){
+		if(n == Z)
+			n = new1(ORETURN, Z, Z);
+		l = n->left;
+		for(i = 0; ; i++){
+			a = arg(cf->right, i);
+			if(a == Z)
+				break;
+			a = protoname(a);
+			if(a == Z || a->op != ONAME)
+				break;
+			if(a->type->mark == TCIN){
+				if(l == Z)
+					l = ncopy(a);
+				else
+					l = new1(OTUPLE, l, ncopy(a));
+			}
+		}
+		n->left = l;
+		n->type = l->type = t;
+	}
+	return n;
+}
+
+static void
+sube(Node *n)
+{
+	Node *l, *r, *nn;
+	Type *tt;
+	static Node *gn;
+	int p;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	switch(n->op){
+		default:
+			sube(l);
+			sube(r);
+			break;
+		case OIND:
+			if(l == Z)
+				return;
+			tt = l->type;
+			sube(l);
+			if(cktype(l, n, TCIN, 0) && iteq(tt, l->type))
+				*n = *n->left;
+			break;
+		case OARRIND:
+			tt = l->type;
+			sube(l);
+			sube(r);
+			if(!isconst(r, 0))
+				break;
+			if(cktype(l, n, TCIN, 0) && iteq(tt, l->type))
+				*n = *n->left;
+			break;
+		case ONAME:
+			if(cktype(n, n, TCALL, 0))
+				setdec(n->sym, n->type);
+			break;
+		case OCAST:
+			sube(l);
+			if(cktype(l, n, TCALL, 0))
+				n->type = l->type;
+			break;
+		case OPROTO:
+			sube(l);
+			sube(r);
+			nn = protoname(n);
+			if(nn != Z && cktype(nn, n, TCALL, 0)){
+				n->type = nn->type;
+				p = argpos(n, gn->right);
+				for(tt = gn->left->type->down; tt != T && p >= 0; tt = tt->down){
+					if(p == 0){
+						trep(tt, nn->type);
+						break;
+					}
+					--p;
+				}
+			}
+			break;
+		case OFUNC:
+			if(n->kind == KEXP)
+				aargs(n);
+			if(n->left->op == ONAME)
+				gn = n;
+			sube(l);
+			sube(r);
+			if(l != Z && cktype(n, n, TCGEN, 0))
+				l->type->link = n->type;
+			break;
+		case OAS:
+			sube(l);
+			sube(r);
+			if(r->op == ORETV){
+				n->left = new1(OTUPLE, l, r->right);
+				n->right = r->left;
+				n->left->type = n->type;
+				break;
+			}
+			if(cktype(r, n, TCGEN, 0)){
+				tcon(l, r->type);
+				n->type = r->type;
+			}
+			if(cktype(l, n, TCGEN, 1)){
+				tcon(r, l->type);
+				n->type = l->type;
+			}
+			break;
+		case OLT:
+		case OGE:
+			sube(l);
+			sube(r);
+			if(cktype(l, n, TCFD, 0) && isconst(r, 0)){
+				n->op = n->op == OLT ? OEQ : ONE;
+				r->op = ONIL;
+				r->type = l->type;
+			}
+			break;
+		case OGT:
+		case OLE:
+			sube(l);
+			sube(r);
+			if(cktype(r, n, TCFD, 0) && isconst(l, 0)){
+				n->op = n->op == OGT ? OEQ : ONE;
+				l->op = ONIL;
+				l->type = r->type;
+			}
+			break;
+	}
+}
+
+static void
+subs(Node *n, int blk, int aut)
+{
+	Node *l, *r;
+
+	if(n == Z)
+		return;
+	if(blk)
+		pushscope(n, SAUTO);
+	nearln = n->lineno;
+	l = n->left;
+	r = n->right;
+	switch(n->op){
+		default:
+			sube(n);
+			break;
+		case ONAME:
+			if(aut && n->kind != KEXP)
+				pushdcl(n, CAUTO);
+			if(cktype(n, n, TCALL, 0))
+				setdec(n->sym, n->type);
+			break;
+		case ODAS:
+			if(aut)
+				pushdcl(l, CAUTO);
+			subs(l, 0, aut);
+			if(cktype(l, n, TCALL, 0))
+				tcon(r, l->type);
+			break;
+		case OSBREAK:
+		case ONUL:
+		case OLABEL:
+		case OGOTO:
+		case OCONTINUE:
+		case OBREAK:
+		case OSET:
+		case OUSED:
+			break;
+		case OBLK:
+			subs(l, 1, aut);
+			break;
+		case OCASE:
+			subs(r, 1, aut);
+			break;
+		case OLIST:
+			subs(l, 0, aut);
+			subs(r, 0, aut);
+			break;
+		case ORETURN:
+			sube(l);
+			if(l != Z && cktype(l, n, TCGEN, 0)){
+				n->type = l->type;
+				tcon(curfn(), l->type);
+			}
+			retval(n);
+			break;
+		case OSWITCH:
+		case OWHILE:
+		case ODWHILE:
+			sube(l);
+			subs(r, 1, aut);
+			break;
+		case OIF:
+			sube(l);
+			subs(r->left, 1, aut);
+			subs(r->right, 1, aut);
+			break;
+		case OFOR:
+			sube(l->left);
+			sube(l->right->left);
+			sube(l->right->right);
+			subs(r, 1, aut);
+			break;
+	}
+	if(blk)
+		popscope();
+}
+
+static Node*
+finddec0(Sym *s, Node *n)
+{
+	Node *nn;
+
+	if(n == Z)
+		return ZZ;
+	switch(n->op){
+		case OLIST:
+			nn = finddec0(s, n->left);
+			if(nn != Z)
+				return nn;
+			return finddec0(s, n->right);
+		case OFUNC:
+			if(n->op != KEXP){
+				if(s == decsym(n))
+					return n;
+				return finddec0(s, n->right);
+			}
+			else
+				return ZZ;
+		case OPROTO:
+		case OIND:
+		case OARRAY:
+			return finddec0(s, n->left);
+		case ODOTDOT:
+			return ZZ;
+		case ONOOP:
+		case OPUSH:
+		case OPOP:
+		case OCODE:
+		case ODECE:
+		case ODECT:
+			return finddec0(s, n->right);
+		case ODECV:
+		case ODECF:
+			if(s == decsym(n->left) && !isfn(n->left->type))
+				return n->left;
+			return finddec0(s, n->right);
+	}
+	if(isdec(n)){
+		if(s == decsym(n) && !isfn(n->type))
+			return n;
+		return Z;
+	}
+	return ZZ;
+}
+
+static Node*
+finddec(Sym *s, int g)
+{
+	Node *n;
+	Scope *sc;
+
+	for(sc = scopes; sc != nil; sc = sc->nxt){
+		if(!g || sc->k == SGLOB){
+			n = finddec0(s, sc->n);
+			if(n != Z && n != ZZ)
+				return n;
+		}
+	}
+	return Z;	
+}
+
+static void
+setdec(Sym *s, Type *t)
+{
+	Node *n;
+
+	if((n = finddec(s, 0)) != Z){
+		n->type = t;
+		if(n->op == ODAS){
+			n = n->left;
+			n->type = t;
+		}
+		n->sym->type = t;
+	}
+}
+		
+typedef struct Syml Syml;
+
+struct Syml{
+	Sym *sym;
+	Syml *nxt;
+};
+
+typedef struct Symq Symq;
+
+struct Symq{
+	Syml *f;
+	Syml *r;
+};
+
+typedef struct Modl Modl;
+
+struct Modl{
+	char *mod;
+	int	ld;
+	Modl *nxt;
+};
+
+static void
+prn(Node *n, int i)
+{
+	int j;
+
+	for(j = 0; j < i; j++)
+		print("\t");
+	if(n == Z){
+		print("Z\n");
+		return;
+	}
+	print("%s", onames[n->op]);
+	if(n->blk)
+		print("	block");
+	if(n->type == T)
+		print("	T");
+	else
+		print("	%s", tnames[n->type->etype]);
+	if(n->op == OCONST)
+		print("	%d", (int)n->vconst);
+	else if(n->op == OSTRING)
+		print("	%s", n->cstring);
+	else if(n->op == ONAME)
+		print("	%s", n->sym->name);
+	print("\n");
+	if(n->op != OLIST)
+		i++;
+	prn(n->left, i);
+	prn(n->right, i);
+}
+
+static int
+isbigv(vlong v)
+{
+	return v > 0xffffffff;
+}
+
+static int
+islbigv(vlong v)
+{
+	return v > 0x7fffffff || v < -0x7fffffff;
+}
+
+static int
+isuintv(vlong v)
+{
+	return !isbigv(v) && (v&0x80000000) != 0;
+}
+
+static int
+isadt(Type *t)
+{
+	return t != T && (t->etype == TSTRUCT || t->etype == TUNION);
+}
+
+static int
+isreal(Type *t)
+{
+	return t != T && (t->etype == TDOUBLE || t->etype == TFLOAT);
+}
+
+static int
+isbyte(Type *t)
+{
+	return t != T && (t->etype == TCHAR || t->etype == TUCHAR);
+}
+
+static int
+isshort(Type *t)
+{
+	return t != T && (t->etype == TSHORT || t->etype == TUSHORT);
+}
+
+static int
+isint(Type *t)
+{
+	return t != T && (t->etype == TINT || t->etype == TUINT);
+}
+
+static int
+islong(Type *t)
+{
+	return t != T && (t->etype == TLONG || t->etype == TULONG);
+}
+
+static int
+isbig(Type *t)
+{
+	return t != T && (t->etype == TVLONG || t->etype == TUVLONG);
+}
+
+static int
+isinteger(Type *t)
+{
+	return isbyte(t) || isshort(t) || isint(t) || islong(t) || isbig(t);
+}
+
+static int
+isptr(Type *t)
+{
+	return t != T && (t->etype == TIND || t->etype == TARRAY || t->etype == TFUNC);
+}
+
+static int
+isscalar(Type *t)
+{
+	return t != T && !isadt(t) && t->etype != TTUPLE;
+}
+
+static int
+isvoid(Type *t)
+{
+	return t == T || t->etype == TVOID;
+}
+
+static int
+isnum(Type *t)
+{
+	return t != T && isscalar(t) && !isptr(t) && !isvoid(t);
+}
+
+static int
+isarray(Type *t)
+{
+	return t != T && (t->etype == TARRAY || (t->etype == TIND && !isadt(t->link)));
+}
+
+static int
+isstr(Type *t)
+{
+	return t != T && (t->etype == TSTRING || isarray(t) && isbyte(t->link));
+}
+
+static int
+isfn(Type *t)
+{
+	return t != T && t->etype == TFUNC;
+}
+
+static int
+iscastable(Type *t, Type *tt)
+{
+	return t != T && (!isptr(t) || isarray(t) && isbyte(t->link) && isstr(tt));
+}
+
+static int
+isname(Node *n)
+{
+	return n->op == ONAME;
+}
+
+static int
+isstring(Node *n)
+{
+	return n->op == OSTRING || n->op == OLSTRING || n->op == ONAME && n->sym->tenum != T && n->sym->tenum->etype == TIND;
+}
+
+static int
+isnil(Node *n)
+{
+	if(!isptr(n->type))
+		return 0;
+	while(n->op == OCAST)
+		n = n->left;
+	return n->op == OCONST && n->vconst == 0 || n->op == ONIL;
+}
+
+static int
+isconst(Node *n, vlong v)
+{
+	while(n->op == OCAST)
+		n = n->left;
+	return n->op == OCONST && n->vconst == v;
+}
+
+static Node*
+cknil(Node *n)
+{
+	if(isconst(n, 0))
+		n->op = ONIL;
+	return n;
+}
+
+static int
+cktype(Node *n, Node *t, int mask, int lev)
+{
+	int g, m, m0;
+
+	g = t->garb > lev;
+	m = marked(n->type) & mask;
+	if(n->op == ONAME){
+		m0 = marked(n->sym->type) & mask;
+		if(m && !m0){
+			n->sym->type = n->type;
+			if(!g)
+				again = 1;
+		}
+		if(!m && m0){
+			n->type = n->sym->type;
+			if(!g)
+				again = 1;
+		}
+		m |= m0;
+	}
+	if(m && t->garb < 2)
+		t->garb++;
+	return m && !g ? m : 0;
+}
+
+int
+isconsym(Sym *s)
+{
+	switch(s->class){
+		case CXXX:
+		case CTYPEDEF:
+			return 1;
+		case CEXTERN:
+		case	CGLOBL:
+		case CSTATIC:
+		case CLOCAL:
+			return s->type != T && s->type->etype == TENUM;
+	}
+	return -1;
+}
+
+static void genstart(void);
+
+static char*
+mprolog[] =
+{
+	"%%: module",
+	"{",
+	"\tPATH: con \"%%%.dis\";",
+	"",
+	nil
+};
+
+static char*
+mepilog[] =
+{
+	"};",
+	nil
+};
+
+static char*
+bprolog[] =
+{
+	"implement %%;",
+	"",
+	"include \"draw.m\";",
+	"",
+	"%%: module",
+	"{",
+	"	init: fn(nil: ref Draw->Context, argl: list of string);",
+	"};",
+	"",
+	nil
+};
+
+static char*
+bmprolog[] =
+{
+	"implement %%;",
+	"",
+	"include \"draw.m\";",
+	"",
+	nil
+};
+
+static char*
+bepilog[] =
+{
+	nil
+};
+
+static void
+pgen0(char **txt)
+{
+	int sub;
+	char *b, *s, *t, **p;
+
+	p = txt;
+	for(;;){
+		s = *p++;
+		if(s == nil)
+			break;
+		sub = 0;
+		for(t = s; *t != 0; t++){
+			if(*t == '%' && *(t+1) == '%'){
+				sub = 1;
+				break;
+			}
+		}
+		if(sub){
+			strcpy(buf, s);
+			b = buf;
+			for(t = s; *t != 0; t++){
+				if(*t == '%' && *(t+1) == '%'){
+					if(*(t+2) == '%'){
+						outmod(mbuf, 0);
+						t++;
+					}
+					else
+						outmod(mbuf, 1);
+					strcpy(b, mbuf);
+					b += strlen(mbuf);
+					t++;
+				}
+				else
+					*b++ = *t;
+			}
+			*b = 0;
+			prline(buf);
+		}
+		else			
+			prline(s);
+	}
+}
+
+static char*
+hasm()
+{
+	outmod(mbuf, 0);
+	strcat(mbuf, ".m");
+	if(exists(mbuf))
+		return mbuf;
+	else if(domod){
+		outmod(buf, 0);
+		strcat(buf, ".h");
+		if(exists(buf))
+			return mbuf;
+	}
+	return nil;
+}
+
+void
+pgen(int b)
+{
+	char **p;
+
+	if(!dolog())
+		return;
+	if(b)
+		p = hasm() ? bmprolog : bprolog;
+	else
+		p = mprolog;
+	pgen0(p);
+	if(b && passes)
+		genstart();
+	if(!b)
+		incind();
+}
+
+void
+epgen(int b)
+{
+	char **p;
+
+	/* output(0x7fffffff, 1); */	/* INFINITY */
+	if(!dolog())
+		return;
+	if(b){
+		if(!passes)
+			genstart();
+		p = bepilog;
+	}
+	else
+		p = mepilog;
+	if(!b)
+		decind();
+	pgen0(p);
+}
+
+static int lastsec = 0;
+
+#define ASSOC		1
+#define RASSOC	2
+#define POSTOP	4
+
+#define LEFT	1
+#define RIGHT	2
+#define PRE	4
+#define POST	8
+
+static int space[] = { 0, 0, 2, 0, 4, 5, 0, 0, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static struct{
+	char *name;
+	int	prec;
+	int	kind;
+} ops[] = {
+	"",		0,	0,	/* ONOOP */
+	"",		16,	0,	/* OXXX, */
+	"+",		12,	ASSOC,	/* OADD, */
+	"&",		14,	RASSOC,	/* OADDR, */
+	"&",		8,	ASSOC,	/* OAND, */
+	"&&",	5,	ASSOC,	/* OANDAND, */
+	"",		16,	0,	/* OARRAY, */
+	"=",		2,	RASSOC,	/* OAS, */
+	"=",		2,	RASSOC,	/* OASI, */
+	"+=",		2,	RASSOC,	/* OASADD, */
+	"&=",		2,	RASSOC,	/* OASAND, */
+	"<<=",	2,	RASSOC,	/* OASASHL, */
+	">>=",	2,	RASSOC,	/* OASASHR, */
+	"/=",		2,	RASSOC,	/* OASDIV, */
+	"<<",		11,	0,	/* OASHL, */
+	">>",		11,	0,	/* OASHR, */
+	"/=",		2,	RASSOC,	/* OASLDIV, */
+	"%=",		2,	RASSOC,	/* OASLMOD, */
+	"*=",		2,	RASSOC,	/* OASLMUL, */
+	">>=",	2,	RASSOC,	/* OASLSHR, */
+	"%=",		2,	RASSOC,	/* OASMOD, */
+	"*=",		2,	RASSOC,	/* OASMUL, */
+	"|=",		2,	RASSOC,	/* OASOR, */
+	"-=",		2,	RASSOC,	/* OASSUB, */
+	"^=",		2,	RASSOC,	/* OASXOR, */
+	"",		-1,	0,	/* OBIT, */
+	"",		-1,	0,	/* OBREAK, */
+	"",		-1,	0,	/* OCASE, */
+	"",		14,	RASSOC,	/* OCAST, */
+	"",		1,	ASSOC,	/* OCOMMA, */
+	"",		3,	RASSOC,	/* OCOND, */
+	"",		16,	0,	/* OCONST, */
+	"",		-1,	0,	/* OCONTINUE, */
+	"/",		13,	0,	/* ODIV, */
+	".",		15,	0,	/* ODOT, */
+	"...",		16,	0,	/* ODOTDOT, */
+	"",		-1,	0,	/* ODWHILE, */
+	"",		-1,	0,	/* OENUM, */
+	"==",		9,	0,	/* OEQ, */
+	"",		-1,	0,	/* OFOR, */
+	"",		15,	0,	/* OFUNC, */
+	">=",		10,	0,	/* OGE, */
+	"",		-1,	0,	/* OGOTO, */
+	">",		10,	0,	/* OGT, */
+	">",		10,	0,	/* OHI, */
+	">=",		10,	0,	/* OHS, */
+	"",		-1,	0,	/* OIF, */
+	"*",		14,	RASSOC,	/* OIND, */
+	"",		-1,	0,	/* OINDREG, */
+	"",		16,	0,	/* OINIT, */
+	"",		-1,	0,	/* OLABEL, */
+	"/",		13,	0,	/* OLDIV, */
+	"<=",		10,	0,	/* OLE, */
+	"",		16,	0,	/* OLIST, */
+	"%",		13,	0,	/* OLMOD, */
+	"*",		13,	ASSOC,	/* OLMUL, */
+	"<",		10,	0,	/* OLO, */
+	"<=",		10,	0,	/* OLS, */
+	">>",		11,	0,	/* OLSHR, */
+	"<",		10,	0,	/* OLT, */
+	"%",		13,	0,	/* OMOD, */
+	"*",		13,	ASSOC,	/* OMUL, */
+	"",		16,	0,	/* ONAME, */
+	"!=",		9,	0,	/* ONE, */
+	"!",		14,	RASSOC,	/* ONOT, */
+	"|",		6,	ASSOC,	/* OOR, */
+	"||",		4,	ASSOC,	/* OOROR, */
+	"--",		14,	RASSOC|POSTOP,	/* OPOSTDEC, */
+	"++",		14,	RASSOC|POSTOP,	/* OPOSTINC, */
+	"--",		14,	RASSOC,	/* OPREDEC, */
+	"++",		14,	RASSOC,	/* OPREINC, */
+	"",		16,	0,	/* OPROTO, */
+	"",		-1,	0,	/* OREGISTER, */
+	"",		0,	0,	/* ORETURN, */
+	"SET",	-1,	0,	/* OSET, */
+	"signof",	14,	RASSOC,	/* OSIGN, */
+	"sizeof",	14,	RASSOC,	/* OSIZE, */
+	"",		16,	0,	/* OSTRING, */
+	"",		16,	0,	/* OLSTRING, */
+	"",		16,	0,	/* OSTRUCT, */
+	"-",		12,	0,	/* OSUB, */
+	"",		-1,	0,	/* OSWITCH, */
+	"",		16,	0,	/* OUNION, */
+	"USED",	-1,	0,	/* OUSED, */
+	"",		-1,	0,	/* OWHILE, */
+	"^",		7,	ASSOC,	/* OXOR, */
+	"-",		14,	RASSOC,	/* ONEG, */
+	"~",		14,	RASSOC,	/* OCOM, */
+	"",		16,	0,	/* OELEM, */
+	"",		-1,	0,	/* OTST, */
+	"",		-1,	0,	/* OINDEX, */
+	"",		-1,	0,	/* OFAS, */
+	"",		-1,	0,	/* OBLK */
+	"+",		14,	RASSOC,	/* OPOS */
+	"",		-1,	0,	/* ONUL */
+	".",		15,	0,	/* ODOTIND */
+	"",		15,	0,	/* OARRIND */
+	"",		-1,	0,	/* ODAS */
+	":=",		2,	RASSOC,	/* OASD */
+	"",		16,	0,	/* OIOTA */
+	"",		14,	RASSOC,	/* OLEN */
+	"",		17,	0,	/* OBRACKET */
+	"",		14,	RASSOC,	/* OREF */
+	"",		14,	RASSOC,	/* OARRAYOF */
+	"",		15,	0,	/* OSLICE */
+	"&",		14,	RASSOC,	/* OSADDR, */
+	"",		16,	0,	/* ONIL */
+	"",		16,	0,	/* OS2AB */
+	"",		16,	0,	/* OAB2S */
+	"",		16,	0,	/* OFILDES */
+	".",		15,	0,	/* OFD */
+	"",		16,	0,	/* OTUPLE */
+	".",		15,	0,	/* OT0 */
+	"",		15,	0,	/* ORETV */
+	"+",		12,	ASSOC,	/* OCAT */
+	"",		-1,	0,	/* OSBREAK, */
+	".",		15,	0,	/* OLDOT */
+	"->",		15,	0,	/* OMDOT */
+	nil,		-1,	0,	/* OCODE */
+	nil,		-1,	0,	/* ODECE */
+	nil,		-1,	0,	/* ODECT */
+	nil,		-1,	0,	/* ODECV */
+	nil,		-1,	0,	/* ODECF */
+	nil,		-1,	0,	/* OPUSH */
+	nil,		-1,	0,	/* OPOP */
+	"",		-1,	0,	/* OEND */
+};
+
+#define COMPLEX	32
+
+#define NOBR	2
+#define NOIN	4
+#define YESBR	8
+#define NONL	16
+#define NOENL	32
+
+enum{
+	LNONE,
+	LSTRLEN,
+	LSTRCMP,
+	LSTRCPY,
+	LSTRCAT,
+	LSTRNCMP,
+	LSTRNCPY,
+	LSTRNCAT,
+	LSTRDUP,
+	LMEMMOVE,
+	LMALLOC,
+	LFREE,
+	LEXIT,
+	LCLOSE,
+	LATOI,
+	LATOL,
+	LATOF,
+	LPRINT,
+	LFPRINT,
+	LSPRINT,
+	LSELF,
+};
+
+static int tmp;
+
+static void egen(Node*, int, int);
+static Node* buildcases(Node*);
+static void tdgen(Node *, int);
+static Node* cfind(Node*);
+static Node* cgen(Node*, Node*);
+static void cgen0(Node*, Node*);
+static int lteq(Type*, Type*);
+static Type* ntype(Node*);
+static int rewe(Node*, Type*, int);
+static void rewlc(Node*, int, Type*);
+static Node* con(vlong);
+static void	clrbrk(Node*);
+static int hasbrk(Node*);
+static int isgen(char*);
+static int simple(Node*);
+static void pfmt(char*);
+static void lpfmt(Rune*);
+static int lline(Node*);
+static void args(Node*);
+static void addmodn(Sym*);
+static void scomplex(Node*);
+static void mset(Node*);
+
+static Node *lastd;
+
+static int
+rev(int op)
+{
+	switch(op){
+		case OLT:	return OGT;
+		case OLE:	return OGE;
+		case OGT:	return OLT;
+		case OGE:	return OLE;
+	}
+	return op;
+}
+
+void
+newsec(int l)
+{
+	if(l != 1 && lastd != Z){
+		tdgen(lastd, 1);
+		lastd = Z;
+	}
+	if(l != 2)
+		etgen2(nil);
+	if(lastsec && l != lastsec)
+		newline();
+	lastsec = l;
+}
+
+static Node*
+defval(Type *t)
+{
+	Node *n;
+
+	if(t == T)
+		t = types[TINT];
+	n = con(0);
+	n->type = types[TINT];
+	n->kind = KDEC;
+	switch(t->etype){
+		case TFLOAT:
+		case TDOUBLE:
+			n->type = types[TDOUBLE];
+			n->fconst = 0.0;
+			n->cstring = "0.0";
+			return n;
+		default:
+			break;
+		case TIND:
+		case TFUNC:
+		case TARRAY:
+			n->type = typ1(TIND, types[TVOID]);
+			return n;
+		case TVOID:
+		case TSTRUCT:
+		case TUNION:
+			free(n);
+			return Z;
+	}
+	if(!lteq(n->type, t)){
+		n = new1(OCAST, n, Z);
+		n->type = t;
+	}
+	return n;
+}
+
+static int
+teq(Type *t1, Type *t2)
+{
+	if(t1 == t2)
+		return 1;
+	return sametype(t1, t2);
+/*
+	if(t1->etype != t2->etype)
+		return 0;
+	switch(t1->etype){
+		case TARRAY:
+			if(t1->width != t2->width)
+				return 0;
+			break;
+		case TFUNC:
+			if(!teq(t1->down, t2->down))
+				return 0;
+			break;
+		case TSTRUCT:
+		case TUNION:
+			return t1->link == t2->link;
+		case TENUM:
+			return 1;
+	}
+	return teq(t1->link, t2->link);
+*/
+}
+
+static int
+tequiv(Type *t1, Type *t2)
+{
+	if(!teq(t1, t2))
+		return 0;
+	if(t1->etype == TSTRUCT || t1->etype == TUNION)
+		return suename(t1) == suename(t2);
+	return 1;
+}
+
+static int
+iteq(Type *t1, Type *t2)
+{
+	if(t1 == T || t2 == T)
+		return 0;
+	return t1->etype == TIND && (teq(t1->link, t2) || (t1->link->etype == TVOID && isnum(t2)));
+}
+
+static Type *
+ltype(Type *t)
+{
+	switch(t->etype){
+		case TUCHAR:
+			return types[TCHAR];
+		case TSHORT:
+		case TUSHORT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TENUM:
+			return types[TINT];
+		case TUVLONG:
+			return types[TVLONG];
+		case TFLOAT:
+			return types[TDOUBLE];
+		default:
+			return t;
+	}
+}
+
+static int
+lteq(Type *t1, Type *t2)
+{
+	if(t1 == T || t2 == T)
+		return 0;
+	if(t1 == t2)
+		return 1;
+	if(t1->etype == TIND && t2->etype == TIND)
+		return lteq(t1->link, t2->link);
+	return sametype(ltype(t1), ltype(t2));
+}
+
+static Type*
+tcp(Type *t)
+{
+	Type *nt;
+
+	if(t == T)
+		return T;
+	nt = typ1(TXXX, T);
+	*nt = *t;
+	return nt;
+}
+
+static Type*
+tuple(Type *t1, Type *t2)
+{
+	Type *t, **at, *l;
+
+	if(t1 == T || t1->etype == TVOID)
+		return tcp(t2);
+	if(t2 == T || t2->etype == TVOID)
+		return tcp(t1);
+	if(t2->etype == TTUPLE)
+		diag(Z, "bad tuple type");
+	t = typ1(TTUPLE, T);
+	at = &t->link;
+	if(t1->etype == TTUPLE){
+		for(l = t1->link; l != T; l = l->down){
+			*at = tcp(l);
+			at = &(*at)->down;
+		}
+	}
+	else{
+		*at = tcp(t1);
+		at = &(*at)->down;
+	}
+	*at = tcp(t2);
+	return t;
+}
+
+static Sym*
+sue(Type *t)
+{
+	int h;
+	Sym *s;
+
+	if(t != T)
+		for(h=0; h<nelem(hash); h++)
+			for(s = hash[h]; s != S; s = s->link)
+				if(s->suetag && s->suetag->link == t)
+					return s;
+	return S;
+}
+
+static void
+pranon(int i)
+{
+	prid("anon_");
+	prnum(i+1, KDEC, T);
+}
+
+static int
+dotpath(Sym *s, Type *t, int pr)
+{
+	int i;
+	Type *t1;
+
+	if(t == T)
+		return 0;
+	for(t1 = t->link; t1 != T; t1 = t1->down){
+		if(t1->sym == s){
+			if(pr){
+				prdelim(".");
+				prsym(s, 0);
+			}
+			return 1;
+		}
+	}
+	i = 0;
+	for(t1 = t->link; t1 != T; t1 = t1->down){
+		if(t1->sym == S){
+			i++;
+			if(typesu[t1->etype] && sametype(s->type, t1)){
+				if(pr){
+					prdelim(".");
+					pranon(i-1);
+				}
+				return 1;
+			}
+		}
+	}
+	i = 0;
+	for(t1 = t->link; t1 != T; t1 = t1->down){
+		if(t1->sym == S){
+			i++;
+			if(typesu[t1->etype] && dotpath(s, t1, 0)){
+				if(pr){
+					prdelim(".");
+					pranon(i-1);
+					dotpath(s, t1, 1);
+				}
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static Sym*
+suename(Type *t)
+{
+	Sym *s;
+
+	s = sue(t->link);
+	if(s != S)
+		return s;
+	else if(t->tag != S)
+		return t->tag;
+	else if(t->sym != S)
+		return t->sym;
+	return S;
+}
+
+static int
+cycle(Type *t, Type *base)
+{
+	int r;
+	Type *l;
+
+	if(t->vis){
+		/* sametype() does structural comparison so have to check names */
+		if(t == base || tequiv(t, base))
+			return 1;
+		return 0;
+	}
+	r = 0;
+	t->vis = 1;
+	switch(t->etype){
+		case TIND:
+		case TARRAY:
+			r = cycle(t->link, base);
+			break;
+		case TSTRUCT:
+		case TUNION:
+		case TTUPLE:
+			for(l = t->link; l != T; l = l->down)
+				r |= cycle(l, base);
+			break;
+	}
+	t->vis = 0;
+	return r;
+}
+
+static void
+addnode(int op, Node *n)
+{
+	Node *nn;
+
+	nn = new1(OXXX, Z, Z);
+	*nn = *n;
+	n->op = op;
+	n->left = nn;
+	n->right = Z;
+	n->type = nn->type;
+}
+
+static void
+cast(Node *n, Type *t)
+{
+	addnode(OCAST, n);
+	n->type = t;
+}
+
+static void
+intcast(Node *n)
+{
+	if(isptr(n->type)){
+		addnode(ONE, n);
+		n->right = con(0);
+		n->right->type = n->left->type;
+		n->type = types[TINT];
+	}
+	else
+		cast(n, types[TINT]);
+}
+
+static void
+strcast(Node *n)
+{
+	cast(n, stringtype);
+}
+
+static void
+bptr(Node *n)
+{
+	if(n == Z)
+		return;
+	switch(n->op){
+		default:
+			if(!lteq(n->type, types[TINT]))
+				intcast(n);
+			break;
+		case ONOT:
+			if(!lteq(n->left->type, types[TINT])){
+				intcast(n->left);
+				if(n->left->op == ONE){
+					n->left->op = OEQ;
+					*n = *n->left;
+				}
+			}
+			break;
+		case OANDAND:
+		case OOROR:
+			bptr(n->left);
+			bptr(n->right);
+			break;
+		case OCOND:
+			bptr(n->right->left);
+			bptr(n->right->right);
+			break;
+	}
+}
+
+static void
+bcomplex(Node *n)
+{
+	if(n == Z)
+		return;
+	if(!passes)
+		complex(n);
+	bptr(n);
+}
+
+static void
+ecomplex(Node *n)
+{
+	if(!passes)
+		complex(n);
+	rewe(n, T, 0);
+}
+
+static void
+becomplex(Node *n)
+{
+	bcomplex(n);
+	rewe(n, T, 0);
+}
+
+static void
+tgen(Type *t, int dec, int arinit)
+{
+	Type *l;
+
+	if(t == T)
+		return;
+	switch(t->etype){
+		case TXXX:
+			prid("int");
+			break;
+		case TCHAR: 
+		case TUCHAR:
+			prid("byte");
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TENUM:
+			prid("int");
+			break;
+		case TVLONG:
+		case TUVLONG:
+			prid("big");
+			break;
+		case TFLOAT:
+		case TDOUBLE:
+			prid("real");
+			break;
+		case TIND:
+			if(strings == 2 && t->link && t->link->etype == TCHAR){
+				prid("string");
+				break;
+			}
+			if(isadt(t->link) || t->link->etype == TFUNC)
+				prid("ref ");
+			else
+				prid("array of ");
+			if(t->link && t->link->etype == TVOID){
+				prid("byte");
+				prcom("was void*", Z);
+			}
+			else
+				tgen(t->link, 1, 0);
+			break;
+		case TFUNC:
+			if(0){
+				prid("int");
+				prcom("was function", Z);
+				break;
+			}
+			prid("fn");
+			prdelim("(");
+			for(l = t->down; l != T; l = l->down){
+				if(l->etype == TVOID && l->down == T)
+					break;
+				if(l->etype == TDOT){
+					prcom("was ...", Z);
+					break;
+				}
+				if(l->sym != S)
+					prsym(l->sym, 0);
+				else
+					prid("nil");
+				prdelim(": ");
+				tgen(l, 1, 0);
+				if(l->down != T && l->down->etype != TDOT)
+					prdelim(", ");
+			}
+			/* tgen(t->down, dec, 0, 0); */
+			prdelim(")");
+			if(!isvoid(t->link)){
+				prdelim(": ");
+				tgen(t->link, dec, 0);
+			}
+			break;
+		case TARRAY:
+			prid("array");
+			if(t->width == LARR)
+				t->width = LARR;
+			else if(dec){
+				if(t->nwidth != Z)
+					prcom("array index was ", t->nwidth);
+				else if(t->width != 0){
+					sprint(buf, "array index was %ld", t->width/t->link->width);
+					prcom(buf, Z);
+				}
+			}
+			else{
+				prdelim("[");
+				if(t->nwidth != Z)
+					egen(t->nwidth, ONOOP, PRE);
+				else if(t->width != 0)
+					prnum(t->width/t->link->width, KDEC, T);
+				prdelim("]");
+			}
+			prdelim(" of ");
+			if(!arinit)
+				tgen(t->link, 1, 0);
+			break;
+		case TVOID:
+			/* prid("void"); */
+			prid("byte");
+			prcom("was void", Z);
+			break;
+		case TSTRUCT:
+		case TUNION:
+			if(t->link != T && t->link->etype == TFD){
+				prid("Sys->FD");
+				usemod(sysop, 0);
+			}
+			else
+				prsym(suename(t), 1);
+			break;
+		case TTUPLE:
+			prdelim("(");
+			for(l = t->link; l != T; l = l->down){
+				tgen(l, dec, 0);
+				if(l->down != T)
+					prdelim(", ");
+			}
+			prdelim(")");
+			break;
+		case TDOT:
+			prdelim("...");
+			break;
+		case TSTRING:
+			prid("string");
+			break;
+		case TFD:
+			prid("fd");
+			break;
+		default:
+			diag(Z, "unknown type");
+			break;
+	}
+}
+
+static Type*
+typn(Type *t, int i)
+{
+	Type *l;
+
+	for(l = t->down; l != T && --i >= 0; l = l->down)
+		;
+	return l;
+}
+
+void
+ttgen2(Type *t)
+{
+	Type *l;
+	Sym *s;
+	int anon = 0;
+
+	switch(t->etype){
+		case TSTRUCT:
+		case TUNION:
+			newsec(0);
+			output(t->lineno, 1);
+			s = suename(t);
+			if(isgen(s->name))
+				addmodn(s);
+			setmod(s);
+			prsym(s, 0);
+			prdelim(": ");
+			prid("adt");
+			prdelim("{");
+			if(t->etype == TUNION)
+				prcom("was union", Z);
+			newline();
+			incind();
+			t->vis = 1;
+			for(l = t->link; l != T; l = l->down){
+				output(l->lineno, 1);
+				if(l->nbits)
+					prcom("was bit field", Z);
+				if(l->sym != S)
+					prsym(l->sym, 0);
+				else
+					pranon(anon++);
+				prdelim(": ");
+				if(cycle(l, t))
+					prid("cyclic ");
+				tgen(l, 1, 0);
+				prdelim(";");
+				newline();
+			}
+			t->vis = 0;
+			decind();
+			prdelim("};");
+			newline();
+			newline();
+			break;
+		default:
+			break;
+	}
+}
+
+static int
+canjoin(Node *n, Node *nn)
+{
+	return teq(n->type, nn->type) && isname(n) && isname(nn) && n->type->etype != TARRAY;
+}
+
+void
+vtgen2(Node *n)
+{
+	int  t, c, comma = 0;
+	Node *nn;
+	Sym *s;
+
+	nn = n;
+	if(n->op == ODAS)
+		nn = n->left;
+	if(nn->type == T || nn->sym == S)
+		return;
+	t = nn->type->etype;
+	c = nn->sym->class;
+	if(0 && c == CTYPEDEF){
+		/* egen(nn, ONOOP, PRE); */
+		/* tdgen(n, 1, 0); */
+		if(isadt(n->type)){
+			s = suename(n->type);
+			if(isgen(s->name)){
+				s->lname = nn->sym->name;
+				ttgen2(n->type);
+			}
+		}
+	}
+	if(c != CGLOBL && c != CSTATIC && c != CLOCAL && c != CEXREG)
+		return;
+	newsec(1);
+	if(lastd != Z){
+		if(t != TFUNC && canjoin(lastd, n))
+			comma = 1;
+		else
+			tdgen(lastd, 1);
+	}
+	output(nn->lineno, 1);
+	if(t == TFUNC){
+		if(ism()){
+			setmod(nn->sym);
+			egen(nn, ONOOP, PRE);
+			tdgen(n, 1);
+		}
+		lastd = Z;
+		return;
+	}
+	if(comma)
+		prdelim(", ");
+	if(nn->op != ONAME)
+		diag(nn, "internal: not name in vtgen");
+	setmod(nn->sym);
+	prsym(nn->sym, 0);
+	/* egen(nn, ONOOP, PRE); */
+	/* tdgen(n, 1, 0); */
+	lastd = n;
+	if(n->op == ODAS)
+		rewe(n->right, T, 1);
+}
+
+static void minseq(Syml*);
+
+static Node*
+con(vlong v)
+{
+	int neg = 0;
+	Node *n;
+
+	if(v < 0){
+		neg = 1;
+		v = -v;
+	}
+	n = new1(OCONST, Z, Z);
+	n->vconst = v;
+	n->kind = KDEC;
+	n->type = types[TINT];
+	if(neg)
+		n = new1(ONEG, n, Z);
+	return n;
+}
+
+/*
+static Node*
+fcon(double v)
+{
+	int neg = 0;
+	Node *n;
+
+	if(v < 0){
+		neg = 1;
+		v = -v;
+	}
+	n = new1(OCONST, Z, Z);
+	n->fconst = v;
+	n->kind = KDEC;
+	n->type = types[TDOUBLE];
+	if(neg)
+		n = new1(ONEG, n, Z);
+	return n;
+}
+*/
+
+static Node*
+add(vlong v, Node *n)
+{
+	if(v == 0)
+		return n;
+	return new1(OADD, con(v), n);
+}
+
+static Node*
+addn(Node *n1, Node *n2)
+{
+	if(n1 == Z || n2 == Z)
+		return Z;
+	if(isconst(n1, 0))
+		return n2;
+	if(isconst(n2, 0))
+		return n1;
+	return new1(OADD, n1, n2);
+}
+
+static Node*
+mul(vlong v, Node *n)
+{
+	if(v == 0)
+		return con(0);
+	else if(v == 1)
+		return n;
+	else if(v == -1)
+		return new1(ONEG, n, Z);
+	return new1(OMUL, con(v), n);
+}
+
+static Node*
+mydiv(Node *n, vlong w)
+{
+	Node *nn;
+
+	if(w == 0)
+		return Z;
+	if(w == 1)
+		return n;
+	else if(w == -1)
+		return new1(ONEG, n, Z);
+	switch(n->op){
+		case OCONST:
+			if(n->vconst % w == 0){
+				n->vconst /= w;
+				if(n->left != Z && mydiv(n->left, w) == Z){
+					n->vconst *= w;
+					break;
+				}
+				return n;
+			}
+			break;
+		case OCAST:
+			return mydiv(n->left, w);
+		case OMUL:
+			nn = mydiv(n->right, w);
+			if(nn != Z){
+				if(isconst(nn, 1))
+					*n = *n->left;
+				return n;
+			}
+			nn = mydiv(n->left, w);
+			if(nn != Z){
+				if(isconst(nn, 1))
+					*n = *n->right;
+				return n;
+			}
+			break;
+		default:
+			break;
+	}
+	return Z;			
+}
+
+static Node*
+iota(void)
+{
+	return new1(OIOTA, Z, Z);
+}
+
+static Node*
+symcon(Sym *s)
+{
+	Node *n;
+
+	if(s->nconst != Z)
+		return s->nconst;
+	n = con(s->vconst);
+	n->kind = s->kind;
+	return n;
+}
+
+#define ARITH	1
+#define GEOM	2
+
+static Syml*
+newsyml(Sym *s, Syml **frees)
+{
+	Syml *sl, *f;
+
+	if((f = *frees) != nil){
+		sl = f;
+		*frees = f->nxt;
+	}
+	else
+		sl = (Syml*)malloc(sizeof(Syml));
+	sl->sym = s;
+	sl->nxt = nil;
+	return sl;
+}
+
+static Syml*
+etseq(Syml *syml)
+{
+	int e, pio, io, comma;
+	vlong d, dd, v0, v1, v, t, tt;
+	Node *expr;
+	Sym *s;
+	Syml *sl, *lsl;
+
+	lsl = nil;
+	pio = io = ARITH|GEOM;
+	e = 0;
+	dd = 0;
+	d = 0;
+	for(sl = syml; sl != nil; sl = sl->nxt){
+		s = sl->sym;
+		if(isreal(s->tenum) || s->tenum->etype == TIND)
+			break;
+		if(e == 0)
+			v0 = s->vconst;
+		if(e == 1){
+			v1 = s->vconst;
+			d = v1-v0;
+		}
+		if(e > 0 && (v <= 0 || s->vconst != 2*v))
+			io &= ~GEOM;
+		if(0 && e > 1 && s->vconst-v != d)
+			io &= ~ARITH;
+		if(e > 1){
+			t = s->vconst-v;
+			tt = t-d;
+			if(e > 2 && tt != dd)
+				io &= ~ARITH;
+			else{
+				d = t;
+				dd = tt;
+			}
+		}
+		if(io == 0)
+			break;
+		v = s->vconst;
+		lsl = sl;
+		pio = io;
+		e++;
+	}
+	if(e < 2)
+		pio = 0;
+	if(pio&GEOM){
+		if(e < 3)
+			pio = 0;
+	}
+	else if(pio&ARITH){
+		int n;
+
+		if(d == 0 && dd == 0)
+			n = 2;
+		else if(dd == 0)
+			n = 3;
+		else
+			n = 4;
+		if(e < n || (dd&1) != 0)
+			pio = 0;
+	}
+	if(lsl == nil || pio == 0)
+		lsl = syml;
+	comma = 0;
+	for(sl = syml; sl != nil; sl = sl->nxt){
+		s = sl->sym;
+		nearln = s->lineno;
+		output(s->lineno, 1);
+		if(pio){
+			if(comma)
+				prdelim(", ");
+			setmod(s);
+			prsym(s, 0);
+			comma = 1;
+		}
+		else{
+			setmod(s);
+			prsym(s, 0);
+			prdelim(": ");
+			prid("con ");
+			if(isbyte(s->tenum) || isbig(s->tenum) && !islbigv(s->vconst) || !isbig(s->tenum) && isuintv(s->vconst)){
+				tgen(s->tenum, 1, 0);
+				prdelim(" ");
+			}
+			if(s->nconst != Z)
+				egen(s->nconst, ONOOP, PRE);
+			else if(s->kind == KCHR)
+				prchar(s->vconst);
+			else if(isreal(s->tenum))
+				prreal(s->fconst, s->cstring, s->kind);
+			else
+				prnum(s->vconst, s->kind, s->tenum);
+			prdelim(";");
+			newline();
+		}
+		if(sl == lsl)
+			break;
+	}
+	if(pio){
+		s = syml->sym;
+		prdelim(": ");
+		prid("con ");
+		if(isbyte(s->tenum) || isbig(s->tenum)){
+			tgen(s->tenum, 1, 0);
+			prdelim(" ");
+		}
+		if(pio&GEOM){
+			if(v0 == 0 || v0 == 1 || v0 == -1)
+				expr = mul(v0, new1(OASHL, con(1), iota()));
+			else
+				expr = new1(OMUL, symcon(s), new1(OASHL, con(1), iota()));
+		}
+		else if(d == 0 && dd == 0)
+			expr = symcon(s);
+		else if(dd == 0)
+			expr = add(v0, mul(d, iota()));
+		else
+			expr = add(v0, new1(OADD, mul(v1-dd/2-v0, iota()), mul(dd/2, new1(OMUL, iota(), iota()))));
+		complex(expr);
+		expr = ckneg(expr);
+		egen(expr, ONOOP, PRE);
+		prdelim(";");
+		newline();
+	}
+	return lsl->nxt;
+}
+
+static void
+adde(Syml *sl, Symq *q)
+{
+	if(q->f == nil)
+		q->f = sl;
+	else
+		q->r->nxt = sl;
+	q->r = sl;
+}
+
+static void
+freeq(Symq *q, Syml **frees)
+{
+	if(q->f){
+		q->r->nxt = *frees;
+		*frees = q->f;
+		q->f = q->r = nil;
+	}
+}
+
+static void
+etgen2(Sym *s)
+{
+	Syml *sl;
+	static Syml *frees;
+	static Symq symq, symq1;
+
+	if(s != nil){
+		newsec(2);
+		sl = newsyml(s, &frees);
+		adde(sl, &symq);
+		if(isinteger(s->tenum) && isbigv(s->vconst) && !isbig(s->tenum))
+			s->tenum = types[TVLONG];
+		return;
+	}
+	/* end of enums */
+	if(symq.f && symq.f == symq.r){	/* try to merge with other singletons */
+		adde(symq.f, &symq1);
+		symq.f = symq.r = nil;
+		return;
+	}
+	if(symq1.f){
+		for(sl = symq1.f; sl != nil; sl = etseq(sl))
+			;
+		freeq(&symq1, &frees);
+	}
+	if(symq.f){
+		for(sl = symq.f; sl != nil; sl = etseq(sl))
+			;
+		freeq(&symq, &frees);
+	}
+}
+
+static void
+lgen(Node *n, int br, int first)
+{
+	if(br)
+		prdelim("(");
+	if(n == Z){
+		if(br)
+			prdelim(")");
+		return;
+	}
+	if(n->op == OLIST || n->op == OTUPLE){
+		lgen(n->left, 0, first);
+		lgen(n->right, 0, 0);
+	}
+	else if(n->op != ODOTDOT){
+		if(!first)
+			prdelim(", ");
+		egen(n, ONOOP, PRE);
+	}
+	else
+		prcom("was ...", Z);
+	if(br)
+		prdelim(")");
+}
+
+static void
+preced(int op1, int op2, int s, int c)
+{
+	int p1, p2, k1, k2, br;
+	char buf[2];
+
+	br = 0;
+	p1 = ops[op1].prec;
+	p2 = ops[op2].prec;
+	if(p1 < 0 || p2 < 0)
+		diag(Z, "-ve precedence");
+	if(p1 > p2)
+		br = 1;
+	else if(p1 == p2){
+		k1 = ops[op1].kind;
+		k2 = ops[op2].kind;
+		if(op1 == op2){
+			if(k1&RASSOC)
+				br = s == LEFT;
+			else
+				br = s == RIGHT && !(k1&ASSOC);
+		}
+		else{
+			if(k1&RASSOC)
+				br = s == LEFT;
+			else
+				br = s == RIGHT && op1 != OADD;
+
+			if(k1&POSTOP && !(k2&POSTOP))
+				br = 1;
+
+			/* funny case */
+			if(op2 == OMDOT && s == LEFT && (op1 == ODOT || op1 == ODOTIND))
+				br = 1;
+		}
+	}
+	if(br){
+		buf[0] = c;
+		buf[1] = '\0';
+		prdelim(buf);
+	}
+}
+
+static void
+egen(Node *n, int op0, int side)
+{
+	int op, p;
+	Type *t;
+	Node *nn;
+
+	if(n == Z){
+		if(op0 == OBRACKET)
+			prdelim("()");
+		return;
+	}
+	if(n->op == OCONST && n->left != Z){	/* actual node in source */
+		n->left->type = n->type;
+		n = n->left;
+	}
+	if((n->op == OSTRING || n->op == OLSTRING) && n->left != Z)	/* actual node in source */
+		n = n->left;
+	if(n->op == OCAST && (lteq(n->type, n->left->type) || isnil(n) || !iscastable(n->type, n->left->type))){
+		if(isnil(n))
+			prid("nil");
+		else
+			egen(n->left, op0, side);
+		return;
+	}
+	if(n->op == ONAME && arrow(n->sym))
+		n->op = OMDOT;
+	if(n->op != OLIST)
+		output(n->lineno, 0);
+	op = n->op;
+	preced(op0, op, side, '(');
+	switch(op){
+		case OLIST:
+		case OTUPLE:
+			lgen(n, 1, 1);
+			break;
+		case OIOTA:
+			prid("iota");
+			break;
+		case OMDOT:
+		case ONAME:
+		case OXXX:
+			prsym(n->sym, 1);
+			break;
+		case OCONST:
+			if(n->kind == KCHR)
+				prchar(n->vconst);
+			else if(isreal(n->type))
+				prreal(n->fconst, n->cstring, n->kind);
+			else if(isnil(n))
+				prid("nil");
+			else
+				prnum(n->vconst, n->kind, n->type);
+			if(n->right != Z)
+				prcom("was ", n->right);
+			break;
+		case OSTRING:
+			prstr(n->cstring);
+			break;
+		case OLSTRING:
+			prlstr(n->rstring);
+			break;
+		case OCOND:
+			egen(n->left, op, POST);
+			prdelim(" ? ");
+			egen(n->right->left, op, PRE|POST);
+			prdelim(" : ");
+			egen(n->right->right, op, PRE);
+			prcom("?", Z);
+			break;
+		case OCOMMA:
+			if(op0 != OCOMMA)
+				prdelim("(");
+			egen(n->left, op, LEFT);
+			prdelim(", ");
+			egen(n->right, op, RIGHT);
+			if(op0 != OCOMMA)
+				prdelim(")");
+			break;
+		case OLDOT:
+			egen(n->left, OMOD, LEFT);	/* any precedence 13 operator */
+			prdelim(".");
+			egen(n->right, op, RIGHT);
+			break;
+		default:
+			p = ops[op].prec;
+			egen(n->left, op, LEFT);
+			if(space[p])
+				prdelim(" ");
+			prdelim(ops[op].name);
+			if(space[p])
+				prdelim(" ");
+			egen(n->right, op, RIGHT);
+			break;
+		case OIND: case OADDR: case OSADDR:
+		case OPOS: case ONEG:
+		case ONOT: case OCOM:
+		case OPREINC: case OPREDEC:
+			if(op == OADDR){
+				n->op = OSADDR;
+				if(!isfn(n->left->type))
+					prcom("was ", n);
+			}
+			else
+				prdelim(ops[op].name);
+			egen(n->left, op, PRE);
+			break;
+		case OPOSTINC: case OPOSTDEC:
+			egen(n->left, op, POST);
+			prdelim(ops[op].name);
+			break;
+		case ODOT:
+			egen(n->left, op, LEFT);
+			dotpath(n->sym, n->left->type, 1);
+			/* prdelim(ops[op].name); */
+			/* prsym(n->sym, 0); */
+			break;
+		case ODOTIND:
+			egen(n->left, op, LEFT);
+			if(isadt(n->left->type))
+				dotpath(n->sym, n->left->type, 1);	/* type may be demoted arg */
+			else
+				dotpath(n->sym, n->left->type->link, 1);
+			/* prdelim(ops[op].name); */
+			/* prsym(n->sym, 0); */
+			break;
+		case OARRIND:
+			egen(n->left, op, LEFT);
+			prdelim("[");
+			egen(n->right, ONOOP, RIGHT);
+			prdelim("]");
+			if(n->right->op == OCONST && n->right->vconst < 0)
+				prcom("negative array index", Z);
+			break;
+		case OLEN:
+			prid("len ");
+			egen(n->right, op, PRE);
+			break;
+		case OREF:
+			prid("ref ");
+			tgen(n->type->link, 0, 0);
+			break;
+		case OARRAYOF:
+			prid("array");
+			prdelim("[");
+			egen(n->left, ONOOP, LEFT);
+			prdelim("]");
+			prid(" of ");
+			tgen(n->type->link, 0, 0);
+			break;
+		case OSLICE:
+			egen(n->left, op, LEFT);
+			prdelim("[");
+			egen(n->right->left, ONOOP, RIGHT);
+			prdelim(": ");
+			egen(n->right->right, ONOOP, RIGHT);
+			prdelim("]");
+			break;
+		case OFUNC:
+			if(n->kind == KEXP)
+				egen(n->left, op, LEFT);
+			else
+				prsym(n->left->sym, 0);
+			lgen(n->right, 1, 1);
+			if(n->kind != KEXP && !isvoid(n->left->type->link)){
+				prdelim(": ");
+				tgen(n->left->type->link, 0, 0);
+			}
+			break;
+		case	ONIL:
+			prid("nil");
+			break;
+		case OCAST:
+			if(isnil(n))
+				prid("nil");
+			else if(iscastable(n->type, n->left->type)){
+				tgen(n->type, 0, 0);
+				prdelim(" ");
+				egen(n->left, op, RIGHT);
+			}
+			else
+				egen(n->left, op0, RIGHT);
+			break;
+		case OARRAY:
+			tgen(n->type, 0, 0);
+			egen(n->left, op, LEFT);
+			prdelim("[");
+			egen(n->right, ONOOP, RIGHT);
+			prdelim("]");
+			break;
+		case OSTRUCT:
+		case OUNION:
+			tgen(n->type, 0, 0);
+			lgen(n->left, 1, 1);
+			break;
+		case OELEM:
+			prdelim(".");
+			/* tgen(n->type, 0, 0, 0); */
+			prsym(n->sym, 0);
+			break;
+		case OSIZE:
+		case OSIGN:
+			prid(ops[op].name);
+			if(n->left != Z)
+				egen(n->left, OBRACKET, RIGHT);
+			else{
+				prdelim(" ");
+				prid(tnames[n->type->etype]);
+				if(typesu[n->type->etype] && n->type->tag){
+					prdelim(" ");
+					prid(n->type->tag->name);
+				}
+			}
+			break;
+		case OPROTO:
+			nn = n;
+			t = n->type;
+			n = protoname(n);
+			if(n != Z)
+				t = n->type;
+			else
+				t = prototype(nn->left, t);
+			if(!isvoid(t) || n != Z){
+				if(n == Z)
+					prid("nil");
+				else if(n->op == ODOTDOT){
+					prcom("was ...", Z);
+					break;
+				}
+				else
+					prsym(n->sym, 0);
+				/* egen(n, ONOOP, PRE); */
+				prdelim(": ");
+				tgen(t, 1, 0);
+			}
+			break;
+		case ODOTDOT:
+			prid("...");
+			break;
+		case OINIT:
+			egen(n->left, ONOOP, PRE);
+			break;
+		case OS2AB:
+			prid("libc0->s2ab");
+			prdelim("(");
+			egen(n->left, ONOOP, PRE);
+			prdelim(")");
+			usemod(libcop, 1);
+			break;
+		case OAB2S:
+			prid("libc0->ab2s");
+			prdelim("(");
+			egen(n->left, ONOOP, PRE);
+			prdelim(")");
+			usemod(libcop, 1);
+			break;
+		case OFILDES:
+			prid("sys->fildes");
+			prdelim("(");
+			egen(n->left, ONOOP, PRE);
+			prdelim(")");
+			usemod(sysop, 1);
+			break;
+		case OFD:
+			egen(n->left, op, LEFT);
+			prdelim(ops[op].name);
+			prid("fd");
+			break;
+		case OT0:
+			egen(n->left, op, LEFT);
+			prdelim(ops[op].name);
+			prid("t0");
+			break;
+		case ORETV:
+			n->op = OAS;
+			nn = n->left;
+			p = isvoid(n->type) || n->type->etype != TTUPLE || n->type->mark == TCPC;
+			if(p)
+				n->left = n->right;
+			else
+				n->left = new1(OTUPLE, new1(ONIL, Z, Z), n->right);
+			n->right = nn;
+			n->left->type = n->type;
+			if(!p && op0 != ONOOP)
+				addnode(OT0, n);
+			egen(n, op0, side);
+			break;
+		case OCAT:
+			egen(n->left, op, LEFT);
+			prdelim(" + ");
+			egen(n->right, op, RIGHT);
+			break;
+	}
+	preced(op0, op, side, ')');
+}
+
+static int
+isexpr(Node *n, Type *t)
+{
+	if(n == Z)
+		return 0;
+	if(n->op == OLIST || n->op == OINIT || n->op == OSTRUCT)
+		return 0;
+	if(teq(t, n->type))
+		return 1;
+	return 0;
+}
+
+static Node *
+nxtval(Node *n, Node **nn)
+{
+	if(n == Z){
+		*nn = Z;
+		return Z;
+	}
+	if(n->op == OLIST){
+		*nn = n->right;
+		return n->left;
+	}
+	*nn = Z;
+	return n;
+}
+
+static Node*
+eagen(Node *n, Type *t, int ar, int *nz, int depth)
+{
+	int i, w, nw, down;
+	Type *t1;
+	Node *nn, *tn;
+
+	if(n != Z){
+		if(n->type == T && t == T){
+			egen(n, ONOOP, PRE);
+			if(ar){
+				prdelim(",");
+				newline();
+			}
+			return Z;
+		}
+		if(ar && n->op == OLIST && n->left->op == OARRAY){
+			egen(n->left->left, ONOOP, PRE);
+			prdelim(" => ");
+			n = n->right;
+		}
+		if(n->op == OLIST && n->left->op == OELEM){
+			prcom("cannot do ", n->left);
+			n = n->right;
+		}
+		if(n->op == OUSED || n->op == ODOTDOT)
+			n = n->left;
+		if(t == T)
+			t = n->type;
+	}
+	switch(t->etype){
+		case TSTRUCT:
+		case TUNION:
+			if(isexpr(n, t))
+				goto Default;
+			down = 0;
+			tn = nxtval(n, &nn);
+			if(tn != Z && (tn->op == OINIT || tn->op == OSTRUCT)){
+				down = 1;
+				n = tn->left;
+			}
+			if(depth > 0){
+				tgen(t, 0, 0);
+				prdelim(" ");
+			}
+			prdelim("(");
+			for(t1 = t->link; t1 != T; t1 = t1->down){
+				if(n == Z)
+					n = defval(t1);
+				n = eagen(n, t1, 0, nil, depth+1);
+				if(t1->down != T){
+					prdelim(",");
+					if(ar)
+						prdelim("\t");
+					else
+						prdelim(" ");
+				}
+			}
+			prdelim(")");
+			if(down)
+				n = nn;
+			break;
+		case TARRAY:
+			if(isexpr(n, t))
+				goto Default;
+			if(depth > 0){
+				tgen(t, 0, 1);
+				prdelim(" ");
+			}
+			prdelim("{");
+			newline();
+			incind();
+			w = t->width/t->link->width;
+			nw = 0;
+			for(i = 0; i < w; i++){
+				down = 0;
+				tn = nxtval(n, &nn);
+				if(tn != Z && (tn->op == OINIT || tn->op == OSTRUCT)){
+					down = 1;
+					n = tn->left;
+				}
+				n = eagen(n, t->link, 1, &nw, depth+1);
+				if(down)
+					n = nn;
+			}
+			if(nw > 0){
+				if(nw > 1)
+					prdelim("* => ");
+				egen(defval(t->link), ONOOP, PRE);
+				newline();
+			}
+			decind();
+			prdelim("}");
+			break;
+		default:
+Default:
+			if(n == Z){
+				if(ar)
+					(*nz)++;
+				else
+					egen(defval(t), ONOOP, PRE);
+				return Z;
+			}
+			n = nxtval(n, &nn);
+			if(ar && isnil(n) && iscastable(t, types[TINT])){
+				tgen(t, 0, 0);
+				prdelim(" ");
+			}
+			egen(n, ONOOP, PRE);
+			n = nn;
+			break;
+	}
+	if(ar){
+		prdelim(",");
+		newline();
+	}
+	return n;
+}
+
+/* better is
+ *	array of byte "abcde\0"
+ * but limbo compiler does not accept this as a constant expression
+ */
+static void
+stob(Node *n)
+{
+	int m;
+	char *s = nil, buf[UTFmax];
+	Rune *u = nil;
+
+	while(n->op == ONAME)
+		n = n->sym->nconst;
+	if(n->op == OSTRING)
+		s = n->cstring;
+	else
+		u = n->rstring;
+	prdelim("{ ");
+	if(s){
+		while(*s){
+			prid("byte ");
+			prchar(*s++);
+			prdelim(", ");
+		}
+	}
+	else{
+		while(*u){
+			m = runetochar(buf, u++);
+			s = buf;
+			while(--m >= 0){
+				prid("byte ");
+				prchar(*s++);
+				prdelim(", ");
+			}
+		}
+	}
+	prid("byte ");
+	prchar('\0');
+	prdelim(" }");
+}
+
+static Type *arrayofchar;
+
+static void
+sdgen(Node *n, int glob)
+{
+	int sop = 0;
+
+	prdelim(" := ");
+	if(glob && n->right->op == OS2AB && isstring(n->right->left)){
+		if(arrayofchar == T){
+			arrayofchar = typ1(TARRAY, types[TCHAR]);
+			arrayofchar->width = 0;
+		}
+		n->type = n->right->type = arrayofchar;
+		sop = 1;
+	}
+	else
+		n->type = n->right->type = T;
+	tgen(n->type, 0, 1);
+	if(sop)
+		stob(n->right->left);
+	else
+		eagen(n->right, n->type, 0, nil, 0);
+	prdelim(";");
+	newline();
+}
+
+static void
+tdgen(Node *n, int glob)
+{
+	int ar, arinit;
+
+	if(ism()){
+		prdelim(": ");
+		tgen(n->type, 1, 0);
+		if(n->op == ODAS)
+			prcom("initial value was ", n->right);
+		prdelim(";");
+		newline();
+		return;
+	}
+	if(n->op == ODAS && (isstring(n->right) || n->right->op == OS2AB)){
+		sdgen(n, glob);
+		return;
+	}
+	ar = n->type->etype == TARRAY && n->type->width != LARR;
+	arinit = ar && n->op == ODAS;
+	if(ar)
+		prdelim(" := ");
+	else
+		prdelim(": ");
+	tgen(n->type, 0, arinit);
+	if(n->op == ODAS){
+		if(!arinit)
+			prdelim(" = ");
+		eagen(n->right, n->type, 0, nil, 0);
+	}
+	prdelim(";");
+	newline();
+}
+
+static int
+isdec(Node *n)
+{
+	return isname(n) && n->kind != KEXP || n->op == ODAS;
+}
+
+static void
+sgen(Node *n, int blk, Node **ln)
+{
+	int comma = 0;
+	Node *nn;
+
+	if(n == Z)
+		return;
+	if(blk){
+		pushscope(n, SAUTO);
+		if(n->op == OLIST && !(blk&NOBR) || (blk&YESBR)){
+			prdelim("{");
+			newline();
+		}
+		else if(!(blk&NONL))
+			newline();
+		if(!(blk&NOIN))
+			incind();
+	}
+	if((nn = *ln) != Z && isdec(nn)){
+		if(isdec(n)){
+			if(canjoin(nn, n))
+				comma = 1;
+			else
+				tdgen(nn, 0);
+		}
+		else if(n->op != OLIST){
+			tdgen(nn, 0);
+			newline();
+		}
+	}
+	if(n->op != OLIST){
+		*ln = n;
+		output(n->lineno, 1);
+	}
+	switch(n->op){
+		default:
+			egen(n, ONOOP, PRE);
+			prdelim(";");
+			newline();
+			break;
+		case ODAS:
+			pushdcl(n->left, CAUTO);
+			egen(n->left, ONOOP, PRE);
+			break;
+		case ONAME:
+			if(n->kind == KEXP){
+				egen(n, ONOOP, PRE);
+				prdelim(";");
+				newline();
+			}
+			else{
+				pushdcl(n, CAUTO);
+				if(comma)
+					prdelim(", ");
+				if(n->op != ONAME)
+					diag(n, "internal: not name in sgen");
+				prsym(n->sym, 0);
+				/* egen(n, ONOOP, PRE); */
+/*
+				prdelim(": ");
+				tgen(n->type, 0, 0, 0);
+				prdelim(";");
+				newline();
+*/
+			}
+			break;
+		case OSBREAK:
+			break;
+		case ONUL:
+			prdelim(";");
+			newline();
+			break;
+		case OBLK:
+			sgen(n->left, 1|YESBR, ln);
+			break;
+		case OLIST:
+			sgen(n->left, 0, ln);
+			sgen(n->right, 0, ln);
+			break;
+		case ORETURN:
+			prkeywd("return");
+			if(n->left != Z)
+				prdelim(" ");
+			egen(n->left, ONOOP, PRE);
+			prdelim(";");
+			newline();
+			break;
+		case OLABEL:
+			prcom("was label ", n->left);
+			/* i = zeroind(); */
+			/* egen(n->left, ONOOP, PRE); */
+			/* prdelim(":"); */
+			newline();
+			/* restoreind(i); */
+			break;
+		case OGOTO:
+			prcom("was goto ", n->left);
+			/* prkeywd("goto "); */
+			/* egen(n->left, ONOOP, PRE); */
+			prdelim(";");
+			newline();
+			break;
+		case OCASE:
+			for(nn = n->left; nn != Z; nn = nn->right){
+				if(nn != n->left)
+					prkeywd(" or ");
+				if(nn->left != Z)
+					egen(nn->left, ONOOP, PRE);
+				else
+					prkeywd("*");
+			}
+			prdelim(" =>");
+			clrbrk(n->right);
+			sgen(n->right, 1|NOBR, ln);
+			if(n->kind != KLAST && !hasbrk(n->right)){
+				prcom("fall through", Z);
+				newline();
+			}
+			break;
+		case OSWITCH:
+			prkeywd("case");
+			egen(n->left, OBRACKET, PRE);
+			sgen(n->right, 1|NOIN|YESBR, ln);
+			break;
+		case OWHILE:
+			prkeywd("while");
+			egen(n->left, OBRACKET, PRE);
+			sgen(n->right, 1, ln);
+			break;
+		case ODWHILE:
+			prkeywd("do");
+			sgen(n->right, 1|NOENL, ln);
+			prkeywd("while");
+			egen(n->left, OBRACKET, PRE);
+			prdelim(";");
+			newline();
+			break;
+		case OFOR:
+			prkeywd("for");
+			prdelim("(");
+			egen(n->left->right->left, ONOOP, PRE);
+			prdelim(";");
+			if(n->left->left != Z)
+				prdelim(" ");
+			egen(n->left->left, ONOOP, PRE);
+			prdelim(";");
+			if(n->left->right->right != Z)
+				prdelim(" ");
+			egen(n->left->right->right, ONOOP, PRE);
+			prdelim(")");
+			sgen(n->right, 1, ln);
+			break;
+		case OCONTINUE:
+			prkeywd("continue");
+			prdelim(";");
+			newline();
+			break;
+		case OBREAK:
+			prkeywd("break");
+			prdelim(";");
+			newline();
+			break;
+		case OIF:
+			prkeywd("if");
+			egen(n->left, OBRACKET, PRE);
+			if(n->right->left->op == OIF && n->right->left->right->right == Z && n->right->right != Z)		/* avoid dangling else */
+				sgen(n->right->left, 1|YESBR, ln);
+			else
+				sgen(n->right->left, 1, ln);
+			if(n->right->right != Z){
+				prdelim("else");
+				if(n->right->right->op == OIF){	/* merge else and if */
+					prdelim(" ");
+					sgen(n->right->right, 1|NONL|NOIN, ln);
+				}
+				else
+					sgen(n->right->right, 1, ln);
+			}
+			break;
+		case OSET:
+		case OUSED:
+			prkeywd(ops[n->op].name);
+			lgen(n->left, 1, 1);
+			prdelim(";");
+			newline();
+			break;
+	}
+	if(blk){
+		if(!(blk&NOIN))
+			decind();
+		if(n->op == OLIST&& !(blk&NOBR) || (blk&YESBR)){
+			prdelim("}");
+			if(!(blk&NOENL))
+				newline();
+		}
+		popscope();
+	}
+}
+
+static void rew(Node*, int);
+
+static void
+rewc0(Node *n, Node *r)
+{
+	Node *nn;
+
+	if((nn = cfind(n)) != Z){
+		cgen0(nn, n);
+		if(r->op == ORETURN){
+			n->right->left = new1(ORETURN, n->right->left, Z);
+			n->right->right = new1(ORETURN, n->right->right, Z);
+			n->right->left->type = n->right->left->left->type;
+			n->right->right->type = n->right->right->left->type;
+			*r = *n;
+		}
+	}
+}
+
+static void
+rewc1(Node *n)
+{
+	Node *c, *nc;
+
+	if(n == Z || n->op != OCOND || side(n) || !simple(n))
+		return;
+	c = n->left;
+	nc = new1(ONOT, ncopy(c), Z);
+	n->op = OOROR;
+	n->left = new1(OANDAND, c, n->right->left);
+	n->right = new1(OANDAND, nc, n->right->right);
+}
+
+static void
+rewc(Node *n, Node *r)
+{
+	Node *nn, *rr, *i;
+
+	if((nn = cfind(n)) != Z){
+		i = cgen(nn, n);
+		rr = new1(OXXX, Z, Z);
+		if(n == r && nn == n)
+			*rr = *nn;
+		else
+			*rr = *r;
+		r->op = OLIST;
+		r->left = i;
+		r->right = rr;
+	}
+}
+
+static int
+rewe(Node *n, Type *t, int lev)
+{
+	int op, k, k1, k2;
+	int v;
+	Node *nn;
+
+	if(n == Z)
+		return -1;
+	switch(n->op){
+		case OCONST:
+			break;
+		case ONAME:
+			if(strings || !isstring(n))
+				break;
+		case OSTRING:
+		case OLSTRING:
+			if(!strings)
+				addnode(OS2AB, n);
+			break;
+		case OCOND:
+			bptr(n->left);
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			break;
+		case OIND:
+			if(isfn(n->type)){
+				*n = *n->left;
+				rewe(n, T, 1);
+				break;
+			}
+			if(!isadt(n->type)){
+				n->op = OARRIND;
+				n->right = con(0);
+				rewe(n, T, 1);
+				break;
+			}
+			rewe(n->left, T, 1);
+			break;
+		case OADDR:
+			if(n->left->op == OARRIND){
+				n->right = n->left;
+				n->left = n->right->left;
+				n->right->left = n->right->right;
+				n->right->right = Z;
+				n->right->op = OLIST;
+				n->op = OSLICE;
+				rewe(n, T, 1);
+				break;
+			}
+			rewe(n->left, T, 1);
+			break;
+		case OSLICE:
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			if(n->left->op == OSLICE){
+				n->right->left = addn(n->left->right->left, n->right->left);
+				n->right->right = addn(n->left->right->left, n->right->right);
+				n->left = n->left->left;
+				rewe(n, T, 1);
+				break;
+			}
+			break;
+		case OCOMMA:
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			if(n->left->op == OAS && n->right->op == OAS){
+				n->op = OAS;
+				n->left->op = n->right->op = OLIST;
+				nn = n->left->right;
+				n->left->right = n->right->left;
+				n->right->left = nn;
+				rewe(n, T, 1);
+				break;
+			}
+			break;
+		case OFUNC:
+			if(n->left->op == ONAME){
+				if((k = n->left->sym->kind) != LNONE){
+					rewlc(n, k, t);
+					rewe(n->left, T, 1);
+					rewe(n->right, T, 1);
+					args(n);
+					return k;
+				}
+			}
+			else
+				rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			args(n);
+			break;
+		case OCAST:
+			rewe(n->left, n->type, 1);
+			break;
+		case OAS:
+		case OASI:
+		case OASD:
+			rewe(n->left, T, 1);
+			rewe(n->right, n->type, 1);
+			break;
+		case ONOT:
+		case OANDAND:
+		case OOROR:
+			bptr(n);
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			break;
+		case OPREINC:
+		case OPOSTINC:
+		case OASADD:
+			if(n->op != OPOSTINC || lev == 0){
+				sliceasgn(n);
+				if(n->op == OAS){
+					rewe(n, T, 1);
+					break;
+				}
+			}
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			break;
+		case OEQ:
+		case ONE:
+		case OLT:
+		case OLE:
+		case OGT:
+		case OGE:
+			k1 = rewe(n->left, T, 1);
+			k2 = rewe(n->right, T, 1);
+			if(k1 == LSTRCMP && n->right->op == OCONST){
+				op = -1;
+				v = n->right->vconst;
+				switch(v){
+					case -1:
+						if(n->op == OEQ)
+							op = OLT;
+						else if(n->op == ONE)
+							op = OGE;
+						break;
+					case 0:
+						op = n->op;
+						break;
+					case 1:
+						if(n->op == OEQ)
+							op = OGT;
+						else if(n->op == ONE)
+							op = OLE;
+						break;
+				}
+				if(op != -1){
+					*n = *n->left;
+					n->op = op;
+				}
+			}
+			if(k2 == LSTRCMP && n->left->op == OCONST){
+				op = -1;
+				v = n->left->vconst;
+				switch(v){
+					case -1:
+						if(n->op == OEQ)
+							op = OLT;
+						else if(n->op == ONE)
+							op = OGE;
+						break;
+					case 0:
+						op = rev(n->op);
+						break;
+					case 1:
+						if(n->op == OEQ)
+							op = OGT;
+						else if(n->op == ONE)
+							op = OLE;
+						break;
+				}
+				if(op != -1){
+					*n = *n->right;
+					n->op = op;
+				}
+			}
+			break;
+		default:
+			rewe(n->left, T, 1);
+			rewe(n->right, T, 1);
+			break;
+	}
+	return -1;	
+}
+
+/*
+static void
+rewf(Node *n)
+{
+	if(n == Z)
+		return;
+	switch(n->op){
+		case OFUNC:
+			if(n->left->op == ONAME)
+				fdargs(n);
+			break;
+		default:
+			rewf(n->left);
+			rewf(n->right);
+			break;
+	}
+}
+*/
+
+static void
+rew(Node *n, int blk)
+{
+	int i;
+	Node *a, *nn;
+
+	if(n == Z)
+		return;
+	if(blk)
+		pushscope(n, SAUTO);
+	nearln = n->lineno;
+	if(n->blk){
+		n->blk = 0;
+		addnode(OBLK, n);
+	}
+	switch(n->op){
+		default:
+			if(simple(n))
+				rewc0(n, n);
+			else
+				rewc(n, n);
+			if(n->op == OLIST || n->op == OIF){
+				rew(n, 0);
+				break;
+			}
+			ecomplex(n);
+			break;
+		case ODAS:
+			pushdcl(n->left, CAUTO);
+			rewe(n->right, T, 1);
+			break;
+		case OSBREAK:
+		case ONUL:
+			break;
+		case ONAME:
+			if(n->kind == KEXP)
+				ecomplex(n);
+			else
+				pushdcl(n, CAUTO);
+			break;
+		case OBLK:
+			rew(n->left, 1);
+			break;
+		case OLIST:
+			rew(n->left, 0);
+			rew(n->right, 0);
+			break;
+		case ORETURN:
+			if(simple(n->left))
+				rewc0(n->left, n);
+			else	
+				rewc(n->left, n);
+			if(n->op != ORETURN){
+				rew(n, 0);
+				break;
+			}
+			ecomplex(n);
+			break;
+		case OLABEL:
+		case OGOTO:
+			break;
+		case OCASE:
+			for(nn = n->left; nn != Z; nn = nn->right)
+				if(nn->left != Z)
+					ecomplex(nn->left);
+			rew(n->right, 1);
+			break;
+		case OSWITCH:
+			rewc(n->left, n);
+			if(n->op == OLIST){
+				rew(n, 0);
+				break;
+			}
+			ecomplex(n->left);
+			if(!lteq(n->left->type, types[TINT]))
+				intcast(n->left);
+			n->right = buildcases(n->right);
+			rew(n->right, 1);
+			break;
+		case OWHILE:
+		case ODWHILE:
+			rewc1(n->left);
+			becomplex(n->left);
+			rew(n->right, 1);
+			break;
+		case OFOR:
+			rewc1(n->left->left);
+			rewc(n->left->right->left, n);
+			if(n->op == OLIST){
+				rew(n, 0);
+				break;
+			}
+			becomplex(n->left->left);
+			ecomplex(n->left->right->left);
+			ecomplex(n->left->right->right);
+			rew(n->right, 1);
+			break;
+		case OCONTINUE:
+			break;
+		case OBREAK:
+			break;
+		case OIF:
+			rewc1(n->left);
+			rewc(n->left, n);
+			if(n->op == OLIST){
+				rew(n, 0);
+				break;
+			}
+			becomplex(n->left);
+			rew(n->right->left, 1);
+			rew(n->right->right, 1);
+			break;
+		case OSET:
+			if(n->left == Z){
+				n->op = ONUL;
+				n->left = n->right = Z;
+				break;
+			}
+			if(n->left->op != OLIST){
+				n->op = OAS;
+				n->right = defval(n->left->type);
+				rew(n, 0);
+				break;
+			}
+			i = 0;
+			nn = Z;
+			for(;;){
+				a = arg(n->left, i);
+				if(a == Z)
+					break;
+				a = new1(OAS, a, defval(a->type));
+				if(i == 0)
+					nn = a;
+				else
+					nn = new1(OLIST, nn, a);
+				i++;
+			}
+			*n = *nn;
+			rew(n, 0);
+			break;
+		case OUSED:
+			if(n->left == Z){
+				n->op = ONUL;
+				n->left = n->right = Z;
+				break;
+			}
+			i = 0;
+			nn = Z;
+			for(;;){
+				a = arg(n->left, i);
+				if(a == Z)
+					break;
+				if(i == 0)
+					nn = a;
+				else
+					nn = new1(OOROR, nn, a);
+				i++;
+			}
+			n->op = OIF;
+			n->left = nn;
+			n->right = new1(OLIST, Z, Z);
+			n->right->left = new1(ONUL, Z, Z);
+			rew(n, 0);
+			break;
+	}
+	if(blk)
+		popscope();
+}
+
+void
+codgen2(Node *n, Node *nn, int lastlno, int rw)
+{
+	Node *ln = Z;
+
+	newsec(0);
+	output(nn->lineno, 1);
+	tmp = 0;
+	/* t = types[TVOID]; */
+	nn = func(nn);
+	pushscope(nn, SPARM);
+	if(rw)
+		rew(n, 1);
+	egen(nn, ONOOP, PRE);
+	newline();
+	prdelim("{");
+	newline();
+	incind();
+	/* rewf(n); */
+	pushscope(n, SAUTO);
+	sgen(n, 0, &ln);
+	if(ln != Z && isdec(ln))
+		tdgen(ln, 0);
+	popscope();
+	popscope();
+	if(n != Z)
+		output(lline(n), 1);
+	output(lastlno, 1);
+	decind();
+	prdelim("}");
+	newline();
+	newline();
+	setmain(nn);
+}
+
+void
+rewall(Node *n, Node *nn, int lastlno)
+{
+	USED(lastlno);
+	tmp = 0;
+	nn = func(nn);
+	pushscope(nn, SPARM);
+	rew(n, 1);
+	popscope();
+	setmain(nn);
+}
+
+void
+suball(Node *n, Node *nn)
+{
+	Node *rn;
+
+	nn = func(nn);
+	pushscope(nn, SPARM);
+	subs(nn, 0, 0);
+	subs(n, 1, 1);
+	nn = lastn(n);
+	if(nn != Z && nn->op != ORETURN){
+		rn = retval(Z);
+		if(rn != Z){
+			addnode(OLIST, nn);
+			nn->right = rn;
+		}
+	}
+	popscope();
+}
+
+void
+ginit(void)
+{
+	thechar = 'o';
+	thestring = "386";
+	tfield = types[TLONG];
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael2);
+		o = align(o, t, Ael1);
+		w = SZ_LONG;	/* because of a pun in cc/dcl.c:contig() */
+		break;
+	}
+	o = round(o, w);
+	if(0)
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_LONG);
+	if(v > max)
+		return v;
+	return max;
+}
+
+static int
+nlen(Node *n)
+{
+	if(n == Z)
+		return 0;
+	if(n->op == OLIST)
+		return nlen(n->left)+nlen(n->right);
+	return 1;
+}
+
+static void
+flatten(Node *n, Node **a, int *i)
+{
+	if(n == Z)
+		return;
+	if(n->op == OLIST){
+		flatten(n->left, a, i);
+		flatten(n->right, a, i);
+		free(n);
+		return;
+	}
+	a[(*i)++] = n;
+}
+
+static Node*
+addcase(Node *n, Node **e, Node **s, int k)
+{
+	Node *nn;
+
+	if(*e != Z){
+		nn = new1(OCASE, *e, *s);
+		nn->right->blk = 0;
+		nn->kind = k;
+	}
+	else
+		nn = *s;
+	*e = *s = Z;
+	if(n == Z)
+		return nn;
+	return new1(OLIST, n, nn);
+}
+
+/* collect case code together */		
+static Node*
+buildcases(Node *n)
+{
+	int i, m, m0, c;
+	Node *e, *s, *nn, **a, **ep;
+
+	m = nlen(n);
+	a = (Node **)malloc(m*sizeof(Node*));
+	m0 = 0;
+	flatten(n, a, &m0);
+	if(m != m0)
+		diag(Z, "internal: bad buildcases()");
+	c = 1;
+	e = s = nn = Z;
+	ep = &e;
+	for(i = 0; i < m; i++){
+		n = a[i];
+		if(n->op == OCASE){
+			if(!c){
+				nn = addcase(nn, &e, &s, KNIL);
+				ep = &e;
+			}
+			*ep = new1(OLIST, n->left, Z);
+			if(n->left == Z)
+				(*ep)->lineno = n->lineno;
+			ep = &(*ep)->right;
+			c = 1;
+		}
+		else{
+			if(s == Z)
+				s = n;
+			else
+				s = new1(OLIST, s, n);
+			c = 0;
+		}
+	}
+	nn = addcase(nn, &e, &s, KLAST);
+	free(a);
+	return nn;
+}
+
+static Sym *
+tmpgen(Type *t)
+{
+	Sym *s;
+
+	sprint(buf, "tmp_%d", ++tmp);
+	s = slookup(buf);
+	s->type = t;
+	s->class = CAUTO;
+	if(t->etype == TENUM)
+		s->type = types[TINT];
+	return s;
+}
+
+static Node*
+cfind(Node *n)
+{
+	Node *nn;
+
+	if(n == Z)
+		return Z;
+	if(n->op == OCOND)
+		return n;
+	nn = cfind(n->left);
+	if(nn != Z)
+		return nn;
+	return cfind(n->right);
+}
+
+Node*
+ncopy(Node *n)
+{
+	Node *nn;
+
+	if(n == Z)
+		return Z;
+	nn = new1(n->op, Z, Z);
+	*nn = *n;
+	nn->left = ncopy(n->left);
+	nn->right = ncopy(n->right);
+	return nn;
+}
+
+static int
+complexity(Node *n, int *cond)
+{
+	int c;
+
+	if(n == Z)
+		return 0;
+	c = complexity(n->left, cond)+1+complexity(n->right, cond);
+	if(n->op == OCOND)
+		(*cond)++;
+	return c;
+}
+
+static int
+simple(Node *n)
+{
+	int c;
+
+	c = 0;
+	return complexity(n, &c) < COMPLEX && c <= 1;
+}
+
+static Type*
+intype(Node *n)
+{
+	Type *t;
+
+	t = ntype(n);
+	if(t == T)
+		return T;
+	return t->link;
+}
+
+static Type*
+ntype(Node *n)
+{
+	Type *t;
+
+	if(n == Z)
+		return T;
+	t = n->type;
+	if(t != T){
+		if(t->etype == TENUM)
+			return n->sym->tenum;
+		return t;
+	}
+	switch(n->op){
+		case OEQ:
+		case ONE:
+		case OLT:
+		case OGE:
+		case OGT:
+		case OLE:
+		case ONOT:
+		case OANDAND:
+		case OOROR:
+		case OIOTA:
+			return types[TINT];
+		case OCOMMA:
+			return ntype(n->right);
+		case OCOND:
+			return maxtype(ntype(n->right->left), ntype(n->right->right));
+		case OFUNC:
+			return intype(n->left);
+		case ODOT:
+			tcomd(n, ntype(n->left));
+			t = n->type;
+			n->type = T;
+			return t;
+		case ODOTIND:
+			tcomd(n, intype(n->left));
+			t = n->type;
+			n->type = T;
+			return t;
+		case OARRIND:
+			return intype(n->left);
+		case OADDR:
+			return typ1(TIND, ntype(n->left));
+		case OIND:
+			return intype(n->left);
+		case OSTRUCT:
+			return T;
+	}
+	return maxtype(ntype(n->left), ntype(n->right));
+}
+
+static Type*
+gettype(Node *n1, Node *n2)
+{
+	Type *t;
+
+	t = maxtype(n1->type, n2->type);
+	if(t != T)
+		return t;
+	return maxtype(ntype(n1), ntype(n2));
+}
+
+static void
+cgen0(Node *n, Node *e)
+{
+	Node *c, *nn, *ed, *ee;
+
+	if(n == e){
+		n->op = OIF;
+		return;
+	}
+	c = n->left;
+	ed = new1(OXXX, Z, Z);
+	*ed = *e;
+	ee = ncopy(e);
+	nn = cfind(ee);
+	*n = *n->right->left;
+	*nn = *nn->right->right;
+	e->op = OIF;
+	e->left = c;
+	e->right = new1(OLIST, ed, ee);
+}
+
+static Node*
+cgen(Node *n, Node *e)
+{
+	Type *t;
+	Node *tn, *i;
+
+	USED(e);
+	tn = new1(ONAME, Z, Z);
+	t = gettype(n->right->left, n->right->right);
+	tn->sym = tmpgen(t);
+	tn->type = tn->sym->type;
+/*
+	if(n == e){
+		n->op = OIF;
+		n->right->left = new1(OASD, tn, n->right->left);
+		n->right->right = new1(OAS, tn, n->right->right);
+		return n;
+	}
+*/
+	i = new1(OIF, n->left, new1(OLIST, new1(OASD, tn, n->right->left), new1(OAS, tn, n->right->right)));
+	*n = *tn;
+	return i;
+}
+
+static struct{
+	char *name;
+	int	args;
+	int	fd;
+	char *lname;
+} sysops[] = {
+	"create",	1,	RET,	nil,
+	"dirstat",	1,	0,	"stat",
+	"dirfstat",	0,	1,	"fstat",
+	"dirwstat",	1,	0,	"wstat",
+	"dirfwstat",	0,	1,	"fwstat",
+	"dirread",	0,	1,	nil,
+	"dup",	0,	0,	nil,
+	"fprint",	2|STAR,	1,	nil,
+	"fprintf",	2|STAR,	1,	"fprint",
+	"open",	1,	RET,	nil,
+	"print",	1|STAR,	0,	nil,
+	"printf",	1|STAR,	0,	"print",
+	"read",	0,	1,	nil,
+	"remove",	1,	0,	nil,
+	"seek",	0,	1,	nil,
+	"sleep",	0,	0,	nil,
+	"sprint",	1|STAR,	0,	nil,
+	"sprintf",	1|STAR,	0,	"sprint",
+	"write",	0,	1,	nil,
+	0
+};
+
+/* dummy entry for module */
+#define BIOTMP	"__bio__"
+
+static struct{
+	char	*name;
+	char	*lname;
+} bioops[] = {
+	"Bflush",	"flush",
+	"Bgetc",	"getc",
+	"Bprint",	"puts",
+	"Bputc",	"putc",
+	"Bread",	"read",
+	"Bseek",	"seek",
+	"Bungetc",	"ungetc",
+	"Bwrite",	"write",
+	BIOTMP,	nil,
+	0
+};
+
+char *libcops[] = {
+	"isalnum",
+	"isalpha",
+	"isascii",
+	"iscntrl",
+	"isdigit",
+	"isgraph",
+	"islower",
+	"isprint",
+	"ispunct",
+	"isspace",
+	"isupper",
+	"isxdigit",
+	"strchr",
+	"strrchr",
+	"toascii",
+	"tolower",
+	"toupper",
+	"abs",
+	"min",
+	"max",
+	0,
+};
+
+static struct{
+	char *name;
+	int	type;
+	int	string;
+} xops[] = {
+	"strlen",	LSTRLEN,	1,
+	"strcmp",	LSTRCMP,	1,
+	"strcpy",	LSTRCPY,	1,
+	"strcat",	LSTRCAT,	1,
+	"strncmp",	LSTRNCMP,	1,
+	"strncpy",	LSTRNCPY,	1,
+	"strncat",	LSTRNCAT,	1,
+	"strdup",	LSTRDUP,	1,
+	"memcpy",	LMEMMOVE,	0,
+	"memmove",	LMEMMOVE,	0,
+	"malloc",	LMALLOC,	0,
+	"free",	LFREE,	0,
+	"exit",	LEXIT,	0,
+	"exits",	LEXIT,	0,
+	"close",	LCLOSE,	0,
+	"atoi",	LATOI,	0,
+	"atol",	LATOI,	0,
+	"atoll",	LATOL,	0,
+	"atof",	LATOF,	0,
+	"atod",	LATOF,	0,
+	"print",	LPRINT,	0,
+	"printf",	LPRINT,	0,
+	"fprint",	LFPRINT,	0,
+	"fprintf",	LFPRINT,	0,
+	"sprint",	LSPRINT,	0,
+	"sprintf",	LSPRINT,	0,
+	0
+};
+
+char *mathsops[] = {
+	"sin",
+	"cos",
+	"tan",
+	"sinh",
+	"cosh",
+	"tanh",
+	"asin",
+	"acos",
+	"atan",
+	"asinh",
+	"acosh",
+	"atanh",
+	"atan2",
+	"sqrt",
+	"cbrt",
+	"pow",
+	"pow10",
+	"exp",
+	"log",
+	"log10",
+	0
+};
+
+Node *glob, *globe;
+
+void
+sysinit(void)
+{
+	int i;
+	Sym *s;
+
+	glob = globe = new1(ONOOP, Z, Z);
+	for(i = 0; sysops[i].name; i++){
+		s = slookup(sysops[i].name);
+		s->class = CEXTERN;
+		s->args = sysops[i].args;
+		s->fd = sysops[i].fd;
+		s->mod = "sys";
+		s->lname = sysops[i].lname;
+		s->limbo = 1;
+		sysop = s;
+	}
+	for(i = 0; bioops[i].name; i++){
+		s = slookup(bioops[i].name);
+		s->class = CEXTERN;
+		if(strcmp(bioops[i].name, BIOTMP) == 0){
+			s->mod = "bufio";
+			bioop = s;
+		}
+		s->lname = bioops[i].lname;
+		s->kind = LSELF;
+		s->limbo = 1;
+	}
+	for(i = 0; mathsops[i]; i++){
+		s = slookup(mathsops[i]);
+		s->class = CEXTERN;
+		s->mod = "math";
+		s->limbo = 1;
+	}
+	for(i = 0; libcops[i]; i++){
+		s = slookup(libcops[i]);
+		s->class = CEXTERN;
+		s->mod = strings ? "libc" : "libc0";
+		s->limbo = 1;
+		libcop = s;
+	}
+	for(i = 0; xops[i].name; i++){
+		s = slookup(xops[i].name);
+		s->class = CEXTERN;
+		if(strings || !xops[i].string)
+			s->kind = xops[i].type;
+		else
+			s->mod = "libc0";
+		if(s->kind == LEXIT)
+			s->lname = "exit";
+		s->limbo = 1;
+	}
+	usemod(sysop, 1);
+	if(!strings)
+		usemod(libcop, 1);
+}
+
+void
+clbegin(void)
+{
+	pushscope(glob, SGLOB);
+}
+
+void
+clend(void)
+{
+	if(passes)
+		swalk();
+	popscope();
+}
+
+static Modl *mods;
+
+void
+usemod(Sym *s, int ld)
+{
+	Modl *ml;
+
+	for(ml = mods; ml != nil; ml = ml->nxt)
+		if(strcmp(ml->mod, s->mod) == 0){
+			ml->ld |= ld;
+			return;
+		}
+	ml = (Modl *)malloc(sizeof(Modl));
+	ml->mod = s->mod;
+	ml->ld = ld;
+	ml->nxt = mods;
+	mods = ml;
+}
+
+static void
+ginc(Modl *ml)
+{
+	int c;
+	char *s;
+
+	if(ml == nil)
+		return;
+	if(ml->nxt != nil)
+		ginc(ml->nxt);
+	s = ml->mod;
+	c = toupper(s[0]);
+	sprint(buf, "include \"%s.m\";", s);
+	prline(buf);
+	if(ml->ld){
+		sprint(buf, "	%s: %c%s;", s, c, s+1);
+		prline(buf);
+	}
+}
+
+static void
+gload(Modl *ml)
+{
+	int c;
+	char *s;
+
+	if(ml == nil)
+		return;
+	if(ml->nxt != nil)
+		gload(ml->nxt);
+	if(ml->ld){
+		s = ml->mod;
+		c = toupper(s[0]);
+		sprint(buf, "	%s = load %c%s %c%s->PATH;", s, c, s+1, c, s+1);
+		prline(buf);
+	}
+}
+
+static void
+callmain(void)
+{
+	if(inmain){
+		if(strings)
+			prline("	main(len argl, argl);");
+		else
+			prline("	main(len argl, libc0->ls2aab(argl));");
+	}
+}
+
+static void
+genstart(void)
+{
+	char *s;
+
+	if(!strings && inmain)
+		usemod(libcop, 1);
+	ginc(mods);
+	s = hasm();
+	if(s){
+		sprint(buf, "include \"%s\";", s);
+		prline(buf);
+	}
+	prline("");
+	prline("init(nil: ref Draw->Context, argl: list of string)");
+	prline("{");
+	gload(mods);
+	callmain();
+	prline("}");
+	prline("");
+}
+
+static int
+argpos0(Node *nn, Node *n, int *p)
+{
+	int pp;
+
+	if(n == Z)
+		return -1;
+	if(n->op == OLIST){
+		pp = argpos0(nn, n->left, p);
+		if(pp >= 0)
+			return pp;
+		return argpos0(nn, n->right, p);
+	}
+	if(n == nn)
+		return *p;
+	(*p)++;
+	return -1;
+}
+
+static int
+argpos(Node *nn, Node *n)
+{
+	int p = 0;
+
+	p = argpos0(nn, n, &p);
+	if(p < 0)
+		diag(Z, "-ve argpos");
+	return p;
+}
+
+static Node*
+arg0(Node *n, int a, int *i)
+{
+	Node *nn;
+
+	if(n == Z)
+		return Z;
+	if(n->op == OLIST){
+		nn = arg0(n->left, a, i);
+		if(nn != Z)
+			return nn;
+		return arg0(n->right, a, i);
+	}
+	if(a == (*i)++)
+		return n;
+	return Z;
+}
+
+static Node*
+arg(Node *n, int a)
+{
+	int i = 0;
+
+	return arg0(n, a, &i);
+}
+
+static Node*
+list(Node *l, Node *r)
+{
+	if(r == Z)
+		return l;
+	if(l == Z)
+		return r;
+	return new1(OLIST, l, r);
+}
+
+static Node*
+droparg(Node *n, int a, int *i)
+{
+	if(n == Z)
+		return Z;
+	if(n->op == OLIST)
+		return list(droparg(n->left, a, i), droparg(n->right, a, i));
+	if(a == (*i)++)
+		return Z;
+	return n;
+}
+
+static void
+sargs(Node *n)
+{
+	int s, f, i, j;
+	Node *a;
+
+	if(strings || (f = n->left->sym->args) == 0)
+		return;
+	s = 0;
+	for(i = 1, j = 0; i < STAR || s; i *= 2, j++){
+		if(f&i || s){
+			a = arg(n->right, j);
+			if(a == Z)
+				break;
+			if(s && !isstr(a->type))
+				continue;
+			if(f&STAR)
+				s++;
+			if(a->op == OS2AB){
+				*a = *a->left;
+				continue;
+			}
+			addnode(OAB2S, a);
+		}
+	}
+}
+
+static void
+fdargs(Node *n)
+{
+	int f, i, j;
+	Node *a;
+
+	if((f = n->left->sym->fd) == 0)
+		return;
+	marktype(pfdtype, TCFD);
+	if(f&RET)
+		tcon(n, pfdtype);
+	for(i = 1, j = 0; i < RET; i *= 2, j++){
+		if(f&i){
+			a = arg(n->right, j);
+			if(a == Z)
+				break;
+			tcon(a, pfdtype);
+		}
+	}
+}
+
+static void
+aargs(Node *n)
+{
+	int i;
+	Node *a, *nn, *fn;
+	Type *t, *t0, *ft, *at, *st;
+
+	if(!doaddr)
+		return;
+	if(n->op != OFUNC || n->left->op != ONAME)
+		return;
+	/* ft = n->left->type; */
+	ft = n->left->sym->type;
+	t = t0 = ft->link;
+	nn = Z;
+	for(i = 0; ; i++){
+		a = arg(n->right, i);
+		if(a == Z)
+			break;
+		at = typn(ft, i);
+		if(at != T && at->etype != TDOT && (a->op == OADDR || iteq(a->type, at) || iteq(at, a->type))){
+			if(iteq(at, a->type))
+				st = at->link;
+			else
+				st = a->type->link;
+			if(doalladdr || isscalar(st)){
+				if(a->op == OADDR)
+					*a = *a->left;
+				else if(iteq(a->type, at))
+					a->type = at;
+				if(t->mark == 0){
+					t = tuple(t, a->type);
+					trep(at, at->link);
+					fn = finddec(n->left->sym, 1);
+					if(fn != Z && fn->op == OFUNC)
+						tind(arg(fn->right, i));
+				}
+				if(nn == Z)
+					nn = cknil(ncopy(a));
+				else{
+					nn = new1(OTUPLE, nn, cknil(ncopy(a)));
+					nn->type = t;
+				}
+			}
+		}
+	}
+	if(nn != Z){
+		if(isvoid(t0) || t->mark == TCPC)
+			marktype(t, TCPC);
+		else
+			marktype(t, TCFC);
+		tcon(n, t);
+		addnode(ORETV, n);
+		n->right = nn;
+	}
+}
+
+static void
+args(Node *n)
+{
+	if(n->op != OFUNC || n->left->op != ONAME)
+		return;
+	sargs(n);
+	if(passes){
+		fdargs(n);
+		aargs(n);
+	}
+}
+
+static Node*
+indir(Node *n)
+{
+	if(n->op == OADDR)
+		return n->left;
+	return new1(OIND, n, Z);
+}
+
+static void
+rewlc(Node *n, int k, Type *t)
+{
+	int i;
+	Type *tt;
+	Node *a0, *a1, *a2, *nn;
+
+	if(t == T)
+		t = n->type;
+	a0 = arg(n->right, 0);
+	a1 = arg(n->right, 1);
+	switch(k){
+		case LSTRLEN:
+			n->op = OLEN;
+			break;
+		case LSTRCMP:
+			n->op = ONE;
+			n->left = a0;
+			n->right = a1;
+			break;
+		case LSTRCPY:
+			n->op = OAS;
+			n->left = a0;
+			n->right = a1;
+			n->type = n->left->type;
+			break;
+		case LSTRCAT:
+			n->op = OASADD;
+			n->left = a0;
+			n->right = a1;
+			n->type = n->left->type;
+			break;
+		case LSTRDUP:
+			*n = *a0;
+			break;
+		case LMEMMOVE:
+			if(!teq(a0->type, a1->type))
+				break;
+			if(a0->type->etype == TIND){
+				tt = a0->type->link;
+				a2 = arg(n->right, 2);
+				if(isadt(tt) && isconst(a2, tt->width)){
+					n->op = OAS;
+					n->left = indir(a0);
+					n->right = indir(a1);
+					n->type = n->left->type = n->right->type = tt;
+					break;
+				}
+				if(mydiv(a2, tt->width) != Z){
+					n->op = OAS;
+					n->left = new1(OSLICE, a0, new1(OLIST, con(0), Z));
+					n->right = new1(OSLICE, a1, new1(OLIST, con(0), a2));
+					n->type = n->left->type = n->right->type = a0->type;
+				}
+			}
+			break;
+		case LMALLOC:
+			if(t->etype == TIND){
+				tt = t->link;
+				if(isadt(tt) && isconst(a0, tt->width)){
+					n->op = OREF;
+					n->left = Z;
+					n->right = Z;
+					n->type = t;
+					break;
+				}
+				if(mydiv(a0, tt->width) != Z){
+					n->op = OARRAYOF;
+					n->left = a0;
+					n->right = Z;
+					n->type = t;
+					if(isadt(tt)){
+						n->type = typ1(TARRAY, tt);
+						n->type->width = LARR;	/* limbo array without bounds */
+						marktype(n->type, TCAR);
+					}
+				}
+			}
+			break;
+		case LFREE:
+			n->op = OAS;
+			n->left = a0;
+			n->right = con(0);
+			n->type = n->left->type;
+			n->right->type = n->type;
+			break;
+		case LEXIT:
+			i = n->kind;
+			*n = *n->left;
+			n->kind = i;
+			break;
+		case LCLOSE:
+			n->op = OAS;
+			n->left = a0;
+			n->right = con(0);
+			n->left->type = typ1(TIND, n->left->type);
+			n->type = n->left->type;
+			n->right->type = n->type;
+			break;
+		case LATOI:
+			if(!strings)
+				strcast(a0);
+			n->op = OCAST;
+			n->left = a0;
+			n->right = Z;
+			n->type = types[TINT];
+			break;
+		case LATOL:
+			if(!strings)
+				strcast(a0);
+			n->op = OCAST;
+			n->left = a0;
+			n->right = Z;
+			n->type = types[TVLONG];
+			break;
+		case LATOF:
+			if(!strings)
+				strcast(a0);
+			n->op = OCAST;
+			n->left = a0;
+			n->right = Z;
+			n->type = types[TDOUBLE];
+			break;
+		case LPRINT:
+			if(a0->op == OSTRING)
+				pfmt(a0->cstring);
+			else if(a0->op == OLSTRING)
+				lpfmt(a0->rstring);
+			break;
+		case LFPRINT:
+			if(a1->op == OSTRING)
+				pfmt(a1->cstring);
+			else if(a1->op == OLSTRING)
+				lpfmt(a1->rstring);
+			break;
+		case LSPRINT:
+			if(n->right->kind != KDROP){
+				if(a1->op == OSTRING)
+					pfmt(a1->cstring);
+				else if(a1->op == OLSTRING)
+					lpfmt(a1->rstring);
+				nn = new1(OXXX, Z, Z);
+				*nn = *n;
+				i = 0;
+				nn->right = droparg(nn->right, 0, &i);
+				nn->right->kind = KDROP;
+				n->op = OAS;
+				n->left = a0;
+				n->right = nn;
+				n->type = nn->type;
+			}
+			break;
+		case LSELF:
+			if(n->right != Z && n->right->kind != KDROP){
+				i = 0;
+				n->right = droparg(n->right, 0, &i);
+				if(n->right != Z)
+					n->right->kind = KDROP;
+				addnode(OLDOT, n->left);
+				n->left->right = n->left->left;
+				n->left->left = a0;
+				usemod(bioop, 1);
+			}
+			break;
+	}
+}
+
+void
+expgen(Node *n)
+{
+	egen(n, ONOOP, PRE);
+}
+
+static void
+clrbrk(Node *n)
+{
+	if(n == Z)
+		return;
+	switch(n->op){
+		case OLIST:
+			clrbrk(n->right);
+			break;
+		case OBREAK:
+			n->op = OSBREAK;
+			n->left = n->right = Z;
+			break;
+	}
+}
+
+static int
+hasbrk(Node *n)
+{
+	if(n == Z)
+		return 0;
+	switch(n->op){
+		case OLIST:
+		case OWHILE:
+		case ODWHILE:
+		case OFOR:
+			return hasbrk(n->right);
+		case OIF:
+			if(n->right->right == Z)
+				return 0;
+			return hasbrk(n->right->left) && hasbrk(n->right->right);
+		case ORETURN:
+		case OGOTO:
+		case OCONTINUE:
+		case OBREAK:
+		case OSBREAK:
+			return 1;
+		default:
+			return 0;
+	}
+}
+
+static int
+isgen(char *s)
+{
+	char *s1, *s2;
+
+	s1 = strchr(s, '_');
+	s2 = strrchr(s, '_');
+	if(s1 == nil || s2-s1 != 4)
+		return 0;
+	return s1[1] == 'a' && s1[2] == 'd' && s1[3] == 't';
+}
+
+static void
+addmodn(Sym *s)
+{
+	char buf[128], *ns;
+
+	if(s->name[0] == '_'){
+		outmod(buf, -1);
+		ns = malloc(strlen(buf)+strlen(s->name)+1);
+		strcpy(ns, buf);
+		strcat(ns, s->name);
+		s->name = ns;
+	}
+}
+		
+static void
+pfmt(char *s)
+{
+	char *t = s;
+
+	while(*s != '\0'){
+		if(*s == '%'){
+			*t++ = *s++;
+			if(*s == 'l'){
+				s++;
+				if(*s == 'l')
+					*t++ = 'b';
+				else
+					*t++ = *s;
+				s++;
+			}
+			else if(*s == 'p'){
+				*t++ = 'x';
+				s++;
+			}
+			else
+				*t++ = *s++;
+		}
+		else
+			*t++ = *s++;
+	}
+	*t = '\0';
+}
+
+static void
+lpfmt(Rune *s)
+{
+	 Rune*t = s;
+
+	while(*s != '\0'){
+		if(*s == '%'){
+			*t++ = *s++;
+			if(*s == 'l'){
+				s++;
+				if(*s == 'l')
+					*t++ = 'b';
+				else
+					*t++ = *s;
+				s++;
+			}
+			else if(*s == 'p'){
+				*t++ = 'x';
+				s++;
+			}
+			else
+				*t++ = *s++;
+		}
+		else
+			*t++ = *s++;
+	}
+	*t = '\0';
+}
+
+int
+line(Node *n)
+{
+	if(n == Z)
+		return 0;
+	if(n->op == OLIST)
+		return line(n->left);
+	return n->lineno;
+}
+
+static int
+lline(Node *n)
+{
+	if(n == Z)
+		return 0;
+	if(n->op == OLIST)
+		return lline(n->right);
+	return n->lineno+1;
+}
+
+static Node*
+lastn(Node *n)
+{
+	while(n != Z && n->op == OLIST)
+		n = n->right;
+	return n;
+}
+
+static Node*
+newnode(int op, Node *l)
+{
+	Node *n;
+
+	n = new1(op, l, Z);
+	globe->right = n;
+	globe = n;
+	return n;
+}
+
+void
+codgen1(Node *n, Node *nn, int lastlno)
+{
+	Node *nnn;
+
+	scomplex(n);
+	nnn = newnode(OCODE, new1(OLIST, n, nn));
+	nnn->lineno = lastlno;
+	mset(n);
+	mset(nn);
+	nn = func(nn);
+	newnode(ODECF, nn);
+	setmain(nn);
+}
+
+void
+vtgen1(Node *n)
+{
+	int c;
+	Node *nn = n;
+
+	if(n->op == ODAS)
+		nn = n->left;
+	if(nn->type == T || nn->sym == S)
+		return;
+	c = nn->sym->class;
+	if(c == CGLOBL || c == CSTATIC || c == CLOCAL || c == CEXREG){
+		newnode(ODECV, n);
+		if(nn->type->etype != TFUNC || ism())
+			setmod(nn->sym);
+	}
+	mset(n);
+}
+
+void
+etgen1(Sym *s)
+{
+	Node *n;
+
+	n = newnode(ODECE, Z);
+	n->sym = s;
+	if(s != S)
+		setmod(s);
+}
+
+void
+ttgen1(Type *t)
+{
+	Node *n;
+
+	n = newnode(ODECT, Z);
+	n->type = t;
+	if(isadt(t))
+		setmod(suename(t));
+}
+
+void
+outpush1(char *s)
+{
+	Node *n;
+	char *t;
+
+	n = newnode(OPUSH, Z);
+	if(s == nil)
+		t = nil;
+	else{
+		t = malloc(strlen(s)+1);
+		strcpy(t, s);
+	}
+	n->cstring = t;
+	outpush0(s, n);
+}
+
+void
+outpop1(int lno)
+{
+	Node *n;
+
+	n = newnode(OPOP, Z);
+	n->lineno = lno;
+	outpop0(lno);
+}
+
+void
+codgen(Node *n, Node *nn, int lastlno)
+{
+	if(passes)
+		codgen1(n, nn, lastlno);
+	else
+		codgen2(n, nn, lastlno, 1);
+}
+
+void
+vtgen(Node *n)
+{
+	if(passes)
+		vtgen1(n);
+	else
+		vtgen2(n);
+}
+
+void
+etgen(Sym *s)
+{
+	if(passes)
+		etgen1(s);
+	else
+		etgen2(s);
+}
+
+void
+ttgen(Type *t)
+{
+	if(passes)
+		ttgen1(t);
+	else
+		ttgen2(t);
+}
+
+void
+outpush(char *s)
+{
+	if(passes)
+		outpush1(s);
+	else
+		outpush2(s, Z);
+}
+
+void
+outpop(int lno)
+{
+	if(passes)
+		outpop1(lno);
+	else
+		outpop2(lno);
+}
+
+static void
+swalk(void)
+{
+	Node *n, *l;
+
+	for(n = glob; n != Z; n = n->right){
+		l = n->left;
+		switch(n->op){
+			case OCODE:
+				rewall(l->left, l->right, n->lineno);
+				break;
+			default:
+				break;
+		}
+	}
+	while(again){
+		again = 0;
+		for(n = glob; n != Z; n = n->right){
+			l = n->left;
+			switch(n->op){
+				case OCODE:
+					suball(l->left, l->right);
+					break;
+				case ODECV:
+					subs(l, 0, 0);
+					break;
+				case ODECE:
+				case ODECT:
+				case ODECF:
+					break;
+				default:
+					break;
+			}
+		}
+	}
+	for(n = glob; n != Z; n = n->right){
+		l = n->left;
+		switch(n->op){
+			case ONOOP:
+				break;
+			case OPUSH:
+				outpush2(n->cstring, n);
+				break;
+			case OPOP:
+				outpop2(n->lineno);
+				break;
+			case OCODE:
+				codgen2(l->left, l->right, n->lineno, 0);
+				break;
+			case ODECV:
+				vtgen2(l);
+				break;
+			case ODECE:
+				etgen2(n->sym);
+				break;
+			case ODECT:
+				ttgen2(n->type);
+				break;
+			case ODECF:
+				break;
+		}
+	}
+}
+
+static void
+scomplex(Node *n)
+{
+	if(n == Z)
+		return;
+	switch(n->op){
+		default:
+			complex(n);
+			break;
+		case ODAS:
+		case OSBREAK:
+		case ONUL:
+		case OLABEL:
+		case OGOTO:
+		case OCONTINUE:
+		case OBREAK:
+			break;
+		case ONAME:
+			if(n->kind == KEXP)
+				complex(n);
+			break;
+		case OBLK:
+		case OSET:
+		case OUSED:
+			scomplex(n->left);
+			break;
+		case OLIST:
+			scomplex(n->left);
+			scomplex(n->right);
+			break;
+		case ORETURN:
+			complex(n);
+			break;
+		case OCASE:
+			complex(n->left);
+			break;
+		case OSWITCH:
+		case OWHILE:
+		case ODWHILE:
+			complex(n->left);
+			scomplex(n->right);
+			break;
+		case OFOR:
+			complex(n->left->left);
+			complex(n->left->right->left);
+			complex(n->left->right->right);
+			scomplex(n->right);
+			break;
+		case OIF:
+			complex(n->left);
+			scomplex(n->right->left);
+			scomplex(n->right->right);
+			break;
+	}
+}
+
+static void
+mtset(Type *t)
+{
+	if(t == T)
+		return;
+	switch(t->etype){
+		case TIND:
+		case TARRAY:
+			mtset(t->link);
+			break;
+		case TSTRUCT:
+		case TUNION:
+			prsym0(suename(t));
+			/*
+			for(l = t->link; l != T; l = l->down)
+				mtset(l);
+			*/
+			break;
+	}
+}
+
+static void
+mset(Node *n)
+{
+	if(n == Z)
+		return;
+	n->garb = 0;
+	if(n->op == ONAME)
+		prsym0(n->sym);
+	mtset(n->type);
+	mset(n->left);
+	mset(n->right);
+}
+
+static int
+sign(Node *n)
+{
+	int s;
+
+	if(n == Z)
+		return 1;
+	switch(n->op){
+		case OCONST:
+			sign(n->left);
+			if(n->vconst < 0){
+				n->vconst = -n->vconst;
+				return -1;
+			}
+			break;
+		case OPOS:
+			s = sign(n->left);
+			*n = *n->left;
+			return s;
+		case ONEG:
+			s = sign(n->left);
+			*n = *n->left;
+			return -s;
+		case OADD:
+			if(sign(n->right) < 0)
+				n->op = OSUB;
+			break;
+		case OSUB:
+			if(sign(n->right) < 0)
+				n->op = OADD;
+			break;
+		case OMUL:
+		case ODIV:
+			return sign(n->left)*sign(n->right);
+		default:
+			break;
+	}
+	return 1;
+}
+
+static Node*
+ckneg(Node *n)
+{
+	if(sign(n) < 0)
+		return new1(ONEG, n, Z);
+	return n;
+}
+
+static void
+sliceasgn(Node *n)
+{
+	Type *t;
+	Node *nn;
+
+	if(side(n->left) || (n->right != Z && side(n->right)))
+		return;
+	t = n->type;
+	if(isarray(t) && (!strings || t->link->etype != TCHAR)){
+		if(n->op == OASADD)
+			nn = n->right;
+		else
+			nn = con(1);
+		n->op = OAS;
+		n->right = new1(OSLICE, ncopy(n->left), new1(OLIST, nn, Z));
+	}
+}
--- /dev/null
+++ b/utils/c2l/cc.h
@@ -1,0 +1,852 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+
+#ifndef	EXTERN
+#define EXTERN	extern
+#endif
+
+typedef	struct	Node	Node;
+typedef	struct	Sym	Sym;
+typedef	struct	Type	Type;
+typedef	struct	Decl	Decl;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+typedef	struct	Term	Term;
+typedef	struct	Init	Init;
+typedef	struct	Bits	Bits;
+
+typedef	Rune	TRune;
+
+#define	NHUNK		50000L
+#define	BUFSIZ		8192
+#define	NSYMB		500
+#define	NHASH		1024
+#define	STRINGSZ	200
+#define	HISTSZ		20
+#define YYMAXDEPTH	500
+#define	NTERM		10
+#define	MAXALIGN	7
+
+#define	SIGN(n)		((uvlong)1<<(n-1))
+#define	MASK(n)		(SIGN(n)|(SIGN(n)-1))
+
+#define	BITS	5
+#define	NVAR	(BITS*sizeof(ulong)*8)
+struct	Bits
+{
+	ulong	b[BITS];
+};
+
+EXTERN	int	lastnumbase;
+
+#define KNIL	0
+#define KCHR	1
+#define KOCT	2
+#define KDEC	3
+#define KHEX	4
+#define KEXP	5
+#define KDROP	6
+#define KLAST	7
+
+struct	Node
+{
+	Node*	left;
+	Node*	right;
+	double	fconst;		/* fp constant */
+	vlong	vconst;		/* non fp const */
+	char*	cstring;	/* character string */
+	Rune*	rstring;	/* rune string */
+
+	Sym*	sym;
+	Type*	type;
+	long	lineno;
+	char	op;
+	char	garb;
+	char kind;
+	char blk;
+};
+#define	Z	((Node*)0)
+#define	ZZ	((Node*)-1)
+
+struct	Sym
+{
+	Sym*	link;
+	Type*	type;
+	Type*	suetag;
+	Type*	tenum;
+	char*	macro;
+	long	lineno;
+	long	offset;
+	vlong	vconst;
+	double	fconst;
+	Node*	nconst;
+	char*	cstring;
+	Node*	label;
+	char	*name;
+	char *lname;
+	char *mod;
+	ushort	lexical;
+	ushort	block;
+	ushort	sueblock;
+	char	class;
+	char kind;
+	char lkw;
+	char	args;
+	char	fd;
+	char limbo;
+};
+#define	S	((Sym*)0)
+
+struct	Decl
+{
+	Decl*	link;
+	Sym*	sym;
+	Type*	type;
+	long	offset;
+	long	lineno;
+	short	val;
+	ushort	block;
+	char	class;
+};
+#define	D	((Decl*)0)
+
+struct	Type
+{
+	Sym*	sym;
+	Sym*	tag;
+	Type*	link;
+	Type*	down;
+	Node*	nwidth;
+	long	width;
+	long	offset;
+	long	lineno;
+	char	shift;
+	char	nbits;
+	char	etype;
+	char	garb;
+	char vis;
+	char	mark;
+};
+#define	T	((Type*)0)
+#define	NODECL	((void(*)(int, Type*, Sym*))0)
+
+struct	Init			/* general purpose initialization */
+{
+	int	code;
+	ulong	value;
+	char*	s;
+};
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char*	p;
+	char	b[BUFSIZ];
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+EXTERN Hist*	hist;
+
+struct	Term
+{
+	vlong	mult;
+	Node	*node;
+};
+
+enum
+{
+	Axxx,
+	Ael1,
+	Ael2,
+	Asu2,
+	Aarg0,
+	Aarg1,
+	Aarg2,
+	Aaut3,
+	NALIGN,
+};
+
+enum				/* also in ../{8a,0a}.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+
+enum
+{
+	DMARK,
+	DAUTO,
+	DSUE,
+	DLABEL,
+};
+enum
+{
+	ONOOP,
+	OXXX,
+	OADD,
+	OADDR,
+	OAND,
+	OANDAND,
+	OARRAY,
+	OAS,
+	OASI,
+	OASADD,
+	OASAND,
+	OASASHL,
+	OASASHR,
+	OASDIV,
+	OASHL,
+	OASHR,
+	OASLDIV,
+	OASLMOD,
+	OASLMUL,
+	OASLSHR,
+	OASMOD,
+	OASMUL,
+	OASOR,
+	OASSUB,
+	OASXOR,
+	OBIT,
+	OBREAK,
+	OCASE,
+	OCAST,
+	OCOMMA,
+	OCOND,
+	OCONST,
+	OCONTINUE,
+	ODIV,
+	ODOT,
+	ODOTDOT,
+	ODWHILE,
+	OENUM,
+	OEQ,
+	OFOR,
+	OFUNC,
+	OGE,
+	OGOTO,
+	OGT,
+	OHI,
+	OHS,
+	OIF,
+	OIND,
+	OINDREG,
+	OINIT,
+	OLABEL,
+	OLDIV,
+	OLE,
+	OLIST,
+	OLMOD,
+	OLMUL,
+	OLO,
+	OLS,
+	OLSHR,
+	OLT,
+	OMOD,
+	OMUL,
+	ONAME,
+	ONE,
+	ONOT,
+	OOR,
+	OOROR,
+	OPOSTDEC,
+	OPOSTINC,
+	OPREDEC,
+	OPREINC,
+	OPROTO,
+	OREGISTER,
+	ORETURN,
+	OSET,
+	OSIGN,
+	OSIZE,
+	OSTRING,
+	OLSTRING,
+	OSTRUCT,
+	OSUB,
+	OSWITCH,
+	OUNION,
+	OUSED,
+	OWHILE,
+	OXOR,
+	ONEG,
+	OCOM,
+	OELEM,
+
+	OTST,		/* used in some compilers */
+	OINDEX,
+	OFAS,
+
+	OBLK,
+	OPOS,
+	ONUL,
+	ODOTIND,
+	OARRIND,
+	ODAS,
+	OASD,
+	OIOTA,
+	OLEN,
+	OBRACKET,
+	OREF,
+	OARRAYOF,
+	OSLICE,
+	OSADDR,
+	ONIL,
+	OS2AB,
+	OAB2S,
+	OFILDES,
+	OFD,
+	OTUPLE,
+	OT0,
+	ORETV,
+	OCAT,
+	OSBREAK,
+	OLDOT,
+	OMDOT,
+
+	OCODE,
+	ODECE,
+	ODECT,
+	ODECV,
+	ODECF,
+	OPUSH,
+	OPOP,
+
+	OEND
+};
+enum
+{
+	TXXX,
+	TCHAR,
+	TUCHAR,
+	TSHORT,
+	TUSHORT,
+	TINT,
+	TUINT,
+	TLONG,
+	TULONG,
+	TVLONG,
+	TUVLONG,
+	TFLOAT,
+	TDOUBLE,
+	TIND,
+	TFUNC,
+	TARRAY,
+	TVOID,
+	TSTRUCT,
+	TUNION,
+	TENUM,
+	NTYPE,
+
+	TAUTO	= NTYPE,
+	TEXTERN,
+	TSTATIC,
+	TTYPEDEF,
+	TREGISTER,
+	TCONSTNT,
+	TVOLATILE,
+	TUNSIGNED,
+	TSIGNED,
+	TDOT,
+	TFILE,
+	TOLD,
+
+	TSTRING,
+	TFD,
+	TTUPLE,
+
+	NALLTYPES,
+};
+enum
+{
+	CXXX,
+	CAUTO,
+	CEXTERN,
+	CGLOBL,
+	CSTATIC,
+	CLOCAL,
+	CTYPEDEF,
+	CPARAM,
+	CSELEM,
+	CLABEL,
+	CEXREG,
+	NCTYPES,
+};
+enum
+{
+	GXXX		= 0,
+	GCONSTNT	= 1<<0,
+	GVOLATILE	= 1<<1,
+	NGTYPES		= 1<<2,
+};
+enum
+{
+	BCHAR		= 1L<<TCHAR,
+	BUCHAR		= 1L<<TUCHAR,
+	BSHORT		= 1L<<TSHORT,
+	BUSHORT		= 1L<<TUSHORT,
+	BINT		= 1L<<TINT,
+	BUINT		= 1L<<TUINT,
+	BLONG		= 1L<<TLONG,
+	BULONG		= 1L<<TULONG,
+	BVLONG		= 1L<<TVLONG,
+	BUVLONG		= 1L<<TUVLONG,
+	BFLOAT		= 1L<<TFLOAT,
+	BDOUBLE		= 1L<<TDOUBLE,
+	BIND		= 1L<<TIND,
+	BFUNC		= 1L<<TFUNC,
+	BARRAY		= 1L<<TARRAY,
+	BVOID		= 1L<<TVOID,
+	BSTRUCT		= 1L<<TSTRUCT,
+	BUNION		= 1L<<TUNION,
+	BENUM		= 1L<<TENUM,
+	BFILE		= 1L<<TFILE,
+	BOLD		= 1L<<TOLD,
+	BDOT		= 1L<<TDOT,
+	BCONSTNT	= 1L<<TCONSTNT,
+	BVOLATILE	= 1L<<TVOLATILE,
+	BUNSIGNED	= 1L<<TUNSIGNED,
+	BSIGNED		= 1L<<TSIGNED,
+	BAUTO		= 1L<<TAUTO,
+	BEXTERN		= 1L<<TEXTERN,
+	BSTATIC		= 1L<<TSTATIC,
+	BTYPEDEF	= 1L<<TTYPEDEF,
+	BREGISTER	= 1L<<TREGISTER,
+
+	BINTEGER	= BCHAR|BUCHAR|BSHORT|BUSHORT|BINT|BUINT|
+				BLONG|BULONG|BVLONG|BUVLONG,
+	BNUMBER		= BINTEGER|BFLOAT|BDOUBLE,
+
+/* these can be overloaded with complex types */
+
+	BCLASS		= BAUTO|BEXTERN|BSTATIC|BTYPEDEF|BREGISTER,
+
+	BGARB		= BCONSTNT|BVOLATILE,
+};
+
+EXTERN struct
+{
+	Type*	tenum;		/* type of entire enum */
+	Type*	cenum;		/* type of current enum run */
+	vlong	lastenum;	/* value of current enum */
+	double	floatenum;	/* value of current enum */
+} en;
+
+EXTERN	int	autobn;
+EXTERN	long	autoffset;
+EXTERN	int	blockno;
+EXTERN	int	comm;
+EXTERN	Decl*	dclstack;
+EXTERN	int	doaddr;
+EXTERN	int	doalladdr;
+EXTERN	int	doinc;
+EXTERN	int	doloc;
+EXTERN	int	domod;
+EXTERN	Hist*	ehist;
+EXTERN	long	firstbit;
+EXTERN	Decl*	firstdcl;
+EXTERN	int	fperror;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	hunk;
+EXTERN	char*	include[20];
+EXTERN	Type*	fdtype;
+EXTERN	int	inmain;
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	int	justcode;
+EXTERN	long	lastbit;
+EXTERN	char	lastclass;
+EXTERN	Type*	lastdcl;
+EXTERN	long	lastfield;
+EXTERN	Type*	lasttype;
+EXTERN	long	lineno;
+EXTERN	long	nearln;
+EXTERN	int	nerrors;
+EXTERN	int	newflag;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Node*	nodproto;
+EXTERN	Node*	nodcast;
+EXTERN	int	passes;
+EXTERN	char*	pathname;
+EXTERN	int	peekc;
+EXTERN	Type*	pfdtype;
+EXTERN	long	pline;
+EXTERN	long	saveline;
+EXTERN	Type*	strf;
+EXTERN	int	strings;
+EXTERN	Type*	stringtype;
+EXTERN	Type*	strl;
+EXTERN	char	symb[NSYMB];
+EXTERN	Sym*	symstring;
+EXTERN	int	taggen;
+EXTERN	Type*	tfield;
+EXTERN	Type*	tufield;
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	Type*	thisfn;
+EXTERN	long	thunk;
+EXTERN	Type*	types[NTYPE];
+EXTERN	Node*	initlist;
+EXTERN	int	nterm;
+EXTERN	int	hjdickflg;
+EXTERN	int	fproundflg;
+EXTERN	Bits	zbits;
+
+extern	char	*onames[], *tnames[], *gnames[];
+extern	char	*cnames[], *qnames[], *bnames[];
+extern	char	tab[NTYPE][NTYPE];
+extern	char	comrel[], invrel[], logrel[];
+extern	long	ncast[], tadd[], tand[];
+extern	long	targ[], tasadd[], tasign[], tcast[];
+extern	long	tdot[], tfunct[], tindir[], tmul[];
+extern	long	tnot[], trel[], tsub[];
+
+extern	char	typeaf[];
+extern	char	typefd[];
+extern	char	typei[];
+extern	char	typesu[];
+extern	char	typesuv[];
+extern	char	typeu[];
+extern	char	typev[];
+extern	char	typec[];
+extern	char	typeh[];
+extern	char	typeil[];
+extern	char	typeilp[];
+extern	char	typechl[];
+extern	char	typechlp[];
+extern	char	typechlpfd[];
+
+extern	ulong	thash1;
+extern	ulong	thash2;
+extern	ulong	thash3;
+extern	ulong	thash[];
+
+/*
+ *	Inferno.c/Posix.c/Nt.c
+ */
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
+
+/*
+ *	parser
+ */
+int	yyparse(void);
+int	mpatof(char*, double*);
+int	mpatov(char*, vlong*);
+
+/*
+ *	lex.c
+ */
+void*	allocn(void*, long, long);
+void*	alloc(long);
+void	cinit(void);
+int	compile(char*, char**, int);
+void	errorexit(void);
+int	filbuf(void);
+int	getc(void);
+long	getr(void);
+int	getnsc(void);
+Sym*	lookup(void);
+void	main(int, char*[]);
+void	newfile(char*, int);
+void	newio(void);
+void	outbegin(char *);
+void	outend(void);
+void	outfun(Node*);
+void	pushio(void);
+long	escchar(long, int, int);
+Sym*	slookup(char*);
+void	syminit(Sym*);
+void	unget(int);
+long	yylex(void);
+int	Lconv(Fmt*);
+int	Tconv(Fmt*);
+int	FNconv(Fmt*);
+int	Oconv(Fmt*);
+int	Qconv(Fmt*);
+int	VBconv(Fmt*);
+void	setinclude(char*);
+
+/*
+ * mac.c
+ */
+void	dodefine(char*);
+void	domacro(void);
+Sym*	getsym(void);
+long	getnsn(void);
+void	linehist(char*, int);
+void	macdef(void);
+void	macprag(void);
+void	macend(void);
+void	macexpand(Sym*, char*);
+void	macif(int);
+void	macinc(void);
+void	maclin(void);
+void	macund(void);
+
+/*
+ * dcl.c
+ */
+Node*	doinit(Sym*, Type*, long, Node*);
+Type*	tcopy(Type*);
+void	init1(Sym*, Type*, long, int);
+Node*	newlist(Node*, Node*);
+void	adecl(int, Type*, Sym*);
+int	anyproto(Node*);
+void	argmark(Node*, int);
+void	dbgdecl(Sym*);
+Node*	dcllabel(Sym*, int);
+Node*	dodecl(void(*)(int, Type*, Sym*), int, Type*, Node*, int);
+Sym*	mkstatic(Sym*);
+void	doenum(Sym*, Node*);
+void	snap(Type*);
+Type*	dotag(Sym*, int, int);
+void	edecl(int, Type*, Sym*);
+Type*	fnproto(Node*);
+Type*	fnproto1(Node*);
+void	markdcl(void);
+Type*	paramconv(Type*, int);
+void	pdecl(int, Type*, Sym*);
+Decl*	push(void);
+Decl*	push1(Sym*);
+Node*	revertdcl(void);
+#undef round
+#define	round	ccround
+long	round(long, int);
+int	rsametype(Type*, Type*, int, int);
+int	sametype(Type*, Type*);
+ulong	signature(Type*, int);
+void	suallign(Type*);
+void	tmerge(Type*, Sym*);
+void	walkparam(Node*, int);
+void	xdecl(int, Type*, Sym*);
+Node*	contig(Sym*, Node*, long);
+
+/*
+ * com.c
+ */
+void	ccom(Node*);
+void	complex(Node*);
+int	tcom(Node*);
+int	tcoma(Node*, Node*, Type*, int);
+int	tcomd(Node*, Type*);
+int	tcomo(Node*, int);
+int	tcomx(Node*);
+int	tlvalue(Node*);
+void	constas(Node*, Type*, Type*);
+
+/*
+ * con.c
+ */
+void	acom(Node*);
+void	acom1(vlong, Node*);
+void	acom2(Node*, Type*);
+int	acomcmp1(void*, void*);
+int	acomcmp2(void*, void*);
+int	addo(Node*);
+void	evconst(Node*);
+
+/*
+ * sub.c
+ */
+void	arith(Node*, int);
+Type*	dotsearch(Sym*, Type*, Node*);
+long	dotoffset(Type*, Type*, Node*);
+void	gethunk(void);
+Node*	invert(Node*);
+int	bitno(long);
+void	makedot(Node*, Type*, long);
+Node*	new(int, Node*, Node*);
+Node*	new1(int, Node*, Node*);
+int	nilcast(Type*, Type*);
+int	nocast(Type*, Type*);
+void	prtree(Node*, char*);
+void	prtree1(Node*, int, int);
+void	relcon(Node*, Node*);
+int	relindex(int);
+int	simpleg(long);
+Type*	garbt(Type*, long);
+int	simplec(long);
+Type*	simplet(long);
+int	stcompat(Node*, Type*, Type*, long[]);
+int	tcompat(Node*, Type*, Type*, long[]);
+void	tinit(void);
+Type*	typ(int, Type*);
+Type*	typ1(int, Type*);
+void	typeext(Type*, Node*);
+void	typeext1(Type*, Node*);
+int	side(Node*);
+int	vconst(Node*);
+int	vlog(Node*);
+int	topbit(ulong);
+long	typebitor(long, long);
+void	diag(Node*, char*, ...);
+void	warn(Node*, char*, ...);
+void	yyerror(char*, ...);
+void	fatal(Node*, char*, ...);
+
+/*
+ * acid.c
+ */
+void	acidtype(Type*);
+void	acidvar(Sym*);
+
+/*
+ * bits.c
+ */
+Bits	bor(Bits, Bits);
+Bits	band(Bits, Bits);
+Bits	bnot(Bits);
+int	bany(Bits*);
+int	bnum(Bits);
+Bits	blsh(uint);
+int	beq(Bits, Bits);
+int	bset(Bits, uint);
+
+/*
+ * dpchk.c
+ */
+void	dpcheck(Node*);
+void	arginit(void);
+void	pragvararg(void);
+void	praghjdicks(void);
+void	pragfpround(void);
+
+/*
+ * calls to machine depend part
+ */
+void	codgen(Node*, Node*, int);
+void	gclean(void);
+void	gextern(Sym*, Node*, long, long);
+void	ginit(void);
+long	outstring(char*, long);
+long	outlstring(Rune*, long);
+void	sextern(Sym*, Node*, long, long);
+void	xcom(Node*);
+long	exreg(Type*);
+long	align(long, Type*, int);
+long	maxround(long, long);
+
+extern	schar	ewidth[];
+
+/*
+ * com64
+ */
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+double	convvtof(vlong);
+vlong	convftov(double);
+double	convftox(double, int);
+vlong	convvtox(vlong, int);
+
+#pragma	varargck	argpos	warn	2
+#pragma	varargck	argpos	diag	2
+#pragma	varargck	argpos	yyerror	1
+
+#pragma	varargck	type	"F"	Node*
+#pragma	varargck	type	"L"	long
+#pragma	varargck	type	"Q"	long
+#pragma	varargck	type	"O"	int
+#pragma	varargck	type	"T"	Type*
+#pragma	varargck	type	"|"	int
+
+/* output routines */
+
+void prline(char*);
+void prstr(char *);
+void prlstr(Rune *);
+void prkeywd(char *);
+void prid(char *);
+void	prsym(Sym*, int);
+void	prsym0(Sym*);
+void prdelim(char *);
+void prchar(vlong);
+void prreal(double, char*, int);
+void prnum(vlong, int, Type*);
+void	prcom(char *, Node*);
+void newline(void);
+void incind(void);
+void decind(void);
+int	zeroind(void);
+void	restoreind(int);
+void startcom(int);
+void	addcom(int);
+void	endcom(void);
+int	outcom(int);
+int	arrow(Sym*);
+
+/* limbo generating routines */
+
+void pgen(int);
+void epgen(int);
+void ttgen(Type*);
+void vtgen(Node*);
+void etgen(Sym*);
+void expgen(Node*);
+
+/* -m routines */
+
+void	newsec(int);
+
+void outpush(char*);
+void outpop(int);
+void outpush0(char*, Node*);
+void outpop0(int);
+void outpush2(char*, Node*);
+void outpop2(int);
+char*	outmod(char*, int);
+
+/* miscellaneous */
+
+int iscon(char*);
+Node* ncopy(Node*);
+void doasenum(Sym*);
+Type *maxtype(Type*, Type*);
+
+void	linit(void);
+void	sysinit(void);
+
+int ism(void);
+int isb(void);
+int dolog(void);
+
+int line(Node*);
+
+void	output(long, int);
+void usemod(Sym*, int);
+void setmod(Sym*);
+
+int	exists(char*);
+int	isconsym(Sym *);
+
+void clbegin(void);
+void clend(void);
+
+int gfltconv(Fmt*);
--- /dev/null
+++ b/utils/c2l/cc.y
@@ -1,0 +1,1239 @@
+%{
+#include "cc.h"
+%}
+%union	{
+	Node*	node;
+	Sym*	sym;
+	Type*	type;
+	struct
+	{
+		Type*	t;
+		char	c;
+	} tycl;
+	struct
+	{
+		Type*	t1;
+		Type*	t2;
+	} tyty;
+	struct
+	{
+		char*	s;
+		long	l;
+	} sval;
+	long	lval;
+	double	dval;
+	vlong	vval;
+}
+%type	<sym>	ltag
+%type	<lval>	gctname cname gname tname
+%type	<lval>	gctnlist zgnlist tnlist
+%type	<type>	tlist etlist sbody complex
+%type	<tycl>	types etypes
+%type	<node>	zarglist arglist zcexpr
+%type	<node>	name block stmnt cexpr expr xuexpr pexpr
+%type	<node>	zelist elist adecl slist uexpr string lstring sstring slstring
+%type	<node>	xdecor xdecor2 labels label ulstmnt
+%type	<node>	adlist edecor tag qual qlist
+%type	<node>	abdecor abdecor1 abdecor2 abdecor3
+%type	<node>	zexpr lexpr init ilist
+
+%left	';'
+%left	','
+%right	'=' LPE LME LMLE LDVE LMDE LRSHE LLSHE LANDE LXORE LORE
+%right	'?' ':'
+%left	LOROR
+%left	LANDAND
+%left	'|'
+%left	'^'
+%left	'&'
+%left	LEQ LNE
+%left	'<' '>' LLE LGE
+%left	LLSH LRSH
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	LMM LPP LMG '.' '[' '('
+
+%token	<sym>	LNAME LCTYPE LSTYPE
+%token	<dval>	LFCONST LDCONST
+%token	<vval>	LCHARACTER LCONST LLCONST LUCONST LULCONST LVLCONST LUVLCONST
+%token	<sval>	LSTRING LLSTRING
+%token		LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO
+%token		LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO
+%token	LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED
+%token	LSTATIC LSTRUCT LSWITCH LTYPEDEF LUNION LUNSIGNED LWHILE
+%token	LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET LSIGNOF LVLONG
+%%
+prog:
+|	prog xdecl
+
+/*
+ * external declarator
+ */
+xdecl:
+	zctlist ';'
+	{
+		dodecl(xdecl, lastclass, lasttype, Z, 1);
+	}
+|	zctlist xdlist ';'
+|	zctlist xdecor
+	{
+		lastdcl = T;
+		dodecl(xdecl, lastclass, lasttype, $2, 0);
+		if(lastdcl == T || lastdcl->etype != TFUNC) {
+			diag($2, "not a function");
+			lastdcl = types[TFUNC];
+		}
+		thisfn = lastdcl;
+		markdcl();
+		firstdcl = dclstack;
+		argmark($2, 0);
+	}
+	pdecl
+	{
+		argmark($2, 1);
+	}
+	block
+	{
+		$6->blk = 0;
+		codgen($6, $2, lineno);
+		revertdcl();
+	}
+
+xdlist:
+	xdecor
+	{
+		dodecl(xdecl, lastclass, lasttype, $1, 1);
+	}
+|	xdecor
+	{
+		$1 = dodecl(xdecl, lastclass, lasttype, $1, 0);
+	}
+	'=' init
+	{
+		$4 = doinit($1->sym, $1->type, 0L, $4);
+		$4 = new(ODAS, $1, $4);
+		$4->type = $1->type;
+		$4->lineno = $1->lineno;
+		vtgen($4);
+	}
+|	xdlist ',' xdlist
+
+xdecor:
+	xdecor2
+|	'*' zgnlist xdecor
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+
+xdecor2:
+	tag
+|	'(' xdecor ')'
+	{
+		$$ = $2;
+	}
+|	xdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+		/* outfun($$); */
+	}
+|	xdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+/*
+ * automatic declarator
+ */
+adecl:
+	{
+		$$ = Z;
+	}
+|	adecl ctlist ';'
+	{
+		$$ = dodecl(adecl, lastclass, lasttype, Z, 1);
+		if($1 != Z)
+			if($$ != Z)
+				$$ = new(OLIST, $1, $$);
+			else
+				$$ = $1;
+	}
+|	adecl ctlist adlist ';'
+	{
+		$$ = $1;
+		if($3 != Z) {
+			$$ = $3;
+			if($1 != Z)
+				$$ = new(OLIST, $1, $3);
+		}
+	}
+
+adlist:
+	xdecor
+	{
+		$$ = dodecl(adecl, lastclass, lasttype, $1, 1);
+		if($$->sym->class == CSTATIC)
+			$$ = Z;
+	}
+|	xdecor
+	{
+		$1 = dodecl(adecl, lastclass, lasttype, $1, 0);
+	}
+	'=' init
+	{
+		/* long w; */
+
+		/* w = $1->sym->type->width; */
+		$$ = doinit($1->sym, $1->type, 0L, $4);
+		/* $$ = contig($1->sym, $$, w); */
+		$$ = new(ODAS, $1, $$);
+		$$->type = $1->type;
+		$$->lineno = $1->lineno;
+		vtgen($$);
+		if($1->sym->class == CSTATIC)
+			$$ = Z;
+	}
+|	adlist ',' adlist
+	{
+		$$ = $1;
+		if($3 != Z) {
+			$$ = $3;
+			if($1 != Z)
+				$$ = new(OLIST, $1, $3);
+		}
+	}
+
+/*
+ * parameter declarator
+ */
+pdecl:
+|	pdecl ctlist pdlist ';'
+
+pdlist:
+	xdecor
+	{
+		dodecl(pdecl, lastclass, lasttype, $1, 1);
+	}
+|	pdlist ',' pdlist
+
+/*
+ * structure element declarator
+ */
+edecl:
+	etlist
+	{
+		lasttype = $1;
+	}
+	zedlist ';'
+|	edecl etlist
+	{
+		lasttype = $2;
+	}
+	zedlist ';'
+
+zedlist:					/* extension */
+	{
+		lastfield = 0;
+		edecl(CXXX, lasttype, S);
+	}
+|	edlist
+
+edlist:
+	edecor
+	{
+		dodecl(edecl, CXXX, lasttype, $1, 1);
+	}
+|	edlist ',' edlist
+
+edecor:
+	xdecor
+	{
+		lastbit = 0;
+		firstbit = 1;
+	}
+|	tag ':' lexpr
+	{
+		$$ = new(OBIT, $1, $3);
+	}
+|	':' lexpr
+	{
+		$$ = new(OBIT, Z, $2);
+	}
+
+/*
+ * abstract declarator
+ */
+abdecor:
+	{
+		$$ = (Z);
+	}
+|	abdecor1
+
+abdecor1:
+	'*' zgnlist
+	{
+		$$ = new(OIND, (Z), Z);
+		$$->garb = simpleg($2);
+	}
+|	'*' zgnlist abdecor1
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+|	abdecor2
+
+abdecor2:
+	abdecor3
+|	abdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+	}
+|	abdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+abdecor3:
+	'(' ')'
+	{
+		$$ = new(OFUNC, (Z), Z);
+	}
+|	'[' zexpr ']'
+	{
+		$$ = new(OARRAY, (Z), $2);
+	}
+|	'(' abdecor1 ')'
+	{
+		$$ = $2;
+	}
+
+init:
+	expr
+|	'{' ilist '}'
+	{
+		$$ = new(OINIT, invert($2), Z);
+	}
+
+qual:
+	'[' lexpr ']'
+	{
+		$$ = new(OARRAY, $2, Z);
+	}
+|	'.' ltag
+	{
+		$$ = new(OELEM, Z, Z);
+		$$->sym = $2;
+	}
+|	qual '='
+
+qlist:
+	init ','
+|	qlist init ','
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+|	qual
+|	qlist qual
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+ilist:
+	qlist
+|	init
+|	qlist init
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+zarglist:
+	{
+		$$ = Z;
+	}
+|	arglist
+	{
+		$$ = invert($1);
+	}
+
+
+arglist:
+	name
+|	tlist abdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	tlist xdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	'.' '.' '.'
+	{
+		$$ = new(ODOTDOT, Z, Z);
+	}
+|	arglist ',' arglist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+block:
+	'{' adecl slist '}'
+	{
+		$$ = invert($3);
+		if($2 != Z)
+			$$ = new(OLIST, $2, $$);
+		if($$ == Z)
+			$$ = new(ONUL, Z, Z);
+		$$->blk = 1;
+	}
+
+slist:
+	{
+		$$ = Z;
+	}
+|	slist stmnt
+	{
+		if($1 == Z)
+			$$ = $2;
+		else
+			$$ = new(OLIST, $1, $2);
+	}
+
+labels:
+	label
+|	labels label
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+label:
+	LCASE expr ':'
+	{
+		$$ = new(OCASE, $2, Z);
+		$$->lineno = $2->lineno;
+	}
+|	LDEFAULT ':'
+	{
+		$$ = new(OCASE, Z, Z);
+	}
+|	LNAME ':'
+	{
+		$$ = new(OLABEL, dcllabel($1, 1), Z);
+		$1->lineno = lineno;
+	}
+
+stmnt:
+	error ';'
+	{
+		$$ = Z;
+	}
+|	ulstmnt
+|	labels ulstmnt
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+ulstmnt:
+	zcexpr ';'
+	{
+		if($$ == Z)
+			$$ = new(ONUL, Z, Z);
+		$$->kind = KEXP;
+	}
+|	{
+		markdcl();
+	}
+	block
+	{
+		revertdcl();
+		$$ = $2;
+	}
+|	LIF '(' cexpr ')' stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, Z));
+		$$->lineno = $3->lineno;
+		$5->blk = 0;
+	}
+|	LIF '(' cexpr ')' stmnt LELSE stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, $7));
+		$$->lineno = $3->lineno;
+		$5->blk = $7->blk = 0;
+	}
+|	LFOR '(' zcexpr ';' zcexpr ';' zcexpr ')' stmnt
+	{
+		$$ = new(OFOR, new(OLIST, $5, new(OLIST, $3, $7)), $9);
+		if($3 != Z)
+			$$->lineno = $3->lineno;
+		else if($5 != Z)
+			$$->lineno = $5->lineno;
+		else if($7 != Z)
+			$$->lineno = $7->lineno;
+		else
+			$$->lineno = line($9);
+		$9->blk = 0;
+	}
+|	LWHILE '(' cexpr ')' stmnt
+	{
+		$$ = new(OWHILE, $3, $5);
+		$$->lineno = $3->lineno;
+		$5->blk = 0;
+	}
+|	LDO stmnt LWHILE '(' cexpr ')' ';'
+	{
+		$$ = new(ODWHILE, $5, $2);
+		$$->lineno = line($2);
+		$2->blk = 0;
+	}
+|	LRETURN zcexpr ';'
+	{
+		$$ = new(ORETURN, $2, Z);
+		$$->type = thisfn->link;
+		if($2 != Z)
+			$$->lineno = $2->lineno;
+	}
+|	LSWITCH '(' cexpr ')' stmnt
+	{
+		$$ = new(OSWITCH, $3, $5);
+		$$->lineno = $3->lineno;
+		$5->blk = 0;
+	}
+|	LBREAK ';'
+	{
+		$$ = new(OBREAK, Z, Z);
+	}
+|	LCONTINUE ';'
+	{
+		$$ = new(OCONTINUE, Z, Z);
+	}
+|	LGOTO LNAME ';'
+	{
+		$$ = new(OGOTO, dcllabel($2, 0), Z);
+		$2->lineno = lineno;
+	}
+|	LUSED '(' zelist ')' ';'
+	{
+		$$ = new(OUSED, $3, Z);
+		$$->lineno = line($3);
+	}
+|	LSET '(' zelist ')' ';'
+	{
+		$$ = new(OSET, $3, Z);
+		$$->lineno = line($3);
+	}
+
+zcexpr:
+	{
+		$$ = Z;
+	}
+|	cexpr
+
+zexpr:
+	{
+		$$ = Z;
+	}
+|	lexpr
+
+lexpr:
+	expr
+	{
+		$$ = new(OCAST, $1, Z);
+		$$->type = types[TLONG];
+	}
+
+cexpr:
+	expr
+|	cexpr ',' cexpr
+	{
+		$$ = new(OCOMMA, $1, $3);
+	}
+
+expr:
+	xuexpr
+|	expr '*' expr
+	{
+		$$ = new(OMUL, $1, $3);
+	}
+|	expr '/' expr
+	{
+		$$ = new(ODIV, $1, $3);
+	}
+|	expr '%' expr
+	{
+		$$ = new(OMOD, $1, $3);
+	}
+|	expr '+' expr
+	{
+		$$ = new(OADD, $1, $3);
+	}
+|	expr '-' expr
+	{
+		$$ = new(OSUB, $1, $3);
+	}
+|	expr LRSH expr
+	{
+		$$ = new(OASHR, $1, $3);
+	}
+|	expr LLSH expr
+	{
+		$$ = new(OASHL, $1, $3);
+	}
+|	expr '<' expr
+	{
+		$$ = new(OLT, $1, $3);
+	}
+|	expr '>' expr
+	{
+		$$ = new(OGT, $1, $3);
+	}
+|	expr LLE expr
+	{
+		$$ = new(OLE, $1, $3);
+	}
+|	expr LGE expr
+	{
+		$$ = new(OGE, $1, $3);
+	}
+|	expr LEQ expr
+	{
+		$$ = new(OEQ, $1, $3);
+	}
+|	expr LNE expr
+	{
+		$$ = new(ONE, $1, $3);
+	}
+|	expr '&' expr
+	{
+		$$ = new(OAND, $1, $3);
+	}
+|	expr '^' expr
+	{
+		$$ = new(OXOR, $1, $3);
+	}
+|	expr '|' expr
+	{
+		$$ = new(OOR, $1, $3);
+	}
+|	expr LANDAND expr
+	{
+		$$ = new(OANDAND, $1, $3);
+	}
+|	expr LOROR expr
+	{
+		$$ = new(OOROR, $1, $3);
+	}
+|	expr '?' cexpr ':' expr
+	{
+		$$ = new(OCOND, $1, new(OLIST, $3, $5));
+	}
+|	expr '=' expr
+	{
+		$$ = new(OAS, $1, $3);
+	}
+|	expr LPE expr
+	{
+		$$ = new(OASADD, $1, $3);
+	}
+|	expr LME expr
+	{
+		$$ = new(OASSUB, $1, $3);
+	}
+|	expr LMLE expr
+	{
+		$$ = new(OASMUL, $1, $3);
+	}
+|	expr LDVE expr
+	{
+		$$ = new(OASDIV, $1, $3);
+	}
+|	expr LMDE expr
+	{
+		$$ = new(OASMOD, $1, $3);
+	}
+|	expr LLSHE expr
+	{
+		$$ = new(OASASHL, $1, $3);
+	}
+|	expr LRSHE expr
+	{
+		$$ = new(OASASHR, $1, $3);
+	}
+|	expr LANDE expr
+	{
+		$$ = new(OASAND, $1, $3);
+	}
+|	expr LXORE expr
+	{
+		$$ = new(OASXOR, $1, $3);
+	}
+|	expr LORE expr
+	{
+		$$ = new(OASOR, $1, $3);
+	}
+
+xuexpr:
+	uexpr
+|	'(' tlist abdecor ')' xuexpr
+	{
+		$$ = new(OCAST, $5, Z);
+		dodecl(NODECL, CXXX, $2, $3, 1);
+		$$->type = lastdcl;
+	}
+|	'(' tlist abdecor ')' '{' ilist '}'	/* extension */
+	{
+		$$ = new(OSTRUCT, $6, Z);
+		dodecl(NODECL, CXXX, $2, $3, 1);
+		$$->type = lastdcl;
+	}
+
+uexpr:
+	pexpr
+|	'*' xuexpr
+	{
+		$$ = new(OIND, $2, Z);
+	}
+|	'&' xuexpr
+	{
+		$$ = new(OADDR, $2, Z);
+	}
+|	'+' xuexpr
+	{
+		$$ = new(OPOS, $2, Z);
+	}
+|	'-' xuexpr
+	{
+		$$ = new(ONEG, $2, Z);
+	}
+|	'!' xuexpr
+	{
+		$$ = new(ONOT, $2, Z);
+	}
+|	'~' xuexpr
+	{
+		$$ = new(OCOM, $2, Z);
+	}
+|	LPP xuexpr
+	{
+		$$ = new(OPREINC, $2, Z);
+	}
+|	LMM xuexpr
+	{
+		$$ = new(OPREDEC, $2, Z);
+	}
+|	LSIZEOF uexpr
+	{
+		$$ = new(OSIZE, $2, Z);
+	}
+|	LSIGNOF uexpr
+	{
+		$$ = new(OSIGN, $2, Z);
+	}
+
+pexpr:
+	'(' cexpr ')'
+	{
+		$$ = $2;
+	}
+|	LSIZEOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIZE, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4, 1);
+		$$->type = lastdcl;
+	}
+|	LSIGNOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIGN, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4, 1);
+		$$->type = lastdcl;
+	}
+|	pexpr '(' zelist ')'
+	{
+		$$ = new(OFUNC, $1, Z);
+		if($1->op == ONAME)
+		if($1->type == T)
+			dodecl(xdecl, CXXX, types[TINT], $$, 1);
+		$$->right = invert($3);
+		$$->kind = KEXP;
+	}
+|	pexpr '[' cexpr ']'
+	{
+		$$ = new(OARRIND, $1, $3);
+	}
+|	pexpr LMG ltag
+	{
+		$$ = new(ODOTIND, $1, Z);
+		$$->sym = $3;
+	}
+|	pexpr '.' ltag
+	{
+		$$ = new(ODOT, $1, Z);
+		$$->sym = $3;
+	}
+|	pexpr LPP
+	{
+		$$ = new(OPOSTINC, $1, Z);
+	}
+|	pexpr LMM
+	{
+		$$ = new(OPOSTDEC, $1, Z);
+	}
+|	name
+|	LCHARACTER
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TINT];
+		$$->vconst = $1;
+		$$->kind = KCHR;
+	}
+|	LCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TINT];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	LLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TLONG];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	LUCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUINT];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	LULCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TULONG];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	LDCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TDOUBLE];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+		$$->kind = lastnumbase;
+	}
+|	LFCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TFLOAT];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+		$$->kind = lastnumbase;
+	}
+|	LVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TVLONG];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	LUVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUVLONG];
+		$$->vconst = $1;
+		$$->kind = lastnumbase;
+	}
+|	string
+|	lstring
+
+sstring:
+	LSTRING
+	{
+		$$ = new(OSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TCHAR]);
+		$$->type->width = $1.l + 1;
+		$$->cstring = $1.s;
+		$$->sym = symstring;
+	}
+
+string:
+	sstring
+	{
+		$$ = $1;
+	}
+|	string sstring
+	{
+		char *s;
+		int n1, n2;
+
+		n1 = $1->type->width - 1;
+		n2 = $2->type->width - 1;
+		s = alloc(n1+n2+MAXALIGN);
+
+		memcpy(s, $1->cstring, n1);
+		memcpy(s+n1, $2->cstring, n2);
+		s[n1+n2] = 0;
+
+		$1->left = new(OCAT, ncopy($1), $2);
+
+		$$ = $1;
+		$$->type->width += n2;
+		$$->cstring = s;
+	}
+
+slstring:
+	LLSTRING
+	{
+		$$ = new(OLSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TUSHORT]);
+		$$->type->width = $1.l + sizeof(TRune);
+		$$->rstring = (TRune*)$1.s;
+		$$->sym = symstring;
+	}
+
+lstring:
+	slstring
+	{
+		$$ = $1;
+	}
+|	lstring slstring
+	{
+		char *s;
+		int n1, n2;
+
+		n1 = $1->type->width - sizeof(TRune);
+		n2 = $2->type->width - sizeof(TRune);
+		s = alloc(n1+n2+MAXALIGN);
+
+		memcpy(s, $1->rstring, n1);
+		memcpy(s+n1, $2->rstring, n2);
+		*(TRune*)(s+n1+n2) = 0;
+
+		$1->left = new(OCAT, ncopy($1), $2);
+
+		$$ = $1;
+		$$->type->width += n2;
+		$$->rstring = (TRune*)s;
+	}
+
+zelist:
+	{
+		$$ = Z;
+	}
+|	elist
+
+elist:
+	expr
+|	elist ',' elist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+sbody:
+	'{'
+	{
+		$<tyty>$.t1 = strf;
+		$<tyty>$.t2 = strl;
+		strf = T;
+		strl = T;
+		lastbit = 0;
+		firstbit = 1;
+	}
+	edecl '}'
+	{
+		$$ = strf;
+		strf = $<tyty>2.t1;
+		strl = $<tyty>2.t2;
+	}
+
+zctlist:
+	{
+		lastclass = CXXX;
+		lasttype = types[TINT];
+	}
+|	ctlist
+
+etypes:
+	complex
+	{
+		$$.t = $1;
+		$$.c = CXXX;
+	}
+|	tnlist
+	{
+		$$.t = simplet($1);
+		$$.c = simplec($1);
+	}
+
+types:
+	complex
+	{
+		$$.t = $1;
+		$$.c = CXXX;
+	}
+|	complex gctnlist
+	{
+		$$.t = $1;
+		$$.c = simplec($2);
+		if($2 & ~BCLASS & ~BGARB)
+			diag(Z, "illegal combination of types 1: %Q/%T", $2, $1);
+	}
+|	gctnlist
+	{
+		$$.t = simplet($1);
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+	}
+|	gctnlist complex
+	{
+		$$.t = $2;
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+		if($1 & ~BCLASS & ~BGARB)
+			diag(Z, "illegal combination of types 2: %Q/%T", $1, $2);
+	}
+|	gctnlist complex gctnlist
+	{
+		$$.t = $2;
+		$$.c = simplec($1|$3);
+		$$.t = garbt($$.t, $1|$3);
+		if(($1|$3) & ~BCLASS & ~BGARB || $3 & BCLASS)
+			diag(Z, "illegal combination of types 3: %Q/%T/%Q", $1, $2, $3);
+	}
+
+etlist:
+	zgnlist etypes
+	{
+		$$ = $2.t;
+		if($2.c != CXXX)
+			diag(Z, "illegal combination of class 4: %s", cnames[$2.c]);
+		$$ = garbt($$, $1);
+	}
+
+tlist:
+	types
+	{
+		$$ = $1.t;
+		if($1.c != CXXX)
+			diag(Z, "illegal combination of class 4: %s", cnames[$1.c]);
+	}
+
+ctlist:
+	types
+	{
+		lasttype = $1.t;
+		lastclass = $1.c;
+	}
+
+complex:
+	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, 0);
+		$$ = $2->suetag;
+		$2->lineno = lineno;
+	}
+|	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, autobn);
+		saveline = $2->lineno = lineno;
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		$$->lineno = saveline;
+		suallign($$);
+	}
+|	LSTRUCT
+	{
+		saveline = lineno;
+	}
+	sbody
+	{
+		char buf[128];
+
+		taggen++;
+		sprint(symb, "%s_adt_%d", outmod(buf, -1), taggen);
+		$$ = dotag(lookup(), TSTRUCT, autobn);
+		$$->link = $3;
+		$$->lineno = saveline;
+		lookup()->lineno = saveline;
+		suallign($$);
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, 0);
+		$$ = $2->suetag;
+		$2->lineno = lineno;
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, autobn);
+		saveline = $2->lineno = lineno;
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		$$->lineno = saveline;
+		suallign($$);
+	}
+|	LUNION
+	{
+		saveline = lineno;
+	}
+	sbody
+	{
+		char buf[128];
+
+		taggen++;
+		sprint(symb, "%s_adt_%d", outmod(buf, -1), taggen);
+		$$ = dotag(lookup(), TUNION, autobn);
+		$$->link = $3;
+		$$->lineno = saveline;
+		lookup()->lineno = saveline;
+		suallign($$);
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, 0);
+		$$ = $2->suetag;
+		if($$->link == T)
+			$$->link = types[TINT];
+		$$ = $$->link;
+		$2->lineno = lineno;
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, autobn);
+		$2->lineno = lineno;
+	}
+	'{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		if(en.tenum == T) {
+			diag(Z, "enum type ambiguous: %s", $2->name);
+			en.tenum = types[TINT];
+		}
+		$$->link = en.tenum;
+		$$ = en.tenum;
+		etgen(nil);
+	}
+|	LENUM '{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = en.tenum;
+		etgen(nil);
+	}
+|	LCTYPE
+	{
+		$$ = tcopy($1->type);
+	}
+|	LSTYPE
+	{
+		$$ = tcopy($1->type);
+	}
+
+tnlist:
+	tname
+|	tnlist tname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gctnlist:
+	gctname
+|	gctnlist gctname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+zgnlist:
+	{
+		$$ = 0;
+	}
+|	zgnlist gname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gctname:
+	tname
+|	gname
+|	cname
+
+enum:
+	LNAME
+	{
+		doenum($1, Z);
+		$1->lineno = lineno;
+	}
+|	LNAME '=' expr
+	{
+		doenum($1, $3);
+		$1->lineno = lineno;
+	}
+|	enum ','
+|	enum ',' enum
+
+tname:	/* type words */
+	LCHAR { $$ = BCHAR; }
+|	LSHORT { $$ = BSHORT; }
+|	LINT { $$ = BINT; }
+|	LLONG { $$ = BLONG; }
+|	LSIGNED { $$ = BSIGNED; }
+|	LUNSIGNED { $$ = BUNSIGNED; }
+|	LFLOAT { $$ = BFLOAT; }
+|	LDOUBLE { $$ = BDOUBLE; }
+|	LVOID { $$ = BVOID; }
+|	LVLONG { $$ = BVLONG|BLONG; }
+
+cname:	/* class words */
+	LAUTO { $$ = BAUTO; }
+|	LSTATIC { $$ = BSTATIC; }
+|	LEXTERN { $$ = BEXTERN; }
+|	LTYPEDEF { $$ = BTYPEDEF; }
+|	LREGISTER { $$ = BREGISTER; }
+
+gname:
+	LCONSTNT { $$ = BCONSTNT; }
+|	LVOLATILE { $$ = BVOLATILE; }
+
+name:
+	LNAME
+	{
+		$$ = new(ONAME, Z, Z);
+		if($1->class == CLOCAL)
+			$1 = mkstatic($1);
+		$$->sym = $1;
+		$$->type = $1->type;
+	}
+tag:
+	ltag
+	{
+		$$ = new(ONAME, Z, Z);
+		$$->sym = $1;
+		$$->type = $1->type;
+	}
+ltag:
+	LNAME
+|	LCTYPE
+|	LSTYPE
+
+%%
--- /dev/null
+++ b/utils/c2l/com.c
@@ -1,0 +1,920 @@
+#include "cc.h"
+
+void
+complex(Node *n)
+{
+
+	if(n == Z)
+		return;
+
+	nearln = n->lineno;
+	if(tcom(n))
+		return;
+	ccom(n);
+	acom(n);
+}
+
+/*
+ * evaluate types
+ * evaluate lvalues (addable == 1)
+ */
+enum
+{
+	ADDROF	= 1<<0,
+	CASTOF	= 1<<1,
+	ADDROP	= 1<<2,
+};
+
+int
+tcom(Node *n)
+{
+
+	return tcomo(n, ADDROF);
+}
+
+int
+tcomo(Node *n, int f)
+{
+	Node *l, *r;
+	Type *t;
+	int o;
+
+	if(n == Z) {
+		diag(Z, "Z in tcom");
+		errorexit();
+	}
+	l = n->left;
+	r = n->right;
+		
+	switch(n->op) {
+	default:
+		diag(n, "unknown op in type complex: %O", n->op);
+		goto bad;
+
+	case ODOTDOT:
+		/*
+		 * tcom has already been called on this subtree
+		 */
+		*n = *n->left;
+		if(n->type == T)
+			goto bad;
+		break;
+
+	case OCAST:
+		if(n->type == T)
+			break;
+		if(n->type->width == types[TLONG]->width) {
+			if(tcomo(l, ADDROF|CASTOF))
+					goto bad;
+		} else
+			if(tcom(l))
+				goto bad;
+		if(tcompat(n, l->type, n->type, tcast))
+			goto bad;
+		break;
+
+	case ORETURN:
+		if(l == Z) {
+			if(n->type->etype != TVOID)
+				warn(n, "null return of a typed function");
+			break;
+		}
+		if(tcom(l))
+			goto bad;
+		typeext(n->type, l);
+		if(tcompat(n, n->type, l->type, tasign))
+			break;
+		constas(n, n->type, l->type);
+		if(!sametype(n->type, l->type)) {
+			l = new1(OCAST, l, Z);
+			l->type = n->type;
+			n->left = l;
+		}
+		break;
+
+	case OASI:	/* same as as, but no test for const */
+		n->op = OAS;
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+
+		typeext(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OAS:
+	case OASD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+
+		typeext(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		constas(n, l->type, r->type);
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OASADD:
+	case OASSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		typeext1(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tasadd))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+	case OASDIV:
+	case OASLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		typeext1(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASDIV)
+				n->op = OASLDIV;
+			if(n->op == OASMUL)
+				n->op = OASLMUL;
+		}
+		break;
+
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->type = l->type;
+		if(typeu[n->type->etype]) {
+			if(n->op == OASASHR)
+				n->op = OASLSHR;
+		}
+		break;
+
+	case OASMOD:
+	case OASLMOD:
+	case OASOR:
+	case OASAND:
+	case OASXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tand))
+			goto bad;
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASMOD)
+				n->op = OASLMOD;
+		}
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+	case OPOSTINC:
+	case OPOSTDEC:
+		if(tcom(l))
+			goto bad;
+		if(tlvalue(l) || tcompat(n, l->type, types[TINT], tadd))
+			goto bad;
+		n->type = l->type;
+		if(n->type->etype == TIND)
+		if(n->type->link->width < 1)
+			diag(n, "inc/dec of a void pointer");
+		break;
+
+	case OEQ:
+	case ONE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		typeext(l->type, r);
+		typeext(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		n->type = types[TINT];
+		break;
+
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		typeext1(l->type, r);
+		typeext1(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		if(typeu[n->type->etype])
+			n->op = logrel[relindex(n->op)];
+		n->type = types[TINT];
+		break;
+
+	case OCOND:
+		o = tcom(l);
+		o |= tcom(r->left);
+		if(o | tcom(r->right))
+			goto bad;
+		if(r->right->type->etype == TIND && vconst(r->left) == 0) {
+			r->left->type = r->right->type;
+			r->left->vconst = 0;
+		}
+		if(r->left->type->etype == TIND && vconst(r->right) == 0) {
+			r->right->type = r->left->type;
+			r->right->vconst = 0;
+		}
+		if(sametype(r->right->type, r->left->type)) {
+			r->type = r->right->type;
+			n->type = r->type;
+			break;
+		}
+		if(tcompat(r, r->left->type, r->right->type, trel))
+			goto bad;
+		arith(r, 0);
+		n->type = r->type;
+		break;
+
+	case OADD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tadd))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tsub))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype]) {
+			if(n->op == ODIV)
+				n->op = OLDIV;
+			if(n->op == OMUL)
+				n->op = OLMUL;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->right = Z;
+		arith(n, 1);
+		n->right = new1(OCAST, r, Z);
+		n->right->type = types[TINT];
+		if(typeu[n->type->etype])
+			if(n->op == OASHR)
+				n->op = OLSHR;
+		break;
+
+	case OAND:
+	case OOR:
+	case OXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMOD:
+	case OLMOD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype])
+			n->op = OLMOD;
+		break;
+
+	case ONOT:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OPOS:
+	case ONEG:
+	case OCOM:
+		if(tcom(l))
+			goto bad; 
+		n->type = l->type;
+		break;
+
+	case ONUL:
+		break;
+
+	case OIOTA:
+		n->type = types[TINT];
+		break;
+
+	case ODAS:
+		n->type = n->left->type;
+		break;
+
+	case OANDAND:
+	case OOROR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, T, l->type, tnot) |
+		   tcompat(n, T, r->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OCOMMA:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		n->type = r->type;
+		break;
+
+
+	case OSIGN:	/* extension signof(type) returns a hash */
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "signof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width < 0) {
+			diag(n, "signof undefined type");
+			goto bad;
+		}
+		n->right = ncopy(n);
+		n->op = OCONST;
+		n->left = Z;
+		/* n->right = Z; */
+		n->vconst = convvtox(signature(n->type, 10), TULONG);
+		n->type = types[TULONG];
+		break;
+
+	case OSIZE:
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "sizeof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width <= 0) {
+			diag(n, "sizeof undefined type");
+			goto bad;
+		}
+		if(n->type->etype == TFUNC) {
+			diag(n, "sizeof function");
+			goto bad;
+		}
+		n->right = ncopy(n);
+		n->op = OCONST;
+		n->left = Z;
+		/* n->right = Z; */
+		n->vconst = convvtox(n->type->width, TINT);
+		n->type = types[TINT];
+		break;
+
+	case OFUNC:
+		o = tcomo(l, 0);
+		if(o)
+			goto bad;
+		if(l->type->etype == TIND && l->type->link->etype == TFUNC) {
+			l = new1(OIND, l, Z);
+			l->type = l->left->type->link;
+			n->left = l;
+		}
+		if(tcompat(n, T, l->type, tfunct))
+			goto bad;
+		if(o | tcoma(l, r, l->type->down, 1))
+			goto bad;
+		n->type = l->type->link;
+		if(1)
+			if(l->type->down == T || l->type->down->etype == TOLD) {
+				nerrors--;
+				diag(n, "function args not checked: %F", l);
+			}
+		dpcheck(n);
+		break;
+
+	case ONAME:
+		if(n->type == T) {
+			diag(n, "name not declared: %F", n);
+			goto bad;
+		}
+		if(n->type->etype == TENUM) {
+			if(n->sym->tenum->etype == TIND){
+				/* n->op = OSTRING; */
+				n->type = n->sym->tenum;
+				/* n->cstring = n->sym->sconst; */
+				break;
+			}
+			n->left = ncopy(n);
+			n->op = OCONST;
+			n->type = n->sym->tenum;
+			if(!typefd[n->type->etype])
+				n->vconst = n->sym->vconst;
+			else{
+				n->fconst = n->sym->fconst;
+				n->cstring = n->sym->cstring;
+			}
+			break;
+		}
+		break;
+
+	case OLSTRING:
+	case OSTRING:
+	case OCONST:
+		break;
+
+	case ODOT:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tdot))
+			goto bad;
+		if(tcomd(n, l->type))
+			goto bad;
+		break;
+
+	case ODOTIND:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tindir))
+			goto bad;
+		if(tcompat(n, T, l->type->link, tdot))
+			goto bad;
+		if(tcomd(n, l->type->link))
+			goto bad;
+		break;
+		
+	case OARRIND:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tindir))
+			goto bad;
+		n->type = l->type->link;
+		if(tcom(r))
+			goto bad;
+		break;
+
+	case OADDR:
+		if(tcomo(l, ADDROP))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(l->type->nbits) {
+			diag(n, "address of a bit field");
+			goto bad;
+		}
+		if(l->op == OREGISTER) {
+			diag(n, "address of a register");
+			goto bad;
+		}
+		n->type = typ1(TIND, l->type);
+		n->type->width = types[TIND]->width;
+		break;
+
+	case OIND:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tindir))
+			goto bad;
+		n->type = l->type->link;
+		break;
+
+	case OSTRUCT:
+		if(tcomx(n))
+			goto bad;
+		break;
+	}
+	t = n->type;
+	if(t == T)
+		goto bad;
+	if(t->width < 0) {
+		snap(t);
+		if(t->width < 0) {
+			if(typesu[t->etype] && t->tag)
+				diag(n, "structure not fully declared %s", t->tag->name);
+			else
+				diag(n, "structure not fully declared");
+			goto bad;
+		}
+	}
+	if(typeaf[t->etype]) {
+		if(f & ADDROF)
+			goto addaddr;
+		if(f & ADDROP)
+			warn(n, "address of array/func ignored");
+	}
+	return 0;
+
+addaddr:
+	if(n->type->etype == TARRAY)
+		n->type = typ1(TIND, n->type->link);
+	return 0;
+#ifdef WHATEVA
+	if(tlvalue(n))
+		goto bad;
+	l = new1(OXXX, Z, Z);
+	*l = *n;
+	n->op = OADDR;
+	if(l->type->etype == TARRAY)
+		l->type = l->type->link;
+	n->left = l;
+	n->right = Z;
+	n->type = typ1(TIND, l->type);
+	n->type->width = types[TIND]->width;
+	return 0;
+#endif
+
+bad:
+	n->type = T;
+	return 1;
+}
+
+int
+tcoma(Node *l, Node *n, Type *t, int f)
+{
+	Node *n1;
+	int o;
+
+	if(t != T)
+	if(t->etype == TOLD || t->etype == TDOT)	/* .../old in prototype */
+		t = T;
+	if(n == Z) {
+		if(t != T && !sametype(t, types[TVOID])) {
+			diag(n, "not enough function arguments: %F", l);
+			return 1;
+		}
+		return 0;
+	}
+	if(n->op == OLIST) {
+		o = tcoma(l, n->left, t, 0);
+		if(t != T) {
+			t = t->down;
+			if(t == T)
+				t = types[TVOID];
+		}
+		return o | tcoma(l, n->right, t, 1);
+	}
+	if(f && t != T)
+		tcoma(l, Z, t->down, 0);
+	if(tcom(n) || tcompat(n, T, n->type, targ))
+		return 1;
+	if(sametype(t, types[TVOID])) {
+		diag(n, "too many function arguments: %F", l);
+		return 1;
+	}
+	if(t != T) {
+		typeext(t, n);
+		if(stcompat(nodproto, t, n->type, tasign)) {
+			diag(l, "argument prototype mismatch \"%T\" for \"%T\": %F",
+				n->type, t, l);
+			return 1;
+		}
+		switch(t->etype) {
+		case TCHAR:
+		case TSHORT:
+			/* t = types[TINT]; */
+			break;
+
+		case TUCHAR:
+		case TUSHORT:
+			/* t = types[TUINT]; */
+			break;
+		}
+	} else {
+		switch(n->type->etype)
+		{
+		case TCHAR:
+		case TSHORT:
+			/* t = types[TINT]; */
+			t = n->type;
+			break;
+
+		case TUCHAR:
+		case TUSHORT:
+			/* t = types[TUINT]; */
+			t = n->type;
+			break;
+
+		case TFLOAT:
+			/* t = types[TDOUBLE]; */
+			t = n->type;
+		}
+	}
+	if(t != T && !sametype(t, n->type)) {
+		n1 = new1(OXXX, Z, Z);
+		*n1 = *n;
+		n->op = OCAST;
+		n->left = n1;
+		n->right = Z;
+		n->type = t;
+	}
+	return 0;
+}
+
+int
+tcomd(Node *n, Type *t)
+{
+	long o;
+
+	o = 0;
+	/* t = n->left->type; */
+	for(;;) {
+		t = dotsearch(n->sym, t->link, n);
+		if(t == T) {
+			diag(n, "not a member of struct/union: %F", n);
+			return 1;
+		}
+		o += t->offset;
+		if(t->sym == n->sym)
+			break;
+		if(sametype(t, n->sym->type))
+			break;
+	}
+	n->type = t;
+	return 0;
+}
+
+int
+tcomx(Node *n)
+{
+	Type *t;
+	Node *l, *r, **ar, **al;
+	int e;
+
+	e = 0;
+	if(n->type->etype != TSTRUCT) {
+		diag(n, "constructor must be a structure");
+		return 1;
+	}
+	l = invert(n->left);
+	n->left = l;
+	al = &n->left;
+	for(t = n->type->link; t != T; t = t->down) {
+		if(l == Z) {
+			diag(n, "constructor list too short");
+			return 1;
+		}
+		if(l->op == OLIST) {
+			r = l->left;
+			ar = &l->left;
+			al = &l->right;
+			l = l->right;
+		} else {
+			r = l;
+			ar = al;
+			l = Z;
+		}
+		if(tcom(r))
+			e++;
+		typeext(t, r);
+		if(tcompat(n, t, r->type, tasign))
+			e++;
+		constas(n, t, r->type);
+		if(!e && !sametype(t, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = t;
+			*ar = r;
+		}
+	}
+	if(l != Z) {
+		diag(n, "constructor list too long");
+		return 1;
+	}
+	return e;
+}
+
+int
+tlvalue(Node *n)
+{
+
+	if(0) {
+		diag(n, "not an l-value");
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ *	general rewrite
+ *	(IND(ADDR x)) ==> x
+ *	(ADDR(IND x)) ==> x
+ *	remove some zero operands
+ *	remove no op casts
+ *	evaluate constants
+ */
+void
+ccom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	case OAS:
+	case OASD:
+	case OASXOR:
+	case OASAND:
+	case OASOR:
+	case OASMOD:
+	case OASLMOD:
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+	case OASDIV:
+	case OASLDIV:
+	case OASMUL:
+	case OASLMUL:
+	case OASSUB:
+	case OASADD:
+		ccom(l);
+		ccom(r);
+		if(n->op == OASLSHR || n->op == OASASHR || n->op == OASASHL)
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst < 0)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		break;
+
+	case OCAST:
+		ccom(l);
+		if(l->op == OCONST) {
+			evconst(n);
+			if(n->op == OCONST)
+				break;
+		}
+		if(nocast(l->type, n->type)) {
+			l->type = n->type;
+			*n = *l;
+		}
+		break;
+
+	case OCOND:
+		ccom(l);
+		ccom(r);
+		break;
+
+	case OREGISTER:
+	case OINDREG:
+	case OCONST:
+	case ONAME:
+		break;
+
+	case OADDR:
+		ccom(l);
+		/* l->etype = TVOID; */
+		if(l->op == OIND) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OIND:
+		ccom(l);
+		if(l->op == OADDR) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OEQ:
+	case ONE:
+
+	case OLE:
+	case OGE:
+	case OLT:
+	case OGT:
+
+	case OLS:
+	case OHS:
+	case OLO:
+	case OHI:
+		ccom(l);
+		ccom(r);
+		relcon(l, r);
+		relcon(r, l);
+		goto common;
+
+	case OASHR:
+	case OASHL:
+	case OLSHR:
+		ccom(l);
+		ccom(r);
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst <= -t)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		goto common;
+
+	default:
+		if(l != Z)
+			ccom(l);
+		if(r != Z)
+			ccom(r);
+	common:
+		if(l != Z)
+		if(l->op != OCONST)
+			break;
+		if(r != Z)
+		if(r->op != OCONST)
+			break;
+		evconst(n);
+	}
+}
--- /dev/null
+++ b/utils/c2l/com64.c
@@ -1,0 +1,52 @@
+#include "cc.h"
+
+/*
+ * this is machine depend, but it is totally
+ * common on all of the 64-bit symulating machines.
+ */
+
+/*
+ * more machine depend stuff.
+ * this is common for 8,16,32,64 bit machines.
+ * this is common for ieee machines.
+ */
+double
+convvtof(vlong v)
+{
+	double d;
+
+	d = v;		/* BOTCH */
+	return d;
+}
+
+vlong
+convftov(double d)
+{
+	vlong v;
+
+
+	v = d;		/* BOTCH */
+	return v;
+}
+
+double
+convftox(double d, int et)
+{
+
+	if(!typefd[et])
+		diag(Z, "bad type in castftox %s", tnames[et]);
+	return d;
+}
+
+vlong
+convvtox(vlong c, int et)
+{
+	int n;
+
+	n = 8 * ewidth[et];
+	c &= MASK(n);
+	if(!typeu[et])
+		if(c & SIGN(n))
+			c |= ~MASK(n);
+	return c;
+}
--- /dev/null
+++ b/utils/c2l/dcl.c
@@ -1,0 +1,1387 @@
+#include "cc.h"
+
+Node*
+dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n, int gen)
+{
+	Sym *s;
+	Node *n1;
+	long v;
+
+	nearln = lineno;
+	lastfield = 0;
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	default:
+		diag(n, "unknown declarator: %O", n->op);
+		break;
+
+	case OARRAY:
+		t = typ(TARRAY, t);
+		t->width = 0;
+		n1 = n->right;
+		n = n->left;
+		if(n1 != Z) {
+			complex(n1);
+			v = -1;
+			if(n1->op == OCONST)
+				v = n1->vconst;
+			if(v <= 0) {
+				diag(n, "array size must be a positive constant");
+				v = 1;
+			}
+			t->width = v * t->link->width;
+			t->nwidth = n1->left;
+		}
+		goto loop;
+
+	case OIND:
+		t = typ(TIND, t);
+		t->garb = n->garb;
+		n = n->left;
+		goto loop;
+
+	case OFUNC:
+		t = typ(TFUNC, t);
+		t->down = fnproto(n);
+		n = n->left;
+		goto loop;
+
+	case OBIT:
+		n1 = n->right;
+		complex(n1);
+		lastfield = -1;
+		if(n1->op == OCONST)
+			lastfield = n1->vconst;
+		if(lastfield < 0) {
+			diag(n, "field width must be non-negative constant");
+			lastfield = 1;
+		}
+		if(lastfield == 0) {
+			lastbit = 0;
+			firstbit = 1;
+			if(n->left != Z) {
+				diag(n, "zero width named field");
+				lastfield = 1;
+			}
+		}
+		if(!typei[t->etype]) {
+			diag(n, "field type must be int-like");
+			t = types[TINT];
+			lastfield = 1;
+		}
+		if(lastfield > tfield->width*8) {
+			diag(n, "field width larger than field unit");
+			lastfield = 1;
+		}
+		lastbit += lastfield;
+		if(lastbit > tfield->width*8) {
+			lastbit = lastfield;
+			firstbit = 1;
+		}
+		n = n->left;
+		goto loop;
+
+	case ONAME:
+		if(f == NODECL)
+			break;
+		s = n->sym;
+		(*f)(c, t, s);
+		if(s->class == CLOCAL)
+			s = mkstatic(s);
+		firstbit = 0;
+		n->sym = s;
+		n->type = s->type;
+		acidvar(s);
+		if(gen)
+			vtgen(n);
+		break;
+	}
+	lastdcl = t;
+	return n;
+}
+
+Sym*
+mkstatic(Sym *s)
+{
+	Sym *s1;
+
+	if(s->class != CLOCAL)
+		return s;
+	snprint(symb, NSYMB, "%s$%d", s->name, s->block);
+	s1 = lookup();
+	if(s1->class != CSTATIC) {
+		s1->type = s->type;
+		s1->offset = s->offset;
+		s1->block = s->block;
+		s1->class = CSTATIC;
+	}
+	return s1;
+}
+
+/*
+ * make a copy of a typedef
+ * the problem is to split out incomplete
+ * arrays so that it is in the variable
+ * rather than the typedef.
+ */
+Type*
+tcopy(Type *t)
+{
+	Type *tl, *tx;
+	int et;
+
+	if(t == T)
+		return t;
+	et = t->etype;
+	if(typesu[et])
+		return t;
+	tl = tcopy(t->link);
+	if(tl != t->link ||
+	  (et == TARRAY && t->width == 0)) {
+		tx = typ(TXXX, 0);
+		*tx = *t;
+		tx->link = tl;
+		return tx;
+	}
+	return t;
+}
+
+Node*
+doinit(Sym *s, Type *t, long o, Node *a)
+{
+	Node *n, *reta;
+
+	if(t == T)
+		return Z;
+	if(s->class == CEXTERN)
+		s->class = CGLOBL;
+	if(0) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "doinit value");
+	}
+
+	n = initlist;
+	if(a->op == OINIT)
+		a = a->left;
+	initlist = a;
+
+	reta = a;
+	init1(s, t, o, 0);
+	if(initlist != Z)
+		diag(initlist, "more initializers than structure: %s",
+			s->name);
+	initlist = n;
+
+	return reta;
+}
+
+/*
+ * get next major operator,
+ * dont advance initlist.
+ */
+Node*
+peekinit(void)
+{
+	Node *a;
+
+	a = initlist;
+
+loop:
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		a = a->left;
+		goto loop;
+	}
+	return a;
+}
+
+/*
+ * consume and return next element on
+ * initlist. expand strings.
+ */
+Node*
+nextinit(void)
+{
+	Node *a, *n;
+
+	a = initlist;
+	n = Z;
+
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		n = a->right;
+		a = a->left;
+	}
+	initlist = n;
+	return a;
+}
+
+int
+isstruct(Node *a, Type *t)
+{
+	Node *n;
+
+	switch(a->op) {
+	case ODOTDOT:
+		n = a->left;
+		if(n && n->type && sametype(n->type, t))
+			return 1;
+	case OSTRING:
+	case OLSTRING:
+	case OCONST:
+	case OINIT:
+	case OELEM:
+		return 0;
+	}
+
+	n = new(ODOTDOT, Z, Z);
+	*n = *a;
+
+	/*
+	 * ODOTDOT is a flag for tcom
+	 * a second tcom will not be performed
+	 */
+	a->op = ODOTDOT;
+	a->left = n;
+	a->right = Z;
+
+	if(tcom(n))
+		return 0;
+
+	if(sametype(n->type, t))
+		return 1;
+	return 0;
+}
+
+void
+init1(Sym *s, Type *t, long o, int exflag)
+{
+	Node *a, *r, nod;
+	Type *t1;
+	long e, w, so, mw;
+
+	a = peekinit();
+	if(a == Z)
+		return;
+
+	if(0) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "init1 value");
+	}
+
+	if(exflag && a->op == OINIT){
+		doinit(s, t, o, nextinit());
+		return;
+	}
+
+	switch(t->etype) {
+	default:
+		diag(Z, "unknown type in initialization: %T to: %s", t, s->name);
+		return;
+
+	case TCHAR:
+	case TUCHAR:
+	case TINT:
+	case TUINT:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+	case TIND:
+	single:
+		if(a->op == OARRAY || a->op == OELEM)
+			return;
+
+		a = nextinit();
+		if(a == Z)
+			return;
+
+		if(t->nbits)
+			diag(Z, "cannot initialize bitfields");
+		if(0 && s->class == CAUTO)
+			return;
+
+		complex(a);
+		if(a->type == T)
+			return;
+
+		if(a->op == OCONST) {
+			if(!sametype(a->type, t)) {
+				/* hoop jumping to save malloc */
+				if(nodcast == Z)
+					nodcast = new(OCAST, Z, Z);
+				nod = *nodcast;
+				nod.left = a;
+				nod.type = t;
+				nod.lineno = a->lineno;
+				complex(&nod);
+				if(nod.type)
+					*a = nod;
+			}
+			if(a->op != OCONST) {
+/*
+				diag(a, "initializer is not a constant: %s",
+					s->name);
+*/
+				return;
+			}
+			if(vconst(a) == 0)
+				return;
+			return;
+		}
+		if(t->etype == TIND) {
+			while(a->op == OCAST) {
+				warn(a, "CAST in initialization ignored");
+				a = a->left;
+			}
+			if(0 && !sametype(t, a->type)) {
+				diag(a, "initialization of incompatible pointers: %s",
+					s->name);
+				print("%T and %T\n", t, a->type);
+			}
+/*
+			if(a->op == OADDR)
+				a = a->left;
+*/
+			return;
+		}
+
+		while(a->op == OCAST)
+			a = a->left;
+		if(a->op == OADDR) {
+			warn(a, "initialize pointer to an integer: %s", s->name);
+			/* a = a->left; */
+			return;
+		}
+		/* diag(a, "initializer is not a constant: %s", s->name); */
+		return;
+
+	case TARRAY:
+		w = t->link->width;
+		if(a->op == OSTRING || a->op == OLSTRING)
+		if(typei[t->link->etype]) {
+
+			/*
+			 * get rid of null if sizes match exactly
+			 */
+			a = nextinit();
+			/* mw = t->width/w; */
+			so = a->type->width/a->type->link->width;
+			if(t->width <= 0)
+				t->width = w*(so-1);
+			USED(a);
+			return;
+		}
+
+		mw = -w;
+		for(e=0;;) {
+			/*
+			 * peek ahead for element initializer
+			 */
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM && t->link->etype != TSTRUCT)
+				break;
+			if(a->op == OARRAY) {
+				if(e && exflag)
+					break;
+				a = nextinit();
+				r = a->left;
+				complex(r);
+				if(r->op != OCONST) {
+					diag(r, "initializer subscript must be constant");
+					return;
+				}
+				e = r->vconst;
+				if(t->width != 0)
+					if(e < 0 || e*w >= t->width) {
+						diag(a, "initialization index out of range: %ld", e);
+						continue;
+					}
+			}
+
+			so = e*w;
+			if(so > mw)
+				mw = so;
+			if(t->width != 0)
+				if(mw >= t->width)
+					break;
+			init1(s, t->link, o+so, 1);
+			e++;
+		}
+		if(t->width == 0)
+			t->width = mw+w;
+		return;
+
+	case TUNION:
+	case TSTRUCT:
+		/*
+		 * peek ahead to find type of rhs.
+		 * if its a structure, then treat
+		 * this element as a variable
+		 * rather than an aggregate.
+		 */
+		if(isstruct(a, t))
+			goto single;
+
+		if(t->width <= 0) {
+			diag(Z, "incomplete structure: %s", s->name);
+			return;
+		}
+
+	again:
+		for(t1 = t->link; t1 != T; t1 = t1->down) {
+			if(a->op == OARRAY && t1->etype != TARRAY)
+				break;
+			if(a->op == OELEM) {
+				if(t1->sym != a->sym)
+					continue;
+				nextinit();
+			}
+			init1(s, t1, o+t1->offset, 1);
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM)
+				goto again;
+		}
+		if(a && a->op == OELEM)
+			diag(a, "structure element not found %F", a);
+		return;
+	}
+}
+
+/*
+Node*
+newlist(Node *l, Node *r)
+{
+	if(r == Z)
+		return l;
+	if(l == Z)
+		return r;
+	return new(OLIST, l, r);
+}
+*/
+
+void
+suallign(Type *t)
+{
+	Type *l;
+	long o, w;
+
+	o = 0;
+	switch(t->etype) {
+
+	case TSTRUCT:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->nbits) {
+				if(l->shift <= 0) {
+					l->shift = -l->shift;
+					w = round(w, tfield->width);
+					o = w;
+					w += tfield->width;
+				}
+				l->offset = o;
+			} else {
+				if(l->width <= 0)
+					if(l->sym)
+						diag(Z, "incomplete structure element: %s",
+							l->sym->name);
+					else
+						diag(Z, "incomplete structure element");
+				w = align(w, l, Ael1);
+				l->offset = w;
+				w = align(w, l, Ael2);
+			}
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		ttgen(t);
+		return;
+
+	case TUNION:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->width <= 0)
+				if(l->sym)
+					diag(Z, "incomplete union element: %s",
+						l->sym->name);
+				else
+					diag(Z, "incomplete union element");
+			l->offset = 0;
+			l->shift = 0;
+			o = align(align(0, l, Ael1), l, Ael2);
+			if(o > w)
+				w = o;
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		ttgen(t);
+		return;
+
+	default:
+		diag(Z, "unknown type in suallign: %T", t);
+		break;
+	}
+}
+
+long
+round(long v, int w)
+{
+	int r;
+
+	if(w <= 0 || w > 8) {
+		diag(Z, "rounding by %d", w);
+		w = 1;
+	}
+	r = v%w;
+	if(r)
+		v += w-r;
+	return v;
+}
+
+Type*
+ofnproto(Node *n)
+{
+	Type *tl, *tr, *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		tl = ofnproto(n->left);
+		tr = ofnproto(n->right);
+		if(tl == T)
+			return tr;
+		tl->down = tr;
+		return tl;
+
+	case ONAME:
+		if(n->type == T)
+			n->type = n->sym->type;
+		t = typ(TXXX, T);
+		*t = *n->sym->type;
+		t->down = T;
+		return t;
+	}
+	return T;
+}
+
+#define	ANSIPROTO	1
+#define	OLDPROTO	2
+
+void
+argmark(Node *n, int pass)
+{
+	Type *t;
+
+	autoffset = align(0, thisfn->link, Aarg0);
+	for(; n->left != Z; n = n->left) {
+		if(n->op != OFUNC || n->left->op != ONAME)
+			continue;
+		walkparam(n->right, pass);
+		if(pass != 0 && anyproto(n->right) == OLDPROTO) {
+			t = typ(TFUNC, n->left->sym->type->link);
+			t->down = typ(TOLD, T);
+			t->down->down = ofnproto(n->right);
+			tmerge(t, n->left->sym);
+			n->left->sym->type = t;
+		}
+		break;
+	}
+	autoffset = 0;
+}
+
+void
+walkparam(Node *n, int pass)
+{
+	Sym *s;
+	Node *n1;
+
+	if(n != Z && n->op == OPROTO && n->left == Z && n->type == types[TVOID])
+		return;
+
+loop:
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+		diag(n, "argument not a name/prototype: %O", n->op);
+		break;
+
+	case OLIST:
+		walkparam(n->left, pass);
+		n = n->right;
+		goto loop;
+
+	case OPROTO:
+		for(n1 = n; n1 != Z; n1=n1->left)
+			if(n1->op == ONAME) {
+				if(pass == 0) {
+					s = n1->sym;
+					push1(s);
+					s->offset = -1;
+					break;
+				}
+				dodecl(pdecl, CPARAM, n->type, n->left, 1);
+				break;
+			}
+		if(n1)
+			break;
+		if(pass == 0) {
+			/*
+			 * extension:
+			 *	allow no name in argument declaration
+			diag(Z, "no name in argument declaration");
+			 */
+			break;
+		}
+		dodecl(NODECL, CPARAM, n->type, n->left, 1);
+		pdecl(CPARAM, lastdcl, S);
+		break;
+
+	case ODOTDOT:
+		break;
+	
+	case ONAME:
+		s = n->sym;
+		if(pass == 0) {
+			push1(s);
+			s->offset = -1;
+			break;
+		}
+		if(s->offset != -1) {
+			autoffset = align(autoffset, s->type, Aarg1);
+			s->offset = autoffset;
+			autoffset = align(autoffset, s->type, Aarg2);
+		} else
+			dodecl(pdecl, CXXX, types[TINT], n, 1);
+		break;
+	}
+}
+
+void
+markdcl(void)
+{
+	Decl *d;
+
+	blockno++;
+	d = push();
+	d->val = DMARK;
+	d->offset = autoffset;
+	d->block = autobn;
+	autobn = blockno;
+}
+
+Node*
+revertdcl(void)
+{
+	Decl *d;
+	Sym *s;
+
+	for(;;) {
+		d = dclstack;
+		if(d == D) {
+			diag(Z, "pop off dcl stack");
+			break;
+		}
+		dclstack = d->link;
+		s = d->sym;
+		switch(d->val) {
+		case DMARK:
+			autoffset = d->offset;
+			autobn = d->block;
+			free(d);
+			return Z;
+
+		case DAUTO:
+			if(0) {
+				if(s->class == CAUTO)
+					warn(Z, "auto declared and not used: %s", s->name);
+				if(s->class == CPARAM)
+					warn(Z, "param declared and not used: %s", s->name);
+			}
+			s->type = d->type;
+			s->class = d->class;
+			s->offset = d->offset;
+			s->block = d->block;
+			s->lineno = d->lineno;
+			break;
+
+		case DSUE:
+			s->suetag = d->type;
+			s->sueblock = d->block;
+			s->lineno = d->lineno;
+			break;
+
+		case DLABEL:
+			if(0 && s->label)
+				warn(s->label, "label declared and not used \"%s\"", s->name);
+			s->label = Z;
+			s->lineno = d->lineno;
+			break;
+		}
+		free(d);
+	}
+	return Z;
+}
+
+Type*
+fnproto(Node *n)
+{
+	int r;
+
+	r = anyproto(n->right);
+	if(r == 0 || (r & OLDPROTO)) {
+		if(r & ANSIPROTO)
+			diag(n, "mixed ansi/old function declaration: %F", n->left);
+		return T;
+	}
+	return fnproto1(n->right);
+}
+
+int
+anyproto(Node *n)
+{
+	int r;
+
+	r = 0;
+
+loop:
+	if(n == Z)
+		return r;
+	switch(n->op) {
+	case OLIST:
+		r |= anyproto(n->left);
+		n = n->right;
+		goto loop;
+
+	case ODOTDOT:
+	case OPROTO:
+		return r | ANSIPROTO;
+	}
+	return r | OLDPROTO;
+}
+
+Type*
+fnproto1(Node *n)
+{
+	Type *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		t = fnproto1(n->left);
+		if(t != T)
+			t->down = fnproto1(n->right);
+		return t;
+
+	case OPROTO:
+		lastdcl = T;
+		n = dodecl(NODECL, CXXX, n->type, n->left, 1);
+		t = typ(TXXX, T);
+		if(lastdcl != T)
+			*t = *paramconv(lastdcl, 1);
+		if(n != Z && n->op == ONAME)
+			t->sym = n->sym;
+		return t;
+
+	case ONAME:
+		diag(n, "incomplete argument prototype");
+		return typ(TINT, T);
+
+	case ODOTDOT:
+		return typ(TDOT, T);
+	}
+	diag(n, "unknown op in fnproto");
+	return T;
+}
+
+void
+dbgdecl(Sym *s)
+{
+
+	print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n",
+		s->name, cnames[s->class], s->block, s->offset, s->type);
+}
+
+Decl*
+push(void)
+{
+	static Decl zdecl;
+	Decl *d;
+
+	d = alloc(sizeof(*d));
+	*d = zdecl;
+	d->link = dclstack;
+	dclstack = d;
+	return d;
+}
+
+Decl*
+push1(Sym *s)
+{
+	Decl *d;
+
+	d = push();
+	d->sym = s;
+	d->val = DAUTO;
+	d->type = s->type;
+	d->class = s->class;
+	d->offset = s->offset;
+	d->block = s->block;
+	d->lineno = s->lineno;
+	return d;
+}
+
+int
+sametype(Type *t1, Type *t2)
+{
+
+	if(t1 == t2)
+		return 1;
+	return rsametype(t1, t2, 5, 1);
+}
+
+int
+rsametype(Type *t1, Type *t2, int n, int f)
+{
+	int et;
+
+	n--;
+	for(;;) {
+		if(t1 == t2)
+			return 1;
+		if(t1 == T || t2 == T)
+			return 0;
+		if(n <= 0)
+			return 1;
+		et = t1->etype;
+		if(et != t2->etype)
+			return 0;
+		if(et == TFUNC) {
+			if(!rsametype(t1->link, t2->link, n, 0))
+				return 0;
+			t1 = t1->down;
+			t2 = t2->down;
+			while(t1 != T && t2 != T) {
+				if(t1->etype == TOLD) {
+					t1 = t1->down;
+					continue;
+				}
+				if(t2->etype == TOLD) {
+					t2 = t2->down;
+					continue;
+				}
+				while(t1 != T || t2 != T) {
+					if(!rsametype(t1, t2, n, 0))
+						return 0;
+					t1 = t1->down;
+					t2 = t2->down;
+				}
+				break;
+			}
+			return 1;
+		}
+		if(et == TARRAY)
+			if(t1->width != t2->width && t1->width != 0 && t2->width != 0)
+				return 0;
+		if(typesu[et] || et == TTUPLE) {
+			if(t1->link == T)
+				snap(t1);
+			if(t2->link == T)
+				snap(t2);
+			t1 = t1->link;
+			t2 = t2->link;
+			for(;;) {
+				if(t1 == t2)
+					return 1;
+				if(!rsametype(t1, t2, n, 0))
+					return 0;
+				t1 = t1->down;
+				t2 = t2->down;
+			}
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+		if((f || 1) && et == TIND) {
+			if(t1 != T && t1->etype == TVOID)
+				return 1;
+			if(t2 != T && t2->etype == TVOID)
+				return 1;
+		}
+	}
+	/* not reached */
+}
+
+ulong
+signature(Type *t, int n)
+{
+	Type *t1;
+	long s;
+
+	s = 0;
+	if(n > 0)
+	for(; t; t=t->link) {
+		s = s*thash1 + thash[t->etype];
+		switch(t->etype) {
+		default:
+			return s;
+		case TARRAY:
+			s = s*thash2 + t->width;
+			break;
+		case TFUNC:
+		case TSTRUCT:
+		case TUNION:
+			for(t1=t; t1; t1=t1->down)
+				s = s*thash3 + signature(t1, n-1);
+		case TIND:
+			break;
+		}
+	}
+	return s;
+}
+
+void
+snap(Type *t)
+{
+	if(typesu[t->etype])
+	if(t->link == T && t->tag && t->tag->suetag) {
+		t->link = t->tag->suetag->link;
+		t->width = t->tag->suetag->width;
+	}
+}
+
+Type*
+dotag(Sym *s, int et, int bn)
+{
+	Decl *d;
+
+	if(bn != 0 && bn != s->sueblock) {
+		d = push();
+		d->sym = s;
+		d->val = DSUE;
+		d->type = s->suetag;
+		d->block = s->sueblock;
+		d->lineno = s->lineno;
+		s->suetag = T;
+	}
+	if(s->suetag == T) {
+		s->suetag = typ(et, T);
+		s->sueblock = autobn;
+	}
+	if(s->suetag->etype != et)
+		diag(Z, "tag used for more than one type: %s",
+			s->name);
+	if(s->suetag->tag == S)
+		s->suetag->tag = s;
+	return s->suetag;
+}
+
+Node*
+dcllabel(Sym *s, int f)
+{
+	Decl *d, d1;
+	Node *n;
+
+	n = s->label;
+	if(n != Z) {
+		if(f) {
+			if(0)
+				diag(Z, "label reused: %s", s->name);
+		}
+		return n;
+	}
+
+	d = push();
+	d->sym = s;
+	d->val = DLABEL;
+	d->lineno = s->lineno;
+	dclstack = d->link;
+
+	d1 = *firstdcl;
+	*firstdcl = *d;
+	*d = d1;
+
+	firstdcl->link = d;
+	firstdcl = d;
+
+	n = new(OXXX, Z, Z);
+	n->sym = s;
+	s->label = n;
+	return n;
+}
+
+Type*
+paramconv(Type *t, int f)
+{
+	f = 1;
+	switch(t->etype) {
+	case TUNION:
+	case TSTRUCT:
+		if(t->width <= 0)
+			diag(Z, "incomplete structure: %s", t->tag->name);
+		break;
+
+	case TARRAY:
+		t = typ(TIND, t->link);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFUNC:
+		t = typ(TIND, t);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFLOAT:
+		if(!f)
+			t = types[TDOUBLE];
+		break;
+
+	case TCHAR:
+	case TSHORT:
+		if(!f)
+			t = types[TINT];
+		break;
+
+	case TUCHAR:
+	case TUSHORT:
+		if(!f)
+			t = types[TUINT];
+		break;
+	}
+	return t;
+}
+
+void
+adecl(int c, Type *t, Sym *s)
+{
+
+	if(c == CSTATIC)
+		c = CLOCAL;
+	if(t->etype == TFUNC) {
+		if(c == CXXX)
+			c = CEXTERN;
+		if(c == CLOCAL)
+			c = CSTATIC;
+		if(c == CAUTO || c == CEXREG)
+			diag(Z, "function cannot be %s %s", cnames[c], s->name);
+	}
+	if(c == CXXX)
+		c = CAUTO;
+	if(s) {
+		if(s->class == CSTATIC)
+			if(c == CEXTERN || c == CGLOBL) {
+				warn(Z, "just say static: %s", s->name);
+				c = CSTATIC;
+			}
+		if(s->class == CAUTO || s->class == CPARAM || s->class == CLOCAL)
+		if(s->block == autobn)
+			diag(Z, "auto redeclaration of: %s", s->name);
+		if(c != CPARAM)
+			push1(s);
+		s->block = autobn;
+		s->offset = 0;
+		s->type = t;
+		s->class = c;
+	}
+	switch(c) {
+	case CAUTO:
+		autoffset = align(autoffset, t, Aaut3);
+		s->offset = -autoffset;
+		break;
+
+	case CPARAM:
+		autoffset = align(autoffset, t, Aarg1);
+		if(s)
+			s->offset = autoffset;
+		autoffset = align(autoffset, t, Aarg2);
+		break;
+	}
+	if(s)
+		s->lineno = lineno;
+}
+
+void
+pdecl(int c, Type *t, Sym *s)
+{
+	if(s && s->offset != -1) {
+		diag(Z, "not a parameter: %s", s->name);
+		return;
+	}
+	t = paramconv(t, c==CPARAM);
+	if(c == CXXX)
+		c = CPARAM;
+	if(c != CPARAM) {
+		diag(Z, "parameter cannot have class: %s", s->name);
+		c = CPARAM;
+	}
+	adecl(c, t, s);
+	if(s)
+		s->lineno = lineno;
+}
+
+void
+xdecl(int c, Type *t, Sym *s)
+{
+	long o;
+
+	o = 0;
+	if(c == CEXREG)
+		c = CEXTERN;
+	if(c == CXXX) {
+		c = CGLOBL;
+		if(s->class == CEXTERN)
+			s->class = c;
+	}
+	if(c == CEXTERN)
+		if(s->class == CGLOBL)
+			c = CGLOBL;
+	if(c == CAUTO) {
+		diag(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+		c = CEXTERN;
+	}
+	if(s->class == CSTATIC)
+		if(c == CEXTERN || c == CGLOBL) {
+			warn(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+			c = CSTATIC;
+		}
+	if(s->type != T)
+		if(s->class != c || !sametype(t, s->type) || t->etype == TENUM) {
+			diag(Z, "external redeclaration of: %s", s->name);
+			print("	%s %T; %s %T\n", cnames[c], t, cnames[s->class], s->type);
+		}
+	tmerge(t, s);
+	s->type = t;
+	s->class = c;
+	s->block = 0;
+	s->offset = o;
+}
+
+void
+tmerge(Type *t1, Sym *s)
+{
+	Type *ta, *tb, *t2;
+
+	t2 = s->type;
+/*print("merge	%T; %T\n", t1, t2);/**/
+	for(;;) {
+		if(t1 == T || t2 == T || t1 == t2)
+			break;
+		if(t1->etype != t2->etype)
+			break;
+		switch(t1->etype) {
+		case TFUNC:
+			ta = t1->down;
+			tb = t2->down;
+			if(ta == T) {
+				t1->down = tb;
+				break;
+			}
+			if(tb == T)
+				break;
+			while(ta != T && tb != T) {
+				if(ta == tb)
+					break;
+				/* ignore old-style flag */
+				if(ta->etype == TOLD) {
+					ta = ta->down;
+					continue;
+				}
+				if(tb->etype == TOLD) {
+					tb = tb->down;
+					continue;
+				}
+				/* checking terminated by ... */
+				if(ta->etype == TDOT && tb->etype == TDOT) {
+					ta = T;
+					tb = T;
+					break;
+				}
+				if(!sametype(ta, tb))
+					break;
+				ta = ta->down;
+				tb = tb->down;
+			}
+			if(ta != tb)
+				diag(Z, "function inconsistently declared: %s", s->name);
+
+			/* take new-style over old-style */
+			ta = t1->down;
+			tb = t2->down;
+			if(ta != T && ta->etype == TOLD)
+				if(tb != T && tb->etype != TOLD)
+					t1->down = tb;
+			break;
+
+		case TARRAY:
+			/* should we check array size change? */
+			if(t2->width > t1->width)
+				t1->width = t2->width;
+			break;
+
+		case TUNION:
+		case TSTRUCT:
+			return;
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+	}
+}
+
+void
+edecl(int c, Type *t, Sym *s)
+{
+	long l;
+	Type *t1;
+
+	if(s == S) {
+		if(!typesu[t->etype])
+			diag(Z, "unnamed structure element must be struct/union");
+		if(c != CXXX)
+			diag(Z, "unnamed structure element cannot have class");
+	} else
+		if(c != CXXX)
+			diag(Z, "structure element cannot have class: %s", s->name);
+	t1 = t;
+	t = typ(TXXX, T);
+	l = t->lineno;
+	*t = *t1;
+	t->lineno = l;
+	t->sym = s;
+	t->down = T;
+	if(lastfield) {
+		t->shift = lastbit - lastfield;
+		t->nbits = lastfield;
+		if(firstbit)
+			t->shift = -t->shift;
+		if(typeu[t->etype])
+			t->etype = tufield->etype;
+		else
+			t->etype = tfield->etype;
+	}
+	if(strf == T)
+		strf = t;
+	else
+		strl->down = t;
+	strl = t;
+}
+
+/*
+ * this routine is very suspect.
+ * ansi requires the enum type to
+ * be represented as an 'int'
+ * this means that 0x81234567
+ * would be illegal. this routine
+ * makes signed and unsigned go
+ * to unsigned.
+ */
+Type*
+maxtype(Type *t1, Type *t2)
+{
+
+	if(t1 == T)
+		return t2;
+	if(t2 == T)
+		return t1;
+	if(t1->etype > t2->etype)
+		return t1;
+	return t2;
+}
+
+void
+doenum(Sym *s, Node *n)
+{
+	int k = KDEC;
+	Node *nc;
+
+	nc = Z;
+	if(n) {
+		k = n->kind;
+		complex(n);
+		if(n->op != OCONST && n->op != OSTRING && n->op != OLSTRING) {
+			diag(n, "enum not a constant: %s", s->name);
+			return;
+		}
+		nc = n->left;
+		en.cenum = n->type;
+		en.tenum = maxtype(en.cenum, en.tenum);
+
+		if(!typefd[en.cenum->etype])
+			en.lastenum = n->vconst;
+		else
+			en.floatenum = n->fconst;
+	}
+	if(dclstack)
+		push1(s);
+	xdecl(CXXX, types[TENUM], s);
+
+	if(en.cenum == T) {
+		en.tenum = types[TINT];
+		en.cenum = types[TINT];
+		en.lastenum = 0;
+	}
+	s->tenum = en.cenum;
+
+	if(s->tenum->etype == TIND){		/* string */
+		nc = n;
+		s->tenum = n->type;
+	}
+	else if(!typefd[s->tenum->etype]) {
+		s->vconst = convvtox(en.lastenum, s->tenum->etype);
+		en.lastenum++;
+		s->tenum = types[TINT];
+	} else {
+		s->fconst = en.floatenum;
+		if(n)
+			s->cstring = n->cstring;
+		else
+			s->cstring = nil;
+		en.floatenum++;
+		s->tenum = types[TDOUBLE];
+	}
+	s->nconst = nc;
+
+	acidvar(s);
+	s->kind = k;
+	etgen(s);
+}
+
+void
+symadjust(Sym *s, Node *n, long del)
+{
+
+	switch(n->op) {
+	default:
+		if(n->left)
+			symadjust(s, n->left, del);
+		if(n->right)
+			symadjust(s, n->right, del);
+		return;
+
+	case ONAME:
+		return;
+
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case OINDREG:
+	case OREGISTER:
+		return;
+	}
+}
--- /dev/null
+++ b/utils/c2l/dpchk.c
@@ -1,0 +1,392 @@
+#include	"cc.h"
+#include	"y.tab.h"
+
+enum
+{
+	Fnone	= 0,
+	Fl,
+	Fvl,
+	Fignor,
+	Fstar,
+	Fadj,
+
+	Fverb	= 10,
+};
+
+typedef	struct	Tprot	Tprot;
+struct	Tprot
+{
+	Type*	type;
+	Bits	flag;
+	Tprot*	link;
+};
+
+typedef	struct	Tname	Tname;
+struct	Tname
+{
+	char*	name;
+	int	param;
+	Tname*	link;
+};
+
+static	Type*	indchar;
+static	uchar	flagbits[256];
+static	char	fmtbuf[100];
+static	int	lastadj;
+static	int	lastverb;
+static	int	nstar;
+static	Tprot*	tprot;
+static	Tname*	tname;
+
+void
+argflag(int c, int v)
+{
+
+	switch(v) {
+	case Fignor:
+	case Fstar:
+	case Fl:
+	case Fvl:
+		flagbits[c] = v;
+		break;
+	case Fverb:
+		flagbits[c] = lastverb;
+/*print("flag-v %c %d\n", c, lastadj);*/
+		lastverb++;
+		break;
+	case Fadj:
+		flagbits[c] = lastadj;
+/*print("flag-l %c %d\n", c, lastadj);*/
+		lastadj++;
+		break;
+	}
+}
+
+Bits
+getflag(char *s)
+{
+	Bits flag;
+	int c, f;
+	char *fmt;
+
+	fmt = fmtbuf;
+	flag = zbits;
+	nstar = 0;
+	while(c = *s++) {
+		*fmt++ = c;
+		f = flagbits[c];
+		switch(f) {
+		case Fnone:
+			argflag(c, Fverb);
+			f = flagbits[c];
+			break;
+		case Fstar:
+			nstar++;
+		case Fignor:
+			continue;
+		case Fl:
+			if(bset(flag, Fl))
+				flag = bor(flag, blsh(Fvl));
+		}
+		flag = bor(flag, blsh(f));
+		if(f >= Fverb)
+			break;
+	}
+	*fmt = 0;
+	return flag;
+}
+
+void
+newprot(Sym *m, Type *t, char *s)
+{
+	Bits flag;
+	Tprot *l;
+
+	if(t == T) {
+		warn(Z, "%s: newprot: type not defined", m->name);
+		return;
+	}
+	flag = getflag(s);
+	for(l=tprot; l; l=l->link)
+		if(beq(flag, l->flag) && sametype(t, l->type))
+			return;
+	l = alloc(sizeof(*l));
+	l->type = t;
+	l->flag = flag;
+	l->link = tprot;
+	tprot = l;
+}
+
+void
+newname(char *s, int p)
+{
+	Tname *l;
+
+	for(l=tname; l; l=l->link)
+		if(strcmp(l->name, s) == 0) {
+			if(l->param != p)
+				yyerror("vargck %s already defined\n", s);
+			return;
+		}
+	l = alloc(sizeof(*l));
+	l->name = s;
+	l->param = p;
+	l->link = tname;
+	tname = l;
+}
+
+void
+arginit(void)
+{
+	int i;
+
+	lastadj = Fadj;
+	lastverb = Fverb;
+	indchar = typ(TIND, types[TCHAR]);
+
+	memset(flagbits, Fnone, sizeof(flagbits));
+
+	for(i='0'; i<='9'; i++)
+		argflag(i, Fignor);
+	argflag('.', Fignor);
+	argflag('#', Fignor);
+	argflag('u', Fignor);
+	argflag('+', Fignor);
+	argflag('-', Fignor);
+
+	argflag('*', Fstar);
+	argflag('l', Fl);
+
+	argflag('o', Fverb);
+	flagbits['x'] = flagbits['o'];
+	flagbits['X'] = flagbits['o'];
+}
+
+void
+pragvararg(void)
+{
+	Sym *s;
+	int n, c;
+	char *t;
+
+	if(1)
+		goto out;
+	s = getsym();
+	if(s && strcmp(s->name, "argpos") == 0)
+		goto ckpos;
+	if(s && strcmp(s->name, "type") == 0)
+		goto cktype;
+	yyerror("syntax in #pragma varargck");
+	goto out;
+
+ckpos:
+/*#pragma	varargck	argpos	warn	2*/
+	s = getsym();
+	if(s == S)
+		goto bad;
+	n = getnsn();
+	if(n < 0)
+		goto bad;
+	newname(s->name, n);
+	goto out;
+
+cktype:
+/*#pragma	varargck	type	O	int*/
+	c = getnsc();
+	if(c != '"')
+		goto bad;
+	t = fmtbuf;
+	for(;;) {
+		c = getc();
+		if(c == ' ' || c == '\n')
+			goto bad;
+		if(c == '"')
+			break;
+		*t++ = c;
+	}
+	*t = 0;
+	t = strdup(fmtbuf);
+	s = getsym();
+	if(s == S)
+		goto bad;
+	c = getnsc();
+	unget(c);
+	if(c == '*')
+		newprot(s, typ(TIND, s->type), t);
+	else
+		newprot(s, s->type, t);
+	goto out;
+
+bad:
+	yyerror("syntax in #pragma varargck");
+
+out:
+	while(getnsc() != '\n')
+		;
+}
+
+Node*
+nextarg(Node *n, Node **a)
+{
+	if(n == Z) {
+		*a = Z;
+		return Z;
+	}
+	if(n->op == OLIST) {
+		*a = n->left;
+		return n->right;
+	}
+	*a = n;
+	return Z;
+}
+
+void
+checkargs(Node *nn, char *s, int pos)
+{
+	Node *a, *n;
+	Bits flag;
+	Tprot *l;
+
+	if(1)
+		return;
+	n = nn;
+	for(;;) {
+		s = strchr(s, '%');
+		if(s == 0) {
+			nextarg(n, &a);
+			if(a != Z)
+				warn(nn, "more arguments than format %T",
+					a->type);
+			return;
+		}
+		s++;
+		flag = getflag(s);
+		while(nstar > 0) {
+			n = nextarg(n, &a);
+			pos++;
+			nstar--;
+			if(a == Z) {
+				warn(nn, "more format than arguments %s",
+					fmtbuf);
+				return;
+			}
+			if(a->type == T)
+				continue;
+			if(!sametype(types[TINT], a->type) &&
+			   !sametype(types[TUINT], a->type))
+				warn(nn, "format mismatch '*' in %s %T, arg %d",
+					fmtbuf, a->type, pos);
+		}
+		for(l=tprot; l; l=l->link)
+			if(sametype(types[TVOID], l->type)) {
+				if(beq(flag, l->flag)) {
+					s++;
+					goto loop;
+				}
+			}
+
+		n = nextarg(n, &a);
+		pos++;
+		if(a == Z) {
+			warn(nn, "more format than arguments %s",
+				fmtbuf);
+			return;
+		}
+		if(a->type == 0)
+			continue;
+		for(l=tprot; l; l=l->link)
+			if(sametype(a->type, l->type))
+				if(beq(flag, l->flag))
+					goto loop;
+		warn(nn, "format mismatch %s %T, arg %d", fmtbuf, a->type, pos);
+	loop:;
+	}
+}
+
+void
+dpcheck(Node *n)
+{
+	char *s;
+	Node *a, *b;
+	Tname *l;
+	int i;
+
+	if(n == Z)
+		return;
+	b = n->left;
+	if(b == Z || b->op != ONAME)
+		return;
+	s = b->sym->name;
+	for(l=tname; l; l=l->link)
+		if(strcmp(s, l->name) == 0)
+			break;
+	if(l == 0)
+		return;
+
+	i = l->param;
+	b = n->right;
+	while(i > 0) {
+		b = nextarg(b, &a);
+		i--;
+	}
+	if(a == Z) {
+		warn(n, "cant find format arg");
+		return;
+	}
+	if(!sametype(indchar, a->type)) {
+		warn(n, "format arg type %T", a->type);
+		return;
+	}
+	if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) {
+/*		warn(n, "format arg not constant string");*/
+		return;
+	}
+	s = a->left->cstring;
+	checkargs(b, s, l->param);
+}
+
+void
+praghjdicks(void)
+{
+	Sym *s;
+
+	hjdickflg = 0;
+	s = getsym();
+	if(s) {
+		hjdickflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0 ||
+		   strcmp(s->name, "dick") == 0)
+			hjdickflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(0)
+		if(hjdickflg)
+			print("%4ld: hjdicks %d\n", lineno, hjdickflg);
+		else
+			print("%4ld: hjdicks off\n", lineno);
+}
+
+void
+pragfpround(void)
+{
+	Sym *s;
+
+	fproundflg = 0;
+	s = getsym();
+	if(s) {
+		hjdickflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0 ||
+		   strcmp(s->name, "dick") == 0)
+			fproundflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(0)
+		if(fproundflg)
+			print("%4ld: fproundflg %d\n", lineno, fproundflg);
+		else
+			print("%4ld: fproundflg off\n", lineno);
+}
--- /dev/null
+++ b/utils/c2l/lex.c
@@ -1,0 +1,1675 @@
+#include	"cc.h"
+#include	"y.tab.h"
+
+#ifndef	CPP
+#define	CPP	"/bin/cpp"
+#endif
+
+static int ansip;
+
+void
+main(int argc, char *argv[])
+{
+	char *defs[50], *p;
+	int nproc, nout, status, i, c, ndef;
+
+	tinit();
+	cinit();
+	ginit();
+	arginit();
+
+	tufield = simplet((1L<<tfield->etype) | BUNSIGNED);
+	ndef = 0;
+	include[ninclude++] = ".";
+	strings = 1;
+	passes = 1;
+
+	ARGBEGIN {
+	default:
+		break;
+
+	case 'p':
+		ansip = 1;
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p) {
+			defs[ndef++] = p;
+			dodefine(p);
+		}
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+
+	case 'm':
+		domod = 1;
+		break;
+
+	case 'i':
+		doinc = 1;
+		break;
+
+	case 'l':
+		doloc = 1;
+		break;
+
+	case 'c':
+		justcode = 1;
+		break;
+
+	case 'v':
+		comm = 1;
+		break;
+
+	case 's':
+		strings = 0;
+		break;
+
+	case 'S':
+		strings = 2;
+		break;
+
+	case 'M':
+		inmain = 1;
+		break;
+
+	case 'q':
+		passes = 0;
+		break;
+
+	case 'a':
+		doaddr = 1;
+		break;
+
+	case 'A':
+		doaddr = 1;
+		doalladdr = 1;
+		break;
+
+	} ARGEND
+
+	if(doinc)
+		domod = doloc = 0;
+
+	linit();
+
+	if(argc != 1) {
+		print("usage: c2l [-options] file\n");
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't compile multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0) {
+						print("cannot create a process\n");
+						errorexit();
+					}
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					fprint(2, "%s:\n", *argv);
+					if (compile(*argv, defs, ndef))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+
+	if(argc == 0)
+		c = compile("stdin", defs, ndef);
+	else
+		c = compile(argv[0], defs, ndef);
+
+	if(c)
+		errorexit();
+	exits(0);
+}
+
+int
+compile(char *file, char **defs, int ndef)
+{
+	char ofile[200], incfile[20];
+	char *p, *av[100], opt[256];
+	int i, c, fd[2];
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		*p++ = 0;
+		include[0] = strdup(ofile);
+	} else
+		p = ofile;
+
+	USED(p);
+	if(p = getenv("INCLUDE")) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile, "/%s/include", thestring);
+			setinclude(strdup(incfile));
+			setinclude("/sys/include");
+		}
+	}
+	newio();
+
+	/* Use an ANSI preprocessor */
+	if(ansip) {
+		if(systemtype(Windows)) {
+			diag(Z, "-p option not supported on windows");
+			errorexit();
+		}
+		if(mypipe(fd) < 0) {
+			diag(Z, "pipe failed");
+			errorexit();
+		}
+		switch(myfork()) {
+		case -1:
+			diag(Z, "fork failed");
+			errorexit();
+		case 0:
+			close(fd[0]);
+			mydup(fd[1], 1);
+			close(fd[1]);
+			av[0] = CPP;
+			i = 1;
+			for(c = 0; c < ndef; c++) {
+				sprint(opt, "-D%s", defs[c]);
+				av[i++] = strdup(opt);
+			}
+			for(c = 0; c < ninclude; c++) {
+				sprint(opt, "-I%s", include[c]);
+				av[i++] = strdup(opt);
+			}
+			if(strcmp(file, "stdin") != 0)
+				av[i++] = file;
+			av[i] = 0;
+			if(0) {
+				for(c = 0; c < i; c++)
+					fprint(2, "%s ", av[c]);
+				print("\n");
+			}
+			myexec(av[0], av);
+			fprint(2, "can't exec C preprocessor %s: %r\n", CPP);
+			errorexit();
+		default:
+			close(fd[1]);
+			newfile(file, fd[0]);
+			break;
+		}
+	} else {
+		if(strcmp(file, "stdin") == 0)
+			newfile(file, 0);
+		else
+			newfile(file, -1);
+	}
+
+	clbegin();
+	yyparse();
+	clend();
+	newsec(0);
+	return nerrors;
+}
+
+void
+errorexit(void)
+{
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	if(0)
+		print("%L: %s\n", lineno, s);
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("c2l: %r: %s", s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+	outpush(s);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	static Sym zsym;
+	Sym *s;
+	ulong h;
+	char *p;
+	int c, n;
+
+	h = 0;
+	for(p=symb; *p;) {
+		h = h * 3;
+		h += *p++;
+	}
+	n = (p - symb) + 1;
+	if((long)h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(strcmp(s->name, symb) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	*s = zsym;
+	s->name = alloc(n);
+	memmove(s->name, symb, n);
+
+	strcpy(s->name, symb);
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+
+	return s;
+}
+
+void
+syminit(Sym *s)
+{
+	s->lexical = LNAME;
+	s->type = T;
+	s->suetag = T;
+	s->class = CXXX;
+	s->lname = s->mod = nil;
+	s->lineno = lineno;
+	s->tenum = T;
+}
+
+#define	EOF	(-1)
+#define	IGN	(-2)
+#define	ESC	(1<<20)
+#define	GETC()	((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff))
+
+enum
+{
+	Numdec		= 1<<0,
+	Numlong		= 1<<1,
+	Numuns		= 1<<2,
+	Numvlong	= 1<<3,
+	Numflt		= 1<<4,
+};
+
+static int ypeek = 0;
+
+long
+yylex(void)
+{
+	vlong vv;
+	long c, c1;
+	char *cp;
+	Rune rune;
+	Sym *s;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c >= Runeself) {
+		/*
+		 * extension --
+		 *	all multibyte runes are alpha
+		 */
+		cp = symb;
+		goto talph;
+	}
+	if(isspace(c)) {
+		if(c == '\n')
+			lineno++;
+		goto l0;
+	}
+	if(isalpha(c)) {
+		cp = symb;
+		if(c != 'L')
+			goto talph;
+		*cp++ = c;
+		c = GETC();
+		if(c == '\'') {
+			/* L'x' */
+			c = escchar('\'', 1, 0);
+			if(c == EOF)
+				c = '\'';
+			c1 = escchar('\'', 1, 0);
+			if(c1 != EOF) {
+				yyerror("missing '");
+				peekc = c1;
+			}
+			yylval.vval = convvtox(c, TUSHORT);
+			return LUCONST;
+		}
+		if(c == '"') {
+			goto caselq;
+		}
+		goto talph;
+	}
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+
+	case EOF:
+		peekc = EOF;
+		return -1;
+
+	case '_':
+		cp = symb;
+		goto talph;
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c1 = GETC();
+		if(isdigit(c1)) {
+			cp = symb;
+			*cp++ = c;
+			c = c1;
+			c1 = 0;
+			goto casedot;
+		}
+		break;
+
+	case '"':
+		strcpy(symb, "\"<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+
+		/* "..." */
+		for(;;) {
+			c = escchar('"', 0, 1);
+			if(c == EOF)
+				break;
+			if(c & ESC) {
+				cp = allocn(cp, c1, 1);
+				cp[c1++] = c;
+			} else {
+				rune = c;
+				c = runelen(rune);
+				cp = allocn(cp, c1, c);
+				runetochar(cp+c1, &rune);
+				c1 += c;
+			}
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, 1);
+			cp[c1++] = 0;
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LSTRING;
+
+	caselq:
+		/* L"..." */
+		strcpy(symb, "\"L<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+		for(;;) {
+			c = escchar('"', 1, 0);
+			if(c == EOF)
+				break;
+			cp = allocn(cp, c1, sizeof(ushort));
+			*(ushort*)(cp + c1) = c;
+			c1 += sizeof(ushort);
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, sizeof(ushort));
+			*(ushort*)(cp + c1) = 0;
+			c1 += sizeof(ushort);
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LLSTRING;
+
+	case '\'':
+		/* '.' */
+		c = escchar('\'', 0, 0);
+		if(c == EOF)
+			c = '\'';
+		c1 = escchar('\'', 0, 0);
+		if(c1 != EOF) {
+			yyerror("missing '");
+			peekc = c1;
+		}
+		vv = c;
+		yylval.vval = convvtox(vv, TUCHAR);
+		if(yylval.vval != vv)
+			yyerror("overflow in character constant: 0x%lx", c);
+		else
+		if(c & 0x80)
+			warn(Z, "sign-extended character constant");
+		yylval.vval = convvtox(vv, TCHAR);
+		return LCHARACTER;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '*') {
+			startcom(lineno);
+			for(;;) {
+				c = getr();
+				if(c == '*'){
+					while(c == '*') {
+						c = getr();
+						if(c == '/'){
+							endcom();
+							goto l0;
+						}
+						addcom('*');
+					}
+					addcom(c);
+				}
+				else
+					addcom(c);
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '/') {
+			startcom(lineno);
+			for(;;) {
+				c = getr();
+				if(c == '\n'){
+					endcom();
+					goto l0;
+				}
+				addcom(c);
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '=')
+			return LDVE;
+		break;
+
+	case '*':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMLE;
+		break;
+
+	case '%':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMDE;
+		break;
+
+	case '+':
+		c1 = GETC();
+		if(c1 == '+')
+			return LPP;
+		if(c1 == '=')
+			return LPE;
+		break;
+
+	case '-':
+		c1 = GETC();
+		if(c1 == '-')
+			return LMM;
+		if(c1 == '=')
+			return LME;
+		if(c1 == '>')
+			return LMG;
+		break;
+
+	case '>':
+		c1 = GETC();
+		if(c1 == '>') {
+			c = LRSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LRSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LGE;
+		break;
+
+	case '<':
+		c1 = GETC();
+		if(c1 == '<') {
+			c = LLSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LLSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LLE;
+		break;
+
+	case '=':
+		c1 = GETC();
+		if(c1 == '=')
+			return LEQ;
+		break;
+
+	case '!':
+		c1 = GETC();
+		if(c1 == '=')
+			return LNE;
+		break;
+
+	case '&':
+		c1 = GETC();
+		if(c1 == '&')
+			return LANDAND;
+		if(c1 == '=')
+			return LANDE;
+		break;
+
+	case '|':
+		c1 = GETC();
+		if(c1 == '|')
+			return LOROR;
+		if(c1 == '=')
+			return LORE;
+		break;
+
+	case '^':
+		c1 = GETC();
+		if(c1 == '=')
+			return LXORE;
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+
+talph:
+	/*
+	 * cp is set to symb and some
+	 * prefix has been stored
+	 */
+	for(;;) {
+		if(c >= Runeself) {
+			for(c1=0;;) {
+				cp[c1++] = c;
+				if(fullrune(cp, c1))
+					break;
+				c = GETC();
+			}
+			cp += c1;
+			c = GETC();
+			continue;
+		}
+		if(!isalnum(c) && c != '_')
+			break;
+		*cp++ = c;
+		c = GETC();
+	}
+	*cp = 0;
+	if(0)
+		print("%L: %s\n", lineno, symb);
+	peekc = c;
+	s = lookup();
+	if(s->macro && !ypeek) {
+		newio();
+		cp = ionext->b;
+		macexpand(s, cp);
+		pushio();
+		ionext->link = iostack;
+		iostack = ionext;
+		fi.p = cp;
+		fi.c = strlen(cp);
+		if(peekc != IGN) {
+			cp[fi.c++] = peekc;
+			cp[fi.c] = 0;
+			peekc = IGN;
+		}
+		/* outpush(nil); */
+		goto l0;
+	}
+	yylval.sym = s;
+	if(s->class == CTYPEDEF) {
+		if(s->type && typesu[s->type->etype])
+			return LCTYPE;
+		return LSTYPE;
+	}
+	return s->lexical;
+
+tnum:
+	lastnumbase = KDEC;
+	c1 = 0;
+	cp = symb;
+	if(c != '0') {
+		c1 |= Numdec;
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			goto dc;
+		}
+	}
+	*cp++ = c;
+	c = GETC();
+	if(c == 'x' || c == 'X'){
+		lastnumbase = KHEX;
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			if(c >= 'a' && c <= 'f')
+				continue;
+			if(c >= 'A' && c <= 'F')
+				continue;
+			if(cp == symb+2)
+				yyerror("malformed hex constant");
+			goto ncu;
+		}
+	}
+	else
+		lastnumbase = KOCT;
+	if(c < '0' || c > '7'){
+		lastnumbase = KDEC;
+		goto dc;
+	}
+	for(;;) {
+		if(c >= '0' && c <= '7') {
+			*cp++ = c;
+			c = GETC();
+			continue;
+		}
+		goto ncu;
+	}
+
+dc:
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E')
+		goto casee;
+
+ncu:
+	if((c == 'U' || c == 'u') && !(c1 & Numuns)) {
+		c = GETC();
+		c1 |= Numuns;
+		goto ncu;
+	}
+	if((c == 'L' || c == 'l') && !(c1 & Numvlong)) {
+		c = GETC();
+		if(c1 & Numlong)
+			c1 |= Numvlong;
+		c1 |= Numlong;
+		goto ncu;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatov(symb, &yylval.vval))
+		yyerror("overflow in constant");
+
+	vv = yylval.vval;
+	if(c1 & Numvlong) {
+		if(c1 & Numuns) {
+			c = LUVLCONST;
+			goto nret;
+		}
+		yylval.vval = convvtox(yylval.vval, TVLONG);
+		if(yylval.vval < 0) {
+			c = LUVLCONST;
+			goto nret;
+		}
+		c = LVLCONST;
+		goto nret;
+	}
+	if(c1 & Numlong) {
+		if(c1 & Numuns) {
+			c = LULCONST;
+			goto nret;
+		}
+		yylval.vval = convvtox(yylval.vval, TLONG);
+		if(yylval.vval < 0) {
+			c = LULCONST;
+			goto nret;
+		}
+		c = LLCONST;
+		goto nret;
+	}
+	if(c1 & Numuns) {
+		c = LUCONST;
+		goto nret;
+	}
+	yylval.vval = convvtox(yylval.vval, TINT);
+	if(yylval.vval < 0) {
+		c = LUCONST;
+		goto nret;
+	}
+	c = LCONST;
+	goto nret;
+
+nret:
+	return c;
+
+casedot:
+	for(;;) {
+		*cp++ = c;
+		c = GETC();
+		if(!isdigit(c))
+			break;
+	}
+	if(c != 'e' && c != 'E')
+		goto caseout;
+
+casee:
+	*cp++ = 'e';
+	c = GETC();
+	if(c == '+' || c == '-') {
+		*cp++ = c;
+		c = GETC();
+	}
+	if(!isdigit(c))
+		yyerror("malformed fp constant exponent");
+	while(isdigit(c)) {
+		*cp++ = c;
+		c = GETC();
+	}
+
+caseout:
+	if(c == 'L' || c == 'l') {
+		c = GETC();
+		c1 |= Numlong;
+	} else
+	if(c == 'F' || c == 'f') {
+		c = GETC();
+		c1 |= Numflt;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatof(symb, &yylval.dval)) {
+		yyerror("overflow in float constant");
+		yylval.dval = 0;
+	}
+	if(c1 & Numflt)
+		return LFCONST;
+	return LDCONST;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+long
+getr(void)
+{
+	int c, i;
+	char str[UTFmax+1];
+	Rune rune;
+
+
+	c = getc();
+	if(c < Runeself)
+		return c;
+	i = 0;
+	str[i++] = c;
+
+loop:
+	c = getc();
+	str[i++] = c;
+	if(!fullrune(str, i))
+		goto loop;
+	c = chartorune(&rune, str);
+	if(rune == Runeerror && c == 1) {
+		/* nearln = lineno; */
+		diag(Z, "illegal rune in string");
+		for(c=0; c<i; c++)
+			print(" %.2x", *(uchar*)(str+c));
+		print("\n");
+	}
+	return rune;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	for(;;) {
+		if(!isspace(c))
+			return c;
+		if(c == '\n') {
+			lineno++;
+			return c;
+		}
+		c = GETC();
+	}
+	/* not reached */
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+long
+escchar(long e, int longflg, int escflg)
+{
+	long c, l;
+	int i;
+
+loop:
+	c = getr();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			c = EOF;
+		return c;
+	}
+	c = getr();
+	if(c == 'x') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 2 hex
+		 */
+		i = 2;
+		if(longflg)
+			i = 4;
+		l = 0;
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '9') {
+				l = l*16 + c-'0';
+				continue;
+			}
+			if(c >= 'a' && c <= 'f') {
+				l = l*16 + c-'a' + 10;
+				continue;
+			}
+			if(c >= 'A' && c <= 'F') {
+				l = l*16 + c-'A' + 10;
+				continue;
+			}
+			unget(c);
+			break;
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	if(c >= '0' && c <= '7') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 3 oct
+		 */
+		i = 2;
+		if(longflg)
+			i = 5;
+		l = c - '0';
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				continue;
+			}
+			unget(c);
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return '\a';
+	case 'v':	return '\v';
+	}
+	return c;
+}
+
+struct
+{
+	char	*name;
+	ushort	lexical;
+	ushort	type;
+} itab[] =
+{
+	"auto",		LAUTO,		0,
+	"break",	LBREAK,		0,
+	"case",		LCASE,		0,
+	"char",		LCHAR,		TCHAR,
+	"const",	LCONSTNT,	0,
+	"continue",	LCONTINUE,	0,
+	"default",	LDEFAULT,	0,
+	"do",		LDO,		0,
+	"double",	LDOUBLE,	TDOUBLE,
+	"else",		LELSE,		0,
+	"enum",		LENUM,		0,
+	"extern",	LEXTERN,	0,
+	"float",	LFLOAT,		TFLOAT,
+	"for",		LFOR,		0,
+	"goto",		LGOTO,		0,
+	"if",		LIF,		0,
+	"int",		LINT,		TINT,
+	"long",		LLONG,		TLONG,
+	"register",	LREGISTER,	0,
+	"return",	LRETURN,	0,
+	"SET",		LSET,		0,
+	"short",	LSHORT,		TSHORT,
+	"signed",	LSIGNED,	0,
+	"signof",	LSIGNOF,	0,
+	"sizeof",	LSIZEOF,	0,
+	"static",	LSTATIC,	0,
+	"struct",	LSTRUCT,	0,
+	"switch",	LSWITCH,	0,
+	"typedef",	LTYPEDEF,	0,
+	"union",	LUNION,		0,
+	"unsigned",	LUNSIGNED,	0,
+	"USED",		LUSED,		0,
+	"void",		LVOID,		TVOID,
+	"volatile",	LVOLATILE,	0,
+	"while",	LWHILE,		0,
+	"__int64",	LVLONG,	TVLONG,			/* for windows */
+	0
+};
+
+static char *litab[] =
+{
+	"adt",
+	"alt",
+	"array",
+	"big",
+	"break",
+	"byte",
+	"case",
+	"chan",
+	"con",
+	"continue",
+	"cyclic",
+	"do",
+	"else",
+	"exit",
+	"fn",
+	"for",
+	"hd",
+	"if",
+	"implement",
+	"import",
+	"include",
+	"int",
+	"len",
+	"list",
+	"load",
+	"module",
+	"nil",
+	"of",
+	"or",
+	"pick",
+	"real",
+	"ref",
+	"return",
+	"self",
+	"spawn",
+	"string",
+	"tagof",
+	"tl",
+	"to",
+	"type",
+	"while",
+	0,
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+	Type *t;
+
+	nerrors = 0;
+	lineno = 1;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+
+	types[TXXX] = T;
+	types[TCHAR] = typ(TCHAR, T);
+	types[TUCHAR] = typ(TUCHAR, T);
+	types[TSHORT] = typ(TSHORT, T);
+	types[TUSHORT] = typ(TUSHORT, T);
+	types[TINT] = typ(TINT, T);
+	types[TUINT] = typ(TUINT, T);
+	types[TLONG] = typ(TLONG, T);
+	types[TULONG] = typ(TULONG, T);
+	types[TVLONG] = typ(TVLONG, T);
+	types[TUVLONG] = typ(TUVLONG, T);
+	types[TFLOAT] = typ(TFLOAT, T);
+	types[TDOUBLE] = typ(TDOUBLE, T);
+	types[TVOID] = typ(TVOID, T);
+	types[TENUM] = typ(TENUM, T);
+	types[TFUNC] = typ(TFUNC, types[TINT]);
+	types[TIND] = typ(TIND, types[TVOID]);
+	stringtype = typ(TSTRING, T);
+	fdtype = typ(TSTRUCT, typ(TFD, T));
+	fdtype->width = 4;
+	pfdtype = typ(TIND, fdtype);
+
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->lexical = itab[i].lexical;
+		if(itab[i].type != 0)
+			s->type = types[itab[i].type];
+	}
+	for(i=0; litab[i]; i++){
+		s = slookup(litab[i]);
+		s->lkw = 1;
+	}
+	blockno = 0;
+	autobn = 0;
+	autoffset = 0;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+	symstring = slookup(".string");
+	symstring->class = CSTATIC;
+	symstring->type = t;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+
+	nodproto = new(OPROTO, Z, Z);
+	dclstack = D;
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+
+	fmtinstall('f', gfltconv);
+	fmtinstall('F', gfltconv);
+	fmtinstall('g', gfltconv);
+	fmtinstall('G', gfltconv);
+	fmtinstall('e', gfltconv);
+	fmtinstall('E', gfltconv);
+	
+	fmtinstall('O', Oconv);
+	fmtinstall('T', Tconv);
+	fmtinstall('F', FNconv);
+	fmtinstall('L', Lconv);
+	fmtinstall('Q', Qconv);
+	fmtinstall('|', VBconv);
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0] & 0xff;
+
+pop:
+	if(i->f >= 0)
+		outpop(lineno);
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++ & 0xff;
+}
+
+int
+Oconv(Fmt *fp)
+{
+	int a;
+	char s[STRINGSZ];
+
+	a = va_arg(fp->args, int);
+	if(a < OXXX || a > OEND) {
+		sprint(s, "***badO %d***", a);
+		fmtstrcpy(fp, s);
+	} else
+		fmtstrcpy(fp, onames[a]);
+	return 0;
+}
+
+int
+Lconv(Fmt *fp)
+{
+	char str[STRINGSZ], s[STRINGSZ];
+	Hist *h;
+	struct
+	{
+		Hist*	incl;	/* start of this include file */
+		long	idel;	/* delta line number to apply to include */
+		Hist*	line;	/* start of this #line directive */
+		long	ldel;	/* delta line number to apply to #line */
+	} a[HISTSZ];
+	long l, d;
+	int i, n;
+
+	l = va_arg(fp->args, long);
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset != 0) {		/* #line directive, not #pragma */
+				if(n > 0 && n < HISTSZ && h->offset >= 0) {
+					a[n-1].line = h;
+					a[n-1].ldel = h->line - h->offset + 1;
+				}
+			} else {
+				if(n < HISTSZ) {	/* beginning of file */
+					a[n].incl = h;
+					a[n].idel = h->line;
+					a[n].line = 0;
+				}
+				n++;
+			}
+			continue;
+		}
+		n--;
+		if(n > 0 && n < HISTSZ) {
+			d = h->line - a[n].incl->line;
+			a[n-1].ldel += d;
+			a[n-1].idel += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	str[0] = 0;
+	for(i=n-1; i>=0; i--) {
+		if(i != n-1) {
+			if(fp->flags & ~(FmtWidth|FmtPrec))	/* BUG ROB - was f3 */
+				break;
+			strcat(str, " ");
+		}
+		if(a[i].line)
+			snprint(s, STRINGSZ, "%s:%ld[%s:%ld]",
+				a[i].line->name, l-a[i].ldel+1,
+				a[i].incl->name, l-a[i].idel+1);
+		else
+			snprint(s, STRINGSZ, "%s:%ld",
+				a[i].incl->name, l-a[i].idel+1);
+		if(strlen(s)+strlen(str) >= STRINGSZ-10)
+			break;
+		strcat(str, s);
+		l = a[i].incl->line - 1;	/* now print out start of this file */
+	}
+	if(n == 0)
+		strcat(str, "<eof>");
+	fmtstrcpy(fp, str);
+	return 0;
+}
+
+int
+Tconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], s[STRINGSZ+20];
+	Type *t, *t1;
+	int et;
+	long n;
+
+	str[0] = 0;
+	for(t = va_arg(fp->args, Type*); t != T; t = t->link) {
+		et = t->etype;
+		if(str[0])
+			strcat(str, " ");
+		if(t->garb) {
+			sprint(s, "%s ", gnames[t->garb]);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		sprint(s, "%s", tnames[et]);
+		if(strlen(str) + strlen(s) < STRINGSZ)
+			strcat(str, s);
+		if(et == TFUNC && (t1 = t->down)) {
+			sprint(s, "(%T", t1);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+			while(t1 = t1->down) {
+				sprint(s, ", %T", t1);
+				if(strlen(str) + strlen(s) < STRINGSZ)
+					strcat(str, s);
+			}
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, ")");
+		}
+		if(et == TARRAY) {
+			n = t->width;
+			if(t->link && t->link->width)
+				n /= t->link->width;
+			sprint(s, "[%ld]", n);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(t->nbits) {
+			sprint(s, " %d:%d", t->shift, t->nbits);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(typesu[et]) {
+			if(t->tag) {
+				strcat(str, " ");
+				if(strlen(str) + strlen(t->tag->name) < STRINGSZ)
+					strcat(str, t->tag->name);
+			} else
+				strcat(str, " {}");
+			break;
+		}
+	}
+	fmtstrcpy(fp, str);
+	return 0;
+}
+
+int
+FNconv(Fmt *fp)
+{
+	char *str;
+	Node *n;
+
+	n = va_arg(fp->args, Node*);
+	str = "<indirect>";
+	if(n != Z && (n->op == ONAME || n->op == ODOT || n->op == OELEM))
+		str = n->sym->name;
+	fmtstrcpy(fp, str);
+	return 0;
+}
+
+int
+Qconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], *s;
+	long b;
+	int i;
+
+	str[0] = 0;
+	for(b = va_arg(fp->args, long); b;) {
+		i = bitno(b);
+		if(str[0])
+			strcat(str, " ");
+		s = qnames[i];
+		if(strlen(str) + strlen(s) >= STRINGSZ)
+			break;
+		strcat(str, s);
+		b &= ~(1L << i);
+	}
+	fmtstrcpy(fp, str);
+	return 0;
+}
+
+int
+VBconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	int i, n, t, pc;
+
+	n = va_arg(fp->args, int);
+	pc = 0;	/*was printcol */
+	i = 0;
+	while(pc < n) {
+		t = (pc+8) & ~7;
+		if(t <= n) {
+			str[i++] = '\t';
+			pc = t;
+			continue;
+		}
+		str[i++] = ' ';
+		pc++;
+	}
+	str[i] = 0;
+	fmtstrcpy(fp, str);
+	return 0;
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((ulong)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+	char *e;
+
+	while(*p != 0) {
+		e = strchr(p, ' ');
+		if(e != 0)
+			*e = '\0';
+
+		for(i=1; i < ninclude; i++)
+			if(strcmp(p, include[i]) == 0)
+				break;
+
+		if(i >= ninclude)
+			include[ninclude++] = p;
+
+		if(ninclude > nelem(include)) {
+			diag(Z, "ninclude too small %d", nelem(include));
+			exits("ninclude");
+		}
+
+		if(e == 0)
+			break;
+		p = e+1;
+	}
+}
+
+static void
+doio(char *s)
+{
+	char *cp;
+
+	newio();
+	cp = ionext->b;
+	strcpy(cp, s);
+	pushio();
+	ionext->link = iostack;
+	iostack = ionext;
+	fi.p = cp;
+	fi.c = strlen(cp);
+	if(peekc != IGN) {
+		cp[fi.c++] = peekc;
+		cp[fi.c] = 0;
+		peekc = IGN;
+	}
+}
+
+static void
+undoio(void)
+{
+	Io *i;
+
+	i = iostack;
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	fi.p = i->p;
+	fi.c = i->c;
+}
+
+/* rm // comment from a string */
+static void
+slashslash(char *s)
+{
+	for( ; *s != '\0'; s++)
+		if(*s == '/' && s[1] == '/'){
+			*s = '\0';
+			return;
+		}
+}
+
+int
+iscon(char *str)
+{
+	int olineno, opeekc, con, tok, t;
+	Sym *s;
+	char buf[1024];
+
+	if(str == nil || *str == 0 || strlen(str)+16 > 1024)
+		return 0;
+	ypeek = 1;
+	olineno = lineno;
+	opeekc = peekc;
+	peekc = IGN;
+	strcpy(buf, str);
+	slashslash(buf);
+	strcat(buf, " break break");
+	doio(buf);
+	tok = 0;
+	con = 1;
+	while(con){
+		t = yylex();
+		if(t == LBREAK)
+			break;
+		switch(t){
+			case LSTRING:
+			case LLSTRING:
+				tok = 1;
+				free(yylval.sval.s);
+				break;
+			case LNAME:
+				tok = 1;
+				s = yylval.sym;
+				if(s->macro || s->type == T || s->type->etype != TENUM)
+					con = 0;
+				break;
+			case LCHARACTER:
+			case LCONST:
+			case LLCONST:
+			case LUCONST:
+			case LULCONST:
+			case LVLCONST:
+			case LUVLCONST:
+			case LFCONST:
+			case LDCONST:
+				tok = 1;
+				break;
+			case '+':
+			case '-':
+			case '*':
+			case '/':
+			case '%':
+			case LPP:
+			case LMM:
+			case '<':
+			case '>':
+			case LGE:
+			case LLE:
+			case LEQ:
+			case LNE:
+			case LLSH:
+			case LRSH:
+			case '!':
+			case '~':
+			case '&':
+			case '|':
+			case '^':
+			case '(':
+			case ')':
+				break;
+			default:
+				con = 0;
+				break;
+		}
+	}
+	undoio();
+	peekc = opeekc;
+	lineno = olineno;
+	ypeek = 0;
+	return con && tok;
+}
+
+void
+doasenum(Sym *s)
+{
+	char *b, buf[1024];
+
+	b = s->macro;
+	s->macro = nil;
+	lineno--;
+	slashslash(b+1);
+	sprint(buf, "enum{ %s = %s };\n", s->name, b+1);
+	doio(buf);
+	/* outpush(nil); */
+	free(b);
+}
--- /dev/null
+++ b/utils/c2l/lexbody
@@ -1,0 +1,650 @@
+/*
+ * common code for all the assemblers
+ */
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((ulong)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+
+	if(p == 0)
+		return;
+	for(i=1; i < ninclude; i++)
+		if(strcmp(p, include[i]) == 0)
+			return;
+
+	if(ninclude >= nelem(include)) {
+		yyerror("ninclude too small %d", nelem(include));
+		exits("ninclude");
+	}
+	include[ninclude++] = p;
+}
+
+void
+errorexit(void)
+{
+
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%ca: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	static Sym zsym;
+	Sym *s;
+	long h;
+	char *p;
+	int c, l;
+
+	h = 0;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	*s = zsym;
+	s->name = alloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+	return s;
+}
+
+long
+yylex(void)
+{
+	int c, c1;
+	char *cp;
+	Sym *s;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c == EOF) {
+		peekc = EOF;
+		return -1;
+	}
+	if(isspace(c)) {
+		if(c == '\n') {
+			lineno++;
+			return ';';
+		}
+		goto l0;
+	}
+	if(isalpha(c))
+		goto talph;
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+	case '\n':
+		lineno++;
+		return ';';
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c = GETC();
+		if(isalpha(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto aloop;
+		}
+		if(isdigit(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto casedot;
+		}
+		peekc = c;
+		return '.';
+
+	talph:
+	case '_':
+	case '@':
+		cp = symb;
+
+	aloop:
+		*cp++ = c;
+		c = GETC();
+		if(isalpha(c) || isdigit(c) || c == '_' || c == '$')
+			goto aloop;
+		*cp = 0;
+		peekc = c;
+		s = lookup();
+		if(s->macro) {
+			newio();
+			cp = ionext->b;
+			macexpand(s, cp);
+			pushio();
+			ionext->link = iostack;
+			iostack = ionext;
+			fi.p = cp;
+			fi.c = strlen(cp);
+			if(peekc != IGN) {
+				cp[fi.c++] = peekc;
+				cp[fi.c] = 0;
+				peekc = IGN;
+			}
+			goto l0;
+		}
+		if(s->type == 0)
+			s->type = LNAME;
+		if(s->type == LNAME ||
+		   s->type == LVAR ||
+		   s->type == LLAB) {
+			yylval.sym = s;
+			return s->type;
+		}
+		yylval.lval = s->value;
+		return s->type;
+
+	tnum:
+		cp = symb;
+		if(c != '0')
+			goto dc;
+		*cp++ = c;
+		c = GETC();
+		c1 = 3;
+		if(c == 'x' || c == 'X') {
+			c1 = 4;
+			c = GETC();
+		} else
+		if(c < '0' || c > '7')
+			goto dc;
+		yylval.lval = 0;
+		for(;;) {
+			if(c >= '0' && c <= '9') {
+				if(c > '7' && c1 == 3)
+					break;
+				yylval.lval <<= c1;
+				yylval.lval += c - '0';
+				c = GETC();
+				continue;
+			}
+			if(c1 == 3)
+				break;
+			if(c >= 'A' && c <= 'F')
+				c += 'a' - 'A';
+			if(c >= 'a' && c <= 'f') {
+				yylval.lval <<= c1;
+				yylval.lval += c - 'a' + 10;
+				c = GETC();
+				continue;
+			}
+			break;
+		}
+		goto ncu;
+
+	dc:
+		for(;;) {
+			if(!isdigit(c))
+				break;
+			*cp++ = c;
+			c = GETC();
+		}
+		if(c == '.')
+			goto casedot;
+		if(c == 'e' || c == 'E')
+			goto casee;
+		*cp = 0;
+		yylval.lval = atol(symb);
+
+	ncu:
+		peekc = c;
+		return LCONST;
+
+	casedot:
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(!isdigit(c))
+				break;
+		}
+		if(c == 'e' || c == 'E')
+			goto casee;
+		goto caseout;
+
+	casee:
+		*cp++ = 'e';
+		c = GETC();
+		if(c == '+' || c == '-') {
+			*cp++ = c;
+			c = GETC();
+		}
+		while(isdigit(c)) {
+			*cp++ = c;
+			c = GETC();
+		}
+
+	caseout:
+		*cp = 0;
+		peekc = c;
+		if(FPCHIP) {
+			yylval.dval = atof(symb);
+			return LFCONST;
+		}
+		yyerror("assembler cannot interpret fp constants");
+		yylval.lval = 1L;
+		return LCONST;
+
+	case '"':
+		memcpy(yylval.sval, nullgen.sval, sizeof(yylval.sval));
+		cp = yylval.sval;
+		c1 = 0;
+		for(;;) {
+			c = escchar('"');
+			if(c == EOF)
+				break;
+			if(c1 < sizeof(yylval.sval))
+				*cp++ = c;
+			c1++;
+		}
+		if(c1 > sizeof(yylval.sval))
+			yyerror("string constant too long");
+		return LSCONST;
+
+	case '\'':
+		c = escchar('\'');
+		if(c == EOF)
+			c = '\'';
+		if(escchar('\'') != EOF)
+			yyerror("missing '");
+		yylval.lval = c;
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '/') {
+			for(;;) {
+				c = GETC();
+				if(c == '\n') {
+					lineno++;
+					goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '*') {
+			for(;;) {
+				c = GETC();
+				while(c == '*') {
+					c = GETC();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+				if(c == '\n')
+					lineno++;
+			}
+		}
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		return c;
+	}
+	c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	for(;;) {
+		c = getc();
+		if(!isspace(c) || c == '\n')
+			return c;
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+int
+escchar(int e)
+{
+	int c, l;
+
+loop:
+	c = getc();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			return EOF;
+		return c;
+	}
+	c = getc();
+	if(c >= '0' && c <= '7') {
+		l = c - '0';
+		c = getc();
+		if(c >= '0' && c <= '7') {
+			l = l*8 + c-'0';
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				return l;
+			}
+		}
+		peekc = c;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return 0x07;
+	case 'v':	return 0x0b;
+	case 'z':	return 0x00;
+	}
+	return c;
+}
+
+void
+pinit(char *f)
+{
+	int i;
+	Sym *s;
+
+	lineno = 1;
+	newio();
+	newfile(f, -1);
+	pc = 0;
+	peekc = IGN;
+	sym = 1;
+	for(i=0; i<NSYM; i++) {
+		h[i].type = 0;
+		h[i].sym = S;
+	}
+	for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			s->macro = 0;
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0];
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++;
+}
+
+void
+yyerror(char *a, ...)
+{
+	char buf[200];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(a, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	prfile(lineno);
+	va_start(arg, a);
+	doprint(buf, buf+sizeof(buf), a, arg);
+	va_end(arg);
+	print("%s\n", buf);
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
+
+void
+prfile(long l)
+{
+	int i, n;
+	Hist a[HISTSZ], *h;
+	long d;
+
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset == 0) {
+				if(n >= 0 && n < HISTSZ)
+					a[n] = *h;
+				n++;
+				continue;
+			}
+			if(n > 0 && n < HISTSZ)
+				if(a[n-1].offset == 0) {
+					a[n] = *h;
+					n++;
+				} else
+					a[n-1] = *h;
+			continue;
+		}
+		n--;
+		if(n >= 0 && n < HISTSZ) {
+			d = h->line - a[n].line;
+			for(i=0; i<n; i++)
+				a[i].line += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	for(i=0; i<n; i++)
+		print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1));
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}
--- /dev/null
+++ b/utils/c2l/mac.c
@@ -1,0 +1,3 @@
+#include	"cc.h"
+
+#include	"macbody"
--- /dev/null
+++ b/utils/c2l/macbody
@@ -1,0 +1,773 @@
+
+long
+getnsn(void)
+{
+	long n;
+	int c;
+
+	c = getnsc();
+	if(c < '0' || c > '9')
+		return -1;
+	n = 0;
+	while(c >= '0' && c <= '9') {
+		n = n*10 + c-'0';
+		c = getc();
+	}
+	unget(c);
+	return n;
+}
+
+Sym*
+getsym(void)
+{
+	int c;
+	char *cp;
+
+	c = getnsc();
+	if(!isalpha(c) && c != '_') {
+		unget(c);
+		return S;
+	}
+	for(cp = symb;;) {
+		if(cp <= symb+NSYMB-4)
+			*cp++ = c;
+		c = getc();
+		if(isalnum(c) || c == '_')
+			continue;
+		unget(c);
+		break;
+	}
+	*cp = 0;
+	if(cp > symb+NSYMB-4)
+		yyerror("symbol too large: %s", symb);
+	return lookup();
+}
+
+int
+getcom(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c != '/')
+			break;
+		c = getc();
+		if(c == '/') {
+			while(c != '\n')
+				c = getc();
+			break;
+		}
+		if(c != '*')
+			break;
+		c = getc();
+		for(;;) {
+			if(c == '*') {
+				c = getc();
+				if(c != '/')
+					continue;
+				c = getc();
+				break;
+			}
+			if(c == '\n') {
+				yyerror("comment across newline");
+				break;
+			}
+			c = getc();
+		}
+		if(c == '\n')
+			break;
+	}
+	return c;
+}
+
+void
+dodefine(char *cp)
+{
+	Sym *s;
+	char *p;
+	long l;
+
+	strcpy(symb, cp);
+	p = strchr(symb, '=');
+	if(p) {
+		*p++ = 0;
+		s = lookup();
+		l = strlen(p) + 2;	/* +1 null, +1 nargs */
+		while(l & 3)
+			l++;
+		while(nhunk < l)
+			gethunk();
+		*hunk = 0;
+		strcpy(hunk+1, p);
+		s->macro = hunk;
+		hunk += l;
+		nhunk -= l;
+	} else {
+		s = lookup();
+		s->macro = "\0001";	/* \000 is nargs */
+	}
+	if(0)
+		print("#define (-D) %s %s\n", s->name, s->macro+1);
+}
+
+struct
+{
+	char	*macname;
+	void	(*macf)(void);
+} mactab[] =
+{
+	"ifdef",	0,	/* macif(0) */
+	"ifndef",	0,	/* macif(1) */
+	"else",		0,	/* macif(2) */
+
+	"line",		maclin,
+	"define",	macdef,
+	"include",	macinc,
+	"undef",	macund,
+
+	"pragma",	macprag,
+	"endif",	macend,
+	0
+};
+
+void
+domacro(void)
+{
+	int i;
+	Sym *s;
+
+	s = getsym();
+	if(s == S)
+		s = slookup("endif");
+	for(i=0; mactab[i].macname; i++)
+		if(strcmp(s->name, mactab[i].macname) == 0) {
+			if(mactab[i].macf)
+				(*mactab[i].macf)();
+			else
+				macif(i);
+			return;
+		}
+	yyerror("unknown #: %s", s->name);
+	macend();
+}
+
+void
+macund(void)
+{
+	Sym *s;
+
+	s = getsym();
+	macend();
+	if(s == S) {
+		yyerror("syntax in #undef");
+		return;
+	}
+	s->macro = 0;
+}
+
+#define	NARG	25
+void
+macdef(void)
+{
+	Sym *s, *a;
+	char *args[NARG], *np, *base;
+	int n, i, c, len;
+
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(s->macro)
+		yyerror("macro redefined: %s", s->name);
+	c = getc();
+	n = -1;
+	if(c == '(') {
+		n++;
+		c = getnsc();
+		if(c != ')') {
+			unget(c);
+			for(;;) {
+				a = getsym();
+				if(a == S)
+					goto bad;
+				if(n >= NARG) {
+					yyerror("too many arguments in #define: %s", s->name);
+					goto bad;
+				}
+				args[n++] = a->name;
+				c = getnsc();
+				if(c == ')')
+					break;
+				if(c != ',')
+					goto bad;
+			}
+		}
+		c = getc();
+	}
+	if(isspace(c))
+		if(c != '\n')
+			c = getnsc();
+	base = hunk;
+	len = 1;
+	for(;;) {
+		if(isalpha(c) || c == '_') {
+			np = symb;
+			*np++ = c;
+			c = getc();
+			while(isalnum(c) || c == '_') {
+				*np++ = c;
+				c = getc();
+			}
+			*np = 0;
+			for(i=0; i<n; i++)
+				if(strcmp(symb, args[i]) == 0)
+					break;
+			if(i >= n) {
+				i = strlen(symb);
+				base = allocn(base, len, i);
+				memcpy(base+len, symb, i);
+				len += i;
+				continue;
+			}
+			base = allocn(base, len, 2);
+			base[len++] = '#';
+			base[len++] = 'a' + i;
+			continue;
+		}
+		if(c == '/') {
+			c = getc();
+			if(c != '*') {
+				base = allocn(base, len, 1);
+				base[len++] = '/';
+				continue;
+			}
+			c = getc();
+			for(;;) {
+				if(c == '*') {
+					c = getc();
+					if(c != '/')
+						continue;
+					c = getc();
+					break;
+				}
+				if(c == '\n') {
+					yyerror("comment and newline in define: %s", s->name);
+					break;
+				}
+				c = getc();
+			}
+			continue;
+		}
+		if(c == '\\') {
+			c = getc();
+			if(c == '\n') {
+				c = getc();
+				continue;
+			}
+			else if(c == '\r') {
+				c = getc();
+				if(c == '\n') {
+					c = getc();
+					continue;
+				}
+			}
+			base = allocn(base, len, 1);
+			base[len++] = '\\';
+			continue;
+		}
+		if(c == '\n')
+			break;
+		if(c == '#')
+		if(n > 0) {
+			base = allocn(base, len, 1);
+			base[len++] = c;
+		}
+		base = allocn(base, len, 1);
+		base[len++] = c;
+		c = ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff));
+		if(c == '\n')
+			lineno++;
+		if(c == -1) {
+			yyerror("eof in a macro: %s", s->name);
+			break;
+		}
+	}
+	do {
+		base = allocn(base, len, 1);
+		base[len++] = 0;
+	} while(len & 3);
+
+	*base = n+1;
+	s->macro = base;
+	if(0)
+		print("#define %s %s\n", s->name, s->macro+1);
+	if(n == -1 && iscon(base+1))
+		doasenum(s);
+	return;
+
+bad:
+	if(s == S)
+		yyerror("syntax in #define");
+	else
+		yyerror("syntax in #define: %s", s->name);
+	macend();
+}
+
+void
+macexpand(Sym *s, char *b)
+{
+	char buf[2000];
+	int n, l, c, nargs;
+	char *arg[NARG], *cp, *ob, *ecp;
+
+	ob = b;
+	USED(ob);
+	nargs = *s->macro - 1;
+	if(nargs < 0) {
+		strcpy(b, s->macro+1);
+		if(0)
+			print("#expand %s %s\n", s->name, ob);
+		return;
+	}
+	c = getnsc();
+	if(c != '(')
+		goto bad;
+	n = 0;
+	c = getc();
+	if(c != ')') {
+		unget(c);
+		l = 0;
+		cp = buf;
+		ecp = cp + sizeof(buf)-4;
+		arg[n++] = cp;
+		for(;;) {
+			if(cp >= ecp)
+				goto toobig;
+			c = getc();
+			if(c == '"')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '"')
+						break;
+				}
+			if(c == '\'')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '\'')
+						break;
+				}
+			if(l == 0) {
+				if(c == ',') {
+					*cp++ = 0;
+					arg[n++] = cp;
+					if(n > nargs)
+						break;
+					continue;
+				}
+				if(c == ')')
+					break;
+			}
+			if(c == '\n')
+				c = ' ';
+			*cp++ = c;
+			if(c == '(')
+				l++;
+			if(c == ')')
+				l--;
+		}
+		*cp = 0;
+	}
+	if(n != nargs) {
+		yyerror("argument mismatch expanding: %s", s->name);
+		*b = 0;
+		return;
+	}
+	cp = s->macro+1;
+	for(;;) {
+		c = *cp++;
+		if(c != '#') {
+			*b++ = c;
+			if(c == 0)
+				break;
+			continue;
+		}
+		c = *cp++;
+		if(c == 0)
+			goto bad;
+		if(c == '#') {
+			*b++ = c;
+			continue;
+		}
+		c -= 'a';
+		if(c < 0 || c >= n)
+			continue;
+		strcpy(b, arg[c]);
+		b += strlen(arg[c]);
+	}
+	*b = 0;
+	if(0)
+		print("#expand %s %s\n", s->name, ob);
+	return;
+
+bad:
+	yyerror("syntax in macro expansion: %s", s->name);
+	*b = 0;
+	return;
+
+toobig:
+	yyerror("too much text in macro expansion: %s", s->name);
+	*b = 0;
+}
+
+void
+macinc(void)
+{
+	int c0, c, i, f;
+	char str[STRINGSZ], *hp;
+
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = str;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	f = -1;
+	for(i=0; i<ninclude; i++) {
+		if(i == 0 && c0 == '>')
+			continue;
+		strcpy(symb, include[i]);
+		strcat(symb, "/");
+		if(strcmp(symb, "./") == 0)
+			symb[0] = 0;
+		strcat(symb, str);
+		f = open(symb, 0);
+		if(f >= 0)
+			break;
+	}
+	if(f < 0)
+		strcpy(symb, str);
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+	newio();
+	pushio();
+	newfile(hp, f);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #include");
+	macend();
+}
+
+void
+maclin(void)
+{
+	char *cp;
+	int c;
+	long n;
+
+	n = getnsn();
+	c = getc();
+	if(n < 0)
+		goto bad;
+
+	for(;;) {
+		if(c == ' ' || c == '\t') {
+			c = getc();
+			continue;
+		}
+		if(c == '"')
+			break;
+		if(c == '\n') {
+			strcpy(symb, "<noname>");
+			goto nn;
+		}
+		goto bad;
+	}
+	cp = symb;
+	for(;;) {
+		c = getc();
+		if(c == '"')
+			break;
+		*cp++ = c;
+	}
+	*cp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+nn:
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	cp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+
+	linehist(cp, n);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #line");
+	macend();
+}
+
+void
+macif(int f)
+{
+	int c, l, bol;
+	Sym *s;
+
+	if(f == 2)
+		goto skip;
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(getcom() != '\n')
+		goto bad;
+	if(s->macro == 0 && s->type != T && s->type->etype == TENUM){
+		if(!f)
+			return;
+	}
+	else if((s->macro != 0) ^ f)
+		return;
+
+skip:
+	bol = 1;
+	l = 0;
+	for(;;) {
+		c = getc();
+		if(c != '#') {
+			if(!isspace(c))
+				bol = 0;
+			if(c == '\n')
+				bol = 1;
+			continue;
+		}
+		if(!bol)
+			continue;
+		s = getsym();
+		if(s == S)
+			continue;
+		if(strcmp(s->name, "endif") == 0) {
+			if(l) {
+				l--;
+				continue;
+			}
+			macend();
+			return;
+		}
+		if(strcmp(s->name, "ifdef") == 0 || strcmp(s->name, "ifndef") == 0) {
+			l++;
+			continue;
+		}
+		if(l == 0 && f != 2 && strcmp(s->name, "else") == 0) {
+			macend();
+			return;
+		}
+	}
+
+bad:
+	yyerror("syntax in #if(n)def");
+	macend();
+}
+
+void
+macprag(void)
+{
+	Sym *s;
+	int c0, c;
+	char *hp;
+	Hist *h;
+
+	s = getsym();
+
+	if(s && strcmp(s->name, "lib") == 0)
+		goto praglib;
+	if(s && strcmp(s->name, "hjdicks") == 0) {
+		praghjdicks();
+		return;
+	}
+	if(s && strcmp(s->name, "fpround") == 0) {
+		pragfpround();
+		return;
+	}
+	if(s && strcmp(s->name, "varargck") == 0) {
+		pragvararg();
+		return;
+	}
+
+	while(getnsc() != '\n')
+		;
+	return;
+
+praglib:
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = symb;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	/*
+	 * put pragma-line in as a funny history 
+	 */
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+
+	h = alloc(sizeof(Hist));
+	h->name = hp;
+	h->line = lineno;
+	h->offset = -1;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #pragma lib");
+	macend();
+}
+
+void
+macend(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c < 0 || c == '\n')
+			return;
+	}
+}
+
+void
+linehist(char *f, int offset)
+{
+	Hist *h;
+
+	/*
+	 * overwrite the last #line directive if
+	 * no alloc has happened since the last one
+	 */
+	if(newflag == 0 && ehist != H && offset != 0 && ehist->offset != 0)
+		if(f && ehist->name && strcmp(f, ehist->name) == 0) {
+			ehist->line = lineno;
+			ehist->offset = offset;
+			return;
+		}
+
+	if(0)
+		if(f) {
+			if(offset)
+				print("%4ld: %s (#line %d)\n", lineno, f, offset);
+			else
+				print("%4ld: %s\n", lineno, f);
+		} else
+			print("%4ld: <pop>\n", lineno);
+	newflag = 0;
+
+	h = alloc(sizeof(Hist));
+	h->name = f;
+	h->line = lineno;
+	h->offset = offset;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 10L*NHUNK)
+		nh = 10L*NHUNK;
+	h = (char*)mysbrk(nh);
+	if(h == (char*)-1) {
+		yyerror("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
--- /dev/null
+++ b/utils/c2l/mkfile
@@ -1,0 +1,38 @@
+<../../mkconfig
+
+TARG=c2l
+
+OFILES=\
+	acid.$O\
+	bits.$O\
+	com.$O\
+	com64.$O\
+	$TARGMODEL.$O\
+	dcl.$O\
+	dpchk.$O\
+	lex.$O\
+	mac.$O\
+	mpatof.$O\
+	out.$O\
+	scon.$O\
+	sub.$O\
+	y.tab.$O\
+	c2l.$O\
+ 
+HFILES=	cc.h\
+	y.tab.h\
+ 
+YFILES=	cc.y\
+
+LIBS=math bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+mac.$O: macbody
+
+lex.$O:	lex.c
+	$CC $CFLAGS '-DCPP="/bin/cpp"' lex.c
--- /dev/null
+++ b/utils/c2l/mpatof.c
@@ -1,0 +1,337 @@
+#include	"cc.h"
+
+enum
+{
+	Mpscale	= 29,		/* safely smaller than bits in a long */
+	Mpprec	= 36,		/* Mpscale*Mpprec sb > largest fp exp */
+	Mpbase	= 1L<<Mpscale,
+};
+
+typedef
+struct
+{
+	long	a[Mpprec];
+	char	ovf;
+} Mp;
+
+int	mpatof(char*, double*);
+int	mpatov(char *s, vlong *v);
+void	mpint(Mp*, int);
+void	mppow(Mp*, int, int);
+void	mpmul(Mp*, int);
+void	mpadd(Mp*, Mp*);
+int	mptof(Mp*, double*);
+
+/*
+ * convert a string, s, to floating in *d
+ * return conversion overflow.
+ * required syntax is [+-]d*[.]d*[e[+-]d*]
+ */
+int
+mpatof(char *s, double *d)
+{
+	Mp a, b;
+	int dp, c, f, ef, ex, zer;
+	double d1, d2;
+
+	dp = 0;		/* digits after decimal point */
+	f = 0;		/* sign */
+	ex = 0;		/* exponent */
+	zer = 1;	/* zero */
+	memset(&a, 0, sizeof(a));
+	for(;;) {
+		switch(c = *s++) {
+		default:
+			goto bad;
+		case '-':
+			f = 1;
+		case ' ':
+		case  '\t':
+		case  '+':
+			continue;
+		case '.':
+			dp = 1;
+			continue;
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			zer = 0;
+		case '0':
+			mpint(&b, c-'0');
+			mpmul(&a, 10);
+			mpadd(&a, &b);
+			if(dp)
+				dp++;
+			continue;
+		case 'E':
+		case 'e':
+			ex = 0;
+			ef = 0;
+			for(;;) {
+				c = *s++;
+				if(c == '+' || c == ' ' || c == '\t')
+					continue;
+				if(c == '-') {
+					ef = 1;
+					continue;
+				}
+				if(c >= '0' && c <= '9') {
+					ex = ex*10 + (c-'0');
+					continue;
+				}
+				break;
+			}
+			if(ef)
+				ex = -ex;
+		case 0:
+			break;
+		}
+		break;
+	}
+	if(a.ovf)
+		goto bad;
+	if(zer) {
+		*d = 0;
+		return 0;
+	}
+	if(dp)
+		dp--;
+	dp -= ex;
+	if(dp > 0) {
+		/*
+		 * must divide by 10**dp
+		 */
+		if(mptof(&a, &d1))
+			goto bad;
+
+		/*
+		 * trial exponent of 8**dp
+		 * 8 (being between 5 and 10)
+		 * should pick up all underflows
+		 * in the division of 5**dp.
+		 */
+		d2 = frexp(d1, &ex);
+		d2 = ldexp(d2, ex-3*dp);
+		if(d2 == 0)
+			goto bad;
+
+		/*
+		 * decompose each 10 into 5*2.
+		 * create 5**dp in fixed point
+		 * and then play with the exponent
+		 * for the remaining 2**dp.
+		 * note that 5**dp will overflow
+		 * with as few as 134 input digits.
+		 */
+		mpint(&a, 1);
+		mppow(&a, 5, dp);
+		if(mptof(&a, &d2))
+			goto bad;
+		d1 = frexp(d1/d2, &ex);
+		d1 = ldexp(d1, ex-dp);
+		if(d1 == 0)
+			goto bad;
+	} else {
+		/*
+		 * must multiply by 10**|dp| --
+		 * just do it in fixed point.
+		 */
+		mppow(&a, 10, -dp);
+		if(mptof(&a, &d1))
+			goto bad;
+	}
+	if(f)
+		d1 = -d1;
+	*d = d1;
+	return 0;
+
+bad:
+	return 1;
+}
+
+/*
+ * convert a to floating in *d
+ * return conversion overflow
+ */
+int
+mptof(Mp *a, double *d)
+{
+	double f, g;
+	long x, *a1;
+	int i;
+
+	if(a->ovf)
+		return 1;
+	a1 = a->a;
+	f = ldexp(*a1++, 0);
+	for(i=Mpscale; i<Mpprec*Mpscale; i+=Mpscale)
+		if(x = *a1++) {
+			g = ldexp(x, i);
+			/*
+			 * NOTE: the test (g==0) is plan9
+			 * specific. ansi compliant overflow
+			 * is signaled by HUGE and errno==ERANGE.
+			 * change this for your particular ldexp.
+			 */
+			if(g == 0)
+				return 1;
+			f += g;		/* this could bomb! */
+		}
+	*d = f;
+	return 0;
+}
+
+/*
+ * return a += b
+ */
+void
+mpadd(Mp *a, Mp *b)
+{
+	int i, c;
+	long x, *a1, *b1;
+
+	if(b->ovf)
+		a->ovf = 1;
+	if(a->ovf)
+		return;
+	c = 0;
+	a1 = a->a;
+	b1 = b->a;
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 + *b1++ + c;
+		c = 0;
+		if(x >= Mpbase) {
+			x -= Mpbase;
+			c = 1;
+		}
+		*a1++ = x;
+	}
+	a->ovf = c;
+}
+
+/*
+ * return a = c
+ */
+void
+mpint(Mp *a, int c)
+{
+
+	memset(a, 0, sizeof(*a));
+	a->a[0] = c;
+}
+
+/*
+ * return a *= c
+ */
+void
+mpmul(Mp *a, int c)
+{
+	Mp p;
+	int b;
+
+	memmove(&p, a, sizeof(p));
+	if(!(c & 1))
+		memset(a, 0, sizeof(*a));
+	c &= ~1;
+	for(b=2; c; b<<=1) {
+		mpadd(&p, &p);
+		if(c & b) {
+			mpadd(a, &p);
+			c &= ~b;
+		}
+	}
+}
+
+/*
+ * return a *= b**e
+ */
+void
+mppow(Mp *a, int b, int e)
+{
+	int b1;
+
+	b1 = b*b;
+	b1 = b1*b1;
+	while(e >= 4) {
+		mpmul(a, b1);
+		e -= 4;
+		if(a->ovf)
+			return;
+	}
+	while(e > 0) {
+		mpmul(a, b);
+		e--;
+	}
+}
+
+/*
+ * convert a string, s, to vlong in *v
+ * return conversion overflow.
+ * required syntax is [0[x]]d*
+ */
+int
+mpatov(char *s, vlong *v)
+{
+	vlong n, nn;
+	int c;
+	n = 0;
+	c = *s;
+	if(c == '0')
+		goto oct;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			nn = n*10 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+oct:
+	s++;
+	c = *s;
+	if(c == 'x' || c == 'X')
+		goto hex;
+	while(c = *s++) {
+		if(c >= '0' || c <= '7')
+			nn = n*8 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+hex:
+	s++;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			c += 0-'0';
+		else
+		if(c >= 'a' && c <= 'f')
+			c += 10-'a';
+		else
+		if(c >= 'A' && c <= 'F')
+			c += 10-'A';
+		else
+			goto bad;
+		nn = n*16 + c;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+out:
+	*v = n;
+	return 0;
+
+bad:
+	*v = ~0;
+	return 1;
+}
--- /dev/null
+++ b/utils/c2l/out.c
@@ -1,0 +1,825 @@
+#include "cc.h"
+
+#define INCREMENT 	8
+#define DEVNULL	"/dev/null"
+
+static int indent = 0;
+static int fd = -1;
+static int nf = 0;
+static int mylineno = 1;
+
+typedef struct Com{
+	int lno;
+	char *s;
+	Node *n;
+	int tba;
+	struct Com *nxt;
+} Com;
+
+Com *hdc, *curc;
+
+typedef struct File{
+	char *s;
+	char *f;
+	char *m;
+	int b;
+	int loc;
+	int in;
+	int tg;
+	Node *n;
+	Com *c;
+	struct File *nxt;
+} File;
+
+typedef struct Create{
+	char *s;
+	struct Create *nxt;
+} Create;
+
+File *fs;
+Create *cs;
+
+static void genmsg(void);
+static int isloc(void);
+
+static void
+addcreate(char *s)
+{
+	Create *c;
+
+	if(strcmp(s, DEVNULL) == 0)
+		return;
+	c = (Create*)malloc(sizeof(Create));
+	c->s = malloc(strlen(s)+1);
+	strcpy(c->s, s);
+	c->nxt = cs;
+	cs = c;
+}
+
+static int
+created(char *s)
+{
+	Create *c;
+
+	for(c = cs; c != nil; c = c->nxt)
+		if(strcmp(s, c->s) == 0)
+			return 1;
+	return 0;
+}
+
+int
+dolog(void)
+{
+	if(justcode)
+		return 0;
+	return domod || !doinc || inmain;
+}
+
+static char*
+curf(void)
+{
+	File *f;
+
+	for(f = fs; f != nil; f = f->nxt)
+		if(f->f != nil)
+			return f->s;
+	return nil;
+}
+
+static char*
+curm(void)
+{
+	File *f;
+
+	for(f = fs; f != nil; f = f->nxt)
+		if(f->f != nil)
+			return f->m;
+	return nil;
+}
+
+void
+setmod(Sym *s)
+{
+	if(domod && s->mod == nil && ism() && !(doloc && !isloc()))
+		s->mod = curm();
+}
+
+char *
+outmod(char *buf, int up)
+{
+	char *s, *t;
+
+	s = curf();
+	if(s == nil)
+		return "";
+	t = strchr(s, '.');
+	if(t != nil)
+		*t = '\0';
+	strcpy(buf, s);
+	if(t != nil)
+		*t = '.';
+	if(up == 1 || (up < 0 && ism()))
+		buf[0] = toupper(buf[0]);
+	return buf;
+}
+
+int
+ism(void)
+{
+	return !isb();
+}
+
+int
+isb(void)
+{
+	File *f;
+
+	for(f = fs; f != nil; f = f->nxt)
+		if(f->f != nil)
+			return f->b;
+	return 0;
+}
+
+static int
+isloc(void)
+{
+	File *f;
+
+	for(f = fs; f != nil; f = f->nxt)
+		if(f->f != nil)
+			return f->loc;
+	return 0;
+}
+ 
+static File*
+pushf(void)
+{
+	static File zfile;
+	File *f;
+
+	f = (File*)malloc(sizeof(File));
+	*f = zfile;
+	f->s = nil;
+	f->f = nil;
+	f->m = nil;
+	f->nxt = fs;
+	fs = f;
+	return f;
+}
+
+static void
+popf(void)
+{
+	File *f;
+
+	f = fs;
+	fs = fs->nxt;
+	if(f->s != nil)
+		free(f->s);
+	free(f);
+}
+
+static void
+setf(File *f, char *s)
+{
+	int n;
+	char *t;
+
+	if(s != nil){
+		t = strrchr(s, '/');
+		f->loc = t == nil;
+		if(t != nil)
+			s = t+1;
+		n = strlen(s);
+		f->s = malloc(n+1);
+		strcpy(f->s, s);
+		s = f->s;
+		if(n > 2 && s[n-2] == '.'){
+			f->m = malloc(n-1);
+			strncpy(f->m, s, n-2);
+			if(s[n-1] == 'h')
+				s[n-1] = 'm';
+			else if(s[n-1] == 'c'){
+				s[n-1] = 'b';
+				f->b = 1;
+			}
+			else
+				s = nil;
+		}
+		else
+			s = nil;
+		if(s == nil){
+			free(f->s);
+			if(f->m != nil)
+				free(f->m);
+			f->s = nil;
+			f->m = nil;
+		}
+	}
+	f->f = f->s;
+	if(f->s != nil && nf > 0){
+		if(doinc || doloc && !f->loc)
+			f->f = DEVNULL;
+		else if(!domod)
+			f->f = nil;
+	}	
+}
+
+void
+outpush0(char *s, Node *n)
+{
+	File *f;
+
+	f = pushf();
+	setf(f, s);
+	if(f->f != nil){
+		nf++;
+		f->tg = taggen;
+		taggen = 0;
+		f->n = n;
+		f->c = hdc;
+		hdc = nil;
+	}	
+}
+
+void
+outpop0(int lno)
+{
+	File *f;
+
+	USED(lno);
+	f = fs;
+	if(f->f != nil){
+		nf--;
+		taggen = f->tg;
+		f->n->left = (void*)hdc;
+		hdc = f->c;
+	}
+	popf();
+}
+
+void
+outpush2(char *s, Node *n)
+{
+	File *f;
+
+	f = pushf();
+	setf(f, s);
+	if(f->f != nil){
+		if(fd >= 0){
+			newsec(0);
+			close(fd);
+			close(1);
+			fd = -1;
+		}
+		if(created(f->f))
+			f->f = DEVNULL;	/* don't overwrite original if included again */
+		fd = create(f->f, OWRITE, 0664);
+		if(fd >= 0)
+			addcreate(f->f);
+		mydup(fd, 1);
+		nf++;
+		f->tg = taggen;
+		taggen = 0;
+		f->c = hdc;
+		if(n != Z)
+			hdc = (void*)n->left;
+		else
+			hdc = nil;
+		f->in = indent;
+		indent = 0;
+		genmsg();
+		pgen(f->b);
+	}	
+}
+
+void
+outpop2(int lno)
+{
+	File *f, *g;
+
+	f = fs;
+	if(f->f != nil){
+		if(fd >= 0){
+			newsec(0);
+			output(lno, 1);
+			epgen(f->b);
+			close(fd);
+			close(1);
+			fd = -1;
+		}
+		for(g = fs->nxt; g != nil; g = g->nxt){
+			if(g->f != nil){
+				fd = open(g->f, OWRITE);
+				seek(fd, 0, 2);
+				mydup(fd, 1);
+				break;
+			}
+		}
+		nf--;
+		taggen = f->tg;
+		hdc = f->c;
+		indent = f->in;
+	}
+	popf();
+}
+
+static void
+xprint(char *s)
+{
+	if(nerrors == 0)
+		print(s);
+}
+
+static int tot = 0;
+
+static void
+doindent(int d)
+{
+	int i;
+
+	for(i = 0; i < d/8; i++)
+		xprint("\t");
+	for(i = 0; i < d%8; i++)
+		xprint(" ");
+}
+
+void
+incind(void)
+{
+	indent += INCREMENT;
+}
+
+void 
+decind(void)
+{
+	indent -= INCREMENT;
+}
+
+int
+zeroind(void)
+{
+	int i = indent;
+
+	indent = 0;
+	return i;
+}
+
+void
+restoreind(int i)
+{
+	indent = i;
+}
+
+void
+newline0(void)
+{
+	xprint("\n");
+	tot = 0;
+	mylineno++;
+}
+
+void
+newline(void)
+{
+	if(!outcom(1)){
+		xprint("\n");
+		mylineno++;
+	}
+	tot = 0;
+}
+
+static void 
+lprint(char *s)
+{
+	if(tot == 0) {
+		doindent(indent);
+		tot += indent;
+	}
+	xprint(s);
+	tot += strlen(s);
+}
+
+void
+prline(char *s)
+{
+	xprint(s);
+	xprint("\n");
+	mylineno++;
+}
+
+void 
+prdelim(char *s)
+{
+	if(*s == '%'){
+		if(*++s == '=')
+			lprint("%%=");
+		else
+			lprint("%%");
+		return;
+	}
+	lprint(s);
+}
+
+void 
+prkeywd(char *kw)
+{
+	lprint(kw);
+}
+
+void
+prid(char *s)
+{
+	lprint(s);
+}
+
+static void
+priddol(char *s, int dol)
+{
+	char *t;
+	char buf[128];
+
+	if(dol){
+		t = strchr(s, '$');
+		if(t != nil)
+			*t = '_';
+		lprint(s);
+		if(t != nil){
+			strcpy(buf, s);
+			while(slookup(buf)->type != T){
+				strcat(buf, "x");
+				lprint("x");
+			}
+			*t = '$';
+		}
+	}
+	else
+		lprint(s);
+}
+
+void
+prsym(Sym *s, int mod)
+{
+	char buf[128];
+	int c;
+
+	if(mod && s->mod && strcmp(s->mod, curm()) != 0 && (!s->limbo || s->class == CEXTERN)){
+		c = isconsym(s);
+		if(c >= 0){
+			if(c){
+				s->mod[0] = toupper(s->mod[0]);
+				lprint(s->mod);
+				s->mod[0] = tolower(s->mod[0]);
+			}
+			else
+				lprint(s->mod);
+			lprint("->");
+			usemod(s, !c);
+		}
+	}
+	if(s->lname)
+		prid(s->lname);
+	else{
+		priddol(s->name, s->class == CSTATIC);
+		if(s->lkw){
+			strcpy(buf, s->name);
+			for(;;){
+				strcat(buf, "x");
+				lprint("x");
+				s = slookup(buf);
+				if(s->type == T)
+					break;
+			}
+		}
+	}
+}
+
+int
+arrow(Sym *s)
+{
+	if(s->mod && strcmp(s->mod, curm()) != 0)
+		return isconsym(s) >= 0;
+	return 0;
+}
+
+void
+prsym0(Sym *s)
+{
+	int c;
+
+	if(s->mod && strcmp(s->mod, curm()) != 0){
+		c = isconsym(s);
+		if(c >= 0)
+			usemod(s, !c);
+	}
+}
+
+static int
+isprintable(int c)
+{
+	if(c >= 0x20 && c <= 0x7e)
+		return 1;
+	return c == '\0' || c == '\n' || c == '\t' || c == '\b' || c == '\r' || c == '\f' || c == '\a' || c == '\v';
+}
+
+static int
+hex(int c)
+{
+	if(c < 10)
+		return c+'0';
+	return c+'a'-10;
+}
+
+void
+prchar0(vlong x, int quote)
+{
+	int c, e, i = 0;
+	static char buf[16];
+
+	if(quote)
+		buf[i++] = '\'';
+	c = x;
+	if(c < 0 || c > 255 || !isprintable(c)){
+		if(c&0xffff0000)
+			diag(Z, "character too big");
+		buf[i++] = '\\';
+		buf[i++] = 'u';
+		buf[i++] = hex((c>>12)&0xf);
+		buf[i++] = hex((c>>8)&0xf);
+		buf[i++] = hex((c>>4)&0xf);
+		buf[i++] = hex((c>>0)&0xf);
+	}
+	else{
+		e = 0;
+		switch(c){
+			case '\n':	e = 'n'; break;
+			case '\t':	e = 't'; break;
+			case '\b':	e = 'b'; break;
+			case '\r':	e = 'r'; break;
+			case '\f':	e = 'f'; break;
+			case '\a':	e = 'a'; break;
+			case '\v':	e = 'v'; break;
+			case '"':	if(!quote) e = '"'; break;
+			case '\'':	if(quote) e = '\''; break;
+			case '\\':	e = '\\'; break;
+			case '%':	buf[i++] = c; break;
+			case 0:	e = '0'; if(strings) prcom("nul byte in string ?", Z); break;
+		}
+		if(e != 0){
+			buf[i++] = '\\';
+			c = e;
+		}
+		buf[i++] = c;
+	}
+	if(quote)
+		buf[i++] = '\'';
+	buf[i] = '\0';
+	lprint(buf);
+}
+
+void
+prchar(vlong x)
+{
+	prchar0(x, 1);
+}
+
+void
+prstr(char *s)
+{
+	uchar *t;
+	Rune r;
+
+	t = (uchar*)s;
+	lprint("\"");
+	while(*t != 0){
+		if(*t & 0x80){
+			t += chartorune(&r, (char*)t);
+			prchar0(r, 0);
+		}
+		else
+			prchar0(*t++, 0);
+	}
+	lprint("\"");
+}
+
+void
+prlstr(Rune *s)
+{
+	lprint("\"");
+	while(*s != 0)
+		prchar0(*s++, 0);
+	lprint("\"");
+}
+
+void
+prreal(double x, char *s, int b)
+{
+	static char buf[128];
+
+	if(b != KDEC)
+		diag(Z, "not base 10 in prreal");
+	if(s != nil)
+		lprint(s);
+	else{
+		sprint(buf, "%f", x);
+		lprint(buf);
+	}
+}
+
+void
+prnum(vlong x, int b, Type *t)
+{
+	static char buf[128];
+	int w;
+	vlong m;
+
+	w = 4;
+	if(t != T)
+		w = ewidth[t->etype];
+	m = MASK(8*w);
+	if(b == KHEX)
+		sprint(buf, "16r%llux", x&m);
+	else if(b == KOCT)
+		sprint(buf, "8r%lluo", x&m);
+	else
+		sprint(buf, "%lld", x);
+	lprint(buf);
+}
+
+char *cb;
+int cn, csz;
+
+static void
+outcom0(Com *c)
+{
+	Node *n;
+	char *s, *t, *u;
+
+	s = c->s;
+	n = c->n;
+	if(comm && c->tba){
+		t = strchr(s, '\n');
+		*t = '\0';
+		fprint(2, "%s:%d: %s", curf(), mylineno, s);
+		*t = '\n';
+		if(n != Z){
+			mydup(2, 1);
+			expgen(n);
+			mydup(fd, 1);
+		}
+		fprint(2, "\n");
+	}
+	while(*s != '\0'){
+		t = strchr(s, '\n');
+		*t = '\0';
+		if(tot != 0)
+			prdelim("\t");
+		prdelim("# ");
+		while((u = strchr(s, '%')) != nil){
+			/* do not let print interpret % ! */
+			*u = 0;
+			lprint(s);
+			*u = '%';
+			lprint("%%");
+			s = u+1;
+		}
+		lprint(s);
+		if(n == Z)
+			newline0();
+		*t = '\n';
+		s = t+1;
+	}
+	if(n != Z){
+		expgen(n);
+		newline0();
+	}
+}
+
+int
+outcom(int f)
+{
+	int lno, nl;
+	Com *c;
+
+	nl = 0;
+	lno = pline+f;
+	c = hdc;
+	while(c != nil && c->lno < lno){
+/* print("outcom: %d < %d (f=%d)\n", c->lno, lno, f); */
+		nl = 1;
+		outcom0(c);
+		hdc = hdc->nxt;
+		free(c->s);
+		free(c);
+		c = hdc;
+	}
+	return nl;
+}
+
+void
+startcom(int lno)
+{
+	Com *c, **ac;
+
+	c = (Com *)malloc(sizeof(Com));
+	c->lno = lno;
+	c->s = nil;
+	c->n = Z;
+	c->tba = 0;
+	c->nxt = nil;
+	for(ac = &hdc; *ac != nil && (*ac)->lno <= lno; ac = &(*ac)->nxt)
+		;
+	c->nxt = *ac;
+	curc = *ac = c;
+}
+
+void
+addcom(int rr)
+{
+	int i, nb;
+	char *ncb;
+	char s[UTFmax];
+	Rune r[1];
+
+	if(rr >= Runeself){
+		r[0] = rr;
+		nb = runetochar(s, r);
+	}
+	else{
+		nb = 1;
+		s[0] = rr;
+	}
+	if(cn+nb-1 >= csz){
+		csz += 32;
+		ncb = malloc(csz);
+		memcpy(ncb, cb, cn);
+		free(cb);
+		cb = ncb;
+	}
+	for(i = 0; i < nb; i++)
+		cb[cn++] = s[i];
+}
+
+void
+endcom(void)
+{
+	char *s;
+
+	addcom('\n');
+	addcom('\0');
+	s = malloc(strlen(cb)+1);
+	strcpy(s, cb);
+	curc->s = s;
+/* print("com %d %s\n", curc->lno, s); */
+	cn = 0;
+}
+
+void
+linit()
+{
+	csz = 32;
+	cb = malloc(csz);
+	sysinit();
+}
+
+static void
+genmsg(void)
+{
+	prline("#");
+	prline("#	initially generated by c2l");
+	prline("#");
+	prline("");
+}
+
+void
+prcom(char *s, Node *n)
+{
+	Com *c;
+
+	startcom(pline);
+	c = curc;
+	sprint(cb, "TBA %s", s);
+	cn = strlen(cb);
+	c->n = n;
+	c->tba = 1;
+	endcom();
+}
+
+void
+output(long lno, int com)
+{
+/* print("output(%ld)\n", lno); */
+	pline = lno;
+	if(com)
+		outcom(0);
+}
+
+int
+exists(char *f)
+{
+	int fd;
+
+	fd = open(f, OREAD);
+	close(fd);
+	return fd >= 0;
+}
--- /dev/null
+++ b/utils/c2l/scon.c
@@ -1,0 +1,250 @@
+#include "cc.h"
+
+void
+evconst(Node *n)
+{
+	Node *l, *r;
+	int et, isf;
+	vlong v;
+	double d;
+
+	if(n == Z || n->type == T)
+		return;
+
+	et = n->type->etype;
+	isf = typefd[et];
+
+	l = n->left;
+	r = n->right;
+
+	d = 0;
+	v = 0;
+
+	switch(n->op) {
+	default:
+		return;
+
+	case OCAST:
+		if(et == TVOID)
+			return;
+		et = l->type->etype;
+		if(isf) {
+			if(typefd[et])
+				d = l->fconst;
+			else
+				d = l->vconst;
+		} else {
+			if(typefd[et])
+				v = l->fconst;
+			else
+				v = convvtox(l->vconst, n->type->etype);
+		}
+		break;
+
+	case OCONST:
+		break;
+
+	case OADD:
+		if(isf)
+			d = l->fconst + r->fconst;
+		else {
+			v = l->vconst + r->vconst;
+		}
+		break;
+
+	case OSUB:
+		if(isf)
+			d = l->fconst - r->fconst;
+		else
+			v = l->vconst - r->vconst;
+		break;
+
+	case OMUL:
+		if(isf)
+			d = l->fconst * r->fconst;
+		else {
+			v = l->vconst * r->vconst;
+		}
+		break;
+
+	case OLMUL:
+		v = (uvlong)l->vconst * (uvlong)r->vconst;
+		break;
+
+
+	case ODIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		if(isf)
+			d = l->fconst / r->fconst;
+		else
+			v = l->vconst / r->vconst;
+		break;
+
+	case OLDIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		v = (uvlong)l->vconst / (uvlong)r->vconst;
+		break;
+
+	case OMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = l->vconst % r->vconst;
+		break;
+
+	case OLMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = (uvlong)l->vconst % (uvlong)r->vconst;
+		break;
+
+	case OAND:
+		v = l->vconst & r->vconst;
+		break;
+
+	case OOR:
+		v = l->vconst | r->vconst;
+		break;
+
+	case OXOR:
+		v = l->vconst ^ r->vconst;
+		break;
+
+	case OLSHR:
+		v = (uvlong)l->vconst >> r->vconst;
+		break;
+
+	case OASHR:
+		v = l->vconst >> r->vconst;
+		break;
+
+	case OASHL:
+		v = l->vconst << r->vconst;
+		break;
+
+	case OLO:
+		v = (uvlong)l->vconst < (uvlong)r->vconst;
+		break;
+
+	case OLT:
+		if(typefd[l->type->etype])
+			v = l->fconst < r->fconst;
+		else
+			v = l->vconst < r->vconst;
+		break;
+
+	case OHI:
+		v = (uvlong)l->vconst > (uvlong)r->vconst;
+		break;
+
+	case OGT:
+		if(typefd[l->type->etype])
+			v = l->fconst > r->fconst;
+		else
+			v = l->vconst > r->vconst;
+		break;
+
+	case OLS:
+		v = (uvlong)l->vconst <= (uvlong)r->vconst;
+		break;
+
+	case OLE:
+		if(typefd[l->type->etype])
+			v = l->fconst <= r->fconst;
+		else
+			v = l->vconst <= r->vconst;
+		break;
+
+	case OHS:
+		v = (uvlong)l->vconst >= (uvlong)r->vconst;
+		break;
+
+	case OGE:
+		if(typefd[l->type->etype])
+			v = l->fconst >= r->fconst;
+		else
+			v = l->vconst >= r->vconst;
+		break;
+
+	case OEQ:
+		if(typefd[l->type->etype])
+			v = l->fconst == r->fconst;
+		else
+			v = l->vconst == r->vconst;
+		break;
+
+	case ONE:
+		if(typefd[l->type->etype])
+			v = l->fconst != r->fconst;
+		else
+			v = l->vconst != r->vconst;
+		break;
+
+	case ONOT:
+		if(typefd[l->type->etype])
+			v = !l->fconst;
+		else
+			v = !l->vconst;
+		break;
+
+	case OANDAND:
+		if(typefd[l->type->etype])
+			v = l->fconst && r->fconst;
+		else
+			v = l->vconst && r->vconst;
+		break;
+
+	case OOROR:
+		if(typefd[l->type->etype])
+			v = l->fconst || r->fconst;
+		else
+			v = l->vconst || r->vconst;
+		break;
+
+	case OPOS:
+		if(isf)
+			d = l->fconst;
+		else
+			v = l->vconst;
+		break;
+
+	case ONEG:
+		if(isf)
+			d = -l->fconst;
+		else
+			v = -l->vconst;
+		break;
+
+	case OCOM:
+		if(typefd[l->type->etype])
+			v = 0;	/* ~l->fconst */
+		else
+			v = ~l->vconst;
+		break;
+	}
+
+	n->left = ncopy(n);
+	n->op = OCONST;
+	/* n->left = Z; */
+	n->right = Z;
+	if(isf) {
+		n->fconst = d;
+	} else {
+		n->vconst = convvtox(v, n->type->etype);
+	}
+}
+
+void
+acom(Node *n)
+{
+	USED(n);
+}
--- /dev/null
+++ b/utils/c2l/sub.c
@@ -1,0 +1,1697 @@
+#include	"cc.h"
+
+Node*
+new(int t, Node *l, Node *r)
+{
+	static Node znode;
+	Node *n;
+
+	n = alloc(sizeof(*n));
+	*n = znode;
+	n->op = t;
+	n->left = l;
+	n->right = r;
+	n->lineno = lineno;
+	n->kind = KNIL;
+	newflag = 1;
+	return n;
+}
+
+Node*
+new1(int o, Node *l, Node *r)
+{
+	Node *n;
+
+	n = new(o, l, r);
+	if(l != Z)
+		n->lineno = l->lineno;
+	else if(r != Z)
+		n->lineno = r->lineno;
+	else
+		n->lineno = nearln;
+	return n;
+}
+
+void
+prtree(Node *n, char *s)
+{
+
+	print(" == %s ==\n", s);
+	prtree1(n, 0, 0);
+	print("\n");
+}
+
+void
+prtree1(Node *n, int d, int f)
+{
+	int i;
+
+	if(f)
+	for(i=0; i<d; i++)
+		print("   ");
+	if(n == Z) {
+		print("Z\n");
+		return;
+	}
+	if(n->op == OLIST) {
+		prtree1(n->left, d, 0);
+		prtree1(n->right, d, 1);
+		return;
+	}
+	d++;
+	print("%O", n->op);
+	i = 3;
+	switch(n->op)
+	{
+	case ONAME:
+		print(" \"%F\"", n);
+		i = 0;
+		break;
+
+	case OINDREG:
+		i = 0;
+		break;
+
+	case OREGISTER:
+		i = 0;
+		break;
+
+	case OSTRING:
+		print(" \"%s\"", n->cstring);
+		i = 0;
+		break;
+
+	case OLSTRING:
+		print(" \"%S\"", n->rstring);
+		i = 0;
+		break;
+
+	case ODOT:
+	case OELEM:
+		print(" \"%F\"", n);
+		break;
+
+	case OCONST:
+		if(typefd[n->type->etype])
+			print(" \"%.8e\"", n->fconst);
+		else
+			print(" \"%lld\"", n->vconst);
+		i = 0;
+		break;
+	}
+	if(n->type != T)
+		print(" %T", n->type);
+	print("\n");
+	if(i & 2)
+		prtree1(n->left, d, 1);
+	if(i & 1)
+		prtree1(n->right, d, 1);
+}
+
+Type*
+typ(int et, Type *d)
+{
+	static Type ztype;
+	Type *t;
+
+	t = alloc(sizeof(*t));
+	*t = ztype;
+	t->etype = et;
+	t->link = d;
+	t->down = T;
+	t->sym = S;
+	t->width = ewidth[et];
+	t->nwidth = Z;
+	t->lineno = lineno;
+	return t;
+}
+
+Type*
+typ1(int et, Type *d)
+{
+	Type *t;
+
+	t = typ(et, d);
+	t->lineno = nearln;
+	return t;
+}
+
+Type*
+garbt(Type *t, long b)
+{
+	Type *t1;
+
+	if(b & BGARB) {
+		t1 = typ(TXXX, T);
+		*t1 = *t;
+		t1->garb = simpleg(b);
+		return t1;
+	}
+	return t;
+}
+
+int
+simpleg(long b)
+{
+
+	b &= BGARB;
+	switch(b) {
+	case BCONSTNT:
+		return GCONSTNT;
+	case BVOLATILE:
+		return GVOLATILE;
+	case BVOLATILE|BCONSTNT:
+		return GCONSTNT|GVOLATILE;
+	}
+	return GXXX;
+}
+
+int
+simplec(long b)
+{
+
+	b &= BCLASS;
+	switch(b) {
+	case 0:
+	case BREGISTER:
+		return CXXX;
+	case BAUTO:
+	case BAUTO|BREGISTER:
+		return CAUTO;
+	case BEXTERN:
+		return CEXTERN;
+	case BEXTERN|BREGISTER:
+		return CEXREG;
+	case BSTATIC:
+		return CSTATIC;
+	case BTYPEDEF:
+		return CTYPEDEF;
+	}
+	diag(Z, "illegal combination of classes %Q", b);
+	return CXXX;
+}
+
+Type*
+simplet(long b)
+{
+
+	b &= ~BCLASS & ~BGARB;
+	switch(b) {
+	case BCHAR:
+	case BCHAR|BSIGNED:
+		return types[TCHAR];
+
+	case BCHAR|BUNSIGNED:
+		return types[TUCHAR];
+
+	case BSHORT:
+	case BSHORT|BINT:
+	case BSHORT|BSIGNED:
+	case BSHORT|BINT|BSIGNED:
+		return types[TSHORT];
+
+	case BUNSIGNED|BSHORT:
+	case BUNSIGNED|BSHORT|BINT:
+		return types[TUSHORT];
+
+	case 0:
+	case BINT:
+	case BINT|BSIGNED:
+	case BSIGNED:
+		return types[TINT];
+
+	case BUNSIGNED:
+	case BUNSIGNED|BINT:
+		return types[TUINT];
+
+	case BLONG:
+	case BLONG|BINT:
+	case BLONG|BSIGNED:
+	case BLONG|BINT|BSIGNED:
+		return types[TLONG];
+
+	case BUNSIGNED|BLONG:
+	case BUNSIGNED|BLONG|BINT:
+		return types[TULONG];
+
+	case BVLONG|BLONG:
+	case BVLONG|BLONG|BINT:
+	case BVLONG|BLONG|BSIGNED:
+	case BVLONG|BLONG|BINT|BSIGNED:
+		return types[TVLONG];
+
+	case BVLONG|BLONG|BUNSIGNED:
+	case BVLONG|BLONG|BINT|BUNSIGNED:
+		return types[TUVLONG];
+
+	case BFLOAT:
+		return types[TFLOAT];
+
+	case BDOUBLE:
+	case BDOUBLE|BLONG:
+	case BFLOAT|BLONG:
+		return types[TDOUBLE];
+
+	case BVOID:
+		return types[TVOID];
+	}
+
+	diag(Z, "illegal combination of types %Q", b);
+	return types[TINT];
+}
+
+int
+stcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+	USED(n); USED(t1); USED(t2); USED(ttab[0]);
+	return 0;
+#ifdef WHATEVA
+	int i;
+	ulong b;
+
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1L << i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ttab[i]) {
+		if(ttab == tasign)
+			if(b == BSTRUCT || b == BUNION)
+				if(!sametype(t1, t2))
+					return 1;
+		if(n->op != OCAST)
+		 	if(b == BIND && i == TIND)
+				if(!sametype(t1, t2))
+					return 1;
+		return 0;
+	}
+	return 1;
+#endif
+}
+
+int
+tcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+
+	if(0 && stcompat(n, t1, t2, ttab)) {
+		if(t1 == T)
+			diag(n, "incompatible type: \"%T\" for op \"%O\"",
+				t2, n->op);
+		else
+			diag(n, "incompatible types: \"%T\" and \"%T\" for op \"%O\"",
+				t1, t2, n->op);
+		return 1;
+	}
+	return 0;
+}
+
+void
+makedot(Node *n, Type *t, long o)
+{
+	USED(n);
+	USED(o);
+	USED(t);
+	return;
+}
+
+Type*
+dotsearch(Sym *s, Type *t, Node *n)
+{
+	Type *t1, *xt;
+
+	xt = T;
+
+	/*
+	 * look it up by name
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == s) {
+			if(xt != T)
+				goto ambig;
+			xt = t1;
+		}
+	if(xt != T)
+		return xt;
+
+	/*
+	 * look it up by type
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == S && typesu[t1->etype])
+			if(sametype(s->type, t1)) {
+				if(xt != T)
+					goto ambig;
+				xt = t1;
+			}
+	if(xt != T)
+		return xt;
+
+	/*
+	 * look it up in unnamed substructures
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == S && typesu[t1->etype])
+			if(dotsearch(s, t1->link, n) != T) {
+				if(xt != T)
+					goto ambig;
+				xt = t1;
+			}
+	return xt;
+
+ambig:
+	diag(n, "ambiguous structure element: %s", s->name);
+	return xt;
+}
+
+long
+dotoffset(Type *st, Type *lt, Node *n)
+{
+	Type *t;
+	Sym *g;
+	long o, o1;
+
+	o = -1;
+	/*
+	 * first try matching at the top level
+	 * for matching tag names
+	 */
+	g = st->tag;
+	if(g != S)
+		for(t=lt->link; t!=T; t=t->down)
+			if(t->sym == S)
+				if(g == t->tag) {
+					if(o >= 0)
+						goto ambig;
+					o = t->offset;
+				}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * second try matching at the top level
+	 * for similar types
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+			if(sametype(st, t)) {
+				if(o >= 0)
+					goto ambig;
+				o = t->offset;
+			}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * last try matching sub-levels
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+		if(typesu[t->etype]) {
+			o1 = dotoffset(st, t, n);
+			if(o1 >= 0) {
+				if(o >= 0)
+					goto ambig;
+				o = o1 + t->offset;
+			}
+		}
+	return o;
+
+ambig:
+	diag(n, "ambiguous unnamed structure element");
+	return o;
+}
+
+/*
+ * look into tree for floating point constant expressions
+ */
+int
+allfloat(Node *n, int flag)
+{
+
+	if(n != Z) {
+		if(n->type->etype != TDOUBLE)
+			return 1;
+		switch(n->op) {
+		case OCONST:
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		case OADD:	/* no need to get more exotic than this */
+		case OSUB:
+		case OMUL:
+		case ODIV:
+			if(!allfloat(n->right, flag))
+				break;
+		case OCAST:
+			if(!allfloat(n->left, flag))
+				break;
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+constas(Node *n, Type *il, Type *ir)
+{
+	Type *l, *r;
+
+	l = il;
+	r = ir;
+
+	if(l == T)
+		return;
+	if(l->garb & GCONSTNT) {
+		warn(n, "assignment to a constant type (%T)", il);
+		return;
+	}
+	if(r == T)
+		return;
+	for(;;) {
+		if(l->etype != TIND || r->etype != TIND)
+			break;
+		l = l->link;
+		r = r->link;
+		if(l == T || r == T)
+			break;
+		if(r->garb & GCONSTNT)
+			if(!(l->garb & GCONSTNT)) {
+				warn(n, "assignment of a constant pointer type (%T)", ir);
+				break;
+			}
+	}
+}
+
+void
+typeext1(Type *st, Node *l)
+{
+	if(st->etype == TFLOAT && allfloat(l, 0))
+		allfloat(l, 1);
+}
+
+void
+typeext(Type *st, Node *l)
+{
+	Type *lt;
+
+	lt = l->type;
+	if(lt == T)
+		return;
+	if(st->etype == TIND && vconst(l) == 0) {
+		l->type = st;
+		l->vconst = 0;
+		return;
+	}
+	typeext1(st, l);
+}
+
+/*
+ * a cast that generates no code
+ * (same size move)
+ */
+int
+nocast(Type *t1, Type *t2)
+{
+	int i, b;
+
+	if(t1->nbits)
+		return 0;
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1<<i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ncast[i])
+		return 1;
+	return 0;
+}
+
+/*
+ * a cast that has a noop semantic
+ * (small to large, convert)
+ */
+int
+nilcast(Type *t1, Type *t2)
+{
+	int et1, et2;
+
+	if(t1 == T)
+		return 0;
+	if(t1->nbits)
+		return 0;
+	if(t2 == T)
+		return 0;
+	et1 = t1->etype;
+	et2 = t2->etype;
+	if(et1 == et2)
+		return 1;
+	if(typefd[et1] && typefd[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	if(typechlp[et1] && typechlp[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	return 0;
+}
+
+/*
+ * "the usual arithmetic conversions are performed"
+ */
+void
+arith(Node *n, int f)
+{
+	Type *t1, *t2;
+	int i, j, k;
+	long w;
+
+	t1 = n->left->type;
+	if(n->right == Z)
+		t2 = t1;
+	else
+		t2 = n->right->type;
+	i = TXXX;
+	if(t1 != T)
+		i = t1->etype;
+	j = TXXX;
+	if(t2 != T)
+		j = t2->etype;
+	k = tab[i][j];
+	if(k == TIND) {
+		if(i == TIND)
+			n->type = t1;
+		else
+		if(j == TIND)
+			n->type = t2;
+	} else {
+		/* convert up to at least int */
+		if(f == 1)
+		while(k < TINT)
+			k += 2;
+		n->type = types[k];
+	}
+	if(n->op == OSUB)
+	if(i == TIND && j == TIND) {
+		w = n->right->type->link->width;
+		if(w < 1)
+			goto bad;
+		n->type = types[TLONG];
+		return;
+	}
+	if(!sametype(n->type, n->left->type)) {
+		n->left = new1(OCAST, n->left, Z);
+		n->left->type = n->type;
+	}
+	if(n->right != Z)
+	if(!sametype(n->type, n->right->type)) {
+		n->right = new1(OCAST, n->right, Z);
+		n->right->type = n->type;
+	}
+	return;
+bad:
+	diag(n, "pointer addition not fully declared: %T", n->type->link);
+}
+
+int
+side(Node *n)
+{
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	case OCAST:
+	case ONOT:
+	case OADDR:
+	case OIND:
+		n = n->left;
+		goto loop;
+
+	case OCOND:
+		if(side(n->left))
+			break;
+		n = n->right;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OADD:
+	case OSUB:
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OMOD:
+	case OLMOD:
+	case OANDAND:
+	case OOROR:
+	case OCOMMA:
+	case ODOT:
+		if(side(n->left))
+			break;
+		n = n->right;
+		goto loop;
+
+	case OSIGN:
+	case OSIZE:
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case ONAME:
+		return 0;
+	}
+	return 1;
+}
+
+int
+vconst(Node *n)
+{
+	int i;
+
+	if(n == Z)
+		goto no;
+	if(n->op != OCONST)
+		goto no;
+	if(n->type == T)
+		goto no;
+	switch(n->type->etype)
+	{
+	case TFLOAT:
+	case TDOUBLE:
+		i = 100;
+		if(n->fconst > i || n->fconst < -i)
+			goto no;
+		i = n->fconst;
+		if(i != n->fconst)
+			goto no;
+		return i;
+
+	case TVLONG:
+	case TUVLONG:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+	}
+no:
+	return -159;	/* first uninteresting constant */
+}
+
+/*
+ * return log(n) if n is a power of 2 constant
+ */
+int
+vlog(Node *n)
+{
+	int s, i;
+	uvlong m, v;
+
+	if(n->op != OCONST)
+		goto bad;
+	if(typefd[n->type->etype])
+		goto bad;
+
+	v = n->vconst;
+
+	s = 0;
+	m = MASK(8*sizeof(uvlong));
+	for(i=32; i; i>>=1) {
+		m >>= i;
+		if(!(v & m)) {
+			v >>= i;
+			s += i;
+		}
+	}
+	if(v == 1)
+		return s;
+
+bad:
+	return -1;
+}
+
+int
+topbit(ulong v)
+{
+	int i;
+
+	for(i = -1; v; i++)
+		v >>= 1;
+	return i;
+}
+
+/*
+ * try to cast a constant down
+ * rather than cast a variable up
+ * example:
+ *	if(c == 'a')
+ */
+void
+relcon(Node *l, Node *r)
+{
+	vlong v;
+	Node *t;
+
+	if(l->op != OCONST)
+		return;
+	if(r->op != OCAST)
+		return;
+	if(!nilcast(r->left->type, r->type))
+		return;
+	switch(r->type->etype) {
+	default:
+		return;
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+		v = convvtox(l->vconst, r->type->etype);
+		if(v != l->vconst)
+			return;
+		break;
+	}
+	t = new1(OXXX, Z, Z);
+	*t = *l;
+	l->op = OCAST;
+	l->left = t;
+	l->right = Z;
+	l->type = r->left->type;
+	*r = *r->left;
+}
+
+int
+relindex(int o)
+{
+
+	switch(o) {
+	default:
+		diag(Z, "bad in relindex: %O", o);
+	case OEQ: return 0;
+	case ONE: return 1;
+	case OLE: return 2;
+	case OLS: return 3;
+	case OLT: return 4;
+	case OLO: return 5;
+	case OGE: return 6;
+	case OHS: return 7;
+	case OGT: return 8;
+	case OHI: return 9;
+	}
+}
+
+Node*
+invert(Node *n)
+{
+	Node *i;
+
+	if(n == Z || n->op != OLIST || n->blk)
+		return n;
+	i = n;
+	for(n = n->left; n != Z; n = n->left) {
+		if(n->op != OLIST || n->blk)
+			break;
+		i->left = n->right;
+		n->right = i;
+		i = n;
+	}
+	i->left = n;
+	return i;
+}
+
+int
+bitno(long b)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(b & (1L<<i))
+			return i;
+	diag(Z, "bad in bitno");
+	return 0;
+}
+
+long
+typebitor(long a, long b)
+{
+	long c;
+
+	c = a | b;
+	if(a & b)
+		if((a & b) == BLONG)
+			c |= BVLONG;		/* long long => vlong */
+		else
+			warn(Z, "once is enough: %Q", a & b);
+	return c;
+}
+
+void
+diag(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(0)
+		abort();
+	if(n != Z)
+	if(0)
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	if(nerrors > 10) {
+		fprint(2, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+warn(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	if(0) {
+		fprint(2, "warning: ");
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		fprint(2, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+		if(n != Z)
+		if(0)
+			prtree(n, "warning");
+	}
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L %s\n", lineno, buf);
+	nerrors++;
+	if(nerrors > 10) {
+		fprint(2, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+fatal(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(0)
+		abort();
+	if(n != Z)
+	if(0)
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	errorexit();
+}
+
+ulong	thash1	= 0x2edab8c9;
+ulong	thash2	= 0x1dc74fb8;
+ulong	thash3	= 0x1f241331;
+ulong	thash[NALLTYPES];
+Init	thashinit[] =
+{
+	TXXX,		0x17527bbd,	0,
+	TCHAR,		0x5cedd32b,	0,
+	TUCHAR,		0x552c4454,	0,
+	TSHORT,		0x63040b4b,	0,
+	TUSHORT,	0x32a45878,	0,
+	TINT,		0x4151d5bd,	0,
+	TUINT,		0x5ae707d6,	0,
+	TLONG,		0x5ef20f47,	0,
+	TULONG,		0x36d8eb8f,	0,
+	TVLONG,		0x6e5e9590,	0,
+	TUVLONG,	0x75910105,	0,
+	TFLOAT,		0x25fd7af1,	0,
+	TDOUBLE,	0x7c40a1b2,	0,
+	TIND,		0x1b832357,	0,
+	TFUNC,		0x6babc9cb,	0,
+	TARRAY,		0x7c50986d,	0,
+	TVOID,		0x44112eff,	0,
+	TSTRUCT,	0x7c2da3bf,	0,
+	TUNION,		0x3eb25e98,	0,
+	TENUM,		0x44b54f61,	0,
+	TFILE,		0x19242ac3,	0,
+	TOLD,		0x22b15988,	0,
+	TDOT,		0x0204f6b3,	0,
+	-1,		0,		0,
+};
+
+char*	bnames[NALIGN];
+Init	bnamesinit[] =
+{
+	Axxx,	0,	"Axxx",
+	Ael1,	0,	"el1",
+	Ael2,	0,	"el2",
+	Asu2,	0,	"su2",
+	Aarg0,	0,	"arg0",
+	Aarg1,	0,	"arg1",
+	Aarg2,	0,	"arg2",
+	Aaut3,	0,	"aut3",
+	-1,	0,	0,
+};
+
+char*	tnames[NALLTYPES];
+Init	tnamesinit[] =
+{
+	TXXX,		0,	"xxx",
+	TCHAR,		0,	"char",
+	TUCHAR,		0,	"uchar",
+	TSHORT,		0,	"short",
+	TUSHORT,	0,	"ushort",
+	TINT,		0,	"int",
+	TUINT,		0,	"uint",
+	TLONG,		0,	"long",
+	TULONG,		0,	"ulong",
+	TVLONG,		0,	"vlong",
+	TUVLONG,	0,	"uvlong",
+	TFLOAT,		0,	"float",
+	TDOUBLE,	0,	"double",
+	TIND,		0,	"pointer",
+	TFUNC,		0,	"function",
+	TARRAY,		0,	"array",
+	TVOID,		0,	"void",
+	TSTRUCT,	0,	"struct",
+	TUNION,		0,	"union",
+	TENUM,		0,	"enum",
+	TFILE,		0,	"file",
+	TOLD,		0,	"old",
+	TDOT,		0,	"dot",
+	TSTRING,		0,	"string",
+	TFD,			0,	"fd",
+	TTUPLE,		0,	"tuple",
+	-1,		0,	0,
+};
+
+char*	gnames[NGTYPES];
+Init	gnamesinit[] =
+{
+	GXXX,			0,	"GXXX",
+	GCONSTNT,		0,	"CONST",
+	GVOLATILE,		0,	"VOLATILE",
+	GVOLATILE|GCONSTNT,	0,	"CONST-VOLATILE",
+	-1,			0,	0,
+};
+
+char*	qnames[NALLTYPES];
+Init	qnamesinit[] =
+{
+	TXXX,		0,	"TXXX",
+	TCHAR,		0,	"CHAR",
+	TUCHAR,		0,	"UCHAR",
+	TSHORT,		0,	"SHORT",
+	TUSHORT,	0,	"USHORT",
+	TINT,		0,	"INT",
+	TUINT,		0,	"UINT",
+	TLONG,		0,	"LONG",
+	TULONG,		0,	"ULONG",
+	TVLONG,		0,	"VLONG",
+	TUVLONG,	0,	"UVLONG",
+	TFLOAT,		0,	"FLOAT",
+	TDOUBLE,	0,	"DOUBLE",
+	TIND,		0,	"IND",
+	TFUNC,		0,	"FUNC",
+	TARRAY,		0,	"ARRAY",
+	TVOID,		0,	"VOID",
+	TSTRUCT,	0,	"STRUCT",
+	TUNION,		0,	"UNION",
+	TENUM,		0,	"ENUM",
+
+	TAUTO,		0,	"AUTO",
+	TEXTERN,	0,	"EXTERN",
+	TSTATIC,	0,	"STATIC",
+	TTYPEDEF,	0,	"TYPEDEF",
+	TREGISTER,	0,	"REGISTER",
+	TCONSTNT,	0,	"CONSTNT",
+	TVOLATILE,	0,	"VOLATILE",
+	TUNSIGNED,	0,	"UNSIGNED",
+	TSIGNED,	0,	"SIGNED",
+	TDOT,		0,	"DOT",
+	TFILE,		0,	"FILE",
+	TOLD,		0,	"OLD",
+	-1,		0,	0,
+};
+char*	cnames[NCTYPES];
+Init	cnamesinit[] =
+{
+	CXXX,		0,	"CXXX",
+	CAUTO,		0,	"AUTO",
+	CEXTERN,	0,	"EXTERN",
+	CGLOBL,		0,	"GLOBL",
+	CSTATIC,	0,	"STATIC",
+	CLOCAL,		0,	"LOCAL",
+	CTYPEDEF,	0,	"TYPEDEF",
+	CPARAM,		0,	"PARAM",
+	CSELEM,		0,	"SELEM",
+	CLABEL,		0,	"LABEL",
+	CEXREG,		0,	"EXREG",
+	-1,		0,	0,
+};
+
+char*	onames[OEND+1];
+Init	onamesinit[] =
+{
+	ONOOP,		0,	"NOOP",
+	OXXX,		0,	"OXXX",
+	OADD,		0,	"ADD",
+	OADDR,		0,	"ADDR",
+	OAND,		0,	"AND",
+	OANDAND,	0,	"ANDAND",
+	OARRAY,		0,	"ARRAY",
+	OAS,		0,	"AS",
+	OASI,		0,	"ASI",
+	OASADD,		0,	"ASADD",
+	OASAND,		0,	"ASAND",
+	OASASHL,	0,	"ASASHL",
+	OASASHR,	0,	"ASASHR",
+	OASDIV,		0,	"ASDIV",
+	OASHL,		0,	"ASHL",
+	OASHR,		0,	"ASHR",
+	OASLDIV,	0,	"ASLDIV",
+	OASLMOD,	0,	"ASLMOD",
+	OASLMUL,	0,	"ASLMUL",
+	OASLSHR,	0,	"ASLSHR",
+	OASMOD,		0,	"ASMOD",
+	OASMUL,		0,	"ASMUL",
+	OASOR,		0,	"ASOR",
+	OASSUB,		0,	"ASSUB",
+	OASXOR,		0,	"ASXOR",
+	OBIT,		0,	"BIT",
+	OBREAK,		0,	"BREAK",
+	OCASE,		0,	"CASE",
+	OCAST,		0,	"CAST",
+	OCOMMA,		0,	"COMMA",
+	OCOND,		0,	"COND",
+	OCONST,		0,	"CONST",
+	OCONTINUE,	0,	"CONTINUE",
+	ODIV,		0,	"DIV",
+	ODOT,		0,	"DOT",
+	ODOTDOT,	0,	"DOTDOT",
+	ODWHILE,	0,	"DWHILE",
+	OENUM,		0,	"ENUM",
+	OEQ,		0,	"EQ",
+	OFOR,		0,	"FOR",
+	OFUNC,		0,	"FUNC",
+	OGE,		0,	"GE",
+	OGOTO,		0,	"GOTO",
+	OGT,		0,	"GT",
+	OHI,		0,	"HI",
+	OHS,		0,	"HS",
+	OIF,		0,	"IF",
+	OIND,		0,	"IND",
+	OINDREG,	0,	"INDREG",
+	OINIT,		0,	"INIT",
+	OLABEL,		0,	"LABEL",
+	OLDIV,		0,	"LDIV",
+	OLE,		0,	"LE",
+	OLIST,		0,	"LIST",
+	OLMOD,		0,	"LMOD",
+	OLMUL,		0,	"LMUL",
+	OLO,		0,	"LO",
+	OLS,		0,	"LS",
+	OLSHR,		0,	"LSHR",
+	OLT,		0,	"LT",
+	OMOD,		0,	"MOD",
+	OMUL,		0,	"MUL",
+	ONAME,		0,	"NAME",
+	ONE,		0,	"NE",
+	ONOT,		0,	"NOT",
+	OOR,		0,	"OR",
+	OOROR,		0,	"OROR",
+	OPOSTDEC,	0,	"POSTDEC",
+	OPOSTINC,	0,	"POSTINC",
+	OPREDEC,	0,	"PREDEC",
+	OPREINC,	0,	"PREINC",
+	OPROTO,		0,	"PROTO",
+	OREGISTER,	0,	"REGISTER",
+	ORETURN,	0,	"RETURN",
+	OSET,		0,	"SET",
+	OSIGN,		0,	"SIGN",
+	OSIZE,		0,	"SIZE",
+	OSTRING,	0,	"STRING",
+	OLSTRING,	0,	"LSTRING",
+	OSTRUCT,	0,	"STRUCT",
+	OSUB,		0,	"SUB",
+	OSWITCH,	0,	"SWITCH",
+	OUNION,		0,	"UNION",
+	OUSED,		0,	"USED",
+	OWHILE,		0,	"WHILE",
+	OXOR,		0,	"XOR",
+	ONEG,		0,	"NEG",
+	OCOM,		0,	"COM",
+	OELEM,		0,	"ELEM",
+	OTST,		0,	"TST",
+	OINDEX,		0,	"INDEX",
+	OFAS,		0,	"FAS",
+	OBLK,		0,	"BLK",
+	OPOS,		0,	"POS",
+	ONUL,		0,	"NUL",
+	ODOTIND,	0,	"DOTIND",
+	OARRIND,		0,	"ARRIND",
+	ODAS,		0,	"ODAS",
+	OASD,		0,	"OASD",
+	OIOTA,		0,	"IOTA",
+	OLEN,		0,	"LEN",
+	OBRACKET,	0,	"BRACKET",
+	OREF,		0,	"REF",
+	OARRAYOF,	0,	"ARRAYOF",
+	OSLICE,		0,	"SLICE",
+	OSADDR,		0,	"SADDR",
+	ONIL,		0,	"NIL",
+	OS2AB,		0,	"S2AB",
+	OAB2S,		0,	"AB2S",
+	OFILDES,		0,	"FILDES",
+	OFD,			0,	"FD",
+	OTUPLE,		0,	"TUPLE",
+	OT0,			0,	"T0",
+	ORETV,		0,	"RETV",
+	OCAT,		0,	"CAT",
+	OSBREAK,		0,	"SBREAK",
+	OLDOT,		0,	"LDOT",
+	OMDOT,		0,	"MDOT",
+	OCODE,		0,	"CODE",
+	ODECE,		0,	"DECE",
+	ODECT,		0,	"DECT",
+	ODECV,		0,	"DECV",
+	ODECF,		0,	"DECF",
+	OPUSH,		0,	"PUSH",
+	OPOP,		0,	"POP",
+	OEND,		0,	"END",
+	-1,		0,	0,
+};
+
+char	comrel[12] =
+{
+	ONE, OEQ, OGT, OHI, OGE, OHS, OLT, OLO, OLE, OLS,
+};
+char	invrel[12] =
+{
+	OEQ, ONE, OGE, OHS, OGT, OHI, OLE, OLS, OLT, OLO,
+};
+char	logrel[12] =
+{
+	OEQ, ONE, OLS, OLS, OLO, OLO, OHS, OHS, OHI, OHI,
+};
+
+char	typei[NTYPE];
+int	typeiinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TVLONG, TUVLONG, -1,
+};
+char	typeu[NTYPE];
+int	typeuinit[] =
+{
+	TUCHAR, TUSHORT, TUINT, TULONG, TUVLONG, TIND, -1,
+};
+
+char	typesuv[NTYPE];
+int	typesuvinit[] =
+{
+	TVLONG, TUVLONG, TSTRUCT, TUNION, -1,
+};
+
+char	typeilp[NTYPE];
+int	typeilpinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, TIND, -1
+};
+
+char	typechl[NTYPE];
+int	typechlinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typechlp[NTYPE];
+int	typechlpinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TIND, -1,
+};
+
+char	typechlpfd[NTYPE];
+int	typechlpfdinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TFLOAT, TDOUBLE, TIND, -1,
+};
+
+char	typec[NTYPE];
+int	typecinit[] =
+{
+	TCHAR, TUCHAR, -1
+};
+
+char	typeh[NTYPE];
+int	typehinit[] =
+{
+	TSHORT, TUSHORT, -1,
+};
+
+char	typeil[NTYPE];
+int	typeilinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typev[NTYPE];
+int	typevinit[] =
+{
+	TVLONG,	TUVLONG, -1,
+};
+
+char	typefd[NTYPE];
+int	typefdinit[] =
+{
+	TFLOAT, TDOUBLE, -1,
+};
+
+char	typeaf[NTYPE];
+int	typeafinit[] =
+{
+	TFUNC, TARRAY, -1,
+};
+
+char	typesu[NTYPE];
+int	typesuinit[] =
+{
+	TSTRUCT, TUNION, -1,
+};
+
+long	tasign[NTYPE];
+Init	tasigninit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BIND,		0,
+	TSTRUCT,	BSTRUCT,	0,
+	TUNION,		BUNION,		0,
+	-1,		0,		0,
+};
+
+long	tasadd[NTYPE];
+Init	tasaddinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	tcast[NTYPE];
+Init	tcastinit[] =
+{
+	TCHAR,		BNUMBER|BIND|BVOID,	0,
+	TUCHAR,		BNUMBER|BIND|BVOID,	0,
+	TSHORT,		BNUMBER|BIND|BVOID,	0,
+	TUSHORT,	BNUMBER|BIND|BVOID,	0,
+	TINT,		BNUMBER|BIND|BVOID,	0,
+	TUINT,		BNUMBER|BIND|BVOID,	0,
+	TLONG,		BNUMBER|BIND|BVOID,	0,
+	TULONG,		BNUMBER|BIND|BVOID,	0,
+	TVLONG,		BNUMBER|BIND|BVOID,	0,
+	TUVLONG,	BNUMBER|BIND|BVOID,	0,
+	TFLOAT,		BNUMBER|BVOID,		0,
+	TDOUBLE,	BNUMBER|BVOID,		0,
+	TIND,		BINTEGER|BIND|BVOID,	0,
+	TVOID,		BVOID,			0,
+	TSTRUCT,	BSTRUCT|BVOID,		0,
+	TUNION,		BUNION|BVOID,		0,
+	-1,		0,			0,
+};
+
+long	tadd[NTYPE];
+Init	taddinit[] =
+{
+	TCHAR,		BNUMBER|BIND,	0,
+	TUCHAR,		BNUMBER|BIND,	0,
+	TSHORT,		BNUMBER|BIND,	0,
+	TUSHORT,	BNUMBER|BIND,	0,
+	TINT,		BNUMBER|BIND,	0,
+	TUINT,		BNUMBER|BIND,	0,
+	TLONG,		BNUMBER|BIND,	0,
+	TULONG,		BNUMBER|BIND,	0,
+	TVLONG,		BNUMBER|BIND,	0,
+	TUVLONG,	BNUMBER|BIND,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	tsub[NTYPE];
+Init	tsubinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER|BIND,	0,
+	-1,		0,		0,
+};
+
+long	tmul[NTYPE];
+Init	tmulinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	-1,		0,		0,
+};
+
+long	tand[NTYPE];
+Init	tandinit[] =
+{
+	TCHAR,		BINTEGER,	0,
+	TUCHAR,		BINTEGER,	0,
+	TSHORT,		BINTEGER,	0,
+	TUSHORT,	BINTEGER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BINTEGER,	0,
+	TULONG,		BINTEGER,	0,
+	TVLONG,		BINTEGER,	0,
+	TUVLONG,	BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	trel[NTYPE];
+Init	trelinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BIND,		0,
+	-1,		0,		0,
+};
+
+long	tfunct[1] =
+{
+	BFUNC,
+};
+
+long	tindir[1] =
+{
+	BIND,
+};
+
+long	tdot[1] =
+{
+	BSTRUCT|BUNION,
+};
+
+long	tnot[1] =
+{
+	BNUMBER|BIND,
+};
+
+long	targ[1] =
+{
+	BNUMBER|BIND|BSTRUCT|BUNION,
+};
+
+char	tab[NTYPE][NTYPE] =
+{
+/*TXXX*/	{ 0,
+		},
+
+/*TCHAR*/	{ 0,	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUCHAR*/	{ 0,	TUCHAR, TUCHAR, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TSHORT*/	{ 0,	TSHORT, TUSHORT, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUSHORT*/	{ 0,	TUSHORT, TUSHORT, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TINT*/	{ 0,	TINT, TUINT, TINT, TUINT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUINT*/	{ 0,	TUINT, TUINT, TUINT, TUINT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TLONG*/	{ 0,	TLONG, TULONG, TLONG, TULONG, TLONG, TULONG, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TULONG*/	{ 0,	TULONG, TULONG, TULONG, TULONG, TULONG, TULONG, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TVLONG*/	{ 0,	TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG,
+			TUVLONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUVLONG*/	{ 0,	TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG,
+			TUVLONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TFLOAT*/	{ 0,	TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT,
+			TFLOAT, TFLOAT, TFLOAT, TFLOAT, TDOUBLE, TIND,
+		},
+/*TDOUBLE*/	{ 0,	TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE,
+			TDOUBLE, TDOUBLE, TDOUBLE, TFLOAT, TDOUBLE, TIND,
+		},
+/*TIND*/	{ 0,	TIND, TIND, TIND, TIND, TIND, TIND, TIND,
+			 TIND, TIND, TIND, TIND, TIND, TIND,
+		},
+};
+
+void
+urk(char *name, int max, int i)
+{
+	if(i >= max) {
+		fprint(2, "bad tinit: %s %d>=%d\n", name, i, max);
+		exits("init");
+	}
+}
+
+void
+tinit(void)
+{
+	int i;
+	Init *p;
+
+	for(p=thashinit; p->code >= 0; p++) {
+		urk("thash", nelem(thash), p->code);
+		thash[p->code] = p->value;
+	}
+	for(p=bnamesinit; p->code >= 0; p++) {
+		urk("bnames", nelem(bnames), p->code);
+		bnames[p->code] = p->s;
+	}
+	for(p=tnamesinit; p->code >= 0; p++) {
+		urk("tnames", nelem(tnames), p->code);
+		tnames[p->code] = p->s;
+	}
+	for(p=gnamesinit; p->code >= 0; p++) {
+		urk("gnames", nelem(gnames), p->code);
+		gnames[p->code] = p->s;
+	}
+	for(p=qnamesinit; p->code >= 0; p++) {
+		urk("qnames", nelem(qnames), p->code);
+		qnames[p->code] = p->s;
+	}
+	for(p=cnamesinit; p->code >= 0; p++) {
+		urk("cnames", nelem(cnames), p->code);
+		cnames[p->code] = p->s;
+	}
+	for(p=onamesinit; p->code >= 0; p++) {
+		urk("onames", nelem(onames), p->code);
+		onames[p->code] = p->s;
+	}
+	for(i=0; typeiinit[i] >= 0; i++) {
+		urk("typei", nelem(typei), typeiinit[i]);
+		typei[typeiinit[i]] = 1;
+	}
+	for(i=0; typeuinit[i] >= 0; i++) {
+		urk("typeu", nelem(typeu), typeuinit[i]);
+		typeu[typeuinit[i]] = 1;
+	}
+	for(i=0; typesuvinit[i] >= 0; i++) {
+		urk("typesuv", nelem(typesuv), typesuvinit[i]);
+		typesuv[typesuvinit[i]] = 1;
+	}
+	for(i=0; typeilpinit[i] >= 0; i++) {
+		urk("typeilp", nelem(typeilp), typeilpinit[i]);
+		typeilp[typeilpinit[i]] = 1;
+	}
+	for(i=0; typechlinit[i] >= 0; i++) {
+		urk("typechl", nelem(typechl), typechlinit[i]);
+		typechl[typechlinit[i]] = 1;
+	}
+	for(i=0; typechlpinit[i] >= 0; i++) {
+		urk("typechlp", nelem(typechlp), typechlpinit[i]);
+		typechlp[typechlpinit[i]] = 1;
+	}
+	for(i=0; typechlpfdinit[i] >= 0; i++) {
+		urk("typechlpfd", nelem(typechlpfd), typechlpfdinit[i]);
+		typechlpfd[typechlpfdinit[i]] = 1;
+	}
+	for(i=0; typecinit[i] >= 0; i++) {
+		urk("typec", nelem(typec), typecinit[i]);
+		typec[typecinit[i]] = 1;
+	}
+	for(i=0; typehinit[i] >= 0; i++) {
+		urk("typeh", nelem(typeh), typehinit[i]);
+		typeh[typehinit[i]] = 1;
+	}
+	for(i=0; typeilinit[i] >= 0; i++) {
+		urk("typeil", nelem(typeil), typeilinit[i]);
+		typeil[typeilinit[i]] = 1;
+	}
+	for(i=0; typevinit[i] >= 0; i++) {
+		urk("typev", nelem(typev), typevinit[i]);
+		typev[typevinit[i]] = 1;
+	}
+	for(i=0; typefdinit[i] >= 0; i++) {
+		urk("typefd", nelem(typefd), typefdinit[i]);
+		typefd[typefdinit[i]] = 1;
+	}
+	for(i=0; typeafinit[i] >= 0; i++) {
+		urk("typeaf", nelem(typeaf), typeafinit[i]);
+		typeaf[typeafinit[i]] = 1;
+	}
+	for(i=0; typesuinit[i] >= 0; i++) {
+		urk("typesu", nelem(typesu), typesuinit[i]);
+		typesu[typesuinit[i]] = 1;
+	}
+	for(p=tasigninit; p->code >= 0; p++) {
+		urk("tasign", nelem(tasign), p->code);
+		tasign[p->code] = p->value;
+	}
+	for(p=tasaddinit; p->code >= 0; p++) {
+		urk("tasadd", nelem(tasadd), p->code);
+		tasadd[p->code] = p->value;
+	}
+	for(p=tcastinit; p->code >= 0; p++) {
+		urk("tcast", nelem(tcast), p->code);
+		tcast[p->code] = p->value;
+	}
+	for(p=taddinit; p->code >= 0; p++) {
+		urk("tadd", nelem(tadd), p->code);
+		tadd[p->code] = p->value;
+	}
+	for(p=tsubinit; p->code >= 0; p++) {
+		urk("tsub", nelem(tsub), p->code);
+		tsub[p->code] = p->value;
+	}
+	for(p=tmulinit; p->code >= 0; p++) {
+		urk("tmul", nelem(tmul), p->code);
+		tmul[p->code] = p->value;
+	}
+	for(p=tandinit; p->code >= 0; p++) {
+		urk("tand", nelem(tand), p->code);
+		tand[p->code] = p->value;
+	}
+	for(p=trelinit; p->code >= 0; p++) {
+		urk("trel", nelem(trel), p->code);
+		trel[p->code] = p->value;
+	}
+}
--- /dev/null
+++ b/utils/cat/cat.c
@@ -1,0 +1,35 @@
+#include <lib9.h>
+
+void
+cat(int f, char *s)
+{
+	char buf[8192];
+	long n;
+
+	while((n=read(f, buf, (long)sizeof buf))>0)
+		if(write(1, buf, n)!=n)
+			sysfatal("write error copying %s: %r", s);
+	if(n < 0)
+		sysfatal("error reading %s: %r", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int f, i;
+
+	argv0 = "cat";
+	if(argc == 1)
+		cat(0, "<stdin>");
+	else for(i=1; i<argc; i++){
+		f = open(argv[i], OREAD);
+		if(f < 0)
+			sysfatal("can't open %s: %r", argv[i]);
+		else{
+			cat(f, argv[i]);
+			close(f);
+		}
+	}
+	exits(0);
+}
+
--- /dev/null
+++ b/utils/cat/mkfile
@@ -1,0 +1,10 @@
+<../../mkconfig
+
+TARG=cat
+
+OFILES=	cat.$O\
+
+LIBS=9
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/cc/Nt.c
@@ -1,0 +1,135 @@
+#include	<windows.h>  
+#include	<lib9.h>
+
+#define	Windows	(1<<2)		/* hack - can't include cc.h because of clashes */
+#define	Chunk	(1*1024*1024)
+
+void*
+mysbrk(ulong size)
+{
+	void *v;
+	static int chunk;
+	static uchar *brk;
+
+	if(chunk < size) {
+		chunk = Chunk;
+		if(chunk < size)
+			chunk = Chunk + size;
+		brk = VirtualAlloc(NULL, chunk, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 	
+		if(brk == 0)
+			return (void*)-1;
+	}
+	v = brk;
+	chunk -= size;
+	brk += size;
+	return v;
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	fprint(2, "mywait called\n");
+	abort();
+	return 0;
+}
+
+int
+mydup(int f1, int f2)
+{
+	fprint(2, "mydup called\n");
+	abort();
+	return 0;
+}
+
+int
+mypipe(int *fd)
+{
+	fprint(2, "mypipe called\n");
+	abort();
+	return 0;
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Windows;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return getcwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	fprint(2, "myexec called\n");
+	abort();
+	return 0;
+}
+
+int
+myfork(void)
+{
+	fprint(2, "myfork called\n");
+	abort();
+	return 0;
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(uint n)
+{
+	return mysbrk(n);
+}
+
+void*
+calloc(uint m, uint n)
+{
+	return mysbrk(m*n);
+}
+
+void*
+realloc(void *p, uint n)
+{
+	void *new;
+
+	new = malloc(n);
+	if(new && p)
+		memmove(new, p, n);
+	return new;
+}
+
+void
+free(void *p)
+{
+}
+
+int
+myaccess(char *f)
+{
+	int fd;
+
+	fd = open(f, OREAD);
+	if(fd < 0)
+		return -1;
+	close(fd);
+	return 0;
+}
--- /dev/null
+++ b/utils/cc/Plan9.c
@@ -1,0 +1,114 @@
+#include	"cc.h"
+
+void*
+mysbrk(ulong size)
+{
+	return sbrk(size);
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	int p;
+	Waitmsg *w;
+
+	if((w = wait()) == nil)
+		return -1;
+	else{
+		p = w->pid;
+		*s = 0;
+		if(w->msg[0])
+			*s = 1;
+		free(w);
+		return p;
+	}
+}
+
+int
+mydup(int f1, int f2)
+{
+	return dup(f1,f2);
+}
+
+int
+mypipe(int *fd)
+{
+	return pipe(fd);
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Plan9;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return getwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	return exec(path, argv);
+}
+
+int
+myfork(void)
+{
+	return fork();
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(ulong n)
+{
+	return alloc(n);
+}
+
+void*
+calloc(ulong m, ulong n)
+{
+	return alloc(m*n);
+}
+
+void*
+realloc(void*, ulong)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void
+free(void*)
+{
+}
+
+int
+myaccess(char *f)
+{
+	return access(f, AEXIST);
+}
+
+void
+setmalloctag(void*, ulong)
+{
+}
--- /dev/null
+++ b/utils/cc/Posix.c
@@ -1,0 +1,100 @@
+#include	"cc.h"
+#include	<sys/wait.h>
+
+void*
+mysbrk(ulong size)
+{
+	return (void*)sbrk(size);
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	return wait(s);
+}
+
+int
+mydup(int f1, int f2)
+{
+	if(f2 < 0)
+		return dup(f1);
+	return dup2(f1,f2);
+}
+
+int
+mypipe(int *fd)
+{
+	return pipe(fd);
+}
+
+int
+systemtype(int sys)
+{
+
+	return sys&Unix;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return (char*)getcwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	return execvp(path, argv);
+}
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(size_t n)
+{
+	return alloc(n);
+}
+
+void*
+calloc(size_t m, size_t n)
+{
+	return alloc(m*n);
+}
+
+void*
+realloc(void *p, size_t n)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void
+free(void *p)
+{
+}
+
+int
+myfork(void)
+{
+	return fork();
+}
+
+int
+myaccess(char *f)
+{
+	return access(f, 0);
+}
--- /dev/null
+++ b/utils/cc/acid.c
@@ -1,0 +1,303 @@
+#include "cc.h"
+
+static char *kwd[] =
+{
+	"$adt", "$aggr", "$append", "$builtin", "$complex", "$defn",
+	"$delete", "$do", "$else", "$eval", "$head", "$if",
+	"$local", "$loop", "$return", "$tail", "$then",
+	"$union", "$whatis", "$while",
+};
+
+char*
+amap(char *s)
+{
+	int i, bot, top, new;
+
+	bot = 0;
+	top = bot + nelem(kwd) - 1;
+	while(bot <= top){
+		new = bot + (top - bot)/2;
+		i = strcmp(kwd[new]+1, s);
+		if(i == 0)
+			return kwd[new];
+
+		if(i < 0)
+			bot = new + 1;
+		else
+			top = new - 1;
+	}
+	return s;
+}
+
+Sym*
+acidsue(Type *t)
+{
+	int h;
+	Sym *s;
+
+	if(t != T)
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->suetag && s->suetag->link == t)
+				return s;
+	return 0;
+}
+
+Sym*
+acidfun(Type *t)
+{
+	int h;
+	Sym *s;
+
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->type == t)
+				return s;
+	return 0;
+}
+
+char	acidchar[NTYPE];
+Init	acidcinit[] =
+{
+	TCHAR,		'C',	0,
+	TUCHAR,		'b',	0,
+	TSHORT,		'd',	0,
+	TUSHORT,	'u',	0,
+	TLONG,		'D',	0,
+	TULONG,		'U',	0,
+	TVLONG,		'V',	0,
+	TUVLONG,	'W',	0,
+	TFLOAT,		'f',	0,
+	TDOUBLE,	'F',	0,
+	TARRAY,		'a',	0,
+	TIND,		'X',	0,
+	-1,		0,	0,
+};
+
+static void
+acidinit(void)
+{
+	Init *p;
+
+	for(p=acidcinit; p->code >= 0; p++)
+		acidchar[p->code] = p->value;
+
+	acidchar[TINT] = acidchar[TLONG];
+	acidchar[TUINT] = acidchar[TULONG];
+	if(types[TINT]->width != types[TLONG]->width) {
+		acidchar[TINT] = acidchar[TSHORT];
+		acidchar[TUINT] = acidchar[TUSHORT];
+		if(types[TINT]->width != types[TSHORT]->width)
+			warn(Z, "acidmember int not long or short");
+	}
+	if(types[TIND]->width == types[TUVLONG]->width)
+		acidchar[TIND] = 'Y';
+	
+}
+
+void
+acidmember(Type *t, long off, int flag)
+{
+	Sym *s, *s1;
+	Type *l;
+	static int acidcharinit = 0;
+
+	if(acidcharinit == 0) {
+		acidinit();
+		acidcharinit = 1;
+	}
+	s = t->sym;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "	T%d\n", t->etype);
+		break;
+
+	case TIND:
+		if(s == S)
+			break;
+		if(flag) {
+			for(l=t; l->etype==TIND; l=l->link)
+				;
+			if(typesu[l->etype]) {
+				s1 = acidsue(l->link);
+				if(s1 != S) {
+					Bprint(&outbuf, "	'A' %s %ld %s;\n",
+						amap(s1->name),
+						t->offset+off, amap(s->name));
+					break;
+				}
+			}
+		} else {
+			Bprint(&outbuf,
+				"\tprint(\"\t%s\t\", addr.%s\\X, \"\\n\");\n",
+				amap(s->name), amap(s->name));
+			break;
+		}
+
+	case TINT:
+	case TUINT:
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+	case TARRAY:
+		if(s == S)
+			break;
+		if(flag) {
+			Bprint(&outbuf, "	'%c' %ld %s;\n",
+			acidchar[t->etype], t->offset+off, amap(s->name));
+		} else {
+			Bprint(&outbuf, "\tprint(\"\t%s\t\", addr.%s, \"\\n\");\n",
+				amap(s->name), amap(s->name));
+		}
+		break;
+
+	case TSTRUCT:
+	case TUNION:
+		s1 = acidsue(t->link);
+		if(s1 == S)
+			break;
+		if(flag) {
+			if(s == S) {
+				Bprint(&outbuf, "	{\n");
+				for(l = t->link; l != T; l = l->down)
+					acidmember(l, t->offset+off, flag);
+				Bprint(&outbuf, "	};\n");
+			} else {
+				Bprint(&outbuf, "	%s %ld %s;\n",
+					amap(s1->name),
+					t->offset+off, amap(s->name));
+			}
+		} else {
+			if(s != S) {
+				Bprint(&outbuf, "\tprint(\"%s %s {\\n\");\n",
+					amap(s1->name), amap(s->name));
+				Bprint(&outbuf, "\t%s(addr.%s);\n",
+					amap(s1->name), amap(s->name));
+				Bprint(&outbuf, "\tprint(\"}\\n\");\n");
+			} else {
+				Bprint(&outbuf, "\tprint(\"%s {\\n\");\n",
+					amap(s1->name));
+				Bprint(&outbuf, "\t\t%s(addr+%ld);\n",
+					amap(s1->name), t->offset+off);
+				Bprint(&outbuf, "\tprint(\"}\\n\");\n");
+			}
+		}
+		break;
+	}
+}
+
+void
+acidtype(Type *t)
+{
+	Sym *s;
+	Type *l;
+	Io *i;
+	int n;
+	char *an;
+
+	if(!debug['a'])
+		return;
+	if(debug['a'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	s = acidsue(t->link);
+	if(s == S)
+		return;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "T%d\n", t->etype);
+		return;
+
+	case TUNION:
+	case TSTRUCT:
+		if(debug['s'])
+			goto asmstr;
+		an = amap(s->name);
+		Bprint(&outbuf, "sizeof%s = %ld;\n", an, t->width);
+		Bprint(&outbuf, "aggr %s\n{\n", an);
+		for(l = t->link; l != T; l = l->down)
+			acidmember(l, 0, 1);
+		Bprint(&outbuf, "};\n\n");
+
+		Bprint(&outbuf, "defn\n%s(addr) {\n\tcomplex %s addr;\n", an, an);
+		for(l = t->link; l != T; l = l->down)
+			acidmember(l, 0, 0);
+		Bprint(&outbuf, "};\n\n");
+		break;
+	asmstr:
+		if(s == S)
+			break;
+		for(l = t->link; l != T; l = l->down)
+			if(l->sym != S)
+				Bprint(&outbuf, "#define\t%s.%s\t%ld\n",
+					s->name,
+					l->sym->name,
+					l->offset);
+		break;
+	}
+}
+
+void
+acidvar(Sym *s)
+{
+	int n;
+	Io *i;
+	Type *t;
+	Sym *s1, *s2;
+
+	if(!debug['a'] || debug['s'])
+		return;
+	if(debug['a'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	t = s->type;
+	while(t && t->etype == TIND)
+		t = t->link;
+	if(t == T)
+		return;
+	if(t->etype == TENUM) {
+		Bprint(&outbuf, "%s = ", amap(s->name));
+		if(!typefd[t->etype])
+			Bprint(&outbuf, "%lld;\n", s->vconst);
+		else
+			Bprint(&outbuf, "%f\n;", s->fconst);
+		return;
+	}
+	if(!typesu[t->etype])
+		return;
+	s1 = acidsue(t->link);
+	if(s1 == S)
+		return;
+	switch(s->class) {
+	case CAUTO:
+	case CPARAM:
+		s2 = acidfun(thisfn);
+		if(s2)
+			Bprint(&outbuf, "complex %s %s:%s;\n",
+				amap(s1->name), amap(s2->name), amap(s->name));
+		break;
+	
+	case CSTATIC:
+	case CEXTERN:
+	case CGLOBL:
+	case CLOCAL:
+		Bprint(&outbuf, "complex %s %s;\n",
+			amap(s1->name), amap(s->name));
+		break;
+	}
+}
--- /dev/null
+++ b/utils/cc/bits.c
@@ -1,0 +1,89 @@
+#include	"cc.h"
+
+Bits
+bor(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] | b.b[i];
+	return c;
+}
+
+Bits
+band(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] & b.b[i];
+	return c;
+}
+
+/*
+Bits
+bnot(Bits a)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = ~a.b[i];
+	return c;
+}
+*/
+
+int
+bany(Bits *a)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a->b[i])
+			return 1;
+	return 0;
+}
+
+int
+beq(Bits a, Bits b)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a.b[i] != b.b[i])
+			return 0;
+	return 1;
+}
+
+int
+bnum(Bits a)
+{
+	int i;
+	long b;
+
+	for(i=0; i<BITS; i++)
+		if(b = a.b[i])
+			return 32*i + bitno(b);
+	diag(Z, "bad in bnum");
+	return 0;
+}
+
+Bits
+blsh(uint n)
+{
+	Bits c;
+
+	c = zbits;
+	c.b[n/32] = 1L << (n%32);
+	return c;
+}
+
+int
+bset(Bits a, uint n)
+{
+	if(a.b[n/32] & (1L << (n%32)))
+		return 1;
+	return 0;
+}
--- /dev/null
+++ b/utils/cc/cc.h
@@ -1,0 +1,788 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+
+#ifndef	EXTERN
+#define EXTERN	extern
+#endif
+
+typedef	struct	Node	Node;
+typedef	struct	Sym	Sym;
+typedef	struct	Type	Type;
+typedef	struct	Funct	Funct;
+typedef	struct	Decl	Decl;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+typedef	struct	Term	Term;
+typedef	struct	Init	Init;
+typedef	struct	Bits	Bits;
+
+typedef	Rune	TRune;	/* target system type */
+
+#define	NHUNK		50000L
+#define	BUFSIZ		8192
+#define	NSYMB		1500
+#define	NHASH		1024
+#define	STRINGSZ	200
+#define	HISTSZ		20
+#define YYMAXDEPTH	1500
+#define	NTERM		10
+#define	MAXALIGN	7
+
+#define	SIGN(n)		((uvlong)1<<(n-1))
+#define	MASK(n)		(SIGN(n)|(SIGN(n)-1))
+
+#define	BITS	5
+#define	NVAR	(BITS*sizeof(ulong)*8)
+struct	Bits
+{
+	ulong	b[BITS];
+};
+
+struct	Node
+{
+	Node*	left;
+	Node*	right;
+	void*	label;
+	long	pc;
+	int	reg;
+	long	xoffset;
+	double	fconst;		/* fp constant */
+	vlong	vconst;		/* non fp const */
+	char*	cstring;	/* character string */
+	TRune*	rstring;	/* rune string */
+
+	Sym*	sym;
+	Type*	type;
+	long	lineno;
+	char	op;
+
+	char	oldop;
+	char	xcast;
+	char	class;
+	char	etype;
+	char	complex;
+	char	addable;
+	char	scale;
+	char	garb;
+};
+#define	Z	((Node*)0)
+
+struct	Sym
+{
+	Sym*	link;
+	Type*	type;
+	Type*	suetag;
+	Type*	tenum;
+	char*	macro;
+	long	varlineno;
+	long	offset;
+	vlong	vconst;
+	double	fconst;
+	Node*	label;
+	ushort	lexical;
+	char	*name;
+	ushort	block;
+	ushort	sueblock;
+	char	class;
+	char	sym;
+	char	aused;
+	char	sig;
+};
+#define	S	((Sym*)0)
+
+enum{
+	SIGNONE = 0,
+	SIGDONE = 1,
+	SIGINTERN = 2,
+
+	SIGNINTERN = 1729*325*1729,
+};
+
+struct	Decl
+{
+	Decl*	link;
+	Sym*	sym;
+	Type*	type;
+	long	varlineno;
+	long	offset;
+	short	val;
+	ushort	block;
+	char	class;
+	char	aused;
+};
+#define	D	((Decl*)0)
+
+struct	Type
+{
+	Sym*	sym;
+	Sym*	tag;
+	Funct*	funct;
+	Type*	link;
+	Type*	down;
+	long	width;
+	long	offset;
+	long	lineno;
+	schar	shift;
+	char	nbits;
+	char	etype;
+	char	garb;
+};
+
+#define	T	((Type*)0)
+#define	NODECL	((void(*)(int, Type*, Sym*))0)
+
+struct	Init			/* general purpose initialization */
+{
+	int	code;
+	ulong	value;
+	char*	s;
+};
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char*	p;
+	char	b[BUFSIZ];
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+EXTERN Hist*	hist;
+
+struct	Term
+{
+	vlong	mult;
+	Node	*node;
+};
+
+enum
+{
+	Axxx,
+	Ael1,
+	Ael2,
+	Asu2,
+	Aarg0,
+	Aarg1,
+	Aarg2,
+	Aaut3,
+	NALIGN,
+};
+
+enum				/* also in ../{8a,0a}.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+
+enum
+{
+	DMARK,
+	DAUTO,
+	DSUE,
+	DLABEL,
+};
+enum
+{
+	OXXX,
+	OADD,
+	OADDR,
+	OAND,
+	OANDAND,
+	OARRAY,
+	OAS,
+	OASI,
+	OASADD,
+	OASAND,
+	OASASHL,
+	OASASHR,
+	OASDIV,
+	OASHL,
+	OASHR,
+	OASLDIV,
+	OASLMOD,
+	OASLMUL,
+	OASLSHR,
+	OASMOD,
+	OASMUL,
+	OASOR,
+	OASSUB,
+	OASXOR,
+	OBIT,
+	OBREAK,
+	OCASE,
+	OCAST,
+	OCOMMA,
+	OCOND,
+	OCONST,
+	OCONTINUE,
+	ODIV,
+	ODOT,
+	ODOTDOT,
+	ODWHILE,
+	OENUM,
+	OEQ,
+	OFOR,
+	OFUNC,
+	OGE,
+	OGOTO,
+	OGT,
+	OHI,
+	OHS,
+	OIF,
+	OIND,
+	OINDREG,
+	OINIT,
+	OLABEL,
+	OLDIV,
+	OLE,
+	OLIST,
+	OLMOD,
+	OLMUL,
+	OLO,
+	OLS,
+	OLSHR,
+	OLT,
+	OMOD,
+	OMUL,
+	ONAME,
+	ONE,
+	ONOT,
+	OOR,
+	OOROR,
+	OPOSTDEC,
+	OPOSTINC,
+	OPREDEC,
+	OPREINC,
+	OPROTO,
+	OREGISTER,
+	ORETURN,
+	OSET,
+	OSIGN,
+	OSIZE,
+	OSTRING,
+	OLSTRING,
+	OSTRUCT,
+	OSUB,
+	OSWITCH,
+	OUNION,
+	OUSED,
+	OWHILE,
+	OXOR,
+	ONEG,
+	OCOM,
+	OPOS,
+	OELEM,
+
+	OTST,		/* used in some compilers */
+	OINDEX,
+	OFAS,
+	OREGPAIR,
+	OEXREG,
+
+	OEND
+};
+enum
+{
+	TXXX,
+	TCHAR,
+	TUCHAR,
+	TSHORT,
+	TUSHORT,
+	TINT,
+	TUINT,
+	TLONG,
+	TULONG,
+	TVLONG,
+	TUVLONG,
+	TFLOAT,
+	TDOUBLE,
+	TIND,
+	TFUNC,
+	TARRAY,
+	TVOID,
+	TSTRUCT,
+	TUNION,
+	TENUM,
+	TDOT,
+	NTYPE,
+
+	TAUTO	= NTYPE,
+	TEXTERN,
+	TSTATIC,
+	TTYPEDEF,
+	TTYPESTR,
+	TREGISTER,
+	TCONSTNT,
+	TVOLATILE,
+	TUNSIGNED,
+	TSIGNED,
+	TFILE,
+	TOLD,
+	NALLTYPES,
+
+	/* adapt size of Rune to target system's size */
+	TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
+};
+enum
+{
+	CXXX,
+	CAUTO,
+	CEXTERN,
+	CGLOBL,
+	CSTATIC,
+	CLOCAL,
+	CTYPEDEF,
+	CTYPESTR,
+	CPARAM,
+	CSELEM,
+	CLABEL,
+	CEXREG,
+	NCTYPES,
+};
+enum
+{
+	GXXX		= 0,
+	GCONSTNT	= 1<<0,
+	GVOLATILE	= 1<<1,
+	NGTYPES		= 1<<2,
+
+	GINCOMPLETE	= 1<<2,
+};
+enum
+{
+	BCHAR		= 1L<<TCHAR,
+	BUCHAR		= 1L<<TUCHAR,
+	BSHORT		= 1L<<TSHORT,
+	BUSHORT		= 1L<<TUSHORT,
+	BINT		= 1L<<TINT,
+	BUINT		= 1L<<TUINT,
+	BLONG		= 1L<<TLONG,
+	BULONG		= 1L<<TULONG,
+	BVLONG		= 1L<<TVLONG,
+	BUVLONG		= 1L<<TUVLONG,
+	BFLOAT		= 1L<<TFLOAT,
+	BDOUBLE		= 1L<<TDOUBLE,
+	BIND		= 1L<<TIND,
+	BFUNC		= 1L<<TFUNC,
+	BARRAY		= 1L<<TARRAY,
+	BVOID		= 1L<<TVOID,
+	BSTRUCT		= 1L<<TSTRUCT,
+	BUNION		= 1L<<TUNION,
+	BENUM		= 1L<<TENUM,
+	BFILE		= 1L<<TFILE,
+	BDOT		= 1L<<TDOT,
+	BCONSTNT	= 1L<<TCONSTNT,
+	BVOLATILE	= 1L<<TVOLATILE,
+	BUNSIGNED	= 1L<<TUNSIGNED,
+	BSIGNED		= 1L<<TSIGNED,
+	BAUTO		= 1L<<TAUTO,
+	BEXTERN		= 1L<<TEXTERN,
+	BSTATIC		= 1L<<TSTATIC,
+	BTYPEDEF	= 1L<<TTYPEDEF,
+	BTYPESTR	= 1L<<TTYPESTR,
+	BREGISTER	= 1L<<TREGISTER,
+
+	BINTEGER	= BCHAR|BUCHAR|BSHORT|BUSHORT|BINT|BUINT|
+				BLONG|BULONG|BVLONG|BUVLONG,
+	BNUMBER		= BINTEGER|BFLOAT|BDOUBLE,
+
+/* these can be overloaded with complex types */
+
+	BCLASS		= BAUTO|BEXTERN|BSTATIC|BTYPEDEF|BTYPESTR|BREGISTER,
+	BGARB		= BCONSTNT|BVOLATILE,
+};
+
+struct	Funct
+{
+	Sym*	sym[OEND];
+	Sym*	castto[NTYPE];
+	Sym*	castfr[NTYPE];
+};
+
+EXTERN struct
+{
+	Type*	tenum;		/* type of entire enum */
+	Type*	cenum;		/* type of current enum run */
+	vlong	lastenum;	/* value of current enum */
+	double	floatenum;	/* value of current enum */
+} en;
+
+EXTERN	int	autobn;
+EXTERN	long	autoffset;
+EXTERN	int	blockno;
+EXTERN	Decl*	dclstack;
+EXTERN	char	debug[256];
+EXTERN	Hist*	ehist;
+EXTERN	long	firstbit;
+EXTERN	Sym*	firstarg;
+EXTERN	Type*	firstargtype;
+EXTERN	Decl*	firstdcl;
+EXTERN	int	fperror;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	int	hasdoubled;
+EXTERN	char*	hunk;
+EXTERN	char*	include[20];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lastbit;
+EXTERN	char	lastclass;
+EXTERN	Type*	lastdcl;
+EXTERN	long	lastfield;
+EXTERN	Type*	lasttype;
+EXTERN	long	lineno;
+EXTERN	long	nearln;
+EXTERN	int	nerrors;
+EXTERN	int	newflag;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Node*	nodproto;
+EXTERN	Node*	nodcast;
+EXTERN	Biobuf	outbuf;
+EXTERN	Biobuf	diagbuf;
+EXTERN	char*	outfile;
+EXTERN	char*	pathname;
+EXTERN	int	peekc;
+EXTERN	long	stkoff;
+EXTERN	Type*	strf;
+EXTERN	Type*	strl;
+EXTERN	char	symb[NSYMB];
+EXTERN	Sym*	symstring;
+EXTERN	int	taggen;
+EXTERN	Type*	tfield;
+EXTERN	Type*	tufield;
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	Type*	thisfn;
+EXTERN	long	thunk;
+EXTERN	Type*	types[NTYPE];
+EXTERN	Type*	fntypes[NTYPE];
+EXTERN	Node*	initlist;
+EXTERN	Term	term[NTERM];
+EXTERN	int	nterm;
+EXTERN	int	packflg;
+EXTERN	int	fproundflg;
+EXTERN	int	profileflg;
+EXTERN	int	ncontin;
+EXTERN	int	newvlongcode;
+EXTERN	int	canreach;
+EXTERN	int	warnreach;
+EXTERN	Bits	zbits;
+
+extern	char	*onames[], *tnames[], *gnames[];
+extern	char	*cnames[], *qnames[], *bnames[];
+extern	char	tab[NTYPE][NTYPE];
+extern	char	comrel[], invrel[], logrel[];
+extern	long	ncast[], tadd[], tand[];
+extern	long	targ[], tasadd[], tasign[], tcast[];
+extern	long	tdot[], tfunct[], tindir[], tmul[];
+extern	long	tnot[], trel[], tsub[];
+
+extern	char	typeaf[];
+extern	char	typefd[];
+extern	char	typei[];
+extern	char	typesu[];
+extern	char	typesuv[];
+extern	char	typeu[];
+extern	char	typev[];
+extern	char	typec[];
+extern	char	typeh[];
+extern	char	typeil[];
+extern	char	typeilp[];
+extern	char	typechl[];
+extern	char	typechlv[];
+extern	char	typechlvp[];
+extern	char	typechlp[];
+extern	char	typechlpfd[];
+EXTERN	char*	typeswitch;
+
+EXTERN	char*	typeword;
+EXTERN	char*	typecmplx;
+
+extern	ulong	thash1;
+extern	ulong	thash2;
+extern	ulong	thash3;
+extern	ulong	thash[];
+
+/*
+ *	Inferno.c/Posix.c/Nt.c
+ */
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+int	myaccess(char*);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
+
+/*
+ *	parser
+ */
+int	yyparse(void);
+int	mpatof(char*, double*);
+int	mpatov(char*, vlong*);
+
+/*
+ *	lex.c
+ */
+void*	allocn(void*, long, long);
+void*	alloc(long);
+void	cinit(void);
+int	compile(char*, char**, int);
+void	errorexit(void);
+int	filbuf(void);
+int	getc(void);
+long	getr(void);
+int	getnsc(void);
+Sym*	lookup(void);
+void	main(int, char*[]);
+void	newfile(char*, int);
+void	newio(void);
+void	pushio(void);
+long	escchar(long, int, int);
+Sym*	slookup(char*);
+void	syminit(Sym*);
+void	unget(int);
+long	yylex(void);
+int	Lconv(Fmt*);
+int	Tconv(Fmt*);
+int	FNconv(Fmt*);
+int	Oconv(Fmt*);
+int	Qconv(Fmt*);
+int	VBconv(Fmt*);
+void	setinclude(char*);
+
+/*
+ * mac.c
+ */
+void	dodefine(char*);
+void	domacro(void);
+Sym*	getsym(void);
+long	getnsn(void);
+void	linehist(char*, int);
+void	macdef(void);
+void	macprag(void);
+void	macend(void);
+void	macexpand(Sym*, char*);
+void	macif(int);
+void	macinc(void);
+void	maclin(void);
+void	macund(void);
+
+/*
+ * dcl.c
+ */
+Node*	doinit(Sym*, Type*, long, Node*);
+Type*	tcopy(Type*);
+Node*	init1(Sym*, Type*, long, int);
+Node*	newlist(Node*, Node*);
+void	adecl(int, Type*, Sym*);
+int	anyproto(Node*);
+void	argmark(Node*, int);
+void	dbgdecl(Sym*);
+Node*	dcllabel(Sym*, int);
+Node*	dodecl(void(*)(int, Type*, Sym*), int, Type*, Node*);
+Sym*	mkstatic(Sym*);
+void	doenum(Sym*, Node*);
+void	snap(Type*);
+Type*	dotag(Sym*, int, int);
+void	edecl(int, Type*, Sym*);
+Type*	fnproto(Node*);
+Type*	fnproto1(Node*);
+void	markdcl(void);
+Type*	paramconv(Type*, int, int);
+void	pdecl(int, Type*, Sym*);
+Decl*	push(void);
+Decl*	push1(Sym*);
+Node*	revertdcl(void);
+#undef round
+#define	round	ccround
+#undef log2
+#define	log2	cclog2
+long	round(long, int);
+int	rsametype(Type*, Type*, int, int);
+int	sametype(Type*, Type*);
+ulong	sign(Sym*);
+ulong	signature(Type*);
+void	sualign(Type*);
+void	tmerge(Type*, Sym*);
+void	walkparam(Node*, int);
+void	xdecl(int, Type*, Sym*);
+Node*	contig(Sym*, Node*, long);
+
+/*
+ * com.c
+ */
+void	ccom(Node*);
+void	complex(Node*);
+int	tcom(Node*);
+int	tcoma(Node*, Node*, Type*, int);
+int	tcomd(Node*);
+int	tcomo(Node*, int);
+int	tcomx(Node*);
+int	tlvalue(Node*);
+void	constas(Node*, Type*, Type*);
+Node*	uncomma(Node*);
+Node*	uncomargs(Node*);
+
+/*
+ * con.c
+ */
+void	acom(Node*);
+void	acom1(vlong, Node*);
+void	acom2(Node*, Type*);
+int	acomcmp1(void*, void*);
+int	acomcmp2(void*, void*);
+int	addo(Node*);
+void	evconst(Node*);
+
+/*
+ * funct.c
+ */
+int	isfunct(Node*);
+void	dclfunct(Type*, Sym*);
+
+/*
+ * sub.c
+ */
+void	arith(Node*, int);
+int	castucom(Node*);
+int	deadheads(Node*);
+Type*	dotsearch(Sym*, Type*, Node*, long*);
+long	dotoffset(Type*, Type*, Node*);
+void	gethunk(void);
+Node*	invert(Node*);
+int	bitno(long);
+void	makedot(Node*, Type*, long);
+int	mixedasop(Type*, Type*);
+Node*	new(int, Node*, Node*);
+Node*	new1(int, Node*, Node*);
+int	nilcast(Type*, Type*);
+int	nocast(Type*, Type*);
+void	prtree(Node*, char*);
+void	prtree1(Node*, int, int);
+void	relcon(Node*, Node*);
+int	relindex(int);
+int	simpleg(long);
+Type*	garbt(Type*, long);
+int	simplec(long);
+Type*	simplet(long);
+int	stcompat(Node*, Type*, Type*, long[]);
+int	tcompat(Node*, Type*, Type*, long[]);
+void	tinit(void);
+Type*	typ(int, Type*);
+Type*	copytyp(Type*);
+void	typeext(Type*, Node*);
+void	typeext1(Type*, Node*);
+int	side(Node*);
+int	vconst(Node*);
+int	log2(uvlong);
+int	vlog(Node*);
+int	topbit(ulong);
+void	simplifyshift(Node*);
+long	typebitor(long, long);
+void	diag(Node*, char*, ...);
+void	warn(Node*, char*, ...);
+void	yyerror(char*, ...);
+void	fatal(Node*, char*, ...);
+
+/*
+ * acid.c
+ */
+void	acidtype(Type*);
+void	acidvar(Sym*);
+
+/*
+ * pickle.c
+ */
+void	pickletype(Type*);
+
+/*
+ * bits.c
+ */
+Bits	bor(Bits, Bits);
+Bits	band(Bits, Bits);
+Bits	bnot(Bits);
+int	bany(Bits*);
+int	bnum(Bits);
+Bits	blsh(uint);
+int	beq(Bits, Bits);
+int	bset(Bits, uint);
+
+/*
+ * dpchk.c
+ */
+void	dpcheck(Node*);
+void	arginit(void);
+void	pragvararg(void);
+void	pragpack(void);
+void	pragfpround(void);
+void pragprofile(void);
+void	pragincomplete(void);
+
+/*
+ * calls to machine depend part
+ */
+void	codgen(Node*, Node*);
+void	gclean(void);
+void	gextern(Sym*, Node*, long, long);
+void	ginit(void);
+long	outstring(char*, long);
+long	outlstring(TRune*, long);
+void	xcom(Node*);
+long	exreg(Type*);
+long	align(long, Type*, int);
+long	maxround(long, long);
+
+extern	schar	ewidth[];
+
+/*
+ * com64
+ */
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+double	convvtof(vlong);
+vlong	convftov(double);
+double	convftox(double, int);
+vlong	convvtox(vlong, int);
+
+/*
+ * machcap
+ */
+int	machcap(Node*);
+
+#pragma	varargck	argpos	warn	2
+#pragma	varargck	argpos	diag	2
+#pragma	varargck	argpos	yyerror	1
+
+#pragma	varargck	type	"F"	Node*
+#pragma	varargck	type	"L"	long
+#pragma	varargck	type	"Q"	long
+#pragma	varargck	type	"O"	int
+#pragma	varargck	type	"T"	Type*
+#pragma	varargck	type	"|"	int
+
+#undef true
+#define	true	mytrue
--- /dev/null
+++ b/utils/cc/cc.y
@@ -1,0 +1,1183 @@
+%{
+#include "cc.h"
+%}
+%union	{
+	Node*	node;
+	Sym*	sym;
+	Type*	type;
+	struct
+	{
+		Type*	t;
+		uchar	c;
+	} tycl;
+	struct
+	{
+		Type*	t1;
+		Type*	t2;
+		Type*	t3;
+		uchar	c;
+	} tyty;
+	struct
+	{
+		char*	s;
+		long	l;
+	} sval;
+	long	lval;
+	double	dval;
+	vlong	vval;
+}
+%type	<sym>	ltag
+%type	<lval>	gctname gcname cname gname tname
+%type	<lval>	gctnlist gcnlist zgnlist
+%type	<type>	tlist sbody complex
+%type	<tycl>	types
+%type	<node>	zarglist arglist zcexpr
+%type	<node>	name block stmnt cexpr expr xuexpr pexpr
+%type	<node>	zelist elist adecl slist uexpr string lstring
+%type	<node>	xdecor xdecor2 labels label ulstmnt
+%type	<node>	adlist edecor tag qual qlist
+%type	<node>	abdecor abdecor1 abdecor2 abdecor3
+%type	<node>	zexpr lexpr init ilist forexpr
+
+%left	';'
+%left	','
+%right	'=' LPE LME LMLE LDVE LMDE LRSHE LLSHE LANDE LXORE LORE
+%right	'?' ':'
+%left	LOROR
+%left	LANDAND
+%left	'|'
+%left	'^'
+%left	'&'
+%left	LEQ LNE
+%left	'<' '>' LLE LGE
+%left	LLSH LRSH
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	LMM LPP LMG '.' '[' '('
+
+%token	<sym>	LNAME LTYPE
+%token	<dval>	LFCONST LDCONST
+%token	<vval>	LCONST LLCONST LUCONST LULCONST LVLCONST LUVLCONST
+%token	<sval>	LSTRING LLSTRING
+%token		LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO
+%token		LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO
+%token	LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED
+%token	LSTATIC LSTRUCT LSWITCH LTYPEDEF LTYPESTR LUNION LUNSIGNED
+%token	LWHILE LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET LSIGNOF
+%token	LRESTRICT LINLINE
+%%
+prog:
+|	prog xdecl
+
+/*
+ * external declarator
+ */
+xdecl:
+	zctlist ';'
+	{
+		dodecl(xdecl, lastclass, lasttype, Z);
+	}
+|	zctlist xdlist ';'
+|	zctlist xdecor
+	{
+		lastdcl = T;
+		firstarg = S;
+		dodecl(xdecl, lastclass, lasttype, $2);
+		if(lastdcl == T || lastdcl->etype != TFUNC) {
+			diag($2, "not a function");
+			lastdcl = types[TFUNC];
+		}
+		thisfn = lastdcl;
+		markdcl();
+		firstdcl = dclstack;
+		argmark($2, 0);
+	}
+	pdecl
+	{
+		argmark($2, 1);
+	}
+	block
+	{
+		Node *n;
+
+		n = revertdcl();
+		if(n)
+			$6 = new(OLIST, n, $6);
+		if(!debug['a'] && !debug['Z'])
+			codgen($6, $2);
+	}
+
+xdlist:
+	xdecor
+	{
+		dodecl(xdecl, lastclass, lasttype, $1);
+	}
+|	xdecor
+	{
+		$1 = dodecl(xdecl, lastclass, lasttype, $1);
+	}
+	'=' init
+	{
+		doinit($1->sym, $1->type, 0L, $4);
+	}
+|	xdlist ',' xdlist
+
+xdecor:
+	xdecor2
+|	'*' zgnlist xdecor
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+
+xdecor2:
+	tag
+|	'(' xdecor ')'
+	{
+		$$ = $2;
+	}
+|	xdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+	}
+|	xdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+/*
+ * automatic declarator
+ */
+adecl:
+	ctlist ';'
+	{
+		$$ = dodecl(adecl, lastclass, lasttype, Z);
+	}
+|	ctlist adlist ';'
+	{
+		$$ = $2;
+	}
+
+adlist:
+	xdecor
+	{
+		dodecl(adecl, lastclass, lasttype, $1);
+		$$ = Z;
+	}
+|	xdecor
+	{
+		$1 = dodecl(adecl, lastclass, lasttype, $1);
+	}
+	'=' init
+	{
+		long w;
+
+		w = $1->sym->type->width;
+		$$ = doinit($1->sym, $1->type, 0L, $4);
+		$$ = contig($1->sym, $$, w);
+	}
+|	adlist ',' adlist
+	{
+		$$ = $1;
+		if($3 != Z) {
+			$$ = $3;
+			if($1 != Z)
+				$$ = new(OLIST, $1, $3);
+		}
+	}
+
+/*
+ * parameter declarator
+ */
+pdecl:
+|	pdecl ctlist pdlist ';'
+
+pdlist:
+	xdecor
+	{
+		dodecl(pdecl, lastclass, lasttype, $1);
+	}
+|	pdlist ',' pdlist
+
+/*
+ * structure element declarator
+ */
+edecl:
+	tlist
+	{
+		lasttype = $1;
+	}
+	zedlist ';'
+|	edecl tlist
+	{
+		lasttype = $2;
+	}
+	zedlist ';'
+
+zedlist:					/* extension */
+	{
+		lastfield = 0;
+		edecl(CXXX, lasttype, S);
+	}
+|	edlist
+
+edlist:
+	edecor
+	{
+		dodecl(edecl, CXXX, lasttype, $1);
+	}
+|	edlist ',' edlist
+
+edecor:
+	xdecor
+	{
+		lastbit = 0;
+		firstbit = 1;
+	}
+|	tag ':' lexpr
+	{
+		$$ = new(OBIT, $1, $3);
+	}
+|	':' lexpr
+	{
+		$$ = new(OBIT, Z, $2);
+	}
+
+/*
+ * abstract declarator
+ */
+abdecor:
+	{
+		$$ = (Z);
+	}
+|	abdecor1
+
+abdecor1:
+	'*' zgnlist
+	{
+		$$ = new(OIND, (Z), Z);
+		$$->garb = simpleg($2);
+	}
+|	'*' zgnlist abdecor1
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+|	abdecor2
+
+abdecor2:
+	abdecor3
+|	abdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+	}
+|	abdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+abdecor3:
+	'(' ')'
+	{
+		$$ = new(OFUNC, (Z), Z);
+	}
+|	'[' zexpr ']'
+	{
+		$$ = new(OARRAY, (Z), $2);
+	}
+|	'(' abdecor1 ')'
+	{
+		$$ = $2;
+	}
+
+init:
+	expr
+|	'{' ilist '}'
+	{
+		$$ = new(OINIT, invert($2), Z);
+	}
+
+qual:
+	'[' lexpr ']'
+	{
+		$$ = new(OARRAY, $2, Z);
+	}
+|	'.' ltag
+	{
+		$$ = new(OELEM, Z, Z);
+		$$->sym = $2;
+	}
+|	qual '='
+
+qlist:
+	init ','
+|	qlist init ','
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+|	qual
+|	qlist qual
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+ilist:
+	qlist
+|	init
+|	qlist init
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+zarglist:
+	{
+		$$ = Z;
+	}
+|	arglist
+	{
+		$$ = invert($1);
+	}
+
+
+arglist:
+	name
+|	tlist abdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	tlist xdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	'.' '.' '.'
+	{
+		$$ = new(ODOTDOT, Z, Z);
+	}
+|	arglist ',' arglist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+block:
+	'{' slist '}'
+	{
+		$$ = invert($2);
+	//	if($2 != Z)
+	//		$$ = new(OLIST, $2, $$);
+		if($$ == Z)
+			$$ = new(OLIST, Z, Z);
+	}
+
+slist:
+	{
+		$$ = Z;
+	}
+|	slist adecl
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+|	slist stmnt
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+labels:
+	label
+|	labels label
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+label:
+	LCASE expr ':'
+	{
+		$$ = new(OCASE, $2, Z);
+	}
+|	LDEFAULT ':'
+	{
+		$$ = new(OCASE, Z, Z);
+	}
+|	LNAME ':'
+	{
+		$$ = new(OLABEL, dcllabel($1, 1), Z);
+	}
+
+stmnt:
+	error ';'
+	{
+		$$ = Z;
+	}
+|	ulstmnt
+|	labels ulstmnt
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+forexpr:
+	zcexpr
+|	ctlist adlist
+	{
+		$$ = $2;
+	}
+
+ulstmnt:
+	zcexpr ';'
+|	{
+		markdcl();
+	}
+	block
+	{
+		$$ = revertdcl();
+		if($$)
+			$$ = new(OLIST, $$, $2);
+		else
+			$$ = $2;
+	}
+|	LIF '(' cexpr ')' stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, Z));
+		if($5 == Z)
+			warn($3, "empty if body");
+	}
+|	LIF '(' cexpr ')' stmnt LELSE stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, $7));
+		if($5 == Z)
+			warn($3, "empty if body");
+		if($7 == Z)
+			warn($3, "empty else body");
+	}
+|	{ markdcl(); } LFOR '(' forexpr ';' zcexpr ';' zcexpr ')' stmnt
+	{
+		$$ = revertdcl();
+		if($$){
+			if($4)
+				$4 = new(OLIST, $$, $4);
+			else
+				$4 = $$;
+		}
+		$$ = new(OFOR, new(OLIST, $6, new(OLIST, $4, $8)), $10);
+	}
+|	LWHILE '(' cexpr ')' stmnt
+	{
+		$$ = new(OWHILE, $3, $5);
+	}
+|	LDO stmnt LWHILE '(' cexpr ')' ';'
+	{
+		$$ = new(ODWHILE, $5, $2);
+	}
+|	LRETURN zcexpr ';'
+	{
+		$$ = new(ORETURN, $2, Z);
+		$$->type = thisfn->link;
+	}
+|	LSWITCH '(' cexpr ')' stmnt
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->vconst = 0;
+		$$->type = types[TINT];
+		$3 = new(OSUB, $$, $3);
+
+		$$ = new(OCONST, Z, Z);
+		$$->vconst = 0;
+		$$->type = types[TINT];
+		$3 = new(OSUB, $$, $3);
+
+		$$ = new(OSWITCH, $3, $5);
+	}
+|	LBREAK ';'
+	{
+		$$ = new(OBREAK, Z, Z);
+	}
+|	LCONTINUE ';'
+	{
+		$$ = new(OCONTINUE, Z, Z);
+	}
+|	LGOTO ltag ';'
+	{
+		$$ = new(OGOTO, dcllabel($2, 0), Z);
+	}
+|	LUSED '(' zelist ')' ';'
+	{
+		$$ = new(OUSED, $3, Z);
+	}
+|	LSET '(' zelist ')' ';'
+	{
+		$$ = new(OSET, $3, Z);
+	}
+
+zcexpr:
+	{
+		$$ = Z;
+	}
+|	cexpr
+
+zexpr:
+	{
+		$$ = Z;
+	}
+|	lexpr
+
+lexpr:
+	expr
+	{
+		$$ = new(OCAST, $1, Z);
+		$$->type = types[TLONG];
+	}
+
+cexpr:
+	expr
+|	cexpr ',' cexpr
+	{
+		$$ = new(OCOMMA, $1, $3);
+	}
+
+expr:
+	xuexpr
+|	expr '*' expr
+	{
+		$$ = new(OMUL, $1, $3);
+	}
+|	expr '/' expr
+	{
+		$$ = new(ODIV, $1, $3);
+	}
+|	expr '%' expr
+	{
+		$$ = new(OMOD, $1, $3);
+	}
+|	expr '+' expr
+	{
+		$$ = new(OADD, $1, $3);
+	}
+|	expr '-' expr
+	{
+		$$ = new(OSUB, $1, $3);
+	}
+|	expr LRSH expr
+	{
+		$$ = new(OASHR, $1, $3);
+	}
+|	expr LLSH expr
+	{
+		$$ = new(OASHL, $1, $3);
+	}
+|	expr '<' expr
+	{
+		$$ = new(OLT, $1, $3);
+	}
+|	expr '>' expr
+	{
+		$$ = new(OGT, $1, $3);
+	}
+|	expr LLE expr
+	{
+		$$ = new(OLE, $1, $3);
+	}
+|	expr LGE expr
+	{
+		$$ = new(OGE, $1, $3);
+	}
+|	expr LEQ expr
+	{
+		$$ = new(OEQ, $1, $3);
+	}
+|	expr LNE expr
+	{
+		$$ = new(ONE, $1, $3);
+	}
+|	expr '&' expr
+	{
+		$$ = new(OAND, $1, $3);
+	}
+|	expr '^' expr
+	{
+		$$ = new(OXOR, $1, $3);
+	}
+|	expr '|' expr
+	{
+		$$ = new(OOR, $1, $3);
+	}
+|	expr LANDAND expr
+	{
+		$$ = new(OANDAND, $1, $3);
+	}
+|	expr LOROR expr
+	{
+		$$ = new(OOROR, $1, $3);
+	}
+|	expr '?' cexpr ':' expr
+	{
+		$$ = new(OCOND, $1, new(OLIST, $3, $5));
+	}
+|	expr '=' expr
+	{
+		$$ = new(OAS, $1, $3);
+	}
+|	expr LPE expr
+	{
+		$$ = new(OASADD, $1, $3);
+	}
+|	expr LME expr
+	{
+		$$ = new(OASSUB, $1, $3);
+	}
+|	expr LMLE expr
+	{
+		$$ = new(OASMUL, $1, $3);
+	}
+|	expr LDVE expr
+	{
+		$$ = new(OASDIV, $1, $3);
+	}
+|	expr LMDE expr
+	{
+		$$ = new(OASMOD, $1, $3);
+	}
+|	expr LLSHE expr
+	{
+		$$ = new(OASASHL, $1, $3);
+	}
+|	expr LRSHE expr
+	{
+		$$ = new(OASASHR, $1, $3);
+	}
+|	expr LANDE expr
+	{
+		$$ = new(OASAND, $1, $3);
+	}
+|	expr LXORE expr
+	{
+		$$ = new(OASXOR, $1, $3);
+	}
+|	expr LORE expr
+	{
+		$$ = new(OASOR, $1, $3);
+	}
+
+xuexpr:
+	uexpr
+|	'(' tlist abdecor ')' xuexpr
+	{
+		$$ = new(OCAST, $5, Z);
+		dodecl(NODECL, CXXX, $2, $3);
+		$$->type = lastdcl;
+		$$->xcast = 1;
+	}
+|	'(' tlist abdecor ')' '{' ilist '}'	/* extension */
+	{
+		$$ = new(OSTRUCT, $6, Z);
+		dodecl(NODECL, CXXX, $2, $3);
+		$$->type = lastdcl;
+	}
+
+uexpr:
+	pexpr
+|	'*' xuexpr
+	{
+		$$ = new(OIND, $2, Z);
+	}
+|	'&' xuexpr
+	{
+		$$ = new(OADDR, $2, Z);
+	}
+|	'+' xuexpr
+	{
+		$$ = new(OPOS, $2, Z);
+	}
+|	'-' xuexpr
+	{
+		$$ = new(ONEG, $2, Z);
+	}
+|	'!' xuexpr
+	{
+		$$ = new(ONOT, $2, Z);
+	}
+|	'~' xuexpr
+	{
+		$$ = new(OCOM, $2, Z);
+	}
+|	LPP xuexpr
+	{
+		$$ = new(OPREINC, $2, Z);
+	}
+|	LMM xuexpr
+	{
+		$$ = new(OPREDEC, $2, Z);
+	}
+|	LSIZEOF uexpr
+	{
+		$$ = new(OSIZE, $2, Z);
+	}
+|	LSIGNOF uexpr
+	{
+		$$ = new(OSIGN, $2, Z);
+	}
+
+pexpr:
+	'(' cexpr ')'
+	{
+		$$ = $2;
+	}
+|	LSIZEOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIZE, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4);
+		$$->type = lastdcl;
+	}
+|	LSIGNOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIGN, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4);
+		$$->type = lastdcl;
+	}
+|	pexpr '(' zelist ')'
+	{
+		$$ = new(OFUNC, $1, Z);
+		if($1->op == ONAME)
+		if($1->type == T)
+			dodecl(xdecl, CXXX, types[TINT], $$);
+		$$->right = invert($3);
+	}
+|	pexpr '[' cexpr ']'
+	{
+		$$ = new(OIND, new(OADD, $1, $3), Z);
+	}
+|	pexpr LMG ltag
+	{
+		$$ = new(ODOT, new(OIND, $1, Z), Z);
+		$$->sym = $3;
+	}
+|	pexpr '.' ltag
+	{
+		$$ = new(ODOT, $1, Z);
+		$$->sym = $3;
+	}
+|	pexpr LPP
+	{
+		$$ = new(OPOSTINC, $1, Z);
+	}
+|	pexpr LMM
+	{
+		$$ = new(OPOSTDEC, $1, Z);
+	}
+|	name
+|	LCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TINT];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LUCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUINT];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LULCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TULONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LDCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TDOUBLE];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LFCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TFLOAT];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TVLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LUVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUVLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	string
+|	lstring
+
+string:
+	LSTRING
+	{
+		$$ = new(OSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TCHAR]);
+		$$->type->width = $1.l + 1;
+		$$->cstring = $1.s;
+		$$->sym = symstring;
+		$$->etype = TARRAY;
+		$$->class = CSTATIC;
+	}
+|	string LSTRING
+	{
+		char *s;
+		int n;
+
+		n = $1->type->width - 1;
+		s = alloc(n+$2.l+MAXALIGN);
+
+		memcpy(s, $1->cstring, n);
+		memcpy(s+n, $2.s, $2.l);
+		s[n+$2.l] = 0;
+
+		$$ = $1;
+		$$->type->width += $2.l;
+		$$->cstring = s;
+	}
+
+lstring:
+	LLSTRING
+	{
+		$$ = new(OLSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TRUNE]);
+		$$->type->width = $1.l + sizeof(TRune);
+		$$->rstring = (TRune*)$1.s;
+		$$->sym = symstring;
+		$$->etype = TARRAY;
+		$$->class = CSTATIC;
+	}
+|	lstring LLSTRING
+	{
+		char *s;
+		int n;
+
+		n = $1->type->width - sizeof(TRune);
+		s = alloc(n+$2.l+MAXALIGN);
+
+		memcpy(s, $1->rstring, n);
+		memcpy(s+n, $2.s, $2.l);
+		*(TRune*)(s+n+$2.l) = 0;
+
+		$$ = $1;
+		$$->type->width += $2.l;
+		$$->rstring = (TRune*)s;
+	}
+
+zelist:
+	{
+		$$ = Z;
+	}
+|	elist
+
+elist:
+	expr
+|	elist ',' elist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+sbody:
+	'{'
+	{
+		$<tyty>$.t1 = strf;
+		$<tyty>$.t2 = strl;
+		$<tyty>$.t3 = lasttype;
+		$<tyty>$.c = lastclass;
+		strf = T;
+		strl = T;
+		lastbit = 0;
+		firstbit = 1;
+		lastclass = CXXX;
+		lasttype = T;
+	}
+	edecl '}'
+	{
+		$$ = strf;
+		strf = $<tyty>2.t1;
+		strl = $<tyty>2.t2;
+		lasttype = $<tyty>2.t3;
+		lastclass = $<tyty>2.c;
+	}
+
+zctlist:
+	{
+		lastclass = CXXX;
+		lasttype = types[TINT];
+	}
+|	ctlist
+
+types:
+	complex
+	{
+		$$.t = $1;
+		$$.c = CXXX;
+	}
+|	tname
+	{
+		$$.t = simplet($1);
+		$$.c = CXXX;
+	}
+|	gcnlist
+	{
+		$$.t = simplet($1);
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+	}
+|	complex gctnlist
+	{
+		$$.t = $1;
+		$$.c = simplec($2);
+		$$.t = garbt($$.t, $2);
+		if($2 & ~BCLASS & ~BGARB)
+			diag(Z, "duplicate types given: %T and %Q", $1, $2);
+	}
+|	tname gctnlist
+	{
+		$$.t = simplet(typebitor($1, $2));
+		$$.c = simplec($2);
+		$$.t = garbt($$.t, $2);
+	}
+|	gcnlist complex zgnlist
+	{
+		$$.t = $2;
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1|$3);
+	}
+|	gcnlist tname
+	{
+		$$.t = simplet($2);
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+	}
+|	gcnlist tname gctnlist
+	{
+		$$.t = simplet(typebitor($2, $3));
+		$$.c = simplec($1|$3);
+		$$.t = garbt($$.t, $1|$3);
+	}
+
+tlist:
+	types
+	{
+		$$ = $1.t;
+		if($1.c != CXXX)
+			diag(Z, "illegal combination of class 4: %s", cnames[$1.c]);
+	}
+
+ctlist:
+	types
+	{
+		lasttype = $1.t;
+		lastclass = $1.c;
+	}
+
+complex:
+	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, 0);
+		$$ = $2->suetag;
+	}
+|	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, autobn);
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		sualign($$);
+	}
+|	LSTRUCT sbody
+	{
+		taggen++;
+		sprint(symb, "_%d_", taggen);
+		$$ = dotag(lookup(), TSTRUCT, autobn);
+		$$->link = $2;
+		sualign($$);
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, 0);
+		$$ = $2->suetag;
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, autobn);
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		sualign($$);
+	}
+|	LUNION sbody
+	{
+		taggen++;
+		sprint(symb, "_%d_", taggen);
+		$$ = dotag(lookup(), TUNION, autobn);
+		$$->link = $2;
+		sualign($$);
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, 0);
+		$$ = $2->suetag;
+		if($$->link == T)
+			$$->link = types[TINT];
+		$$ = $$->link;
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, autobn);
+	}
+	'{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		if(en.tenum == T) {
+			diag(Z, "enum type ambiguous: %s", $2->name);
+			en.tenum = types[TINT];
+		}
+		$$->link = en.tenum;
+		$$ = en.tenum;
+	}
+|	LENUM '{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = en.tenum;
+	}
+|	LTYPE
+	{
+		$$ = tcopy($1->type);
+	}
+
+gctnlist:
+	gctname
+|	gctnlist gctname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+zgnlist:
+	{
+		$$ = 0;
+	}
+|	zgnlist gname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gctname:
+	tname
+|	gname
+|	cname
+
+gcnlist:
+	gcname
+|	gcnlist gcname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gcname:
+	gname
+|	cname
+
+enum:
+	LNAME
+	{
+		doenum($1, Z);
+	}
+|	LNAME '=' expr
+	{
+		doenum($1, $3);
+	}
+|	enum ','
+|	enum ',' enum
+
+tname:	/* type words */
+	LCHAR { $$ = BCHAR; }
+|	LSHORT { $$ = BSHORT; }
+|	LINT { $$ = BINT; }
+|	LLONG { $$ = BLONG; }
+|	LSIGNED { $$ = BSIGNED; }
+|	LUNSIGNED { $$ = BUNSIGNED; }
+|	LFLOAT { $$ = BFLOAT; }
+|	LDOUBLE { $$ = BDOUBLE; }
+|	LVOID { $$ = BVOID; }
+
+cname:	/* class words */
+	LAUTO { $$ = BAUTO; }
+|	LSTATIC { $$ = BSTATIC; }
+|	LEXTERN { $$ = BEXTERN; }
+|	LTYPEDEF { $$ = BTYPEDEF; }
+|	LTYPESTR { $$ = BTYPESTR; }
+|	LREGISTER { $$ = BREGISTER; }
+|	LINLINE { $$ = 0; }
+
+gname:	/* garbage words */
+	LCONSTNT { $$ = BCONSTNT; }
+|	LVOLATILE { $$ = BVOLATILE; }
+|	LRESTRICT { $$ = 0; }
+
+name:
+	LNAME
+	{
+		$$ = new(ONAME, Z, Z);
+		if($1->class == CLOCAL)
+			$1 = mkstatic($1);
+		$$->sym = $1;
+		$$->type = $1->type;
+		$$->etype = TVOID;
+		if($$->type != T)
+			$$->etype = $$->type->etype;
+		$$->xoffset = $1->offset;
+		$$->class = $1->class;
+		$1->aused = 1;
+	}
+tag:
+	ltag
+	{
+		$$ = new(ONAME, Z, Z);
+		$$->sym = $1;
+		$$->type = $1->type;
+		$$->etype = TVOID;
+		if($$->type != T)
+			$$->etype = $$->type->etype;
+		$$->xoffset = $1->offset;
+		$$->class = $1->class;
+	}
+ltag:
+	LNAME
+|	LTYPE
+%%
--- /dev/null
+++ b/utils/cc/com.c
@@ -1,0 +1,1464 @@
+#include "cc.h"
+
+typedef struct Com Com;
+struct Com
+{
+	int	n;
+	Node	*t[500];
+};
+
+int compar(Node*, int);
+static void comma(Node*);
+static Node*	commas(Com*, Node*);
+
+void
+complex(Node *n)
+{
+
+	if(n == Z)
+		return;
+
+	nearln = n->lineno;
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "pre complex");
+	if(tcom(n))
+		return;
+	if(debug['y'] || 1)
+		comma(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "t complex");
+	ccom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "c complex");
+	acom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "a complex");
+	xcom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "x complex");
+}
+
+/*
+ * evaluate types
+ * evaluate lvalues (addable == 1)
+ */
+enum
+{
+	ADDROF	= 1<<0,
+	CASTOF	= 1<<1,
+	ADDROP	= 1<<2,
+};
+
+int
+tcom(Node *n)
+{
+
+	return tcomo(n, ADDROF);
+}
+
+int
+tcomo(Node *n, int f)
+{
+	Node *l, *r;
+	Type *t;
+	int o;
+	static TRune zer;
+
+	if(n == Z) {
+		diag(Z, "Z in tcom");
+		errorexit();
+	}
+	n->addable = 0;
+	l = n->left;
+	r = n->right;
+
+	switch(n->op) {
+	default:
+		diag(n, "unknown op in type complex: %O", n->op);
+		goto bad;
+
+	case ODOTDOT:
+		/*
+		 * tcom has already been called on this subtree
+		 */
+		*n = *n->left;
+		if(n->type == T)
+			goto bad;
+		break;
+
+	case OCAST:
+		if(n->type == T)
+			break;
+		if(n->type->width == types[TLONG]->width) {
+			if(tcomo(l, ADDROF|CASTOF))
+				goto bad;
+		} else
+			if(tcom(l))
+				goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, n->type, tcast))
+			goto bad;
+		break;
+
+	case ORETURN:
+		if(l == Z) {
+			if(n->type->etype != TVOID)
+				warn(n, "null return of a typed function");
+			break;
+		}
+		if(tcom(l))
+			goto bad;
+		typeext(n->type, l);
+		if(tcompat(n, n->type, l->type, tasign))
+			break;
+		constas(n, n->type, l->type);
+		if(!sametype(n->type, l->type)) {
+			l = new1(OCAST, l, Z);
+			l->type = n->type;
+			n->left = l;
+		}
+		break;
+
+	case OASI:	/* same as as, but no test for const */
+		n->op = OAS;
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+
+		typeext(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OAS:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext(l->type, r);
+		if(tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		constas(n, l->type, r->type);
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OASADD:
+	case OASSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		if(tcompat(n, l->type, r->type, tasadd))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+	case OASDIV:
+	case OASLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		if(tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASDIV)
+				n->op = OASLDIV;
+			if(n->op == OASMUL)
+				n->op = OASLMUL;
+		}
+		break;
+
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->type = l->type;
+		n->right = new1(OCAST, r, Z);
+		n->right->type = types[TINT];
+		if(typeu[n->type->etype]) {
+			if(n->op == OASASHR)
+				n->op = OASLSHR;
+		}
+		break;
+
+	case OASMOD:
+	case OASLMOD:
+	case OASOR:
+	case OASAND:
+	case OASXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASMOD)
+				n->op = OASLMOD;
+		}
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+	case OPOSTINC:
+	case OPOSTDEC:
+		if(tcom(l))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, types[TINT], tadd))
+			goto bad;
+		n->type = l->type;
+		if(n->type->etype == TIND)
+		if(n->type->link->width < 1) {
+			snap(n->type->link);
+			if(n->type->link->width < 1)
+				diag(n, "inc/dec of a void pointer");
+		}
+		break;
+
+	case OEQ:
+	case ONE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext(l->type, r);
+		typeext(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		n->type = types[TINT];
+		break;
+
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		typeext1(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		if(typeu[n->type->etype])
+			n->op = logrel[relindex(n->op)];
+		n->type = types[TINT];
+		break;
+
+	case OCOND:
+		o = tcom(l);
+		o |= tcom(r->left);
+		if(o | tcom(r->right))
+			goto bad;
+		if(r->right->type->etype == TIND && vconst(r->left) == 0) {
+			r->left->type = r->right->type;
+			r->left->vconst = 0;
+		}
+		if(r->left->type->etype == TIND && vconst(r->right) == 0) {
+			r->right->type = r->left->type;
+			r->right->vconst = 0;
+		}
+		if(sametype(r->right->type, r->left->type)) {
+			r->type = r->right->type;
+			n->type = r->type;
+			break;
+		}
+		if(tcompat(r, r->left->type, r->right->type, trel))
+			goto bad;
+		arith(r, 0);
+		n->type = r->type;
+		break;
+
+	case OADD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tadd))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tsub))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype]) {
+			if(n->op == ODIV)
+				n->op = OLDIV;
+			if(n->op == OMUL)
+				n->op = OLMUL;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->right = Z;
+		arith(n, 1);
+		n->right = new1(OCAST, r, Z);
+		n->right->type = types[TINT];
+		if(typeu[n->type->etype])
+			if(n->op == OASHR)
+				n->op = OLSHR;
+		break;
+
+	case OAND:
+	case OOR:
+	case OXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMOD:
+	case OLMOD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype])
+			n->op = OLMOD;
+		break;
+
+	case OPOS:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		r = l;
+		l = new(OCONST, Z, Z);
+		l->vconst = 0;
+		l->type = types[TINT];
+		n->op = OADD;
+		n->right = r;
+		n->left = l;
+
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tsub))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case ONEG:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		if(!machcap(n)) {
+			r = l;
+			l = new(OCONST, Z, Z);
+			l->vconst = 0;
+			l->type = types[TINT];
+			n->op = OSUB;
+			n->right = r;
+			n->left = l;
+
+			if(tcom(l))
+				goto bad;
+			if(tcompat(n, l->type, r->type, tsub))
+				goto bad;
+		}
+		arith(n, 1);
+		break;
+
+	case OCOM:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		if(!machcap(n)) {
+			r = l;
+			l = new(OCONST, Z, Z);
+			l->vconst = -1;
+			l->type = types[TINT];
+			n->op = OXOR;
+			n->right = r;
+			n->left = l;
+
+			if(tcom(l))
+				goto bad;
+			if(tcompat(n, l->type, r->type, tand))
+				goto bad;
+		}
+		arith(n, 1);
+		break;
+
+	case ONOT:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, T, l->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OANDAND:
+	case OOROR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, T, l->type, tnot) |
+		   tcompat(n, T, r->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OCOMMA:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		n->type = r->type;
+		break;
+
+
+	case OSIGN:	/* extension signof(type) returns a hash */
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "signof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width < 0) {
+			diag(n, "signof undefined type");
+			goto bad;
+		}
+		n->op = OCONST;
+		n->left = Z;
+		n->right = Z;
+		n->vconst = convvtox(signature(n->type), TULONG);
+		n->type = types[TULONG];
+		break;
+
+	case OSIZE:
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "sizeof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width <= 0) {
+			diag(n, "sizeof undefined type");
+			goto bad;
+		}
+		if(n->type->etype == TFUNC) {
+			diag(n, "sizeof function");
+			goto bad;
+		}
+		n->op = OCONST;
+		n->left = Z;
+		n->right = Z;
+		n->vconst = convvtox(n->type->width, TINT);
+		n->type = types[TINT];
+		break;
+
+	case OFUNC:
+		o = tcomo(l, 0);
+		if(o)
+			goto bad;
+		if(l->type->etype == TIND && l->type->link->etype == TFUNC) {
+			l = new1(OIND, l, Z);
+			l->type = l->left->type->link;
+			n->left = l;
+		}
+		if(tcompat(n, T, l->type, tfunct))
+			goto bad;
+		if(o | tcoma(l, r, l->type->down, 1))
+			goto bad;
+		n->type = l->type->link;
+		if(!debug['B'])
+			if(l->type->down == T || l->type->down->etype == TOLD) {
+				nerrors--;
+				diag(n, "function args not checked: %F", l);
+			}
+		dpcheck(n);
+		break;
+
+	case ONAME:
+		if(n->type == T) {
+			diag(n, "name not declared: %F", n);
+			goto bad;
+		}
+		if(n->type->etype == TENUM) {
+			n->op = OCONST;
+			n->type = n->sym->tenum;
+			if(!typefd[n->type->etype])
+				n->vconst = n->sym->vconst;
+			else
+				n->fconst = n->sym->fconst;
+			break;
+		}
+		n->addable = 1;
+		if(n->class == CEXREG) {
+			n->op = OREGISTER;
+			if(thechar == '8')
+				n->op = OEXREG;
+			n->reg = n->sym->offset;
+			n->xoffset = 0;
+			break;
+		}
+		break;
+
+	case OLSTRING:
+		if(n->type->link != types[TRUNE]) {
+			o = outstring(0, 0);
+			while(o & 3) {
+				outlstring(&zer, sizeof(TRune));
+				o = outlstring(0, 0);
+			}
+		}
+		n->op = ONAME;
+		n->xoffset = outlstring(n->rstring, n->type->width);
+		n->addable = 1;
+		break;
+
+	case OSTRING:
+		if(n->type->link != types[TCHAR]) {
+			o = outstring(0, 0);
+			while(o & 3) {
+				outstring("", 1);
+				o = outstring(0, 0);
+			}
+		}
+		n->op = ONAME;
+		n->xoffset = outstring(n->cstring, n->type->width);
+		n->addable = 1;
+		break;
+
+	case OCONST:
+		break;
+
+	case ODOT:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tdot))
+			goto bad;
+		if(tcomd(n))
+			goto bad;
+		break;
+
+	case OADDR:
+		if(tcomo(l, ADDROP))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(l->type->nbits) {
+			diag(n, "address of a bit field");
+			goto bad;
+		}
+		if(l->op == OREGISTER) {
+			diag(n, "address of a register");
+			goto bad;
+		}
+		n->type = typ(TIND, l->type);
+		n->type->width = types[TIND]->width;
+		break;
+
+	case OIND:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tindir))
+			goto bad;
+		n->type = l->type->link;
+		n->addable = 1;
+		break;
+
+	case OSTRUCT:
+		if(tcomx(n))
+			goto bad;
+		break;
+	}
+	t = n->type;
+	if(t == T)
+		goto bad;
+	if(t->width < 0) {
+		snap(t);
+		if(t->width < 0) {
+			if(typesu[t->etype] && t->tag)
+				diag(n, "structure not fully declared %s", t->tag->name);
+			else
+				diag(n, "structure not fully declared");
+			goto bad;
+		}
+	}
+	if(typeaf[t->etype]) {
+		if(f & ADDROF)
+			goto addaddr;
+		if(f & ADDROP)
+			warn(n, "address of array/func ignored");
+	}
+	return 0;
+
+addaddr:
+	if(tlvalue(n))
+		goto bad;
+	l = new1(OXXX, Z, Z);
+	*l = *n;
+	n->op = OADDR;
+	if(l->type->etype == TARRAY)
+		l->type = l->type->link;
+	n->left = l;
+	n->right = Z;
+	n->addable = 0;
+	n->type = typ(TIND, l->type);
+	n->type->width = types[TIND]->width;
+	return 0;
+
+bad:
+	n->type = T;
+	return 1;
+}
+
+int
+tcoma(Node *l, Node *n, Type *t, int f)
+{
+	Node *n1;
+	int o;
+
+	if(t != T)
+	if(t->etype == TOLD || t->etype == TDOT)	/* .../old in prototype */
+		t = T;
+	if(n == Z) {
+		if(t != T && !sametype(t, types[TVOID])) {
+			diag(n, "not enough function arguments: %F", l);
+			return 1;
+		}
+		return 0;
+	}
+	if(n->op == OLIST) {
+		o = tcoma(l, n->left, t, 0);
+		if(t != T) {
+			t = t->down;
+			if(t == T)
+				t = types[TVOID];
+		}
+		return o | tcoma(l, n->right, t, 1);
+	}
+	if(f && t != T)
+		tcoma(l, Z, t->down, 0);
+	if(tcom(n) || tcompat(n, T, n->type, targ))
+		return 1;
+	if(sametype(t, types[TVOID])) {
+		diag(n, "too many function arguments: %F", l);
+		return 1;
+	}
+	if(t != T) {
+		typeext(t, n);
+		if(stcompat(nodproto, t, n->type, tasign)) {
+			diag(l, "argument prototype mismatch \"%T\" for \"%T\": %F",
+				n->type, t, l);
+			return 1;
+		}
+		switch(t->etype) {
+		case TCHAR:
+		case TSHORT:
+			t = types[TINT];
+			break;
+
+		case TUCHAR:
+		case TUSHORT:
+			t = types[TUINT];
+			break;
+		}
+	} else
+	switch(n->type->etype)
+	{
+	case TCHAR:
+	case TSHORT:
+		t = types[TINT];
+		break;
+
+	case TUCHAR:
+	case TUSHORT:
+		t = types[TUINT];
+		break;
+
+	case TFLOAT:
+		t = types[TDOUBLE];
+	}
+	if(t != T && !sametype(t, n->type)) {
+		n1 = new1(OXXX, Z, Z);
+		*n1 = *n;
+		n->op = OCAST;
+		n->left = n1;
+		n->right = Z;
+		n->type = t;
+		n->addable = 0;
+	}
+	return 0;
+}
+
+int
+tcomd(Node *n)
+{
+	Type *t;
+	long o;
+
+	o = 0;
+	t = dotsearch(n->sym, n->left->type->link, n, &o);
+	if(t == T) {
+		diag(n, "not a member of struct/union: %F", n);
+		return 1;
+	}
+	makedot(n, t, o);
+	return 0;
+}
+
+int
+tcomx(Node *n)
+{
+	Type *t;
+	Node *l, *r, **ar, **al;
+	int e;
+
+	e = 0;
+	if(n->type->etype != TSTRUCT) {
+		diag(n, "constructor must be a structure");
+		return 1;
+	}
+	l = invert(n->left);
+	n->left = l;
+	al = &n->left;
+	for(t = n->type->link; t != T; t = t->down) {
+		if(l == Z) {
+			diag(n, "constructor list too short");
+			return 1;
+		}
+		if(l->op == OLIST) {
+			r = l->left;
+			ar = &l->left;
+			al = &l->right;
+			l = l->right;
+		} else {
+			r = l;
+			ar = al;
+			l = Z;
+		}
+		if(tcom(r))
+			e++;
+		typeext(t, r);
+		if(tcompat(n, t, r->type, tasign))
+			e++;
+		constas(n, t, r->type);
+		if(!e && !sametype(t, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = t;
+			*ar = r;
+		}
+	}
+	if(l != Z) {
+		diag(n, "constructor list too long");
+		return 1;
+	}
+	return e;
+}
+
+int
+tlvalue(Node *n)
+{
+
+	if(!n->addable) {
+		diag(n, "not an l-value");
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * hoist comma operators out of expressions
+ *	(a,b) OP c => (a, b OP c)
+ *	OP(a,b) =>	(a, OP b)
+ *	a OP (b,c) => (b, a OP c)
+ */
+
+static Node*
+comargs(Com *com, Node *n)
+{
+	if(n != Z && n->op == OLIST){
+		n->left = comargs(com, n->left);
+		n->right = comargs(com, n->right);
+	}
+	return commas(com, n);
+}
+
+static Node*
+commas(Com *com, Node *n)
+{
+	Node *t;
+
+	if(n == Z)
+		return n;
+	switch(n->op){
+	case OREGISTER:
+	case OINDREG:
+	case OCONST:
+	case ONAME:
+	case OSTRING:
+		/* leaf */
+		return n;
+
+	case OCOMMA:
+		t = commas(com, n->left);
+		if(com->n >= nelem(com->t))
+			fatal(n, "comma list overflow");
+		com->t[com->n++] = t;
+		return commas(com, n->right);
+
+	case OFUNC:
+		n->left = commas(com, n->left);
+		n->right = comargs(com, n->right);
+		return n;
+
+	case OCOND:
+		n->left = commas(com, n->left);
+		comma(n->right->left);
+		comma(n->right->right);
+		return n;
+
+	case OANDAND:
+	case OOROR:
+		n->left = commas(com, n->left);
+		comma(n->right);
+		return n;
+
+	case ORETURN:
+		comma(n->left);
+		return n;
+	}
+	n->left = commas(com, n->left);
+	if(n->right != Z)
+		n->right = commas(com, n->right);
+	return n;
+}
+
+static void
+comma(Node *n)
+{
+	Com com;
+	Node *nn;
+
+	com.n = 0;
+	nn = commas(&com, n);
+	if(com.n > 0){
+if(debug['y'])print("n=%d\n", com.n);
+if(debug['y']) prtree(nn, "res");
+		if(nn != n)
+			*n = *nn;
+		while(com.n > 0){
+if(debug['y']) prtree(com.t[com.n-1], "tree");
+			nn = new1(OXXX, Z, Z);
+			*nn = *n;
+			n->op = OCOMMA;
+			n->type = nn->type;
+			n->left = com.t[--com.n];
+			n->right = nn;
+			n->lineno = n->left->lineno;
+		}
+if(debug['y']) prtree(n, "final");
+	}else if(n != nn)
+		fatal(n, "odd tree");
+}
+
+/*
+ *	general rewrite
+ *	(IND(ADDR x)) ==> x
+ *	(ADDR(IND x)) ==> x
+ *	remove some zero operands
+ *	remove no op casts
+ *	evaluate constants
+ */
+void
+ccom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+loop:
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	case OAS:
+	case OASXOR:
+	case OASAND:
+	case OASOR:
+	case OASMOD:
+	case OASLMOD:
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+	case OASDIV:
+	case OASLDIV:
+	case OASMUL:
+	case OASLMUL:
+	case OASSUB:
+	case OASADD:
+		ccom(l);
+		ccom(r);
+		if(n->op == OASLSHR || n->op == OASASHR || n->op == OASASHL)
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst < 0)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		break;
+
+	case OCAST:
+		if(castucom(n))
+			warn(n, "32-bit unsigned complement zero-extended to 64 bits");
+		ccom(l);
+		if(l->op == OCONST) {
+			evconst(n);
+			if(n->op == OCONST)
+				break;
+		}
+		if(nocast(l->type, n->type) &&
+		   (!typefd[l->type->etype] || typeu[l->type->etype] && typeu[n->type->etype])) {
+			l->type = n->type;
+			*n = *l;
+		}
+		break;
+
+	case OCOND:
+		ccom(l);
+		ccom(r);
+		if(l->op == OCONST)
+			if(vconst(l) == 0)
+				*n = *r->right;
+			else
+				*n = *r->left;
+		break;
+
+	case OREGISTER:
+	case OINDREG:
+	case OCONST:
+	case ONAME:
+		break;
+
+	case OADDR:
+		ccom(l);
+		l->etype = TVOID;
+		if(l->op == OIND) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OIND:
+		ccom(l);
+		if(l->op == OADDR) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OEQ:
+	case ONE:
+
+	case OLE:
+	case OGE:
+	case OLT:
+	case OGT:
+
+	case OLS:
+	case OHS:
+	case OLO:
+	case OHI:
+		ccom(l);
+		ccom(r);
+		if(compar(n, 0) || compar(n, 1))
+			break;
+		relcon(l, r);
+		relcon(r, l);
+		goto common;
+
+	case OASHR:
+	case OASHL:
+	case OLSHR:
+		ccom(l);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		if(vconst(r) == 0) {
+			*n = *l;
+			break;
+		}
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst <= -t)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		goto common;
+
+	case OMUL:
+	case OLMUL:
+		ccom(l);
+		t = vconst(l);
+		if(t == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		if(t == 1) {
+			*n = *r;
+			goto loop;
+		}
+		ccom(r);
+		t = vconst(r);
+		if(t == 0 && !side(l)) {
+			*n = *r;
+			break;
+		}
+		if(t == 1) {
+			*n = *l;
+			break;
+		}
+		goto common;
+
+	case ODIV:
+	case OLDIV:
+		ccom(l);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		t = vconst(r);
+		if(t == 0) {
+			diag(n, "divide check");
+			*n = *r;
+			break;
+		}
+		if(t == 1) {
+			*n = *l;
+			break;
+		}
+		goto common;
+
+	case OSUB:
+		ccom(r);
+		if(r->op == OCONST) {
+			if(typefd[r->type->etype]) {
+				n->op = OADD;
+				r->fconst = -r->fconst;
+				goto loop;
+			} else {
+				n->op = OADD;
+				r->vconst = -r->vconst;
+				goto loop;
+			}
+		}
+		ccom(l);
+		goto common;
+
+	case OXOR:
+	case OOR:
+	case OADD:
+		ccom(l);
+		if(vconst(l) == 0) {
+			*n = *r;
+			goto loop;
+		}
+		ccom(r);
+		if(vconst(r) == 0) {
+			*n = *l;
+			break;
+		}
+		goto commute;
+
+	case OAND:
+		ccom(l);
+		ccom(r);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		if(vconst(r) == 0 && !side(l)) {
+			*n = *r;
+			break;
+		}
+
+	commute:
+		/* look for commutative constant */
+		if(r->op == OCONST) {
+			if(l->op == n->op) {
+				if(l->left->op == OCONST) {
+					n->right = l->right;
+					l->right = r;
+					goto loop;
+				}
+				if(l->right->op == OCONST) {
+					n->right = l->left;
+					l->left = r;
+					goto loop;
+				}
+			}
+		}
+		if(l->op == OCONST) {
+			if(r->op == n->op) {
+				if(r->left->op == OCONST) {
+					n->left = r->right;
+					r->right = l;
+					goto loop;
+				}
+				if(r->right->op == OCONST) {
+					n->left = r->left;
+					r->left = l;
+					goto loop;
+				}
+			}
+		}
+		goto common;
+
+	case OANDAND:
+		ccom(l);
+		if(vconst(l) == 0) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		goto common;
+
+	case OOROR:
+		ccom(l);
+		if(l->op == OCONST && l->vconst != 0) {
+			*n = *l;
+			n->vconst = 1;
+			break;
+		}
+		ccom(r);
+		goto common;
+
+	default:
+		if(l != Z)
+			ccom(l);
+		if(r != Z)
+			ccom(r);
+	common:
+		if(l != Z)
+		if(l->op != OCONST)
+			break;
+		if(r != Z)
+		if(r->op != OCONST)
+			break;
+		evconst(n);
+	}
+}
+
+/*	OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
+static char *cmps[12] = 
+{
+	"==", "!=", "<=", "<=", "<", "<", ">=", ">=", ">", ">",
+};
+
+/* 128-bit numbers */
+typedef struct Big Big;
+struct Big
+{
+	vlong a;
+	uvlong b;
+};
+static int
+cmp(Big x, Big y)
+{
+	if(x.a != y.a){
+		if(x.a < y.a)
+			return -1;
+		return 1;
+	}
+	if(x.b != y.b){
+		if(x.b < y.b)
+			return -1;
+		return 1;
+	}
+	return 0;
+}
+static Big
+add(Big x, int y)
+{
+	uvlong ob;
+	
+	ob = x.b;
+	x.b += y;
+	if(y > 0 && x.b < ob)
+		x.a++;
+	if(y < 0 && x.b > ob)
+		x.a--;
+	return x;
+} 
+
+Big
+big(vlong a, uvlong b)
+{
+	Big x;
+
+	x.a = a;
+	x.b = b;
+	return x;
+}
+
+int
+compar(Node *n, int reverse)
+{
+	Big lo, hi, x;
+	int op;
+	char xbuf[40], cmpbuf[50];
+	Node *l, *r;
+	Type *lt, *rt;
+
+	/*
+	 * The point of this function is to diagnose comparisons 
+	 * that can never be true or that look misleading because
+	 * of the `usual arithmetic conversions'.  As an example 
+	 * of the latter, if x is a ulong, then if(x <= -1) really means
+	 * if(x <= 0xFFFFFFFF), while if(x <= -1LL) really means
+	 * what it says (but 8c compiles it wrong anyway).
+	 */
+
+	if(reverse){
+		r = n->left;
+		l = n->right;
+		op = comrel[relindex(n->op)];
+	}else{
+		l = n->left;
+		r = n->right;
+		op = n->op;
+	}
+
+	/*
+	 * Skip over left casts to find out the original expression range.
+	 */
+	while(l->op == OCAST)
+		l = l->left;
+	if(l->op == OCONST)
+		return 0;
+	lt = l->type;
+	if(l->op == ONAME && l->sym->type){
+		lt = l->sym->type;
+		if(lt->etype == TARRAY)
+			lt = lt->link;
+	}
+	if(lt == T)
+		return 0;
+	if(lt->etype == TXXX || lt->etype > TUVLONG)
+		return 0;
+	
+	/*
+	 * Skip over the right casts to find the on-screen value.
+	 */
+	if(r->op != OCONST)
+		return 0;
+	while(r->oldop == OCAST && !r->xcast)
+		r = r->left;
+	rt = r->type;
+	if(rt == T)
+		return 0;
+
+	x.b = r->vconst;
+	x.a = 0;
+	if((rt->etype&1) && r->vconst < 0)	/* signed negative */
+		x.a = ~(uvlong)0;
+
+	if((lt->etype&1)==0){
+		/* unsigned */
+		lo = big(0, 0);
+		if(lt->width == 8)
+			hi = big(0, ~(uvlong)0);
+		else
+			hi = big(0, ((uvlong)1<<(l->type->width*8))-1);
+	}else{
+		lo = big(~(uvlong)0, -((uvlong)1<<(l->type->width*8-1)));
+		hi = big(0, ((uvlong)1<<(l->type->width*8-1))-1);
+	}
+
+	switch(op){
+	case OLT:
+	case OLO:
+	case OGE:
+	case OHS:
+		if(cmp(x, lo) <= 0)
+			goto useless;
+		if(cmp(x, add(hi, 1)) >= 0)
+			goto useless;
+		break;
+	case OLE:
+	case OLS:
+	case OGT:
+	case OHI:
+		if(cmp(x, add(lo, -1)) <= 0)
+			goto useless;
+		if(cmp(x, hi) >= 0)
+			goto useless;
+		break;
+	case OEQ:
+	case ONE:
+		/*
+		 * Don't warn about comparisons if the expression
+		 * is as wide as the value: the compiler-supplied casts
+		 * will make both outcomes possible.
+		 */
+		if(lt->width >= rt->width && debug['w'] < 2)
+			return 0;
+		if(cmp(x, lo) < 0 || cmp(x, hi) > 0)
+			goto useless;
+		break;
+	}
+	return 0;
+
+useless:
+	if((x.a==0 && x.b<=9) || (x.a==~(uvlong)0 && x.b >= -(uvlong)9))
+		snprint(xbuf, sizeof xbuf, "%lld", x.b);
+	else if(x.a == 0)
+		snprint(xbuf, sizeof xbuf, "%#llux", x.b);
+	else
+		snprint(xbuf, sizeof xbuf, "%#llx", x.b);
+	if(reverse)
+		snprint(cmpbuf, sizeof cmpbuf, "%s %s %T",
+			xbuf, cmps[relindex(n->op)], lt);
+	else
+		snprint(cmpbuf, sizeof cmpbuf, "%T %s %s",
+			lt, cmps[relindex(n->op)], xbuf);
+if(debug['y']) prtree(n, "strange");
+	warn(n, "useless or misleading comparison: %s", cmpbuf);
+	return 0;
+}
+
--- /dev/null
+++ b/utils/cc/com64.c
@@ -1,0 +1,619 @@
+#include "cc.h"
+
+/*
+ * this is machine dependent, but it is totally
+ * common on all of the 64-bit symulating machines.
+ */
+
+#define	FNX	100	/* botch -- redefinition */
+
+Node*	nodaddv;
+Node*	nodsubv;
+Node*	nodmulv;
+Node*	noddivv;
+Node*	noddivvu;
+Node*	nodmodv;
+Node*	nodmodvu;
+Node*	nodlshv;
+Node*	nodrshav;
+Node*	nodrshlv;
+Node*	nodandv;
+Node*	nodorv;
+Node*	nodxorv;
+Node*	nodnegv;
+Node*	nodcomv;
+
+Node*	nodtestv;
+Node*	nodeqv;
+Node*	nodnev;
+Node*	nodlev;
+Node*	nodltv;
+Node*	nodgev;
+Node*	nodgtv;
+Node*	nodhiv;
+Node*	nodhsv;
+Node*	nodlov;
+Node*	nodlsv;
+
+Node*	nodf2v;
+Node*	nodd2v;
+Node*	nodp2v;
+Node*	nodsi2v;
+Node*	nodui2v;
+Node*	nodsl2v;
+Node*	nodul2v;
+Node*	nodsh2v;
+Node*	noduh2v;
+Node*	nodsc2v;
+Node*	noduc2v;
+
+Node*	nodv2f;
+Node*	nodv2d;
+Node*	nodv2ui;
+Node*	nodv2si;
+Node*	nodv2ul;
+Node*	nodv2sl;
+Node*	nodv2uh;
+Node*	nodv2sh;
+Node*	nodv2uc;
+Node*	nodv2sc;
+
+Node*	nodvpp;
+Node*	nodppv;
+Node*	nodvmm;
+Node*	nodmmv;
+
+Node*	nodvasop;
+
+char	etconv[NTYPE];	/* for _vasop */
+Init	initetconv[] =
+{
+	TCHAR,		1,	0,
+	TUCHAR,		2,	0,
+	TSHORT,		3,	0,
+	TUSHORT,	4,	0,
+	TLONG,		5,	0,
+	TULONG,		6,	0,
+	TVLONG,		7,	0,
+	TUVLONG,	8,	0,
+	TINT,		9,	0,
+	TUINT,		10,	0,
+	-1,		0,	0,
+};
+
+Node*
+fvn(char *name, int type)
+{
+	Node *n;
+
+	n = new(ONAME, Z, Z);
+	n->sym = slookup(name);
+	n->sym->sig = SIGINTERN;
+	if(fntypes[type] == 0)
+		fntypes[type] = typ(TFUNC, types[type]);
+	n->type = fntypes[type];
+	n->etype = type;
+	n->class = CGLOBL;
+	n->addable = 10;
+	n->complex = 0;
+	return n;
+}
+
+void
+com64init(void)
+{
+	Init *p;
+
+	nodaddv = fvn("_addv", TVLONG);
+	nodsubv = fvn("_subv", TVLONG);
+	nodmulv = fvn("_mulv", TVLONG);
+	noddivv = fvn("_divv", TVLONG);
+	noddivvu = fvn("_divvu", TVLONG);
+	nodmodv = fvn("_modv", TVLONG);
+	nodmodvu = fvn("_modvu", TVLONG);
+	nodlshv = fvn("_lshv", TVLONG);
+	nodrshav = fvn("_rshav", TVLONG);
+	nodrshlv = fvn("_rshlv", TVLONG);
+	nodandv = fvn("_andv", TVLONG);
+	nodorv = fvn("_orv", TVLONG);
+	nodxorv = fvn("_xorv", TVLONG);
+	nodnegv = fvn("_negv", TVLONG);
+	nodcomv = fvn("_comv", TVLONG);
+
+	nodtestv = fvn("_testv", TLONG);
+	nodeqv = fvn("_eqv", TLONG);
+	nodnev = fvn("_nev", TLONG);
+	nodlev = fvn("_lev", TLONG);
+	nodltv = fvn("_ltv", TLONG);
+	nodgev = fvn("_gev", TLONG);
+	nodgtv = fvn("_gtv", TLONG);
+	nodhiv = fvn("_hiv", TLONG);
+	nodhsv = fvn("_hsv", TLONG);
+	nodlov = fvn("_lov", TLONG);
+	nodlsv = fvn("_lsv", TLONG);
+
+	nodf2v = fvn("_f2v", TVLONG);
+	nodd2v = fvn("_d2v", TVLONG);
+	nodp2v = fvn("_p2v", TVLONG);
+	nodsi2v = fvn("_si2v", TVLONG);
+	nodui2v = fvn("_ui2v", TVLONG);
+	nodsl2v = fvn("_sl2v", TVLONG);
+	nodul2v = fvn("_ul2v", TVLONG);
+	nodsh2v = fvn("_sh2v", TVLONG);
+	noduh2v = fvn("_uh2v", TVLONG);
+	nodsc2v = fvn("_sc2v", TVLONG);
+	noduc2v = fvn("_uc2v", TVLONG);
+
+	nodv2f = fvn("_v2f", TFLOAT);
+	nodv2d = fvn("_v2d", TDOUBLE);
+	nodv2sl = fvn("_v2sl", TLONG);
+	nodv2ul = fvn("_v2ul", TULONG);
+	nodv2si = fvn("_v2si", TINT);
+	nodv2ui = fvn("_v2ui", TUINT);
+	nodv2sh = fvn("_v2sh", TSHORT);
+	nodv2uh = fvn("_v2ul", TUSHORT);
+	nodv2sc = fvn("_v2sc", TCHAR);
+	nodv2uc = fvn("_v2uc", TUCHAR);
+
+	nodvpp = fvn("_vpp", TVLONG);
+	nodppv = fvn("_ppv", TVLONG);
+	nodvmm = fvn("_vmm", TVLONG);
+	nodmmv = fvn("_mmv", TVLONG);
+
+	nodvasop = fvn("_vasop", TVLONG);
+
+	for(p = initetconv; p->code >= 0; p++)
+		etconv[p->code] = p->value;
+}
+
+int
+com64(Node *n)
+{
+	Node *l, *r, *a, *t;
+	int lv, rv;
+
+	if(n->type == 0)
+		return 0;
+
+	l = n->left;
+	r = n->right;
+
+	lv = 0;
+	if(l && l->type && typev[l->type->etype])
+		lv = 1;
+	rv = 0;
+	if(r && r->type && typev[r->type->etype])
+		rv = 1;
+
+	if(lv) {
+		switch(n->op) {
+		case OEQ:
+			a = nodeqv;
+			goto setbool;
+		case ONE:
+			a = nodnev;
+			goto setbool;
+		case OLE:
+			a = nodlev;
+			goto setbool;
+		case OLT:
+			a = nodltv;
+			goto setbool;
+		case OGE:
+			a = nodgev;
+			goto setbool;
+		case OGT:
+			a = nodgtv;
+			goto setbool;
+		case OHI:
+			a = nodhiv;
+			goto setbool;
+		case OHS:
+			a = nodhsv;
+			goto setbool;
+		case OLO:
+			a = nodlov;
+			goto setbool;
+		case OLS:
+			a = nodlsv;
+			goto setbool;
+
+		case OANDAND:
+		case OOROR:
+			if(machcap(n))
+				return 1;
+
+			if(rv) {
+				r = new(OFUNC, nodtestv, r);
+				n->right = r;
+				r->complex = FNX;
+				r->op = OFUNC;
+				r->type = types[TLONG];
+			}
+
+		case OCOND:
+		case ONOT:
+			if(machcap(n))
+				return 1;
+
+			l = new(OFUNC, nodtestv, l);
+			n->left = l;
+			l->complex = FNX;
+			l->op = OFUNC;
+			l->type = types[TLONG];
+			n->complex = FNX;
+			return 1;
+		}
+	}
+
+	if(rv) {
+		if(machcap(n))
+			return 1;
+		switch(n->op) {
+		case OANDAND:
+		case OOROR:
+			r = new(OFUNC, nodtestv, r);
+			n->right = r;
+			r->complex = FNX;
+			r->op = OFUNC;
+			r->type = types[TLONG];
+			return 1;
+		case OCOND:
+			return 1;
+		}
+	}
+
+	if(typev[n->type->etype]) {
+		if(machcap(n))
+			return 1;
+		switch(n->op) {
+		default:
+			diag(n, "unknown vlong %O", n->op);
+		case OFUNC:
+			n->complex = FNX;
+		case ORETURN:
+		case OAS:
+		case OIND:
+		case OLIST:
+		case OCOMMA:
+			return 1;
+		case OADD:
+			a = nodaddv;
+			goto setbop;
+		case OSUB:
+			a = nodsubv;
+			goto setbop;
+		case OMUL:
+		case OLMUL:
+			a = nodmulv;
+			goto setbop;
+		case ODIV:
+			a = noddivv;
+			goto setbop;
+		case OLDIV:
+			a = noddivvu;
+			goto setbop;
+		case OMOD:
+			a = nodmodv;
+			goto setbop;
+		case OLMOD:
+			a = nodmodvu;
+			goto setbop;
+		case OASHL:
+			a = nodlshv;
+			goto setbop;
+		case OASHR:
+			a = nodrshav;
+			goto setbop;
+		case OLSHR:
+			a = nodrshlv;
+			goto setbop;
+		case OAND:
+			a = nodandv;
+			goto setbop;
+		case OOR:
+			a = nodorv;
+			goto setbop;
+		case OXOR:
+			a = nodxorv;
+			goto setbop;
+		case OPOSTINC:
+			a = nodvpp;
+			goto setvinc;
+		case OPOSTDEC:
+			a = nodvmm;
+			goto setvinc;
+		case OPREINC:
+			a = nodppv;
+			goto setvinc;
+		case OPREDEC:
+			a = nodmmv;
+			goto setvinc;
+		case ONEG:
+			a = nodnegv;
+			goto setfnx;
+		case OCOM:
+			a = nodcomv;
+			goto setfnx;
+		case OCAST:
+			switch(l->type->etype) {
+			case TCHAR:
+				a = nodsc2v;
+				goto setfnxl;
+			case TUCHAR:
+				a = noduc2v;
+				goto setfnxl;
+			case TSHORT:
+				a = nodsh2v;
+				goto setfnxl;
+			case TUSHORT:
+				a = noduh2v;
+				goto setfnxl;
+			case TINT:
+				a = nodsi2v;
+				goto setfnx;
+			case TUINT:
+				a = nodui2v;
+				goto setfnx;
+			case TLONG:
+				a = nodsl2v;
+				goto setfnx;
+			case TULONG:
+				a = nodul2v;
+				goto setfnx;
+			case TFLOAT:
+				a = nodf2v;
+				goto setfnx;
+			case TDOUBLE:
+				a = nodd2v;
+				goto setfnx;
+			case TIND:
+				a = nodp2v;
+				goto setfnx;
+			}
+			diag(n, "unknown %T->vlong cast", l->type);
+			return 1;
+		case OASADD:
+			a = nodaddv;
+			goto setasop;
+		case OASSUB:
+			a = nodsubv;
+			goto setasop;
+		case OASMUL:
+		case OASLMUL:
+			a = nodmulv;
+			goto setasop;
+		case OASDIV:
+			a = noddivv;
+			goto setasop;
+		case OASLDIV:
+			a = noddivvu;
+			goto setasop;
+		case OASMOD:
+			a = nodmodv;
+			goto setasop;
+		case OASLMOD:
+			a = nodmodvu;
+			goto setasop;
+		case OASASHL:
+			a = nodlshv;
+			goto setasop;
+		case OASASHR:
+			a = nodrshav;
+			goto setasop;
+		case OASLSHR:
+			a = nodrshlv;
+			goto setasop;
+		case OASAND:
+			a = nodandv;
+			goto setasop;
+		case OASOR:
+			a = nodorv;
+			goto setasop;
+		case OASXOR:
+			a = nodxorv;
+			goto setasop;
+		}
+	}
+
+	if(typefd[n->type->etype] && l && l->op == OFUNC) {
+		switch(n->op) {
+		case OASADD:
+		case OASSUB:
+		case OASMUL:
+		case OASLMUL:
+		case OASDIV:
+		case OASLDIV:
+		case OASMOD:
+		case OASLMOD:
+		case OASASHL:
+		case OASASHR:
+		case OASLSHR:
+		case OASAND:
+		case OASOR:
+		case OASXOR:
+			if(l->right && typev[l->right->etype]) {
+				diag(n, "sorry float <asop> vlong not implemented\n");
+			}
+		}
+	}
+
+	if(n->op == OCAST) {
+		if(l->type && typev[l->type->etype]) {
+			if(machcap(n))
+				return 1;
+			switch(n->type->etype) {
+			case TDOUBLE:
+				a = nodv2d;
+				goto setfnx;
+			case TFLOAT:
+				a = nodv2f;
+				goto setfnx;
+			case TLONG:
+				a = nodv2sl;
+				goto setfnx;
+			case TULONG:
+				a = nodv2ul;
+				goto setfnx;
+			case TINT:
+				a = nodv2si;
+				goto setfnx;
+			case TUINT:
+				a = nodv2ui;
+				goto setfnx;
+			case TSHORT:
+				a = nodv2sh;
+				goto setfnx;
+			case TUSHORT:
+				a = nodv2uh;
+				goto setfnx;
+			case TCHAR:
+				a = nodv2sc;
+				goto setfnx;
+			case TUCHAR:
+				a = nodv2uc;
+				goto setfnx;
+			case TIND:	// small pun here
+				a = nodv2ul;
+				goto setfnx;
+			}
+			diag(n, "unknown vlong->%T cast", n->type);
+			return 1;
+		}
+	}
+
+	return 0;
+
+setbop:
+	n->left = a;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setfnxl:
+	l = new(OCAST, l, 0);
+	l->type = types[TLONG];
+	l->complex = l->left->complex;
+
+setfnx:
+	n->left = a;
+	n->right = l;
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setvinc:
+	n->left = a;
+	l = new(OADDR, l, Z);
+	l->type = typ(TIND, l->left->type);
+	l->complex = l->left->complex;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setbool:
+	if(machcap(n))
+		return 1;
+	n->left = a;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	n->type = types[TLONG];
+	return 1;
+
+setasop:
+	if(l->op == OFUNC) {
+		l = l->right;
+		goto setasop;
+	}
+
+	t = new(OCONST, 0, 0);
+	t->vconst = etconv[l->type->etype];
+	t->type = types[TLONG];
+	t->addable = 20;
+	r = new(OLIST, t, r);
+
+	t = new(OADDR, a, 0);
+	t->type = typ(TIND, a->type);
+	r = new(OLIST, t, r);
+
+	t = new(OADDR, l, 0);
+	t->type = typ(TIND, l->type);
+	t->complex = l->complex;
+	r = new(OLIST, t, r);
+
+	n->left = nodvasop;
+	n->right = r;
+	n->complex = FNX;
+	n->op = OFUNC;
+
+	return 1;
+}
+
+void
+bool64(Node *n)
+{
+	Node *n1;
+
+	if(machcap(Z))
+		return;
+	if(typev[n->type->etype]) {
+		n1 = new(OXXX, 0, 0);
+		*n1 = *n;
+
+		n->right = n1;
+		n->left = nodtestv;
+		n->complex = FNX;
+		n->addable = 0;
+		n->op = OFUNC;
+		n->type = types[TLONG];
+	}
+}
+
+/*
+ * more machine depend stuff.
+ * this is common for 8,16,32,64 bit machines.
+ * this is common for ieee machines.
+ */
+double
+convvtof(vlong v)
+{
+	double d;
+
+	d = v;		/* BOTCH */
+	return d;
+}
+
+vlong
+convftov(double d)
+{
+	vlong v;
+
+
+	v = d;		/* BOTCH */
+	return v;
+}
+
+double
+convftox(double d, int et)
+{
+
+	if(!typefd[et])
+		diag(Z, "bad type in castftox %s", tnames[et]);
+	return d;
+}
+
+vlong
+convvtox(vlong c, int et)
+{
+	int n;
+
+	n = 8 * ewidth[et];
+	c &= MASK(n);
+	if(!typeu[et])
+		if(c & SIGN(n))
+			c |= ~MASK(n);
+	return c;
+}
--- /dev/null
+++ b/utils/cc/dcl.c
@@ -1,0 +1,1640 @@
+#include "cc.h"
+
+Node*
+dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n)
+{
+	Sym *s;
+	Node *n1;
+	long v;
+
+	nearln = lineno;
+	lastfield = 0;
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	default:
+		diag(n, "unknown declarator: %O", n->op);
+		break;
+
+	case OARRAY:
+		t = typ(TARRAY, t);
+		t->width = 0;
+		n1 = n->right;
+		n = n->left;
+		if(n1 != Z) {
+			complex(n1);
+			v = -1;
+			if(n1->op == OCONST)
+				v = n1->vconst;
+			if(v <= 0) {
+				diag(n, "array size must be a positive constant");
+				v = 1;
+			}
+			t->width = v * t->link->width;
+		}
+		goto loop;
+
+	case OIND:
+		t = typ(TIND, t);
+		t->garb = n->garb;
+		n = n->left;
+		goto loop;
+
+	case OFUNC:
+		t = typ(TFUNC, t);
+		t->down = fnproto(n);
+		n = n->left;
+		goto loop;
+
+	case OBIT:
+		n1 = n->right;
+		complex(n1);
+		lastfield = -1;
+		if(n1->op == OCONST)
+			lastfield = n1->vconst;
+		if(lastfield < 0) {
+			diag(n, "field width must be non-negative constant");
+			lastfield = 1;
+		}
+		if(lastfield == 0) {
+			lastbit = 0;
+			firstbit = 1;
+			if(n->left != Z) {
+				diag(n, "zero width named field");
+				lastfield = 1;
+			}
+		}
+		if(!typei[t->etype]) {
+			diag(n, "field type must be int-like");
+			t = types[TINT];
+			lastfield = 1;
+		}
+		if(lastfield > tfield->width*8) {
+			diag(n, "field width larger than field unit");
+			lastfield = 1;
+		}
+		lastbit += lastfield;
+		if(lastbit > tfield->width*8) {
+			lastbit = lastfield;
+			firstbit = 1;
+		}
+		n = n->left;
+		goto loop;
+
+	case ONAME:
+		if(f == NODECL)
+			break;
+		s = n->sym;
+		(*f)(c, t, s);
+		if(s->class == CLOCAL)
+			s = mkstatic(s);
+		firstbit = 0;
+		n->sym = s;
+		n->type = s->type;
+		n->xoffset = s->offset;
+		n->class = s->class;
+		n->etype = TVOID;
+		if(n->type != T)
+			n->etype = n->type->etype;
+		if(debug['d'])
+			dbgdecl(s);
+		acidvar(s);
+		s->varlineno = lineno;
+		break;
+	}
+	lastdcl = t;
+	return n;
+}
+
+Sym*
+mkstatic(Sym *s)
+{
+	Sym *s1;
+
+	if(s->class != CLOCAL)
+		return s;
+	snprint(symb, NSYMB, "%s$%d", s->name, s->block);
+	s1 = lookup();
+	if(s1->class != CSTATIC) {
+		s1->type = s->type;
+		s1->offset = s->offset;
+		s1->block = s->block;
+		s1->class = CSTATIC;
+	}
+	return s1;
+}
+
+/*
+ * make a copy of a typedef
+ * the problem is to split out incomplete
+ * arrays so that it is in the variable
+ * rather than the typedef.
+ */
+Type*
+tcopy(Type *t)
+{
+	Type *tl, *tx;
+	int et;
+
+	if(t == T)
+		return t;
+	et = t->etype;
+	if(typesu[et])
+		return t;
+	tl = tcopy(t->link);
+	if(tl != t->link ||
+	  (et == TARRAY && t->width == 0)) {
+		tx = copytyp(t);
+		tx->link = tl;
+		return tx;
+	}
+	return t;
+}
+
+Node*
+doinit(Sym *s, Type *t, long o, Node *a)
+{
+	Node *n;
+
+	if(t == T)
+		return Z;
+	if(s->class == CEXTERN) {
+		s->class = CGLOBL;
+		if(debug['d'])
+			dbgdecl(s);
+	}
+	if(debug['i']) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "doinit value");
+	}
+
+
+	n = initlist;
+	if(a->op == OINIT)
+		a = a->left;
+	initlist = a;
+
+	a = init1(s, t, o, 0);
+	if(initlist != Z)
+		diag(initlist, "more initializers than structure: %s",
+			s->name);
+	initlist = n;
+
+	return a;
+}
+
+/*
+ * get next major operator,
+ * dont advance initlist.
+ */
+Node*
+peekinit(void)
+{
+	Node *a;
+
+	a = initlist;
+
+loop:
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		a = a->left;
+		goto loop;
+	}
+	return a;
+}
+
+/*
+ * consume and return next element on
+ * initlist. expand strings.
+ */
+Node*
+nextinit(void)
+{
+	Node *a, *b, *n;
+
+	a = initlist;
+	n = Z;
+
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		n = a->right;
+		a = a->left;
+	}
+	if(a->op == OUSED) {
+		a = a->left;
+		b = new(OCONST, Z, Z);
+		b->type = a->type->link;
+		if(a->op == OSTRING) {
+			b->vconst = convvtox(*a->cstring, TCHAR);
+			a->cstring++;
+		}
+		if(a->op == OLSTRING) {
+			b->vconst = convvtox(*a->rstring, TRUNE);
+			a->rstring++;
+		}
+		a->type->width -= b->type->width;
+		if(a->type->width <= 0)
+			initlist = n;
+		return b;
+	}
+	initlist = n;
+	return a;
+}
+
+int
+isstruct(Node *a, Type *t)
+{
+	Node *n;
+
+	switch(a->op) {
+	case ODOTDOT:
+		n = a->left;
+		if(n && n->type && sametype(n->type, t))
+			return 1;
+	case OSTRING:
+	case OLSTRING:
+	case OCONST:
+	case OINIT:
+	case OELEM:
+		return 0;
+	}
+
+	n = new(ODOTDOT, Z, Z);
+	*n = *a;
+
+	/*
+	 * ODOTDOT is a flag for tcom
+	 * a second tcom will not be performed
+	 */
+	a->op = ODOTDOT;
+	a->left = n;
+	a->right = Z;
+
+	if(tcom(n))
+		return 0;
+
+	if(sametype(n->type, t))
+		return 1;
+	return 0;
+}
+
+Node*
+init1(Sym *s, Type *t, long o, int exflag)
+{
+	Node *a, *l, *r, nod;
+	Type *t1;
+	long e, w, so, mw;
+
+	a = peekinit();
+	if(a == Z)
+		return Z;
+
+	if(debug['i']) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "init1 value");
+	}
+
+	if(exflag && a->op == OINIT)
+		return doinit(s, t, o, nextinit());
+
+	switch(t->etype) {
+	default:
+		diag(Z, "unknown type in initialization: %T to: %s", t, s->name);
+		return Z;
+
+	case TCHAR:
+	case TUCHAR:
+	case TINT:
+	case TUINT:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+	case TIND:
+	single:
+		if(a->op == OARRAY || a->op == OELEM)
+			return Z;
+
+		a = nextinit();
+		if(a == Z)
+			return Z;
+
+		if(t->nbits)
+			diag(Z, "cannot initialize bitfields");
+		if(s->class == CAUTO) {
+			l = new(ONAME, Z, Z);
+			l->sym = s;
+			l->type = t;
+			l->etype = TVOID;
+			if(s->type)
+				l->etype = s->type->etype;
+			l->xoffset = s->offset + o;
+			l->class = s->class;
+
+			l = new(OASI, l, a);
+			return l;
+		}
+
+		complex(a);
+		if(a->type == T)
+			return Z;
+
+		if(a->op == OCONST) {
+			if(vconst(a) && t->etype == TIND && a->type && a->type->etype != TIND){
+				diag(a, "initialize pointer to an integer: %s", s->name);
+				return Z;
+			}
+			if(!sametype(a->type, t)) {
+				/* hoop jumping to save malloc */
+				if(nodcast == Z)
+					nodcast = new(OCAST, Z, Z);
+				nod = *nodcast;
+				nod.left = a;
+				nod.type = t;
+				nod.lineno = a->lineno;
+				complex(&nod);
+				if(nod.type)
+					*a = nod;
+			}
+			if(a->op != OCONST) {
+				diag(a, "initializer is not a constant: %s",
+					s->name);
+				return Z;
+			}
+			if(vconst(a) == 0)
+				return Z;
+			goto gext;
+		}
+		if(t->etype == TIND) {
+			while(a->op == OCAST) {
+				warn(a, "CAST in initialization ignored");
+				a = a->left;
+			}
+			if(!sametype(t, a->type)) {
+				diag(a, "initialization of incompatible pointers: %s\n%T and %T",
+					s->name, t, a->type);
+			}
+			if(a->op == OADDR)
+				a = a->left;
+			goto gext;
+		}
+
+		while(a->op == OCAST)
+			a = a->left;
+		if(a->op == OADDR) {
+			warn(a, "initialize pointer to an integer: %s", s->name);
+			a = a->left;
+			goto gext;
+		}
+		diag(a, "initializer is not a constant: %s", s->name);
+		return Z;
+
+	gext:
+		gextern(s, a, o, t->width);
+
+		return Z;
+
+	case TARRAY:
+		w = t->link->width;
+		if(a->op == OSTRING || a->op == OLSTRING)
+		if(typei[t->link->etype]) {
+			/*
+			 * get rid of null if sizes match exactly
+			 */
+			a = nextinit();
+			mw = t->width/w;
+			so = a->type->width/a->type->link->width;
+			if(mw && so > mw) {
+				if(so != mw+1)
+					diag(a, "string initialization larger than array");
+				a->type->width -= a->type->link->width;
+			}
+
+			/*
+			 * arrange strings to be expanded
+			 * inside OINIT braces.
+			 */
+			a = new(OUSED, a, Z);
+			return doinit(s, t, o, a);
+		}
+
+		mw = -w;
+		l = Z;
+		for(e=0;;) {
+			/*
+			 * peek ahead for element initializer
+			 */
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM && t->link->etype != TSTRUCT)
+				break;
+			if(a->op == OARRAY) {
+				if(e && exflag)
+					break;
+				a = nextinit();
+				r = a->left;
+				complex(r);
+				if(r->op != OCONST) {
+					diag(r, "initializer subscript must be constant");
+					return Z;
+				}
+				e = r->vconst;
+				if(t->width != 0)
+					if(e < 0 || e*w >= t->width) {
+						diag(a, "initialization index out of range: %ld", e);
+						continue;
+					}
+			}
+
+			so = e*w;
+			if(so > mw)
+				mw = so;
+			if(t->width != 0)
+				if(mw >= t->width)
+					break;
+			r = init1(s, t->link, o+so, 1);
+			l = newlist(l, r);
+			e++;
+		}
+		if(t->width == 0)
+			t->width = mw+w;
+		return l;
+
+	case TUNION:
+	case TSTRUCT:
+		/*
+		 * peek ahead to find type of rhs.
+		 * if its a structure, then treat
+		 * this element as a variable
+		 * rather than an aggregate.
+		 */
+		if(isstruct(a, t))
+			goto single;
+
+		if(t->width <= 0) {
+			diag(Z, "incomplete structure: %s", s->name);
+			return Z;
+		}
+		l = Z;
+
+	again:
+		for(t1 = t->link; t1 != T; t1 = t1->down) {
+			if(a->op == OARRAY && t1->etype != TARRAY)
+				break;
+			if(a->op == OELEM) {
+				if(t1->sym != a->sym)
+					continue;
+				nextinit();
+			}
+			r = init1(s, t1, o+t1->offset, 1);
+			l = newlist(l, r);
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM)
+				goto again;
+		}
+		if(a && a->op == OELEM)
+			diag(a, "structure element not found %F", a);
+		return l;
+	}
+}
+
+Node*
+newlist(Node *l, Node *r)
+{
+	if(r == Z)
+		return l;
+	if(l == Z)
+		return r;
+	return new(OLIST, l, r);
+}
+
+void
+sualign(Type *t)
+{
+	Type *l;
+	long o, w;
+
+	o = 0;
+	switch(t->etype) {
+
+	case TSTRUCT:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->nbits) {
+				if(l->shift <= 0) {
+					l->shift = -l->shift;
+					w = round(w, tfield->width);
+					o = w;
+					w += tfield->width;
+				}
+				l->offset = o;
+			} else {
+				if(l->width < 0 ||
+				   l->width == 0 && l->down != T)
+					if(l->sym)
+						diag(Z, "incomplete structure element: %s",
+							l->sym->name);
+					else
+						diag(Z, "incomplete structure element");
+				w = align(w, l, Ael1);
+				l->offset = w;
+				w = align(w, l, Ael2);
+			}
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		pickletype(t);
+		return;
+
+	case TUNION:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->width <= 0)
+				if(l->sym)
+					diag(Z, "incomplete union element: %s",
+						l->sym->name);
+				else
+					diag(Z, "incomplete union element");
+			l->offset = 0;
+			l->shift = 0;
+			o = align(align(0, l, Ael1), l, Ael2);
+			if(o > w)
+				w = o;
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		pickletype(t);
+		return;
+
+	default:
+		diag(Z, "unknown type in sualign: %T", t);
+		break;
+	}
+}
+
+long
+round(long v, int w)
+{
+	int r;
+
+	if(w <= 0 || w > 8) {
+		diag(Z, "rounding by %d", w);
+		w = 1;
+	}
+	r = v%w;
+	if(r)
+		v += w-r;
+	return v;
+}
+
+Type*
+ofnproto(Node *n)
+{
+	Type *tl, *tr, *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		tl = ofnproto(n->left);
+		tr = ofnproto(n->right);
+		if(tl == T)
+			return tr;
+		tl->down = tr;
+		return tl;
+
+	case ONAME:
+		t = copytyp(n->sym->type);
+		t->down = T;
+		return t;
+	}
+	return T;
+}
+
+#define	ANSIPROTO	1
+#define	OLDPROTO	2
+
+void
+argmark(Node *n, int pass)
+{
+	Type *t;
+
+	autoffset = align(0, thisfn->link, Aarg0);
+	stkoff = 0;
+	for(; n->left != Z; n = n->left) {
+		if(n->op != OFUNC || n->left->op != ONAME)
+			continue;
+		walkparam(n->right, pass);
+		if(pass != 0 && anyproto(n->right) == OLDPROTO) {
+			t = typ(TFUNC, n->left->sym->type->link);
+			t->down = typ(TOLD, T);
+			t->down->down = ofnproto(n->right);
+			tmerge(t, n->left->sym);
+			n->left->sym->type = t;
+		}
+		break;
+	}
+	autoffset = 0;
+	stkoff = 0;
+}
+
+void
+walkparam(Node *n, int pass)
+{
+	Sym *s;
+	Node *n1;
+
+	if(n != Z && n->op == OPROTO && n->left == Z && n->type == types[TVOID])
+		return;
+
+loop:
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+		diag(n, "argument not a name/prototype: %O", n->op);
+		break;
+
+	case OLIST:
+		walkparam(n->left, pass);
+		n = n->right;
+		goto loop;
+
+	case OPROTO:
+		for(n1 = n; n1 != Z; n1=n1->left)
+			if(n1->op == ONAME) {
+				if(pass == 0) {
+					s = n1->sym;
+					push1(s);
+					s->offset = -1;
+					break;
+				}
+				dodecl(pdecl, CPARAM, n->type, n->left);
+				break;
+			}
+		if(n1)
+			break;
+		if(pass == 0) {
+			/*
+			 * extension:
+			 *	allow no name in argument declaration
+			diag(Z, "no name in argument declaration");
+			 */
+			break;
+		}
+		dodecl(NODECL, CPARAM, n->type, n->left);
+		pdecl(CPARAM, lastdcl, S);
+		break;
+
+	case ODOTDOT:
+		break;
+	
+	case ONAME:
+		s = n->sym;
+		if(pass == 0) {
+			push1(s);
+			s->offset = -1;
+			break;
+		}
+		if(s->offset != -1) {
+			if(autoffset == 0) {
+				firstarg = s;
+				firstargtype = s->type;
+			}
+			autoffset = align(autoffset, s->type, Aarg1);
+			s->offset = autoffset;
+			autoffset = align(autoffset, s->type, Aarg2);
+		} else
+			dodecl(pdecl, CXXX, types[TINT], n);
+		break;
+	}
+}
+
+void
+markdcl(void)
+{
+	Decl *d;
+
+	blockno++;
+	d = push();
+	d->val = DMARK;
+	d->offset = autoffset;
+	d->block = autobn;
+	autobn = blockno;
+}
+
+Node*
+revertdcl(void)
+{
+	Decl *d;
+	Sym *s;
+	Node *n, *n1;
+
+	n = Z;
+	for(;;) {
+		d = dclstack;
+		if(d == D) {
+			diag(Z, "pop off dcl stack");
+			break;
+		}
+		dclstack = d->link;
+		s = d->sym;
+		switch(d->val) {
+		case DMARK:
+			autoffset = d->offset;
+			autobn = d->block;
+			return n;
+
+		case DAUTO:
+			if(debug['d'])
+				print("revert1 \"%s\"\n", s->name);
+			if(s->aused == 0) {
+				nearln = s->varlineno;
+				if(s->class == CAUTO)
+					warn(Z, "auto declared and not used: %s", s->name);
+				if(s->class == CPARAM)
+					warn(Z, "param declared and not used: %s", s->name);
+			}
+			if(s->type && (s->type->garb & GVOLATILE)) {
+				n1 = new(ONAME, Z, Z);
+				n1->sym = s;
+				n1->type = s->type;
+				n1->etype = TVOID;
+				if(n1->type != T)
+					n1->etype = n1->type->etype;
+				n1->xoffset = s->offset;
+				n1->class = s->class;
+
+				n1 = new(OADDR, n1, Z);
+				n1 = new(OUSED, n1, Z);
+				if(n == Z)
+					n = n1;
+				else
+					n = new(OLIST, n1, n);
+			}
+			s->type = d->type;
+			s->class = d->class;
+			s->offset = d->offset;
+			s->block = d->block;
+			s->varlineno = d->varlineno;
+			s->aused = d->aused;
+			break;
+
+		case DSUE:
+			if(debug['d'])
+				print("revert2 \"%s\"\n", s->name);
+			s->suetag = d->type;
+			s->sueblock = d->block;
+			break;
+
+		case DLABEL:
+			if(debug['d'])
+				print("revert3 \"%s\"\n", s->name);
+			if(s->label && s->label->addable == 0)
+				warn(s->label, "label declared and not used \"%s\"", s->name);
+			s->label = Z;
+			break;
+		}
+	}
+	return n;
+}
+
+Type*
+fnproto(Node *n)
+{
+	int r;
+
+	r = anyproto(n->right);
+	if(r == 0 || (r & OLDPROTO)) {
+		if(r & ANSIPROTO)
+			diag(n, "mixed ansi/old function declaration: %F", n->left);
+		return T;
+	}
+	return fnproto1(n->right);
+}
+
+int
+anyproto(Node *n)
+{
+	int r;
+
+	r = 0;
+
+loop:
+	if(n == Z)
+		return r;
+	switch(n->op) {
+	case OLIST:
+		r |= anyproto(n->left);
+		n = n->right;
+		goto loop;
+
+	case ODOTDOT:
+	case OPROTO:
+		return r | ANSIPROTO;
+	}
+	return r | OLDPROTO;
+}
+
+Type*
+fnproto1(Node *n)
+{
+	Type *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		t = fnproto1(n->left);
+		if(t != T)
+			t->down = fnproto1(n->right);
+		return t;
+
+	case OPROTO:
+		lastdcl = T;
+		dodecl(NODECL, CXXX, n->type, n->left);
+		t = typ(TXXX, T);
+		if(lastdcl != T)
+			*t = *paramconv(lastdcl, 1, 0);
+		return t;
+
+	case ONAME:
+		diag(n, "incomplete argument prototype");
+		return typ(TINT, T);
+
+	case ODOTDOT:
+		return typ(TDOT, T);
+	}
+	diag(n, "unknown op in fnproto");
+	return T;
+}
+
+void
+dbgdecl(Sym *s)
+{
+	print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n",
+		s->name, cnames[s->class], s->block, s->offset, s->type);
+}
+
+Decl*
+push(void)
+{
+	Decl *d;
+
+	d = alloc(sizeof(*d));
+	d->link = dclstack;
+	dclstack = d;
+	return d;
+}
+
+Decl*
+push1(Sym *s)
+{
+	Decl *d;
+
+	d = push();
+	d->sym = s;
+	d->val = DAUTO;
+	d->type = s->type;
+	d->class = s->class;
+	d->offset = s->offset;
+	d->block = s->block;
+	d->varlineno = s->varlineno;
+	d->aused = s->aused;
+	return d;
+}
+
+int
+sametype(Type *t1, Type *t2)
+{
+
+	if(t1 == t2)
+		return 1;
+	return rsametype(t1, t2, 5, 1);
+}
+
+int
+rsametype(Type *t1, Type *t2, int n, int f)
+{
+	int et;
+
+	n--;
+	for(;;) {
+		if(t1 == t2)
+			return 1;
+		if(t1 == T || t2 == T)
+			return 0;
+		if(n <= 0)
+			return 1;
+		et = t1->etype;
+		if(et != t2->etype)
+			return 0;
+		if(et == TFUNC) {
+			if(!rsametype(t1->link, t2->link, n, 0))
+				return 0;
+			t1 = t1->down;
+			t2 = t2->down;
+			while(t1 != T && t2 != T) {
+				if(t1->etype == TOLD) {
+					t1 = t1->down;
+					continue;
+				}
+				if(t2->etype == TOLD) {
+					t2 = t2->down;
+					continue;
+				}
+				while(t1 != T || t2 != T) {
+					if(!rsametype(t1, t2, n, 0))
+						return 0;
+					t1 = t1->down;
+					t2 = t2->down;
+				}
+				break;
+			}
+			return 1;
+		}
+		if(et == TARRAY)
+			if(t1->width != t2->width && t1->width != 0 && t2->width != 0)
+				return 0;
+		if(typesu[et]) {
+			if(t1->link == T)
+				snap(t1);
+			if(t2->link == T)
+				snap(t2);
+			if(t1 != t2 && t1->link == T && t2->link == T){
+				/* structs with missing or different tag names aren't considered equal */
+				if(t1->tag == nil || t2->tag == nil ||
+				   strcmp(t1->tag->name, t2->tag->name) != 0)
+					return 0;
+			}
+			t1 = t1->link;
+			t2 = t2->link;
+			for(;;) {
+				if(t1 == t2)
+					return 1;
+				if(!rsametype(t1, t2, n, 0))
+					return 0;
+				t1 = t1->down;
+				t2 = t2->down;
+			}
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+		if((f || !debug['V']) && et == TIND) {
+			if(t1 != T && t1->etype == TVOID)
+				return 1;
+			if(t2 != T && t2->etype == TVOID)
+				return 1;
+		}
+	}
+}
+
+typedef struct Typetab Typetab;
+
+struct Typetab{
+	int n;
+	Type **a;
+};
+
+static int
+sigind(Type *t, Typetab *tt)
+{
+	int n;
+	Type **a, **na, **p, **e;
+
+	n = tt->n;
+	a = tt->a;
+	e = a+n;
+	/* linear search seems ok */
+	for(p = a ; p < e; p++)
+		if(sametype(*p, t))
+			return p-a;
+	if((n&15) == 0){
+		na = malloc((n+16)*sizeof(Type*));
+		memmove(na, a, n*sizeof(Type*));
+		free(a);
+		a = tt->a = na;
+	}
+	a[tt->n++] = t;
+	return -1;
+}
+
+static ulong
+signat(Type *t, Typetab *tt)
+{
+	int i;
+	Type *t1;
+	long s;
+
+	s = 0;
+	for(; t; t=t->link) {
+		s = s*thash1 + thash[t->etype];
+		if(t->garb&GINCOMPLETE)
+			return s;
+		switch(t->etype) {
+		default:
+			return s;
+		case TARRAY:
+			s = s*thash2 + 0;	/* was t->width */
+			break;
+		case TFUNC:
+			for(t1=t->down; t1; t1=t1->down)
+				s = s*thash3 + signat(t1, tt);
+			break;
+		case TSTRUCT:
+		case TUNION:
+			if((i = sigind(t, tt)) >= 0){
+				s = s*thash2 + i;
+				return s;
+			}
+			for(t1=t->link; t1; t1=t1->down)
+				s = s*thash3 + signat(t1, tt);
+			return s;
+		case TIND:
+			break;
+		}
+	}
+	return s;
+}
+
+ulong
+signature(Type *t)
+{
+	ulong s;
+	Typetab tt;
+
+	tt.n = 0;
+	tt.a = nil;
+	s = signat(t, &tt);
+	free(tt.a);
+	return s;
+}
+
+ulong
+sign(Sym *s)
+{
+	ulong v;
+	Type *t;
+
+	if(s->sig == SIGINTERN)
+		return SIGNINTERN;
+	if((t = s->type) == T)
+		return 0;
+	v = signature(t);
+	if(v == 0)
+		v = SIGNINTERN;
+	return v;
+}
+
+void
+snap(Type *t)
+{
+	if(typesu[t->etype])
+	if(t->link == T && t->tag && t->tag->suetag) {
+		t->link = t->tag->suetag->link;
+		t->width = t->tag->suetag->width;
+	}
+}
+
+Type*
+dotag(Sym *s, int et, int bn)
+{
+	Decl *d;
+
+	if(bn != 0 && bn != s->sueblock) {
+		d = push();
+		d->sym = s;
+		d->val = DSUE;
+		d->type = s->suetag;
+		d->block = s->sueblock;
+		s->suetag = T;
+	}
+	if(s->suetag == T) {
+		s->suetag = typ(et, T);
+		s->sueblock = autobn;
+	}
+	if(s->suetag->etype != et)
+		diag(Z, "tag used for more than one type: %s",
+			s->name);
+	if(s->suetag->tag == S)
+		s->suetag->tag = s;
+	return s->suetag;
+}
+
+Node*
+dcllabel(Sym *s, int f)
+{
+	Decl *d, d1;
+	Node *n;
+
+	n = s->label;
+	if(n != Z) {
+		if(f) {
+			if(n->complex)
+				diag(Z, "label reused: %s", s->name);
+			n->complex = 1;	// declared
+		} else
+			n->addable = 1;	// used
+		return n;
+	}
+
+	d = push();
+	d->sym = s;
+	d->val = DLABEL;
+	dclstack = d->link;
+
+	d1 = *firstdcl;
+	*firstdcl = *d;
+	*d = d1;
+
+	firstdcl->link = d;
+	firstdcl = d;
+
+	n = new(OXXX, Z, Z);
+	n->sym = s;
+	n->complex = f;
+	n->addable = !f;
+	s->label = n;
+
+	if(debug['d'])
+		dbgdecl(s);
+	return n;
+}
+
+Type*
+paramconv(Type *t, int f, int defining)
+{
+
+	switch(t->etype) {
+	case TUNION:
+	case TSTRUCT:
+		if(t->width <= 0 && defining)
+			diag(Z, "incomplete structure: %s", t->tag->name);
+		break;
+
+	case TARRAY:
+		t = typ(TIND, t->link);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFUNC:
+		t = typ(TIND, t);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFLOAT:
+		if(!f)
+			t = types[TDOUBLE];
+		break;
+
+	case TCHAR:
+	case TSHORT:
+		if(!f)
+			t = types[TINT];
+		break;
+
+	case TUCHAR:
+	case TUSHORT:
+		if(!f)
+			t = types[TUINT];
+		break;
+	}
+	return t;
+}
+
+void
+adecl(int c, Type *t, Sym *s)
+{
+
+	if(c == CSTATIC)
+		c = CLOCAL;
+	if(t->etype == TFUNC) {
+		if(c == CXXX)
+			c = CEXTERN;
+		if(c == CLOCAL)
+			c = CSTATIC;
+		if(c == CAUTO || c == CEXREG)
+			diag(Z, "function cannot be %s %s", cnames[c], s->name);
+	}
+	if(c == CXXX)
+		c = CAUTO;
+	if(s) {
+		if(s->class == CSTATIC)
+			if(c == CEXTERN || c == CGLOBL) {
+				warn(Z, "just say static: %s", s->name);
+				c = CSTATIC;
+			}
+		if(s->class == CAUTO || s->class == CPARAM || s->class == CLOCAL)
+		if(s->block == autobn)
+			diag(Z, "auto redeclaration of: %s", s->name);
+		if(c != CPARAM)
+			push1(s);
+		s->block = autobn;
+		s->offset = 0;
+		s->type = t;
+		s->class = c;
+		s->aused = 0;
+	}
+	switch(c) {
+	case CAUTO:
+		autoffset = align(autoffset, t, Aaut3);
+		stkoff = maxround(stkoff, autoffset);
+		s->offset = -autoffset;
+		break;
+
+	case CPARAM:
+		if(autoffset == 0) {
+			firstarg = s;
+			firstargtype = t;
+		}
+		autoffset = align(autoffset, t, Aarg1);
+		if(s)
+			s->offset = autoffset;
+		autoffset = align(autoffset, t, Aarg2);
+		break;
+	}
+}
+
+void
+pdecl(int c, Type *t, Sym *s)
+{
+	if(s && s->offset != -1) {
+		diag(Z, "not a parameter: %s", s->name);
+		return;
+	}
+	t = paramconv(t, c==CPARAM, 1);
+	if(c == CXXX)
+		c = CPARAM;
+	if(c != CPARAM) {
+		diag(Z, "parameter cannot have class: %s", s->name);
+		c = CPARAM;
+	}
+	adecl(c, t, s);
+}
+
+void
+xdecl(int c, Type *t, Sym *s)
+{
+	long o;
+
+	o = 0;
+	switch(c) {
+	case CEXREG:
+		o = exreg(t);
+		if(o == 0)
+			c = CEXTERN;
+		if(s->class == CGLOBL)
+			c = CGLOBL;
+		break;
+
+	case CEXTERN:
+		if(s->class == CGLOBL)
+			c = CGLOBL;
+		break;
+
+	case CXXX:
+		c = CGLOBL;
+		if(s->class == CEXTERN)
+			s->class = CGLOBL;
+		break;
+
+	case CAUTO:
+		diag(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+		c = CEXTERN;
+		break;
+
+	case CTYPESTR:
+		if(!typesuv[t->etype]) {
+			diag(Z, "typestr must be struct/union: %s", s->name);
+			break;
+		}
+		dclfunct(t, s);
+		break;
+	}
+
+	if(s->class == CSTATIC)
+		if(c == CEXTERN || c == CGLOBL) {
+			warn(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+			c = CSTATIC;
+		}
+	if(s->type != T)
+		if(s->class != c || !sametype(t, s->type) || t->etype == TENUM) {
+			diag(Z, "external redeclaration of: %s", s->name);
+			Bprint(&diagbuf, "	%s %T %L\n", cnames[c], t, nearln);
+			Bprint(&diagbuf, "	%s %T %L\n", cnames[s->class], s->type, s->varlineno);
+		}
+	tmerge(t, s);
+	s->type = t;
+	s->class = c;
+	s->block = 0;
+	s->offset = o;
+}
+
+void
+tmerge(Type *t1, Sym *s)
+{
+	Type *ta, *tb, *t2;
+
+	t2 = s->type;
+/*print("merge	%T; %T\n", t1, t2);/**/
+	for(;;) {
+		if(t1 == T || t2 == T || t1 == t2)
+			break;
+		if(t1->etype != t2->etype)
+			break;
+		switch(t1->etype) {
+		case TFUNC:
+			ta = t1->down;
+			tb = t2->down;
+			if(ta == T) {
+				t1->down = tb;
+				break;
+			}
+			if(tb == T)
+				break;
+			while(ta != T && tb != T) {
+				if(ta == tb)
+					break;
+				/* ignore old-style flag */
+				if(ta->etype == TOLD) {
+					ta = ta->down;
+					continue;
+				}
+				if(tb->etype == TOLD) {
+					tb = tb->down;
+					continue;
+				}
+				/* checking terminated by ... */
+				if(ta->etype == TDOT && tb->etype == TDOT) {
+					ta = T;
+					tb = T;
+					break;
+				}
+				if(!sametype(ta, tb))
+					break;
+				ta = ta->down;
+				tb = tb->down;
+			}
+			if(ta != tb)
+				diag(Z, "function inconsistently declared: %s", s->name);
+
+			/* take new-style over old-style */
+			ta = t1->down;
+			tb = t2->down;
+			if(ta != T && ta->etype == TOLD)
+				if(tb != T && tb->etype != TOLD)
+					t1->down = tb;
+			break;
+
+		case TARRAY:
+			/* should we check array size change? */
+			if(t2->width > t1->width)
+				t1->width = t2->width;
+			break;
+
+		case TUNION:
+		case TSTRUCT:
+			return;
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+	}
+}
+
+void
+edecl(int c, Type *t, Sym *s)
+{
+	Type *t1;
+
+	if(s == S) {
+		if(!typesu[t->etype])
+			diag(Z, "unnamed structure element must be struct/union");
+		if(c != CXXX)
+			diag(Z, "unnamed structure element cannot have class");
+	} else
+		if(c != CXXX)
+			diag(Z, "structure element cannot have class: %s", s->name);
+	t1 = t;
+	t = copytyp(t1);
+	t->sym = s;
+	t->down = T;
+	if(lastfield) {
+		t->shift = lastbit - lastfield;
+		t->nbits = lastfield;
+		if(firstbit)
+			t->shift = -t->shift;
+		if(typeu[t->etype])
+			t->etype = tufield->etype;
+		else
+			t->etype = tfield->etype;
+	}
+	if(strf == T)
+		strf = t;
+	else
+		strl->down = t;
+	strl = t;
+}
+
+/*
+ * this routine is very suspect.
+ * ansi requires the enum type to
+ * be represented as an 'int'
+ * this means that 0x81234567
+ * would be illegal. this routine
+ * makes signed and unsigned go
+ * to unsigned.
+ */
+Type*
+maxtype(Type *t1, Type *t2)
+{
+
+	if(t1 == T)
+		return t2;
+	if(t2 == T)
+		return t1;
+	if(t1->etype > t2->etype)
+		return t1;
+	return t2;
+}
+
+void
+doenum(Sym *s, Node *n)
+{
+
+	if(n) {
+		complex(n);
+		if(n->op != OCONST) {
+			diag(n, "enum not a constant: %s", s->name);
+			return;
+		}
+		en.cenum = n->type;
+		en.tenum = maxtype(en.cenum, en.tenum);
+
+		if(!typefd[en.cenum->etype])
+			en.lastenum = n->vconst;
+		else
+			en.floatenum = n->fconst;
+	}
+	if(dclstack)
+		push1(s);
+	xdecl(CXXX, types[TENUM], s);
+
+	if(en.cenum == T) {
+		en.tenum = types[TINT];
+		en.cenum = types[TINT];
+		en.lastenum = 0;
+	}
+	s->tenum = en.cenum;
+
+	if(!typefd[s->tenum->etype]) {
+		s->vconst = convvtox(en.lastenum, s->tenum->etype);
+		en.lastenum++;
+	} else {
+		s->fconst = en.floatenum;
+		en.floatenum++;
+	}
+
+	if(debug['d'])
+		dbgdecl(s);
+	acidvar(s);
+}
+
+void
+symadjust(Sym *s, Node *n, long del)
+{
+
+	switch(n->op) {
+	default:
+		if(n->left)
+			symadjust(s, n->left, del);
+		if(n->right)
+			symadjust(s, n->right, del);
+		return;
+
+	case ONAME:
+		if(n->sym == s)
+			n->xoffset -= del;
+		return;
+
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case OINDREG:
+	case OREGISTER:
+		return;
+	}
+}
+
+Node*
+contig(Sym *s, Node *n, long v)
+{
+	Node *p, *r, *q, *m;
+	long w;
+	Type *zt;
+
+	if(debug['i']) {
+		print("contig v = %ld; s = %s\n", v, s->name);
+		prtree(n, "doinit value");
+	}
+
+	if(n == Z)
+		goto no;
+	w = s->type->width;
+
+	/*
+	 * nightmare: an automatic array whose size
+	 * increases when it is initialized
+	 */
+	if(v != w) {
+		if(v != 0)
+			diag(n, "automatic adjustable array: %s", s->name);
+		v = s->offset;
+		autoffset = align(autoffset, s->type, Aaut3);
+		s->offset = -autoffset;
+		stkoff = maxround(stkoff, autoffset);
+		symadjust(s, n, v - s->offset);
+	}
+	if(w <= ewidth[TIND])
+		goto no;
+	if(n->op == OAS)
+		diag(Z, "oops in contig");
+/*ZZZ this appears incorrect
+need to check if the list completely covers the data.
+if not, bail
+ */
+	if(n->op == OLIST)
+		goto no;
+	if(n->op == OASI)
+		if(n->left->type)
+		if(n->left->type->width == w)
+			goto no;
+	while(w & (ewidth[TIND]-1))
+		w++;
+/*
+ * insert the following code, where long becomes vlong if pointers are fat
+ *
+	*(long**)&X = (long*)((char*)X + sizeof(X));
+	do {
+		*(long**)&X -= 1;
+		**(long**)&X = 0;
+	} while(*(long**)&X);
+ */
+
+	for(q=n; q->op != ONAME; q=q->left)
+		;
+
+	zt = ewidth[TIND] > ewidth[TLONG]? types[TVLONG]: types[TLONG];
+
+	p = new(ONAME, Z, Z);
+	*p = *q;
+	p->type = typ(TIND, zt);
+	p->xoffset = s->offset;
+
+	r = new(ONAME, Z, Z);
+	*r = *p;
+	r = new(OPOSTDEC, r, Z);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	q = new(OIND, q, Z);
+
+	m = new(OCONST, Z, Z);
+	m->vconst = 0;
+	m->type = zt;
+
+	q = new(OAS, q, m);
+
+	r = new(OLIST, r, q);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	r = new(ODWHILE, q, r);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	q->type = q->type->link;
+	q->xoffset += w;
+	q = new(OADDR, q, 0);
+
+	q = new(OASI, p, q);
+	r = new(OLIST, q, r);
+
+	n = new(OLIST, r, n);
+
+no:
+	return n;
+}
--- /dev/null
+++ b/utils/cc/dpchk.c
@@ -1,0 +1,494 @@
+#include	"cc.h"
+#include	"y.tab.h"
+
+enum
+{
+	Fnone	= 0,
+	Fl,
+	Fvl,
+	Fignor,
+	Fstar,
+	Fadj,
+
+	Fverb	= 10,
+};
+
+typedef	struct	Tprot	Tprot;
+struct	Tprot
+{
+	Type*	type;
+	Bits	flag;
+	Tprot*	link;
+};
+
+typedef	struct	Tname	Tname;
+struct	Tname
+{
+	char*	name;
+	int	param;
+	Tname*	link;
+};
+
+static	Type*	indchar;
+static	uchar	flagbits[512];
+static	char	fmtbuf[100];
+static	int	lastadj;
+static	int	lastverb;
+static	int	nstar;
+static	Tprot*	tprot;
+static	Tname*	tname;
+
+void
+argflag(int c, int v)
+{
+
+	switch(v) {
+	case Fignor:
+	case Fstar:
+	case Fl:
+	case Fvl:
+		flagbits[c] = v;
+		break;
+	case Fverb:
+		flagbits[c] = lastverb;
+/*print("flag-v %c %d\n", c, lastadj);*/
+		lastverb++;
+		break;
+	case Fadj:
+		flagbits[c] = lastadj;
+/*print("flag-l %c %d\n", c, lastadj);*/
+		lastadj++;
+		break;
+	}
+}
+
+Bits
+getflag(char *s)
+{
+	Bits flag;
+	int f;
+	char *fmt;
+	Rune c;
+
+	fmt = fmtbuf;
+	flag = zbits;
+	nstar = 0;
+	for(;;) {
+		s += chartorune(&c, s);
+		if(c == 0 || c >= nelem(flagbits))
+			break;
+		fmt += runetochar(fmt, &c);
+		f = flagbits[c];
+		switch(f) {
+		case Fnone:
+			argflag(c, Fverb);
+			f = flagbits[c];
+			break;
+		case Fstar:
+			nstar++;
+		case Fignor:
+			continue;
+		case Fl:
+			if(bset(flag, Fl))
+				flag = bor(flag, blsh(Fvl));
+		}
+		flag = bor(flag, blsh(f));
+		if(f >= Fverb)
+			break;
+	}
+	*fmt = 0;
+	return flag;
+}
+
+void
+newprot(Sym *m, Type *t, char *s)
+{
+	Bits flag;
+	Tprot *l;
+
+	if(t == T) {
+		warn(Z, "%s: newprot: type not defined", m->name);
+		return;
+	}
+	flag = getflag(s);
+	for(l=tprot; l; l=l->link)
+		if(beq(flag, l->flag) && sametype(t, l->type))
+			return;
+	l = alloc(sizeof(*l));
+	l->type = t;
+	l->flag = flag;
+	l->link = tprot;
+	tprot = l;
+}
+
+void
+newname(char *s, int p)
+{
+	Tname *l;
+
+	for(l=tname; l; l=l->link)
+		if(strcmp(l->name, s) == 0) {
+			if(l->param != p)
+				yyerror("vargck %s already defined\n", s);
+			return;
+		}
+	l = alloc(sizeof(*l));
+	l->name = s;
+	l->param = p;
+	l->link = tname;
+	tname = l;
+}
+
+void
+arginit(void)
+{
+	int i;
+
+/* debug['F'] = 1;*/
+/* debug['w'] = 1;*/
+
+	lastadj = Fadj;
+	lastverb = Fverb;
+	indchar = typ(TIND, types[TCHAR]);
+
+	memset(flagbits, Fnone, sizeof(flagbits));
+
+	for(i='0'; i<='9'; i++)
+		argflag(i, Fignor);
+	argflag('.', Fignor);
+	argflag('#', Fignor);
+	argflag('u', Fignor);
+	argflag('h', Fignor);
+	argflag('+', Fignor);
+	argflag('-', Fignor);
+
+	argflag('*', Fstar);
+	argflag('l', Fl);
+
+	argflag('o', Fverb);
+	flagbits['x'] = flagbits['o'];
+	flagbits['X'] = flagbits['o'];
+}
+
+void
+pragvararg(void)
+{
+	Sym *s;
+	int n, c;
+	char *t;
+	Rune r;
+	Type *ty;
+
+	if(!debug['F'])
+		goto out;
+	s = getsym();
+	if(s && strcmp(s->name, "argpos") == 0)
+		goto ckpos;
+	if(s && strcmp(s->name, "type") == 0)
+		goto cktype;
+	if(s && strcmp(s->name, "flag") == 0)
+		goto ckflag;
+	yyerror("syntax in #pragma varargck");
+	goto out;
+
+ckpos:
+/*#pragma	varargck	argpos	warn	2*/
+	s = getsym();
+	if(s == S)
+		goto bad;
+	n = getnsn();
+	if(n < 0)
+		goto bad;
+	newname(s->name, n);
+	goto out;
+
+ckflag:
+/*#pragma	varargck	flag	'c'*/
+	c = getnsc();
+	if(c != '\'')
+		goto bad;
+	c = getr();
+	if(c == '\\')
+		c = getr();
+	else if(c == '\'')
+		goto bad;
+	if(c == '\n')
+		goto bad;
+	if(getc() != '\'')
+		goto bad;
+	argflag(c, Fignor);
+	goto out;
+
+cktype:
+/*#pragma	varargck	type	O	int*/
+	c = getnsc();
+	if(c != '"')
+		goto bad;
+	t = fmtbuf;
+	for(;;) {
+		r = getr();
+		if(r == ' ' || r == '\n')
+			goto bad;
+		if(r == '"')
+			break;
+		t += runetochar(t, &r);
+	}
+	*t = 0;
+	t = strdup(fmtbuf);
+	s = getsym();
+	if(s == S)
+		goto bad;
+	ty = s->type;
+	while((c = getnsc()) == '*')
+		ty = typ(TIND, ty);
+	unget(c);
+	newprot(s, ty, t);
+	goto out;
+
+bad:
+	yyerror("syntax in #pragma varargck");
+
+out:
+	while(getnsc() != '\n')
+		;
+}
+
+Node*
+nextarg(Node *n, Node **a)
+{
+	if(n == Z) {
+		*a = Z;
+		return Z;
+	}
+	if(n->op == OLIST) {
+		*a = n->left;
+		return n->right;
+	}
+	*a = n;
+	return Z;
+}
+
+void
+checkargs(Node *nn, char *s, int pos)
+{
+	Node *a, *n;
+	Bits flag;
+	Tprot *l;
+
+	if(!debug['F'])
+		return;
+	n = nn;
+	for(;;) {
+		s = strchr(s, '%');
+		if(s == 0) {
+			nextarg(n, &a);
+			if(a != Z)
+				warn(nn, "more arguments than format %T",
+					a->type);
+			return;
+		}
+		s++;
+		flag = getflag(s);
+		while(nstar > 0) {
+			n = nextarg(n, &a);
+			pos++;
+			nstar--;
+			if(a == Z) {
+				warn(nn, "more format than arguments %s",
+					fmtbuf);
+				return;
+			}
+			if(a->type == T)
+				continue;
+			if(!sametype(types[TINT], a->type) &&
+			   !sametype(types[TUINT], a->type))
+				warn(nn, "format mismatch '*' in %s %T, arg %d",
+					fmtbuf, a->type, pos);
+		}
+		for(l=tprot; l; l=l->link)
+			if(sametype(types[TVOID], l->type)) {
+				if(beq(flag, l->flag)) {
+					s++;
+					goto loop;
+				}
+			}
+
+		n = nextarg(n, &a);
+		pos++;
+		if(a == Z) {
+			warn(nn, "more format than arguments %s",
+				fmtbuf);
+			return;
+		}
+		if(a->type == 0)
+			continue;
+		for(l=tprot; l; l=l->link)
+			if(sametype(a->type, l->type)) {
+/*print("checking %T/%ulx %T/%ulx\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/
+				if(beq(flag, l->flag))
+					goto loop;
+			}
+		warn(nn, "format mismatch %s %T, arg %d", fmtbuf, a->type, pos);
+	loop:;
+	}
+}
+
+void
+dpcheck(Node *n)
+{
+	char *s;
+	Node *a, *b;
+	Tname *l;
+	int i;
+
+	if(n == Z)
+		return;
+	b = n->left;
+	if(b == Z || b->op != ONAME)
+		return;
+	s = b->sym->name;
+	for(l=tname; l; l=l->link)
+		if(strcmp(s, l->name) == 0)
+			break;
+	if(l == 0)
+		return;
+
+	i = l->param;
+	b = n->right;
+	while(i > 0) {
+		b = nextarg(b, &a);
+		i--;
+	}
+	if(a == Z) {
+		warn(n, "cant find format arg");
+		return;
+	}
+	if(!sametype(indchar, a->type)) {
+		warn(n, "format arg type %T", a->type);
+		return;
+	}
+	if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) {
+/*		warn(n, "format arg not constant string");*/
+		return;
+	}
+	s = a->left->cstring;
+	checkargs(b, s, l->param);
+}
+
+void
+pragpack(void)
+{
+	Sym *s;
+
+	packflg = 0;
+	s = getsym();
+	if(s) {
+		packflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			packflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f'])
+		if(packflg)
+			print("%4ld: pack %d\n", lineno, packflg);
+		else
+			print("%4ld: pack off\n", lineno);
+}
+
+void
+pragfpround(void)
+{
+	Sym *s;
+
+	fproundflg = 0;
+	s = getsym();
+	if(s) {
+		fproundflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			fproundflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f'])
+		if(fproundflg)
+			print("%4ld: fproundflg %d\n", lineno, fproundflg);
+		else
+			print("%4ld: fproundflg off\n", lineno);
+}
+
+void
+pragprofile(void)
+{
+	Sym *s;
+
+	profileflg = 0;
+	s = getsym();
+	if(s) {
+		profileflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			profileflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f'])
+		if(profileflg)
+			print("%4ld: profileflg %d\n", lineno, profileflg);
+		else
+			print("%4ld: profileflg off\n", lineno);
+}
+
+void
+pragincomplete(void)
+{
+	Sym *s;
+	Type *t;
+	int istag, w, et;
+
+	istag = 0;
+	s = getsym();
+	if(s == nil)
+		goto out;
+	et = 0;
+	w = s->lexical;
+	if(w == LSTRUCT)
+		et = TSTRUCT;
+	else if(w == LUNION)
+		et = TUNION;
+	if(et != 0){
+		s = getsym();
+		if(s == nil){
+			yyerror("missing struct/union tag in pragma incomplete");
+			goto out;
+		}
+		if(s->lexical != LNAME && s->lexical != LTYPE){
+			yyerror("invalid struct/union tag: %s", s->name);
+			goto out;
+		}
+		dotag(s, et, 0);
+		istag = 1;
+	}else if(strcmp(s->name, "_off_") == 0){
+		debug['T'] = 0;
+		goto out;
+	}else if(strcmp(s->name, "_on_") == 0){
+		debug['T'] = 1;
+		goto out;
+	}
+	t = s->type;
+	if(istag)
+		t = s->suetag;
+	if(t == T)
+		yyerror("unknown type %s in pragma incomplete", s->name);
+	else if(!typesu[t->etype])
+		yyerror("not struct/union type in pragma incomplete: %s", s->name);
+	else
+		t->garb |= GINCOMPLETE;
+out:
+	while(getnsc() != '\n')
+		;
+	if(debug['f'])
+		print("%s incomplete\n", s->name);
+}
--- /dev/null
+++ b/utils/cc/funct.c
@@ -1,0 +1,400 @@
+#include	"cc.h"
+
+typedef	struct	Ftab	Ftab;
+struct	Ftab
+{
+	char	op;
+	char*	name;
+	char	typ;
+};
+typedef	struct	Gtab	Gtab;
+struct	Gtab
+{
+	char	etype;
+	char*	name;
+};
+
+Ftab	ftabinit[OEND];
+Gtab	gtabinit[NTYPE];
+
+int
+isfunct(Node *n)
+{
+	Type *t, *t1;
+	Funct *f;
+	Node *l;
+	Sym *s;
+	int o;
+
+	o = n->op;
+	if(n->left == Z)
+		goto no;
+	t = n->left->type;
+	if(t == T)
+		goto no;
+	f = t->funct;
+
+	switch(o) {
+	case OAS:	// put cast on rhs
+	case OASI:
+	case OASADD:
+	case OASAND:
+	case OASASHL:
+	case OASASHR:
+	case OASDIV:
+	case OASLDIV:
+	case OASLMOD:
+	case OASLMUL:
+	case OASLSHR:
+	case OASMOD:
+	case OASMUL:
+	case OASOR:
+	case OASSUB:
+	case OASXOR:
+		if(n->right == Z)
+			goto no;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto no;
+		if(t1->funct == f)
+			break;
+
+		l = new(OXXX, Z, Z);
+		*l = *n->right;
+
+		n->right->left = l;
+		n->right->right = Z;
+		n->right->type = t;
+		n->right->op = OCAST;
+
+		if(!isfunct(n->right))
+			prtree(n, "isfunc !");
+		break;
+
+	case OCAST:	// t f(T) or T f(t)
+		t1 = n->type;
+		if(t1 == T)
+			goto no;
+		if(f != nil) {
+			s = f->castfr[t1->etype];
+			if(s == S)
+				goto no;
+			n->right = n->left;
+			goto build;
+		}
+		f = t1->funct;
+		if(f != nil) {
+			s = f->castto[t->etype];
+			if(s == S)
+				goto no;
+			n->right = n->left;
+			goto build;
+		}
+		goto no;
+	}
+
+	if(f == nil)
+		goto no;
+	s = f->sym[o];
+	if(s == S)
+		goto no;
+
+	/*
+	 * the answer is yes,
+	 * now we rewrite the node
+	 * and give diagnostics
+	 */
+	switch(o) {
+	default:
+		diag(n, "isfunct op missing %O\n", o);
+		goto bad;
+
+	case OADD:	// T f(T, T)
+	case OAND:
+	case OASHL:
+	case OASHR:
+	case ODIV:
+	case OLDIV:
+	case OLMOD:
+	case OLMUL:
+	case OLSHR:
+	case OMOD:
+	case OMUL:
+	case OOR:
+	case OSUB:
+	case OXOR:
+
+	case OEQ:	// int f(T, T)
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLE:
+	case OLO:
+	case OLS:
+	case OLT:
+	case ONE:
+		if(n->right == Z)
+			goto bad;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto bad;
+		if(t1->funct != f)
+			goto bad;
+		n->right = new(OLIST, n->left, n->right);
+		break;
+
+	case OAS:	// structure copies done by the compiler
+	case OASI:
+		goto no;
+
+	case OASADD:	// T f(T*, T)
+	case OASAND:
+	case OASASHL:
+	case OASASHR:
+	case OASDIV:
+	case OASLDIV:
+	case OASLMOD:
+	case OASLMUL:
+	case OASLSHR:
+	case OASMOD:
+	case OASMUL:
+	case OASOR:
+	case OASSUB:
+	case OASXOR:
+		if(n->right == Z)
+			goto bad;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto bad;
+		if(t1->funct != f)
+			goto bad;
+		n->right = new(OLIST, new(OADDR, n->left, Z), n->right);
+		break;
+
+	case OPOS:	// T f(T)
+	case ONEG:
+	case ONOT:
+	case OCOM:
+		n->right = n->left;
+		break;
+
+
+	}
+
+build:
+	l = new(ONAME, Z, Z);
+	l->sym = s;
+	l->type = s->type;
+	l->etype = s->type->etype;
+	l->xoffset = s->offset;
+	l->class = s->class;
+	tcomo(l, 0);
+
+	n->op = OFUNC;
+	n->left = l;
+	n->type = l->type->link;
+	if(tcompat(n, T, l->type, tfunct))
+		goto bad;
+	if(tcoma(n->left, n->right, l->type->down, 1))
+		goto bad;
+	return 1;
+
+no:
+	return 0;
+
+bad:
+	diag(n, "cant rewrite typestr for op %O\n", o);
+	prtree(n, "isfunct");
+	n->type = T;
+	return 1;
+}
+
+void
+dclfunct(Type *t, Sym *s)
+{
+	Funct *f;
+	Node *n;
+	Type *f1, *f2, *f3, *f4;
+	int o, i, c;
+	char str[100];
+
+	if(t->funct)
+		return;
+
+	// recognize generated tag of dorm _%d_
+	if(t->tag == S)
+		goto bad;
+	for(i=0; c = t->tag->name[i]; i++) {
+		if(c == '_') {
+			if(i == 0 || t->tag->name[i+1] == 0)
+				continue;
+			break;
+		}
+		if(c < '0' || c > '9')
+			break;
+	}
+	if(c == 0)
+		goto bad;
+
+	f = alloc(sizeof(*f));
+	for(o=0; o<nelem(f->sym); o++)
+		f->sym[o] = S;
+
+	t->funct = f;
+
+	f1 = typ(TFUNC, t);
+	f1->down = copytyp(t);
+	f1->down->down = t;
+
+	f2 = typ(TFUNC, types[TINT]);
+	f2->down = copytyp(t);
+	f2->down->down = t;
+
+	f3 = typ(TFUNC, t);
+	f3->down = typ(TIND, t);
+	f3->down->down = t;
+
+	f4 = typ(TFUNC, t);
+	f4->down = t;
+
+	for(i=0;; i++) {
+		o = ftabinit[i].op;
+		if(o == OXXX)
+			break;
+		sprint(str, "%s_%s_", t->tag->name, ftabinit[i].name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->sym[o] = n->sym;
+		switch(ftabinit[i].typ) {
+		default:
+			diag(Z, "dclfunct op missing %d\n", ftabinit[i].typ);
+			break;
+
+		case 1:	// T f(T,T)	+
+			dodecl(xdecl, CEXTERN, f1, n);
+			break;
+
+		case 2:	// int f(T,T)	==
+			dodecl(xdecl, CEXTERN, f2, n);
+			break;
+
+		case 3:	// void f(T*,T)	+=
+			dodecl(xdecl, CEXTERN, f3, n);
+			break;
+
+		case 4:	// T f(T)	~
+			dodecl(xdecl, CEXTERN, f4, n);
+			break;
+		}
+	}
+	for(i=0;; i++) {
+		o = gtabinit[i].etype;
+		if(o == TXXX)
+			break;
+
+		/*
+		 * OCAST types T1 _T2_T1_(T2)
+		 */
+		sprint(str, "_%s%s_", gtabinit[i].name, t->tag->name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->castto[o] = n->sym;
+
+		f1 = typ(TFUNC, t);
+		f1->down = types[o];
+		dodecl(xdecl, CEXTERN, f1, n);
+
+		sprint(str, "%s_%s_", t->tag->name, gtabinit[i].name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->castfr[o] = n->sym;
+
+		f1 = typ(TFUNC, types[o]);
+		f1->down = t;
+		dodecl(xdecl, CEXTERN, f1, n);
+	}
+	return;
+bad:
+	diag(Z, "dclfunct bad %T %s\n", t, s->name);
+}
+
+Gtab	gtabinit[NTYPE] =
+{
+	TCHAR,		"c",
+	TUCHAR,		"uc",
+	TSHORT,		"h",
+	TUSHORT,	"uh",
+	TINT,		"i",
+	TUINT,		"ui",
+	TLONG,		"l",
+	TULONG,		"ul",
+	TVLONG,		"v",
+	TUVLONG,	"uv",
+	TFLOAT,		"f",
+	TDOUBLE,	"d",
+	TXXX
+};
+
+Ftab	ftabinit[OEND] =
+{
+	OADD,		"add",		1,
+	OAND,		"and",		1,
+	OASHL,		"ashl",		1,
+	OASHR,		"ashr",		1,
+	ODIV,		"div",		1,
+	OLDIV,		"ldiv",		1,
+	OLMOD,		"lmod",		1,
+	OLMUL,		"lmul",		1,
+	OLSHR,		"lshr",		1,
+	OMOD,		"mod",		1,
+	OMUL,		"mul",		1,
+	OOR,		"or",		1,
+	OSUB,		"sub",		1,
+	OXOR,		"xor",		1,
+
+	OEQ,		"eq",		2,
+	OGE,		"ge",		2,
+	OGT,		"gt",		2,
+	OHI,		"hi",		2,
+	OHS,		"hs",		2,
+	OLE,		"le",		2,
+	OLO,		"lo",		2,
+	OLS,		"ls",		2,
+	OLT,		"lt",		2,
+	ONE,		"ne",		2,
+
+	OASADD,		"asadd",	3,
+	OASAND,		"asand",	3,
+	OASASHL,	"asashl",	3,
+	OASASHR,	"asashr",	3,
+	OASDIV,		"asdiv",	3,
+	OASLDIV,	"asldiv",	3,
+	OASLMOD,	"aslmod",	3,
+	OASLMUL,	"aslmul",	3,
+	OASLSHR,	"aslshr",	3,
+	OASMOD,		"asmod",	3,
+	OASMUL,		"asmul",	3,
+	OASOR,		"asor",		3,
+	OASSUB,		"assub",	3,
+	OASXOR,		"asxor",	3,
+
+	OPOS,		"pos",		4,
+	ONEG,		"neg",		4,
+	OCOM,		"com",		4,
+	ONOT,		"not",		4,
+
+//	OPOSTDEC,
+//	OPOSTINC,
+//	OPREDEC,
+//	OPREINC,
+
+	OXXX,
+};
+
+//	Node*	nodtestv;
+
+//	Node*	nodvpp;
+//	Node*	nodppv;
+//	Node*	nodvmm;
+//	Node*	nodmmv;
--- /dev/null
+++ b/utils/cc/lex.c
@@ -1,0 +1,1547 @@
+#include	"cc.h"
+#include	"y.tab.h"
+
+#ifndef	CPP
+#define	CPP	"/bin/cpp"
+#endif
+
+/*
+ * known debug flags
+ *	-a		acid declaration output
+ *	-A		!B
+ *	-B		non ANSI
+ *	-d		print declarations
+ *	-D name		define
+ *	-F		format specification check
+ *	-i		print initialization
+ *	-I path		include
+ *	-l		generate little-endian code
+ *	-L		print every NAME symbol
+ *	-M		constant multiplication
+ *	-m		print add/sub/mul trees
+ *	-n		print acid to file (%.c=%.acid) (with -a or -aa)
+ *	-o file		output file
+ *	-p		use standard cpp ANSI preprocessor (not on windows)
+ *	-r		print registerization
+ *	-s		print structure offsets (with -a or -aa)
+ *	-S		print assembly
+ *	-t		print type trees
+ *	-V		enable void* conversion warnings
+ *	-v		verbose printing
+ *	-w		print warnings
+ *	-X		abort on error
+ *	-.		Inhibit search for includes in source directory
+ */
+
+void
+main(int argc, char *argv[])
+{
+	char *defs[50], *p;
+	int nproc, nout, status, i, c, ndef;
+
+	memset(debug, 0, sizeof(debug));
+	tinit();
+	cinit();
+	ginit();
+	arginit();
+
+	profileflg = 1;	/* #pragma can turn it off */
+	tufield = simplet((1L<<tfield->etype) | BUNSIGNED);
+	ndef = 0;
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+
+	case 'l':			/* for little-endian mips */
+		if(thechar != 'v'){
+			print("can only use -l with vc");
+			errorexit();
+		}
+		thechar = '0';
+		thestring = "spim";
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p) {
+			defs[ndef++] = p;
+			dodefine(p);
+		}
+		break;
+
+	case 'I':
+		p = ARGF();
+		if(p)
+			setinclude(p);
+		break;
+	} ARGEND
+	if(argc < 1 && outfile == 0) {
+		print("usage: %cc [-options] files\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't compile multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		/*
+		 * if we're writing acid to standard output, don't compile
+		 * concurrently, to avoid interleaving output.
+		 */
+		if(((!debug['a'] && !debug['Z']) || debug['n']) &&
+		    (p = getenv("NPROC")) != nil)
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0) {
+						print("cannot create a process\n");
+						errorexit();
+					}
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					fprint(2, "%s:\n", *argv);
+					if (compile(*argv, defs, ndef))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+
+	if(argc == 0)
+		c = compile("stdin", defs, ndef);
+	else
+		c = compile(argv[0], defs, ndef);
+
+	if(c)
+		errorexit();
+	exits(0);
+}
+
+int
+compile(char *file, char **defs, int ndef)
+{
+	char ofile[400], incfile[20];
+	char *p, *av[100], opt[256];
+	int i, c, fd[2];
+	static int first = 1;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		*p++ = 0;
+		if(!debug['.'])
+			include[0] = strdup(ofile);
+	} else
+		p = ofile;
+
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile) {
+			if(p = utfrrune(outfile, '.'))
+				if(p[1] == 'c' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			if(debug['a'] && debug['n'])
+				strcat(p, ".acid");
+			else if(debug['Z'] && debug['n'])
+				strcat(p, "_pickle.c");
+			else {
+				p[0] = '.';
+				p[1] = thechar;
+				p[2] = 0;
+			}
+		} else
+			outfile = "/dev/null";
+	}
+
+	if(p = getenv("INCLUDE")) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile, "/%s/include", thestring);
+			setinclude(strdup(incfile));
+			setinclude("/sys/include");
+		}
+	}
+	if (first)
+		Binit(&diagbuf, 1, OWRITE);
+	/*
+	 * if we're writing acid to standard output, don't keep scratching
+	 * outbuf.
+	 */
+	if((debug['a'] || debug['Z']) && !debug['n']) {
+		if (first) {
+			outfile = 0;
+			Binit(&outbuf, mydup(1, -1), OWRITE);
+			mydup(2, 1);
+		}
+	} else {
+		c = mycreat(outfile, 0664);
+		if(c < 0) {
+			diag(Z, "cannot open %s - %r", outfile);
+			outfile = 0;
+			errorexit();
+		}
+		Binit(&outbuf, c, OWRITE);
+	}
+	newio();
+	first = 0;
+
+	/* Use an ANSI preprocessor */
+	if(debug['p']) {
+		if(systemtype(Windows)) {
+			diag(Z, "-p option not supported on windows");
+			errorexit();
+		}
+		if(myaccess(file) < 0) {
+			diag(Z, "%s does not exist", file);
+			errorexit();
+		}
+		if(mypipe(fd) < 0) {
+			diag(Z, "pipe failed");
+			errorexit();
+		}
+		switch(myfork()) {
+		case -1:
+			diag(Z, "fork failed");
+			errorexit();
+		case 0:
+			close(fd[0]);
+			mydup(fd[1], 1);
+			close(fd[1]);
+			av[0] = CPP;
+			i = 1;
+			if(debug['.'])
+				av[i++] = strdup("-.");
+			/* 1999 ANSI C requires recognising // comments */
+			av[i++] = strdup("-+");
+			for(c = 0; c < ndef; c++) {
+				sprint(opt, "-D%s", defs[c]);
+				av[i++] = strdup(opt);
+			}
+			for(c = 0; c < ninclude; c++) {
+				sprint(opt, "-I%s", include[c]);
+				av[i++] = strdup(opt);
+			}
+			if(strcmp(file, "stdin") != 0)
+				av[i++] = file;
+			av[i] = 0;
+			if(debug['p'] > 1) {
+				for(c = 0; c < i; c++)
+					fprint(2, "%s ", av[c]);
+				fprint(2, "\n");
+			}
+			myexec(av[0], av);
+			fprint(2, "can't exec C preprocessor %s: %r\n", CPP);
+			errorexit();
+		default:
+			close(fd[1]);
+			newfile(file, fd[0]);
+			break;
+		}
+	} else {
+		if(strcmp(file, "stdin") == 0)
+			newfile(file, 0);
+		else
+			newfile(file, -1);
+	}
+	yyparse();
+	if(!debug['a'] && !debug['Z'])
+		gclean();
+	return nerrors;
+}
+
+void
+errorexit(void)
+{
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	if(debug['e'])
+		print("%L: %s\n", lineno, s);
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%cc: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	Sym *s;
+	ulong h;
+	char *p;
+	int c, n;
+
+	h = 0;
+	for(p=symb; *p;) {
+		h = h * 3;
+		h += *p++;
+	}
+	n = (p - symb) + 1;
+	if((long)h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(strcmp(s->name, symb) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	s->name = alloc(n);
+	memmove(s->name, symb, n);
+
+	strcpy(s->name, symb);
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+
+	return s;
+}
+
+void
+syminit(Sym *s)
+{
+	s->lexical = LNAME;
+	s->block = 0;
+	s->offset = 0;
+	s->type = T;
+	s->suetag = T;
+	s->class = CXXX;
+	s->aused = 0;
+	s->sig = SIGNONE;
+}
+
+#define	EOF	(-1)
+#define	IGN	(-2)
+#define	ESC	(1<<20)
+#define	GETC()	((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff))
+
+enum
+{
+	Numdec		= 1<<0,
+	Numlong		= 1<<1,
+	Numuns		= 1<<2,
+	Numvlong	= 1<<3,
+	Numflt		= 1<<4,
+};
+
+long
+yylex(void)
+{
+	vlong vv;
+	long c, c1, t;
+	char *cp;
+	Rune rune;
+	Sym *s;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c >= Runeself) {
+		/*
+		 * extension --
+		 *	all multibyte runes are alpha
+		 */
+		cp = symb;
+		goto talph;
+	}
+	if(isspace(c)) {
+		if(c == '\n')
+			lineno++;
+		goto l0;
+	}
+	if(isalpha(c)) {
+		cp = symb;
+		if(c != 'L')
+			goto talph;
+		*cp++ = c;
+		c = GETC();
+		if(c == '\'') {
+			/* L'x' */
+			c = escchar('\'', 1, 0);
+			if(c == EOF)
+				c = '\'';
+			c1 = escchar('\'', 1, 0);
+			if(c1 != EOF) {
+				yyerror("missing '");
+				peekc = c1;
+			}
+			yylval.vval = convvtox(c, TRUNE);
+			return LUCONST;
+		}
+		if(c == '"') {
+			goto caselq;
+		}
+		goto talph;
+	}
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+
+	case EOF:
+		peekc = EOF;
+		return -1;
+
+	case '_':
+		cp = symb;
+		goto talph;
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c1 = GETC();
+		if(isdigit(c1)) {
+			cp = symb;
+			*cp++ = c;
+			c = c1;
+			c1 = 0;
+			goto casedot;
+		}
+		break;
+
+	case '"':
+		strcpy(symb, "\"<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+
+		/* "..." */
+		for(;;) {
+			c = escchar('"', 0, 1);
+			if(c == EOF)
+				break;
+			if(c & ESC) {
+				cp = allocn(cp, c1, 1);
+				cp[c1++] = c;
+			} else {
+				rune = c;
+				c = runelen(rune);
+				cp = allocn(cp, c1, c);
+				runetochar(cp+c1, &rune);
+				c1 += c;
+			}
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, 1);
+			cp[c1++] = 0;
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LSTRING;
+
+	caselq:
+		/* L"..." */
+		strcpy(symb, "\"L<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+		for(;;) {
+			c = escchar('"', 1, 0);
+			if(c == EOF)
+				break;
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = c;
+			c1 += sizeof(TRune);
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = 0;
+			c1 += sizeof(TRune);
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LLSTRING;
+
+	case '\'':
+		/* '.' */
+		c = escchar('\'', 0, 0);
+		if(c == EOF)
+			c = '\'';
+		c1 = escchar('\'', 0, 0);
+		if(c1 != EOF) {
+			yyerror("missing '");
+			peekc = c1;
+		}
+		vv = c;
+		yylval.vval = convvtox(vv, TUCHAR);
+		if(yylval.vval != vv)
+			yyerror("overflow in character constant: 0x%lx", c);
+		else
+		if(c & 0x80){
+			nearln = lineno;
+			warn(Z, "sign-extended character constant");
+		}
+		yylval.vval = convvtox(vv, TCHAR);
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '*') {
+			for(;;) {
+				c = getr();
+				while(c == '*') {
+					c = getr();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '/') {
+			for(;;) {
+				c = getr();
+				if(c == '\n')
+					goto l0;
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '=')
+			return LDVE;
+		break;
+
+	case '*':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMLE;
+		break;
+
+	case '%':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMDE;
+		break;
+
+	case '+':
+		c1 = GETC();
+		if(c1 == '+')
+			return LPP;
+		if(c1 == '=')
+			return LPE;
+		break;
+
+	case '-':
+		c1 = GETC();
+		if(c1 == '-')
+			return LMM;
+		if(c1 == '=')
+			return LME;
+		if(c1 == '>')
+			return LMG;
+		break;
+
+	case '>':
+		c1 = GETC();
+		if(c1 == '>') {
+			c = LRSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LRSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LGE;
+		break;
+
+	case '<':
+		c1 = GETC();
+		if(c1 == '<') {
+			c = LLSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LLSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LLE;
+		break;
+
+	case '=':
+		c1 = GETC();
+		if(c1 == '=')
+			return LEQ;
+		break;
+
+	case '!':
+		c1 = GETC();
+		if(c1 == '=')
+			return LNE;
+		break;
+
+	case '&':
+		c1 = GETC();
+		if(c1 == '&')
+			return LANDAND;
+		if(c1 == '=')
+			return LANDE;
+		break;
+
+	case '|':
+		c1 = GETC();
+		if(c1 == '|')
+			return LOROR;
+		if(c1 == '=')
+			return LORE;
+		break;
+
+	case '^':
+		c1 = GETC();
+		if(c1 == '=')
+			return LXORE;
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+
+talph:
+	/*
+	 * cp is set to symb and some
+	 * prefix has been stored
+	 */
+	for(;;) {
+		if(c >= Runeself) {
+			for(c1=0;;) {
+				cp[c1++] = c;
+				if(fullrune(cp, c1))
+					break;
+				c = GETC();
+			}
+			cp += c1;
+			c = GETC();
+			continue;
+		}
+		if(!isalnum(c) && c != '_')
+			break;
+		*cp++ = c;
+		c = GETC();
+	}
+	*cp = 0;
+	if(debug['L'])
+		print("%L: %s\n", lineno, symb);
+	peekc = c;
+	s = lookup();
+	if(s->macro) {
+		newio();
+		cp = ionext->b;
+		macexpand(s, cp);
+		pushio();
+		ionext->link = iostack;
+		iostack = ionext;
+		fi.p = cp;
+		fi.c = strlen(cp);
+		if(peekc != IGN) {
+			cp[fi.c++] = peekc;
+			cp[fi.c] = 0;
+			peekc = IGN;
+		}
+		goto l0;
+	}
+	yylval.sym = s;
+	if(s->class == CTYPEDEF || s->class == CTYPESTR)
+		return LTYPE;
+	return s->lexical;
+
+tnum:
+	c1 = 0;
+	cp = symb;
+	if(c != '0') {
+		c1 |= Numdec;
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			goto dc;
+		}
+	}
+	*cp++ = c;
+	c = GETC();
+	if(c == 'x' || c == 'X')
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			if(c >= 'a' && c <= 'f')
+				continue;
+			if(c >= 'A' && c <= 'F')
+				continue;
+			if(cp == symb+2)
+				yyerror("malformed hex constant");
+			goto ncu;
+		}
+	if(c < '0' || c > '7')
+		goto dc;
+	for(;;) {
+		if(c >= '0' && c <= '7') {
+			*cp++ = c;
+			c = GETC();
+			continue;
+		}
+		goto ncu;
+	}
+
+dc:
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E')
+		goto casee;
+
+ncu:
+	if((c == 'U' || c == 'u') && !(c1 & Numuns)) {
+		c = GETC();
+		c1 |= Numuns;
+		goto ncu;
+	}
+	if((c == 'L' || c == 'l') && !(c1 & Numvlong)) {
+		c = GETC();
+		if(c1 & Numlong)
+			c1 |= Numvlong;
+		c1 |= Numlong;
+		goto ncu;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatov(symb, &yylval.vval))
+		yyerror("overflow in constant");
+
+	vv = yylval.vval;
+	if(c1 & Numvlong) {
+		if((c1 & Numuns) || convvtox(vv, TVLONG) < 0) {
+			c = LUVLCONST;
+			t = TUVLONG;
+			goto nret;
+		}
+		c = LVLCONST;
+		t = TVLONG;
+		goto nret;
+	}
+	if(c1 & Numlong) {
+		if((c1 & Numuns) || convvtox(vv, TLONG) < 0) {
+			c = LULCONST;
+			t = TULONG;
+			goto nret;
+		}
+		c = LLCONST;
+		t = TLONG;
+		goto nret;
+	}
+	if((c1 & Numuns) || convvtox(vv, TINT) < 0) {
+		c = LUCONST;
+		t = TUINT;
+		goto nret;
+	}
+	c = LCONST;
+	t = TINT;
+	goto nret;
+
+nret:
+	yylval.vval = convvtox(vv, t);
+	if(yylval.vval != vv){
+		nearln = lineno;
+		warn(Z, "truncated constant: %T %s", types[t], symb);
+	}
+	return c;
+
+casedot:
+	for(;;) {
+		*cp++ = c;
+		c = GETC();
+		if(!isdigit(c))
+			break;
+	}
+	if(c != 'e' && c != 'E')
+		goto caseout;
+
+casee:
+	*cp++ = 'e';
+	c = GETC();
+	if(c == '+' || c == '-') {
+		*cp++ = c;
+		c = GETC();
+	}
+	if(!isdigit(c))
+		yyerror("malformed fp constant exponent");
+	while(isdigit(c)) {
+		*cp++ = c;
+		c = GETC();
+	}
+
+caseout:
+	if(c == 'L' || c == 'l') {
+		c = GETC();
+		c1 |= Numlong;
+	} else
+	if(c == 'F' || c == 'f') {
+		c = GETC();
+		c1 |= Numflt;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatof(symb, &yylval.dval)) {
+		yyerror("overflow in float constant");
+		yylval.dval = 0;
+	}
+	if(c1 & Numflt)
+		return LFCONST;
+	return LDCONST;
+}
+
+/*
+ * convert a string, s, to vlong in *v
+ * return conversion overflow.
+ * required syntax is [0[x]]d*
+ */
+int
+mpatov(char *s, vlong *v)
+{
+	vlong n, nn;
+	int c;
+
+	n = 0;
+	c = *s;
+	if(c == '0')
+		goto oct;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			nn = n*10 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+oct:
+	s++;
+	c = *s;
+	if(c == 'x' || c == 'X')
+		goto hex;
+	while(c = *s++) {
+		if(c >= '0' || c <= '7')
+			nn = n*8 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+hex:
+	s++;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			c += 0-'0';
+		else
+		if(c >= 'a' && c <= 'f')
+			c += 10-'a';
+		else
+		if(c >= 'A' && c <= 'F')
+			c += 10-'A';
+		else
+			goto bad;
+		nn = n*16 + c;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+out:
+	*v = n;
+	return 0;
+
+bad:
+	*v = ~0;
+	return 1;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+long
+getr(void)
+{
+	int c, i;
+	char str[UTFmax+1];
+	Rune rune;
+
+
+	c = getc();
+	if(c < Runeself)
+		return c;
+	i = 0;
+	str[i++] = c;
+
+loop:
+	c = getc();
+	str[i++] = c;
+	if(!fullrune(str, i))
+		goto loop;
+	c = chartorune(&rune, str);
+	if(rune == Runeerror && c == 1) {
+		nearln = lineno;
+		diag(Z, "illegal rune in string");
+		for(c=0; c<i; c++)
+			print(" %.2x", *(uchar*)(str+c));
+		print("\n");
+	}
+	return rune;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	for(;;) {
+		if(c >= Runeself || !isspace(c))
+			return c;
+		if(c == '\n') {
+			lineno++;
+			return c;
+		}
+		c = GETC();
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+long
+escchar(long e, int longflg, int escflg)
+{
+	long c, l;
+	int i;
+
+loop:
+	c = getr();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			c = EOF;
+		return c;
+	}
+	c = getr();
+	if(c == 'x') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 2 hex
+		 */
+		i = 2;
+		if(longflg)
+			i = 6;
+		l = 0;
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '9') {
+				l = l*16 + c-'0';
+				continue;
+			}
+			if(c >= 'a' && c <= 'f') {
+				l = l*16 + c-'a' + 10;
+				continue;
+			}
+			if(c >= 'A' && c <= 'F') {
+				l = l*16 + c-'A' + 10;
+				continue;
+			}
+			unget(c);
+			break;
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	if(c >= '0' && c <= '7') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 3 oct
+		 */
+		i = 2;
+		if(longflg)
+			i = 8;
+		l = c - '0';
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				continue;
+			}
+			unget(c);
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return '\a';
+	case 'v':	return '\v';
+	}
+	return c;
+}
+
+struct
+{
+	char	*name;
+	ushort	lexical;
+	ushort	type;
+} itab[] =
+{
+	"auto",		LAUTO,		0,
+	"break",	LBREAK,		0,
+	"case",		LCASE,		0,
+	"char",		LCHAR,		TCHAR,
+	"const",	LCONSTNT,	0,
+	"continue",	LCONTINUE,	0,
+	"default",	LDEFAULT,	0,
+	"do",		LDO,		0,
+	"double",	LDOUBLE,	TDOUBLE,
+	"else",		LELSE,		0,
+	"enum",		LENUM,		0,
+	"extern",	LEXTERN,	0,
+	"float",	LFLOAT,		TFLOAT,
+	"for",		LFOR,		0,
+	"goto",		LGOTO,		0,
+	"if",		LIF,		0,
+	"inline",	LINLINE,	0,
+	"int",		LINT,		TINT,
+	"long",		LLONG,		TLONG,
+	"register",	LREGISTER,	0,
+	"restrict",	LRESTRICT,	0,
+	"return",	LRETURN,	0,
+	"SET",		LSET,		0,
+	"short",	LSHORT,		TSHORT,
+	"signed",	LSIGNED,	0,
+	"signof",	LSIGNOF,	0,
+	"sizeof",	LSIZEOF,	0,
+	"static",	LSTATIC,	0,
+	"struct",	LSTRUCT,	0,
+	"switch",	LSWITCH,	0,
+	"typedef",	LTYPEDEF,	0,
+	"typestr",	LTYPESTR,	0,
+	"union",	LUNION,		0,
+	"unsigned",	LUNSIGNED,	0,
+	"USED",		LUSED,		0,
+	"void",		LVOID,		TVOID,
+	"volatile",	LVOLATILE,	0,
+	"while",	LWHILE,		0,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+	Type *t;
+
+	nerrors = 0;
+	lineno = 1;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+
+	types[TXXX] = T;
+	types[TCHAR] = typ(TCHAR, T);
+	types[TUCHAR] = typ(TUCHAR, T);
+	types[TSHORT] = typ(TSHORT, T);
+	types[TUSHORT] = typ(TUSHORT, T);
+	types[TINT] = typ(TINT, T);
+	types[TUINT] = typ(TUINT, T);
+	types[TLONG] = typ(TLONG, T);
+	types[TULONG] = typ(TULONG, T);
+	types[TVLONG] = typ(TVLONG, T);
+	types[TUVLONG] = typ(TUVLONG, T);
+	types[TFLOAT] = typ(TFLOAT, T);
+	types[TDOUBLE] = typ(TDOUBLE, T);
+	types[TVOID] = typ(TVOID, T);
+	types[TENUM] = typ(TENUM, T);
+	types[TFUNC] = typ(TFUNC, types[TINT]);
+	types[TIND] = typ(TIND, types[TVOID]);
+
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->lexical = itab[i].lexical;
+		if(itab[i].type != 0)
+			s->type = types[itab[i].type];
+	}
+	blockno = 0;
+	autobn = 0;
+	autoffset = 0;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+	symstring = slookup(".string");
+	symstring->class = CSTATIC;
+	symstring->type = t;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+
+	nodproto = new(OPROTO, Z, Z);
+	dclstack = D;
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+
+	fmtinstall('O', Oconv);
+	fmtinstall('T', Tconv);
+	fmtinstall('F', FNconv);
+	fmtinstall('L', Lconv);
+	fmtinstall('Q', Qconv);
+	fmtinstall('|', VBconv);
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0] & 0xff;
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++ & 0xff;
+}
+
+int
+Oconv(Fmt *fp)
+{
+	int a;
+
+	a = va_arg(fp->args, int);
+	if(a < OXXX || a > OEND)
+		return fmtprint(fp, "***badO %d***", a);
+
+	return fmtstrcpy(fp, onames[a]);
+}
+
+int
+Lconv(Fmt *fp)
+{
+	char str[STRINGSZ], s[STRINGSZ];
+	Hist *h;
+	struct
+	{
+		Hist*	incl;	/* start of this include file */
+		long	idel;	/* delta line number to apply to include */
+		Hist*	line;	/* start of this #line directive */
+		long	ldel;	/* delta line number to apply to #line */
+	} a[HISTSZ];
+	long l, d;
+	int i, n;
+
+	l = va_arg(fp->args, long);
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset != 0) {		/* #line directive, not #pragma */
+				if(n > 0 && n < HISTSZ && h->offset >= 0) {
+					a[n-1].line = h;
+					a[n-1].ldel = h->line - h->offset + 1;
+				}
+			} else {
+				if(n < HISTSZ) {	/* beginning of file */
+					a[n].incl = h;
+					a[n].idel = h->line;
+					a[n].line = 0;
+				}
+				n++;
+			}
+			continue;
+		}
+		n--;
+		if(n > 0 && n < HISTSZ) {
+			d = h->line - a[n].incl->line;
+			a[n-1].ldel += d;
+			a[n-1].idel += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	str[0] = 0;
+	for(i=n-1; i>=0; i--) {
+		if(i != n-1) {
+			if(fp->flags & ~(FmtWidth|FmtPrec))	/* BUG ROB - was f3 */
+				break;
+			strcat(str, " ");
+		}
+		if(a[i].line)
+			snprint(s, STRINGSZ, "%s:%ld[%s:%ld]",
+				a[i].line->name, l-a[i].ldel+1,
+				a[i].incl->name, l-a[i].idel+1);
+		else
+			snprint(s, STRINGSZ, "%s:%ld",
+				a[i].incl->name, l-a[i].idel+1);
+		if(strlen(s)+strlen(str) >= STRINGSZ-10)
+			break;
+		strcat(str, s);
+		l = a[i].incl->line - 1;	/* now print out start of this file */
+	}
+	if(n == 0)
+		strcat(str, "<eof>");
+	return fmtstrcpy(fp, str);
+}
+
+int
+Tconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], s[STRINGSZ+20];
+	Type *t, *t1;
+	int et;
+	long n;
+
+	str[0] = 0;
+	for(t = va_arg(fp->args, Type*); t != T; t = t->link) {
+		et = t->etype;
+		if(str[0])
+			strcat(str, " ");
+		if(t->garb&~GINCOMPLETE) {
+			sprint(s, "%s ", gnames[t->garb&~GINCOMPLETE]);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		sprint(s, "%s", tnames[et]);
+		if(strlen(str) + strlen(s) < STRINGSZ)
+			strcat(str, s);
+		if(et == TFUNC && (t1 = t->down)) {
+			sprint(s, "(%T", t1);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+			while(t1 = t1->down) {
+				sprint(s, ", %T", t1);
+				if(strlen(str) + strlen(s) < STRINGSZ)
+					strcat(str, s);
+			}
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, ")");
+		}
+		if(et == TARRAY) {
+			n = t->width;
+			if(t->link && t->link->width)
+				n /= t->link->width;
+			sprint(s, "[%ld]", n);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(t->nbits) {
+			sprint(s, " %d:%d", t->shift, t->nbits);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(typesu[et]) {
+			if(t->tag) {
+				strcat(str, " ");
+				if(strlen(str) + strlen(t->tag->name) < STRINGSZ)
+					strcat(str, t->tag->name);
+			} else
+				strcat(str, " {}");
+			break;
+		}
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+FNconv(Fmt *fp)
+{
+	char *str;
+	Node *n;
+
+	n = va_arg(fp->args, Node*);
+	str = "<indirect>";
+	if(n != Z && (n->op == ONAME || n->op == ODOT || n->op == OELEM))
+		str = n->sym->name;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Qconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], *s;
+	long b;
+	int i;
+
+	str[0] = 0;
+	for(b = va_arg(fp->args, long); b;) {
+		i = bitno(b);
+		if(str[0])
+			strcat(str, " ");
+		s = qnames[i];
+		if(strlen(str) + strlen(s) >= STRINGSZ)
+			break;
+		strcat(str, s);
+		b &= ~(1L << i);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+VBconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	int i, n, t, pc;
+
+	n = va_arg(fp->args, int);
+	pc = 0;	/* BUG: was printcol */
+	i = 0;
+	while(pc < n) {
+		t = (pc+4) & ~3;
+		if(t <= n) {
+			str[i++] = '\t';
+			pc = t;
+			continue;
+		}
+		str[i++] = ' ';
+		pc++;
+	}
+	str[i] = 0;
+
+	return fmtstrcpy(fp, str);
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+	char *e;
+
+	while(*p != 0) {
+		e = strchr(p, ' ');
+		if(e != 0)
+			*e = '\0';
+
+		for(i=1; i < ninclude; i++)
+			if(strcmp(p, include[i]) == 0)
+				break;
+
+		if(i >= ninclude)
+			include[ninclude++] = p;
+
+		if(ninclude > nelem(include)) {
+			diag(Z, "ninclude too small %d", nelem(include));
+			exits("ninclude");
+		}
+
+		if(e == 0)
+			break;
+		p = e+1;
+	}
+}
--- /dev/null
+++ b/utils/cc/lexbody
@@ -1,0 +1,688 @@
+/*
+ * common code for all the assemblers
+ */
+
+void
+pragpack(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragvararg(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragfpround(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragprofile(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragincomplete(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+
+	if(p == 0)
+		return;
+	for(i=1; i < ninclude; i++)
+		if(strcmp(p, include[i]) == 0)
+			return;
+
+	if(ninclude >= nelem(include)) {
+		yyerror("ninclude too small %d", nelem(include));
+		exits("ninclude");
+	}
+	include[ninclude++] = p;
+}
+
+void
+errorexit(void)
+{
+
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%ca: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	Sym *s;
+	long h;
+	char *p;
+	int c, l;
+
+	h = 0;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	s->name = alloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+	return s;
+}
+
+long
+yylex(void)
+{
+	int c, c1;
+	char *cp;
+	Sym *s;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c == EOF) {
+		peekc = EOF;
+		return -1;
+	}
+	if(isspace(c)) {
+		if(c == '\n') {
+			lineno++;
+			return ';';
+		}
+		goto l0;
+	}
+	if(isalpha(c))
+		goto talph;
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+	case '\n':
+		lineno++;
+		return ';';
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c = GETC();
+		if(isalpha(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto aloop;
+		}
+		if(isdigit(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto casedot;
+		}
+		peekc = c;
+		return '.';
+
+	talph:
+	case '_':
+	case '@':
+		cp = symb;
+
+	aloop:
+		*cp++ = c;
+		c = GETC();
+		if(isalpha(c) || isdigit(c) || c == '_' || c == '$')
+			goto aloop;
+		*cp = 0;
+		peekc = c;
+		s = lookup();
+		if(s->macro) {
+			newio();
+			cp = ionext->b;
+			macexpand(s, cp);
+			pushio();
+			ionext->link = iostack;
+			iostack = ionext;
+			fi.p = cp;
+			fi.c = strlen(cp);
+			if(peekc != IGN) {
+				cp[fi.c++] = peekc;
+				cp[fi.c] = 0;
+				peekc = IGN;
+			}
+			goto l0;
+		}
+		if(s->type == 0)
+			s->type = LNAME;
+		if(s->type == LNAME ||
+		   s->type == LVAR ||
+		   s->type == LLAB) {
+			yylval.sym = s;
+			return s->type;
+		}
+		yylval.lval = s->value;
+		return s->type;
+
+	tnum:
+		cp = symb;
+		if(c != '0')
+			goto dc;
+		*cp++ = c;
+		c = GETC();
+		c1 = 3;
+		if(c == 'x' || c == 'X') {
+			c1 = 4;
+			c = GETC();
+		} else
+		if(c < '0' || c > '7')
+			goto dc;
+		yylval.lval = 0;
+		for(;;) {
+			if(c >= '0' && c <= '9') {
+				if(c > '7' && c1 == 3)
+					break;
+				yylval.lval <<= c1;
+				yylval.lval += c - '0';
+				c = GETC();
+				continue;
+			}
+			if(c1 == 3)
+				break;
+			if(c >= 'A' && c <= 'F')
+				c += 'a' - 'A';
+			if(c >= 'a' && c <= 'f') {
+				yylval.lval <<= c1;
+				yylval.lval += c - 'a' + 10;
+				c = GETC();
+				continue;
+			}
+			break;
+		}
+		goto ncu;
+
+	dc:
+		for(;;) {
+			if(!isdigit(c))
+				break;
+			*cp++ = c;
+			c = GETC();
+		}
+		if(c == '.')
+			goto casedot;
+		if(c == 'e' || c == 'E')
+			goto casee;
+		*cp = 0;
+		if(sizeof(yylval.lval) == sizeof(vlong))
+			yylval.lval = strtoll(symb, nil, 10);
+		else
+			yylval.lval = strtol(symb, nil, 10);
+
+	ncu:
+		while(c == 'U' || c == 'u' || c == 'l' || c == 'L')
+			c = GETC();
+		peekc = c;
+		return LCONST;
+
+	casedot:
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(!isdigit(c))
+				break;
+		}
+		if(c == 'e' || c == 'E')
+			goto casee;
+		goto caseout;
+
+	casee:
+		*cp++ = 'e';
+		c = GETC();
+		if(c == '+' || c == '-') {
+			*cp++ = c;
+			c = GETC();
+		}
+		while(isdigit(c)) {
+			*cp++ = c;
+			c = GETC();
+		}
+
+	caseout:
+		*cp = 0;
+		peekc = c;
+		if(FPCHIP) {
+			yylval.dval = atof(symb);
+			return LFCONST;
+		}
+		yyerror("assembler cannot interpret fp constants");
+		yylval.lval = 1L;
+		return LCONST;
+
+	case '"':
+		memcpy(yylval.sval, nullgen.sval, sizeof(yylval.sval));
+		cp = yylval.sval;
+		c1 = 0;
+		for(;;) {
+			c = escchar('"');
+			if(c == EOF)
+				break;
+			if(c1 < sizeof(yylval.sval))
+				*cp++ = c;
+			c1++;
+		}
+		if(c1 > sizeof(yylval.sval))
+			yyerror("string constant too long");
+		return LSCONST;
+
+	case '\'':
+		c = escchar('\'');
+		if(c == EOF)
+			c = '\'';
+		if(escchar('\'') != EOF)
+			yyerror("missing '");
+		yylval.lval = c;
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '/') {
+			for(;;) {
+				c = GETC();
+				if(c == '\n') {
+					lineno++;
+					goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '*') {
+			for(;;) {
+				c = GETC();
+				while(c == '*') {
+					c = GETC();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+				if(c == '\n')
+					lineno++;
+			}
+		}
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		return c;
+	}
+	c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	for(;;) {
+		c = getc();
+		if(!isspace(c) || c == '\n')
+			return c;
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+int
+escchar(int e)
+{
+	int c, l;
+
+loop:
+	c = getc();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			return EOF;
+		return c;
+	}
+	c = getc();
+	if(c >= '0' && c <= '7') {
+		l = c - '0';
+		c = getc();
+		if(c >= '0' && c <= '7') {
+			l = l*8 + c-'0';
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				return l;
+			}
+		}
+		peekc = c;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return 0x07;
+	case 'v':	return 0x0b;
+	case 'z':	return 0x00;
+	}
+	return c;
+}
+
+void
+pinit(char *f)
+{
+	int i;
+	Sym *s;
+
+	lineno = 1;
+	newio();
+	newfile(f, -1);
+	pc = 0;
+	peekc = IGN;
+	sym = 1;
+	for(i=0; i<NSYM; i++) {
+		h[i].type = 0;
+		h[i].sym = S;
+	}
+	for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			s->macro = 0;
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0];
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++;
+}
+
+void
+yyerror(char *a, ...)
+{
+	char buf[200];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(a, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	prfile(lineno);
+	va_start(arg, a);
+	vseprint(buf, buf+sizeof(buf), a, arg);
+	va_end(arg);
+	print("%s\n", buf);
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
+
+void
+prfile(long l)
+{
+	int i, n;
+	Hist a[HISTSZ], *h;
+	long d;
+
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset == 0) {
+				if(n >= 0 && n < HISTSZ)
+					a[n] = *h;
+				n++;
+				continue;
+			}
+			if(n > 0 && n < HISTSZ)
+				if(a[n-1].offset == 0) {
+					a[n] = *h;
+					n++;
+				} else
+					a[n-1] = *h;
+			continue;
+		}
+		n--;
+		if(n >= 0 && n < HISTSZ) {
+			d = h->line - a[n].line;
+			for(i=0; i<n; i++)
+				a[i].line += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	for(i=0; i<n; i++)
+		print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1));
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}
--- /dev/null
+++ b/utils/cc/mac.c
@@ -1,0 +1,3 @@
+#include	"cc.h"
+
+#include	"macbody"
--- /dev/null
+++ b/utils/cc/macbody
@@ -1,0 +1,856 @@
+#define VARMAC 0x80
+
+long
+getnsn(void)
+{
+	long n;
+	int c;
+
+	c = getnsc();
+	if(c < '0' || c > '9')
+		return -1;
+	n = 0;
+	while(c >= '0' && c <= '9') {
+		n = n*10 + c-'0';
+		c = getc();
+	}
+	unget(c);
+	return n;
+}
+
+Sym*
+getsym(void)
+{
+	int c;
+	char *cp;
+
+	c = getnsc();
+	if(!isalpha(c) && c != '_' && c < Runeself) {
+		unget(c);
+		return S;
+	}
+	for(cp = symb;;) {
+		if(cp <= symb+NSYMB-4)
+			*cp++ = c;
+		c = getc();
+		if(isalnum(c) || c == '_' || c >= Runeself)
+			continue;
+		unget(c);
+		break;
+	}
+	*cp = 0;
+	if(cp > symb+NSYMB-4)
+		yyerror("symbol too large: %s", symb);
+	return lookup();
+}
+
+Sym*
+getsymdots(int *dots)
+{
+	int c;
+	Sym *s;
+
+	s = getsym();
+	if(s != S)
+		return s;
+
+	c = getnsc();
+	if(c != '.'){
+		unget(c);
+		return S;
+	}
+	if(getc() != '.' || getc() != '.')
+		yyerror("bad dots in macro");
+	*dots = 1;
+	return slookup("__VA_ARGS__");
+}
+
+int
+getcom(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c != '/')
+			break;
+		c = getc();
+		if(c == '/') {
+			while(c != '\n')
+				c = getc();
+			break;
+		}
+		if(c != '*')
+			break;
+		c = getc();
+		for(;;) {
+			if(c == '*') {
+				c = getc();
+				if(c != '/')
+					continue;
+				c = getc();
+				break;
+			}
+			if(c == '\n') {
+				yyerror("comment across newline");
+				break;
+			}
+			c = getc();
+		}
+		if(c == '\n')
+			break;
+	}
+	return c;
+}
+
+void
+dodefine(char *cp)
+{
+	Sym *s;
+	char *p;
+	long l;
+
+	strcpy(symb, cp);
+	p = strchr(symb, '=');
+	if(p) {
+		*p++ = 0;
+		s = lookup();
+		l = strlen(p) + 2;	/* +1 null, +1 nargs */
+		while(l & 3)
+			l++;
+		while(nhunk < l)
+			gethunk();
+		*hunk = 0;
+		strcpy(hunk+1, p);
+		s->macro = hunk;
+		hunk += l;
+		nhunk -= l;
+	} else {
+		s = lookup();
+		s->macro = "\0001";	/* \000 is nargs */
+	}
+	if(debug['m'])
+		print("#define (-D) %s %s\n", s->name, s->macro+1);
+}
+
+struct
+{
+	char	*macname;
+	void	(*macf)(void);
+} mactab[] =
+{
+	"ifdef",	0,	/* macif(0) */
+	"ifndef",	0,	/* macif(1) */
+	"else",		0,	/* macif(2) */
+
+	"line",		maclin,
+	"define",	macdef,
+	"include",	macinc,
+	"undef",	macund,
+
+	"pragma",	macprag,
+	"endif",	macend,
+	0
+};
+
+void
+domacro(void)
+{
+	int i;
+	Sym *s;
+
+	s = getsym();
+	if(s == S)
+		s = slookup("endif");
+	for(i=0; mactab[i].macname; i++)
+		if(strcmp(s->name, mactab[i].macname) == 0) {
+			if(mactab[i].macf)
+				(*mactab[i].macf)();
+			else
+				macif(i);
+			return;
+		}
+	yyerror("unknown #: %s", s->name);
+	macend();
+}
+
+void
+macund(void)
+{
+	Sym *s;
+
+	s = getsym();
+	macend();
+	if(s == S) {
+		yyerror("syntax in #undef");
+		return;
+	}
+	s->macro = 0;
+}
+
+#define	NARG	25
+void
+macdef(void)
+{
+	Sym *s, *a;
+	char *args[NARG], *np, *base;
+	int n, i, c, len, dots;
+	int ischr;
+
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(s->macro)
+		yyerror("macro redefined: %s", s->name);
+	c = getc();
+	n = -1;
+	dots = 0;
+	if(c == '(') {
+		n++;
+		c = getnsc();
+		if(c != ')') {
+			unget(c);
+			for(;;) {
+				a = getsymdots(&dots);
+				if(a == S)
+					goto bad;
+				if(n >= NARG) {
+					yyerror("too many arguments in #define: %s", s->name);
+					goto bad;
+				}
+				args[n++] = a->name;
+				c = getnsc();
+				if(c == ')')
+					break;
+				if(c != ',' || dots)
+					goto bad;
+			}
+		}
+		c = getc();
+	}
+	if(isspace(c))
+		if(c != '\n')
+			c = getnsc();
+	base = hunk;
+	len = 1;
+	ischr = 0;
+	for(;;) {
+		if(isalpha(c) || c == '_') {
+			np = symb;
+			*np++ = c;
+			c = getc();
+			while(isalnum(c) || c == '_') {
+				*np++ = c;
+				c = getc();
+			}
+			*np = 0;
+			for(i=0; i<n; i++)
+				if(strcmp(symb, args[i]) == 0)
+					break;
+			if(i >= n) {
+				i = strlen(symb);
+				base = allocn(base, len, i);
+				memcpy(base+len, symb, i);
+				len += i;
+				continue;
+			}
+			base = allocn(base, len, 2);
+			base[len++] = '#';
+			base[len++] = 'a' + i;
+			continue;
+		}
+		if(ischr){
+			if(c == '\\'){ 
+				base = allocn(base, len, 1);
+				base[len++] = c;
+				c = getc();
+			}else if(c == ischr)
+				ischr = 0;
+		}else{
+			if(c == '"' || c == '\''){
+				base = allocn(base, len, 1);
+				base[len++] = c;
+				ischr = c;
+				c = getc();
+				continue;
+			}
+			if(c == '/') {
+				c = getc();
+				if(c == '/'){
+					c = getc();
+					for(;;) {
+						if(c == '\n')
+							break;
+						c = getc();
+					}
+					continue;
+				}
+				if(c == '*'){
+					c = getc();
+					for(;;) {
+						if(c == '*') {
+							c = getc();
+							if(c != '/')
+								continue;
+							c = getc();
+							break;
+						}
+						if(c == '\n') {
+							yyerror("comment and newline in define: %s", s->name);
+							break;
+						}
+						c = getc();
+					}
+					continue;
+				}
+				base = allocn(base, len, 1);
+				base[len++] = '/';
+				continue;
+			}
+		}
+		if(c == '\\') {
+			c = getc();
+			if(c == '\n') {
+				c = getc();
+				continue;
+			}
+			else if(c == '\r') {
+				c = getc();
+				if(c == '\n') {
+					c = getc();
+					continue;
+				}
+			}
+			base = allocn(base, len, 1);
+			base[len++] = '\\';
+			continue;
+		}
+		if(c == '\n')
+			break;
+		if(c == '#')
+		if(n > 0) {
+			base = allocn(base, len, 1);
+			base[len++] = c;
+		}
+		base = allocn(base, len, 1);
+		base[len++] = c;
+		c = ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff));
+		if(c == '\n')
+			lineno++;
+		if(c == -1) {
+			yyerror("eof in a macro: %s", s->name);
+			break;
+		}
+	}
+	do {
+		base = allocn(base, len, 1);
+		base[len++] = 0;
+	} while(len & 3);
+
+	*base = n+1;
+	if(dots)
+		*base |= VARMAC;
+	s->macro = base;
+	if(debug['m'])
+		print("#define %s %s\n", s->name, s->macro+1);
+	return;
+
+bad:
+	if(s == S)
+		yyerror("syntax in #define");
+	else
+		yyerror("syntax in #define: %s", s->name);
+	macend();
+}
+
+void
+macexpand(Sym *s, char *b)
+{
+	char buf[2000];
+	int n, l, c, nargs;
+	char *arg[NARG], *cp, *ob, *ecp, dots;
+
+	ob = b;
+	if(*s->macro == 0) {
+		strcpy(b, s->macro+1);
+		if(debug['m'])
+			print("#expand %s %s\n", s->name, ob);
+		return;
+	}
+	
+	nargs = (char)(*s->macro & ~VARMAC) - 1;
+	dots = *s->macro & VARMAC;
+
+	c = getnsc();
+	if(c != '(')
+		goto bad;
+	n = 0;
+	c = getc();
+	if(c != ')') {
+		unget(c);
+		l = 0;
+		cp = buf;
+		ecp = cp + sizeof(buf)-4;
+		arg[n++] = cp;
+		for(;;) {
+			if(cp >= ecp)
+				goto toobig;
+			c = getc();
+			if(c == '"')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '"')
+						break;
+				}
+			if(c == '\'')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '\'')
+						break;
+				}
+			if(c == '/') {
+				c = getc();
+				switch(c) {
+				case '*':
+					for(;;) {
+						c = getc();
+						if(c == '*') {
+							c = getc();
+							if(c == '/')
+								break;
+						}
+					}
+					*cp++ = ' ';
+					continue;
+				case '/':
+					while((c = getc()) != '\n')
+						;
+					break;
+				default:
+					unget(c);
+					c = '/';
+				}
+			}
+			if(l == 0) {
+				if(c == ',') {
+					if(n == nargs && dots) {
+						*cp++ = ',';
+						continue;
+					}
+					*cp++ = 0;
+					arg[n++] = cp;
+					if(n > nargs)
+						break;
+					continue;
+				}
+				if(c == ')')
+					break;
+			}
+			if(c == '\n')
+				c = ' ';
+			*cp++ = c;
+			if(c == '(')
+				l++;
+			if(c == ')')
+				l--;
+		}
+		*cp = 0;
+	}
+	if(n != nargs) {
+		yyerror("argument mismatch expanding: %s", s->name);
+		*b = 0;
+		return;
+	}
+	cp = s->macro+1;
+	for(;;) {
+		c = *cp++;
+		if(c == '\n')
+			c = ' ';
+		if(c != '#') {
+			*b++ = c;
+			if(c == 0)
+				break;
+			continue;
+		}
+		c = *cp++;
+		if(c == 0)
+			goto bad;
+		if(c == '#') {
+			*b++ = c;
+			continue;
+		}
+		c -= 'a';
+		if(c < 0 || c >= n)
+			continue;
+		strcpy(b, arg[c]);
+		b += strlen(arg[c]);
+	}
+	*b = 0;
+	if(debug['m'])
+		print("#expand %s %s\n", s->name, ob);
+	return;
+
+bad:
+	yyerror("syntax in macro expansion: %s", s->name);
+	*b = 0;
+	return;
+
+toobig:
+	yyerror("too much text in macro expansion: %s", s->name);
+	*b = 0;
+}
+
+void
+macinc(void)
+{
+	int c0, c, i, f;
+	char str[STRINGSZ], *hp;
+
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = str;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	f = -1;
+	for(i=0; i<ninclude; i++) {
+		if(i == 0 && c0 == '>')
+			continue;
+		strcpy(symb, include[i]);
+		strcat(symb, "/");
+		if(strcmp(symb, "./") == 0)
+			symb[0] = 0;
+		strcat(symb, str);
+		f = open(symb, 0);
+		if(f >= 0)
+			break;
+	}
+	if(f < 0)
+		strcpy(symb, str);
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+	newio();
+	pushio();
+	newfile(hp, f);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #include");
+	macend();
+}
+
+void
+maclin(void)
+{
+	char *cp;
+	int c;
+	long n;
+
+	n = getnsn();
+	c = getc();
+	if(n < 0)
+		goto bad;
+
+	for(;;) {
+		if(c == ' ' || c == '\t') {
+			c = getc();
+			continue;
+		}
+		if(c == '"')
+			break;
+		if(c == '\n') {
+			strcpy(symb, "<noname>");
+			goto nn;
+		}
+		goto bad;
+	}
+	cp = symb;
+	for(;;) {
+		c = getc();
+		if(c == '"')
+			break;
+		*cp++ = c;
+	}
+	*cp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+nn:
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	cp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+	linehist(cp, n);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #line");
+	macend();
+}
+
+void
+macif(int f)
+{
+	int c, l, bol;
+	Sym *s;
+
+	if(f == 2)
+		goto skip;
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(getcom() != '\n')
+		goto bad;
+	if((s->macro != 0) ^ f)
+		return;
+
+skip:
+	bol = 1;
+	l = 0;
+	for(;;) {
+		c = getc();
+		if(c != '#') {
+			if(!isspace(c))
+				bol = 0;
+			if(c == '\n')
+				bol = 1;
+			continue;
+		}
+		if(!bol)
+			continue;
+		s = getsym();
+		if(s == S)
+			continue;
+		if(strcmp(s->name, "endif") == 0) {
+			if(l) {
+				l--;
+				continue;
+			}
+			macend();
+			return;
+		}
+		if(strcmp(s->name, "ifdef") == 0 || strcmp(s->name, "ifndef") == 0) {
+			l++;
+			continue;
+		}
+		if(l == 0 && f != 2 && strcmp(s->name, "else") == 0) {
+			macend();
+			return;
+		}
+	}
+
+bad:
+	yyerror("syntax in #if(n)def");
+	macend();
+}
+
+void
+macprag(void)
+{
+	Sym *s;
+	int c0, c;
+	char *hp;
+	Hist *h;
+
+	s = getsym();
+
+	if(s && strcmp(s->name, "lib") == 0)
+		goto praglib;
+	if(s && strcmp(s->name, "pack") == 0) {
+		pragpack();
+		return;
+	}
+	if(s && strcmp(s->name, "fpround") == 0) {
+		pragfpround();
+		return;
+	}
+	if(s && strcmp(s->name, "profile") == 0) {
+		pragprofile();
+		return;
+	}
+	if(s && strcmp(s->name, "varargck") == 0) {
+		pragvararg();
+		return;
+	}
+	if(s && strcmp(s->name, "incomplete") == 0) {
+		pragincomplete();
+		return;
+	}
+	while(getnsc() != '\n')
+		;
+	return;
+
+praglib:
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = symb;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	/*
+	 * put pragma-line in as a funny history 
+	 */
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+
+	h = alloc(sizeof(Hist));
+	h->name = hp;
+	h->line = lineno;
+	h->offset = -1;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #pragma lib");
+	macend();
+}
+
+void
+macend(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c < 0 || c == '\n')
+			return;
+	}
+}
+
+void
+linehist(char *f, int offset)
+{
+	Hist *h;
+
+	/*
+	 * overwrite the last #line directive if
+	 * no alloc has happened since the last one
+	 */
+	if(newflag == 0 && ehist != H && offset != 0 && ehist->offset != 0)
+		if(f && ehist->name && strcmp(f, ehist->name) == 0) {
+			ehist->line = lineno;
+			ehist->offset = offset;
+			return;
+		}
+
+	if(debug['f'])
+		if(f) {
+			if(offset)
+				print("%4ld: %s (#line %d)\n", lineno, f, offset);
+			else
+				print("%4ld: %s\n", lineno, f);
+		} else
+			print("%4ld: <pop>\n", lineno);
+	newflag = 0;
+
+	h = alloc(sizeof(Hist));
+	h->name = f;
+	h->line = lineno;
+	h->offset = offset;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 10L*NHUNK)
+		nh = 10L*NHUNK;
+	h = (char*)mysbrk(nh);
+	if(h == (char*)-1) {
+		yyerror("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
--- /dev/null
+++ b/utils/cc/mkfile
@@ -1,0 +1,35 @@
+<../../mkconfig
+
+LIB=libcc.a
+
+OFILES=\
+	acid.$O\
+	bits.$O\
+	com.$O\
+	com64.$O\
+	$TARGMODEL.$O\
+	dcl.$O\
+	dpchk.$O\
+	funct.$O\
+	lex.$O\
+	mac.$O\
+	mpatof.$O\
+	pickle.$O\
+	scon.$O\
+	sub.$O\
+	y.tab.$O\
+	omachcap.$O\
+ 
+HFILES=cc.h\
+	y.tab.h\
+ 
+YFILES=cc.y\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+mac.$O: macbody
+
+lex.$O:	lex.c
+	$CC $CFLAGS '-DCPP="/bin/cpp"' lex.c
--- /dev/null
+++ b/utils/cc/mpatof.c
@@ -1,0 +1,271 @@
+#include	"cc.h"
+
+enum
+{
+	Mpscale	= 29,		/* safely smaller than bits in a long */
+	Mpprec	= 36,		/* Mpscale*Mpprec sb > largest fp exp */
+	Mpbase	= 1L<<Mpscale,
+};
+
+typedef
+struct
+{
+	long	a[Mpprec];
+	char	ovf;
+} Mp;
+
+int	mpatof(char*, double*);
+int	mpatov(char *s, vlong *v);
+void	mpint(Mp*, int);
+void	mppow(Mp*, int, int);
+void	mpmul(Mp*, int);
+void	mpadd(Mp*, Mp*);
+int	mptof(Mp*, double*);
+
+/*
+ * convert a string, s, to floating in *d
+ * return conversion overflow.
+ * required syntax is [+-]d*[.]d*[e[+-]d*]
+ */
+int
+mpatof(char *s, double *d)
+{
+	Mp a, b;
+	int dp, c, f, ef, ex, zer;
+	double d1, d2;
+
+	dp = 0;		/* digits after decimal point */
+	f = 0;		/* sign */
+	ex = 0;		/* exponent */
+	zer = 1;	/* zero */
+	memset(&a, 0, sizeof(a));
+	for(;;) {
+		switch(c = *s++) {
+		default:
+			goto bad;
+		case '-':
+			f = 1;
+		case ' ':
+		case  '\t':
+		case  '+':
+			continue;
+		case '.':
+			dp = 1;
+			continue;
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			zer = 0;
+		case '0':
+			mpint(&b, c-'0');
+			mpmul(&a, 10);
+			mpadd(&a, &b);
+			if(dp)
+				dp++;
+			continue;
+		case 'E':
+		case 'e':
+			ex = 0;
+			ef = 0;
+			for(;;) {
+				c = *s++;
+				if(c == '+' || c == ' ' || c == '\t')
+					continue;
+				if(c == '-') {
+					ef = 1;
+					continue;
+				}
+				if(c >= '0' && c <= '9') {
+					ex = ex*10 + (c-'0');
+					continue;
+				}
+				break;
+			}
+			if(ef)
+				ex = -ex;
+		case 0:
+			break;
+		}
+		break;
+	}
+	if(a.ovf)
+		goto bad;
+	if(zer) {
+		*d = 0;
+		return 0;
+	}
+	if(dp)
+		dp--;
+	dp -= ex;
+	if(dp > 0) {
+		/*
+		 * must divide by 10**dp
+		 */
+		if(mptof(&a, &d1))
+			goto bad;
+
+		/*
+		 * trial exponent of 8**dp
+		 * 8 (being between 5 and 10)
+		 * should pick up all underflows
+		 * in the division of 5**dp.
+		 */
+		d2 = frexp(d1, &ex);
+		d2 = ldexp(d2, ex-3*dp);
+		if(d2 == 0)
+			goto bad;
+
+		/*
+		 * decompose each 10 into 5*2.
+		 * create 5**dp in fixed point
+		 * and then play with the exponent
+		 * for the remaining 2**dp.
+		 * note that 5**dp will overflow
+		 * with as few as 134 input digits.
+		 */
+		mpint(&a, 1);
+		mppow(&a, 5, dp);
+		if(mptof(&a, &d2))
+			goto bad;
+		d1 = frexp(d1/d2, &ex);
+		d1 = ldexp(d1, ex-dp);
+		if(d1 == 0)
+			goto bad;
+	} else {
+		/*
+		 * must multiply by 10**|dp| --
+		 * just do it in fixed point.
+		 */
+		mppow(&a, 10, -dp);
+		if(mptof(&a, &d1))
+			goto bad;
+	}
+	if(f)
+		d1 = -d1;
+	*d = d1;
+	return 0;
+
+bad:
+	return 1;
+}
+
+/*
+ * convert a to floating in *d
+ * return conversion overflow
+ */
+int
+mptof(Mp *a, double *d)
+{
+	double f, g;
+	long x, *a1;
+	int i;
+
+	if(a->ovf)
+		return 1;
+	a1 = a->a;
+	f = ldexp(*a1++, 0);
+	for(i=Mpscale; i<Mpprec*Mpscale; i+=Mpscale)
+		if(x = *a1++) {
+			g = ldexp(x, i);
+			/*
+			 * NOTE: the test (g==0) is plan9
+			 * specific. ansi compliant overflow
+			 * is signaled by HUGE and errno==ERANGE.
+			 * change this for your particular ldexp.
+			 */
+			if(g == 0)
+				return 1;
+			f += g;		/* this could bomb! */
+		}
+	*d = f;
+	return 0;
+}
+
+/*
+ * return a += b
+ */
+void
+mpadd(Mp *a, Mp *b)
+{
+	int i, c;
+	long x, *a1, *b1;
+
+	if(b->ovf)
+		a->ovf = 1;
+	if(a->ovf)
+		return;
+	c = 0;
+	a1 = a->a;
+	b1 = b->a;
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 + *b1++ + c;
+		c = 0;
+		if(x >= Mpbase) {
+			x -= Mpbase;
+			c = 1;
+		}
+		*a1++ = x;
+	}
+	a->ovf = c;
+}
+
+/*
+ * return a = c
+ */
+void
+mpint(Mp *a, int c)
+{
+
+	memset(a, 0, sizeof(*a));
+	a->a[0] = c;
+}
+
+/*
+ * return a *= c
+ */
+void
+mpmul(Mp *a, int c)
+{
+	Mp p;
+	int b;
+
+	memmove(&p, a, sizeof(p));
+	if(!(c & 1))
+		memset(a, 0, sizeof(*a));
+	c &= ~1;
+	for(b=2; c; b<<=1) {
+		mpadd(&p, &p);
+		if(c & b) {
+			mpadd(a, &p);
+			c &= ~b;
+		}
+	}
+}
+
+/*
+ * return a *= b**e
+ */
+void
+mppow(Mp *a, int b, int e)
+{
+	int b1;
+
+	b1 = b*b;
+	b1 = b1*b1;
+	while(e >= 4) {
+		mpmul(a, b1);
+		e -= 4;
+		if(a->ovf)
+			return;
+	}
+	while(e > 0) {
+		mpmul(a, b);
+		e--;
+	}
+}
--- /dev/null
+++ b/utils/cc/omachcap.c
@@ -1,0 +1,9 @@
+#include	"cc.h"
+
+/* default, like old cc */
+int
+machcap(Node *n)
+{
+	USED(n);
+	return 0;
+}
--- /dev/null
+++ b/utils/cc/pgen.c
@@ -1,0 +1,592 @@
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+	Prog *sp;
+	Node *n1, nod, nod1;
+
+	cursafe = 0;
+	curarg = 0;
+	maxargsafe = 0;
+	hasdoubled = 0;
+
+	/*
+	 * isolate name
+	 */
+	for(n1 = nn;; n1 = n1->left) {
+		if(n1 == Z) {
+			diag(nn, "cant find function name");
+			return;
+		}
+		if(n1->op == ONAME)
+			break;
+	}
+	nearln = nn->lineno;
+	gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+	sp = p;
+
+	if(typecmplx[thisfn->link->etype]) {
+		if(nodret == nil) {
+			nodret = new(ONAME, Z, Z);
+			nodret->sym = slookup(".ret");
+			nodret->class = CPARAM;
+			nodret->type = types[TIND];
+			nodret->etype = TIND;
+			nodret = new(OIND, nodret, Z);
+		}
+		n1 = nodret->left;
+		if(n1->type == T || n1->type->link != thisfn->link) {
+			n1->type = typ(TIND, thisfn->link);
+			n1->etype = n1->type->etype;
+			nodret = new(OIND, n1, Z);
+			complex(nodret);
+		}
+	}
+
+	/*
+	 * isolate first argument
+	 */
+	if(REGARG >= 0) {	
+		if(typecmplx[thisfn->link->etype]) {
+			nod1 = *nodret->left;
+			nodreg(&nod, &nod1, REGARG);
+			gmove(&nod, &nod1);
+		} else
+		if(firstarg && typeword[firstargtype->etype]) {
+			nod1 = znode;
+			nod1.op = ONAME;
+			nod1.sym = firstarg;
+			nod1.type = firstargtype;
+			nod1.class = CPARAM;
+			nod1.xoffset = align(0, firstargtype, Aarg1);
+			nod1.etype = firstargtype->etype;
+			xcom(&nod1);
+			nodreg(&nod, &nod1, REGARG);
+			gmove(&nod, &nod1);
+		}
+	}
+
+	canreach = 1;
+	warnreach = 1;
+	gen(n);
+	if(canreach && thisfn->link->etype != TVOID){
+		if(debug['B'])
+			warn(Z, "no return at end of function: %s", n1->sym->name);
+		else
+			diag(Z, "no return at end of function: %s", n1->sym->name);
+	}
+	noretval(3);
+	gbranch(ORETURN);
+
+	if(!debug['N'] || debug['R'] || debug['P'])
+		regopt(sp);
+	
+	if(thechar=='6' || thechar=='7' || thechar=='9' || hasdoubled)	/* [sic] */
+		maxargsafe = round(maxargsafe, 8);
+	sp->to.offset += maxargsafe;
+}
+
+void
+supgen(Node *n)
+{
+	int owarn;
+	long spc;
+	Prog *sp;
+
+	if(n == Z)
+		return;
+	suppress++;
+	owarn = warnreach;
+	warnreach = 0;
+	spc = pc;
+	sp = lastp;
+	gen(n);
+	lastp = sp;
+	pc = spc;
+	sp->link = nil;
+	suppress--;
+	warnreach = owarn;
+}
+
+Node*
+uncomma(Node *n)
+{
+	while(n != Z && n->op == OCOMMA) {
+		cgen(n->left, Z);
+		n = n->right;
+	}
+	return n;
+}
+
+void
+gen(Node *n)
+{
+	Node *l, nod, rn;
+	Prog *sp, *spc, *spb;
+	Case *cn;
+	long sbc, scc;
+	int snbreak, sncontin;
+	int f, o, oldreach;
+
+loop:
+	if(n == Z)
+		return;
+	nearln = n->lineno;
+	o = n->op;
+	if(debug['G'])
+		if(o != OLIST)
+			print("%L %O\n", nearln, o);
+
+	if(!canreach) {
+		switch(o) {
+		case OLABEL:
+		case OCASE:
+		case OLIST:
+		case OCOMMA:
+		case OBREAK:
+		case OFOR:
+		case OWHILE:
+		case ODWHILE:
+			/* all handled specially - see switch body below */
+			break;
+		default:
+			if(warnreach) {
+				warn(n, "unreachable code %O", o);
+				warnreach = 0;
+			}
+		}
+	}
+
+	switch(o) {
+
+	default:
+		complex(n);
+		cgen(n, Z);
+		break;
+
+	case OLIST:
+	case OCOMMA:
+		gen(n->left);
+
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		canreach = 0;
+		warnreach = !suppress;
+		complex(n);
+		if(n->type == T)
+			break;
+		l = uncomma(n->left);
+		if(l == Z) {
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(typecmplx[n->type->etype]) {
+			nod = znode;
+			nod.op = OAS;
+			nod.left = nodret;
+			nod.right = l;
+			nod.type = n->type;
+			nod.complex = l->complex;
+			cgen(&nod, Z);
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(newvlongcode && !typefd[n->type->etype]){
+			regret(&rn, n);
+			regfree(&rn);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = &rn;
+			nod.right = l;
+			nod.type = n->type;
+			nod.complex = l->complex;
+			cgen(&nod, Z);
+			noretval(2);
+			gbranch(ORETURN);
+			break;
+		}
+		regret(&nod, n);
+		cgen(l, &nod);
+		regfree(&nod);
+		if(typefd[n->type->etype])
+			noretval(1);
+		else
+			noretval(2);
+		gbranch(ORETURN);
+		break;
+
+	case OLABEL:
+		canreach = 1;
+		l = n->left;
+		if(l) {
+			l->pc = pc;
+			if(l->label)
+				patch(l->label, pc);
+		}
+		gbranch(OGOTO);	/* prevent self reference in reg */
+		patch(p, pc);
+		goto rloop;
+
+	case OGOTO:
+		canreach = 0;
+		warnreach = !suppress;
+		n = n->left;
+		if(n == Z)
+			return;
+		if(n->complex == 0) {
+			diag(Z, "label undefined: %s", n->sym->name);
+			return;
+		}
+		if(suppress)
+			return;
+		gbranch(OGOTO);
+		if(n->pc) {
+			patch(p, n->pc);
+			return;
+		}
+		if(n->label)
+			patch(n->label, pc-1);
+		n->label = p;
+		return;
+
+	case OCASE:
+		canreach = 1;
+		l = n->left;
+		if(cases == C)
+			diag(n, "case/default outside a switch");
+		if(l == Z) {
+			casf();
+			cases->val = 0;
+			cases->def = 1;
+			cases->label = pc;
+			cases->isv = 0;
+			goto rloop;
+		}
+		complex(l);
+		if(l->type == T)
+			goto rloop;
+		if(l->op != OCONST || !typeswitch[l->type->etype]) {
+			diag(n, "case expression must be integer constant");
+			goto rloop;
+		}
+		casf();
+		cases->val = l->vconst;
+		cases->def = 0;
+		cases->label = pc;
+		cases->isv = typev[l->type->etype];
+		goto rloop;
+
+	case OSWITCH:
+		l = n->left;
+		complex(l);
+		if(l->type == T)
+			break;
+		if(!typeswitch[l->type->etype]) {
+			diag(n, "switch expression must be integer");
+			break;
+		}
+
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		cn = cases;
+		cases = C;
+		casf();
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		gen(n->right);		/* body */
+		if(canreach){
+			gbranch(OGOTO);
+			patch(p, breakpc);
+			nbreak++;
+		}
+
+		patch(sp, pc);
+		regalloc(&nod, l, Z);
+		/* always signed */
+		if(typev[l->type->etype])
+			nod.type = types[TVLONG];
+		else
+			nod.type = types[TLONG];
+		cgen(l, &nod);
+		doswit(&nod);
+		regfree(&nod);
+		patch(spb, pc);
+
+		cases = cn;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		break;
+
+	case OWHILE:
+	case ODWHILE:
+		l = n->left;
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		if(n->op == OWHILE)
+			patch(sp, pc);
+		bcomplex(l, Z);		/* test */
+		patch(p, breakpc);
+		if(l->op != OCONST || vconst(l) == 0)
+			nbreak++;
+
+		if(n->op == ODWHILE)
+			patch(sp, pc);
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		break;
+
+	case OFOR:
+		l = n->left;
+		if(!canreach && l->right->left && warnreach) {
+			warn(n, "unreachable code FOR");
+			warnreach = 0;
+		}
+		gen(l->right->left);	/* init */
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		/* 
+		 * if there are no incoming labels in the 
+		 * body and the top's not reachable, warn
+		 */
+		if(!canreach && warnreach && deadheads(n)) {
+			warn(n, "unreachable code %O", o);
+			warnreach = 0;
+		}
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		sncontin = ncontin;
+		ncontin = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		gen(l->right->right);	/* inc */
+		patch(sp, pc);	
+		if(l->left != Z) {	/* test */
+			bcomplex(l->left, Z);
+			patch(p, breakpc);
+			if(l->left->op != OCONST || vconst(l->left) == 0)
+				nbreak++;
+		}
+		canreach = 1;
+		gen(n->right);		/* body */
+		if(canreach){
+			gbranch(OGOTO);
+			patch(p, continpc);
+			ncontin++;
+		}
+		if(!ncontin && l->right->right && warnreach) {
+			warn(l->right->right, "unreachable FOR inc");
+			warnreach = 0;
+		}
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		ncontin = sncontin;
+		break;
+
+	case OCONTINUE:
+		if(continpc < 0) {
+			diag(n, "continue not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, continpc);
+		ncontin++;
+		canreach = 0;
+		warnreach = !suppress;
+		break;
+
+	case OBREAK:
+		if(breakpc < 0) {
+			diag(n, "break not in a loop");
+			break;
+		}
+		/*
+		 * Don't complain about unreachable break statements.
+		 * There are breaks hidden in yacc's output and some people
+		 * write return; break; in their switch statements out of habit.
+		 * However, don't confuse the analysis by inserting an 
+		 * unreachable reference to breakpc either.
+		 */
+		if(!canreach)
+			break;
+		gbranch(OGOTO);
+		patch(p, breakpc);
+		nbreak++;
+		canreach = 0;
+		warnreach = !suppress;
+		break;
+
+	case OIF:
+		l = n->left;
+		if(bcomplex(l, n->right)) {
+			if(typefd[l->type->etype])
+				f = !l->fconst;
+			else
+				f = !l->vconst;
+			if(debug['c'])
+				print("%L const if %s\n", nearln, f ? "false" : "true");
+			if(f) {
+				canreach = 1;
+				supgen(n->right->left);
+				oldreach = canreach;
+				canreach = 1;
+				gen(n->right->right);
+				/*
+				 * treat constant ifs as regular ifs for 
+				 * reachability warnings.
+				 */
+				if(!canreach && oldreach && debug['w'] < 2)
+					warnreach = 0;
+			}
+			else {
+				canreach = 1;
+				gen(n->right->left);
+				oldreach = canreach;
+				canreach = 1;
+				supgen(n->right->right);
+				/*
+				 * treat constant ifs as regular ifs for 
+				 * reachability warnings.
+				 */
+				if(!oldreach && canreach && debug['w'] < 2)
+					warnreach = 0;
+				canreach = oldreach;
+			}
+		}
+		else {
+			sp = p;
+			canreach = 1;
+			if(n->right->left != Z)
+				gen(n->right->left);
+			oldreach = canreach;
+			canreach = 1;
+			if(n->right->right != Z) {
+				gbranch(OGOTO);
+				patch(sp, pc);
+				sp = p;
+				gen(n->right->right);
+			}
+			patch(sp, pc);
+			canreach = canreach || oldreach;
+			if(canreach == 0)
+				warnreach = !suppress;
+		}
+		break;
+
+	case OSET:
+	case OUSED:
+		usedset(n->left, o);
+		break;
+	}
+}
+
+void
+usedset(Node *n, int o)
+{
+	if(n->op == OLIST) {
+		usedset(n->left, o);
+		usedset(n->right, o);
+		return;
+	}
+	complex(n);
+	switch(n->op) {
+	case OADDR:	/* volatile */
+		gins(ANOP, n, Z);
+		break;
+	case ONAME:
+		if(o == OSET)
+			gins(ANOP, Z, n);
+		else
+			gins(ANOP, n, Z);
+		break;
+	}
+}
+
+int
+bcomplex(Node *n, Node *c)
+{
+	Node *b, nod;
+
+
+	complex(n);
+	if(n->type != T)
+	if(tcompat(n, T, n->type, tnot))
+		n->type = T;
+	if(n->type == T) {
+		gbranch(OGOTO);
+		return 0;
+	}
+	if(c != Z && n->op == OCONST && deadheads(c))
+		return 1;
+	if(newvlongcode && typev[n->type->etype] && machcap(Z)) {
+		nod = znode;
+		b = &nod;
+		b->op = ONE;
+		b->left = n;
+		b->right = new(0, Z, Z);
+		*b->right = *nodconst(0);
+		b->right->type = n->type;
+		b->type = types[TLONG];
+		xcom(b);
+		boolgen(b, 1, Z);
+		return 0;
+	}
+	bool64(n);
+	boolgen(n, 1, Z);
+	return 0;
+}
--- /dev/null
+++ b/utils/cc/pickle.c
@@ -1,0 +1,268 @@
+#include "cc.h"
+
+static char *kwd[] =
+{
+	"$adt", "$aggr", "$append", "$builtin", "$complex", "$defn",
+	"$delete", "$do", "$else", "$eval", "$head", "$if",
+	"$local", "$loop", "$return", "$tail", "$then",
+	"$union", "$whatis", "$while",
+};
+static char picklestr[] = "\tbp = pickle(bp, ep, un, ";
+
+static char*
+pmap(char *s)
+{
+	int i, bot, top, new;
+
+	bot = 0;
+	top = bot + nelem(kwd) - 1;
+	while(bot <= top){
+		new = bot + (top - bot)/2;
+		i = strcmp(kwd[new]+1, s);
+		if(i == 0)
+			return kwd[new];
+
+		if(i < 0)
+			bot = new + 1;
+		else
+			top = new - 1;
+	}
+	return s;
+}
+
+Sym*
+picklesue(Type *t)
+{
+	int h;
+	Sym *s;
+
+	if(t != T)
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->suetag && s->suetag->link == t)
+				return s;
+	return 0;
+}
+
+Sym*
+picklefun(Type *t)
+{
+	int h;
+	Sym *s;
+
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->type == t)
+				return s;
+	return 0;
+}
+
+char	picklechar[NTYPE];
+Init	picklecinit[] =
+{
+	TCHAR,		'C',	0,
+	TUCHAR,		'b',	0,
+	TSHORT,		'd',	0,
+	TUSHORT,		'u',	0,
+	TLONG,		'D',	0,
+	TULONG,		'U',	0,
+	TVLONG,		'V',	0,
+	TUVLONG,	'W',	0,
+	TFLOAT,		'f',	0,
+	TDOUBLE,		'F',	0,
+	TARRAY,		'a',	0,
+	TIND,		'X',	0,
+	-1,		0,	0,
+};
+
+static void
+pickleinit(void)
+{
+	Init *p;
+
+	for(p=picklecinit; p->code >= 0; p++)
+		picklechar[p->code] = p->value;
+
+	picklechar[TINT] = picklechar[TLONG];
+	picklechar[TUINT] = picklechar[TULONG];
+	if(types[TINT]->width != types[TLONG]->width) {
+		picklechar[TINT] = picklechar[TSHORT];
+		picklechar[TUINT] = picklechar[TUSHORT];
+		if(types[TINT]->width != types[TSHORT]->width)
+			warn(Z, "picklemember int not long or short");
+	}
+	
+}
+
+void
+picklemember(Type *t, long off)
+{
+	Sym *s, *s1;
+	static int picklecharinit = 0;
+
+	if(picklecharinit == 0) {
+		pickleinit();
+		picklecharinit = 1;
+	}
+	s = t->sym;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "	T%d\n", t->etype);
+		break;
+
+	case TIND:
+		if(s == S)
+			Bprint(&outbuf,
+				"%s\"p\", (char*)addr+%ld+_i*%ld);\n",
+				picklestr, t->offset+off, t->width);
+		else
+			Bprint(&outbuf,
+				"%s\"p\", &addr->%s);\n",
+				picklestr, pmap(s->name));
+		break;
+
+	case TINT:
+	case TUINT:
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+		if(s == S)
+			Bprint(&outbuf, "%s\"%c\", (char*)addr+%ld+_i*%ld);\n",
+				picklestr, picklechar[t->etype], t->offset+off, t->width);
+		else
+			Bprint(&outbuf, "%s\"%c\", &addr->%s);\n",
+				picklestr, picklechar[t->etype], pmap(s->name));
+		break;
+	case TARRAY:
+		Bprint(&outbuf, "\tfor(_i = 0; _i < %ld; _i++) {\n\t",
+			t->width/t->link->width);
+		picklemember(t->link, t->offset+off);
+		Bprint(&outbuf, "\t}\n\t_i = 0;\n\tUSED(_i);\n");
+		break;
+
+	case TSTRUCT:
+	case TUNION:
+		s1 = picklesue(t->link);
+		if(s1 == S)
+			break;
+		if(s == S) {
+			Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, (%s*)((char*)addr+%ld+_i*%ld));\n",
+				pmap(s1->name), pmap(s1->name), t->offset+off, t->width);
+		} else {
+			Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, &addr->%s);\n",
+				pmap(s1->name), pmap(s->name));
+		}
+		break;
+	}
+}
+
+void
+pickletype(Type *t)
+{
+	Sym *s;
+	Type *l;
+	Io *i;
+	int n;
+	char *an;
+
+	if(!debug['P'])
+		return;
+	if(debug['P'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	s = picklesue(t->link);
+	if(s == S)
+		return;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "T%d\n", t->etype);
+		return;
+
+	case TUNION:
+	case TSTRUCT:
+		if(debug['s'])
+			goto asmstr;
+		an = pmap(s->name);
+
+		Bprint(&outbuf, "uchar*\npickle_%s(uchar *bp, uchar *ep, int un, %s *addr)\n{\n\tint _i = 0;\n\n\tUSED(_i);\n", an, an);
+		for(l = t->link; l != T; l = l->down)
+			picklemember(l, 0);
+		Bprint(&outbuf, "\treturn bp;\n}\n\n");
+		break;
+	asmstr:
+		if(s == S)
+			break;
+		for(l = t->link; l != T; l = l->down)
+			if(l->sym != S)
+				Bprint(&outbuf, "#define\t%s.%s\t%ld\n",
+					s->name,
+					l->sym->name,
+					l->offset);
+		break;
+	}
+}
+
+void
+picklevar(Sym *s)
+{
+	int n;
+	Io *i;
+	Type *t;
+	Sym *s1, *s2;
+
+	if(!debug['P'] || debug['s'])
+		return;
+	if(debug['P'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	t = s->type;
+	while(t && t->etype == TIND)
+		t = t->link;
+	if(t == T)
+		return;
+	if(t->etype == TENUM) {
+		Bprint(&outbuf, "%s = ", pmap(s->name));
+		if(!typefd[t->etype])
+			Bprint(&outbuf, "%lld;\n", s->vconst);
+		else
+			Bprint(&outbuf, "%f\n;", s->fconst);
+		return;
+	}
+	if(!typesu[t->etype])
+		return;
+	s1 = picklesue(t->link);
+	if(s1 == S)
+		return;
+	switch(s->class) {
+	case CAUTO:
+	case CPARAM:
+		s2 = picklefun(thisfn);
+		if(s2)
+			Bprint(&outbuf, "complex %s %s:%s;\n",
+				pmap(s1->name), pmap(s2->name), pmap(s->name));
+		break;
+	
+	case CSTATIC:
+	case CEXTERN:
+	case CGLOBL:
+	case CLOCAL:
+		Bprint(&outbuf, "complex %s %s;\n",
+			pmap(s1->name), pmap(s->name));
+		break;
+	}
+}
--- /dev/null
+++ b/utils/cc/pswt.c
@@ -1,0 +1,197 @@
+#include "gc.h"
+
+int
+swcmp(void *a1, void *a2)
+{
+	C1 *p1, *p2;
+
+	p1 = (C1*)a1;
+	p2 = (C1*)a2;
+	if(p1->val < p2->val)
+		return -1;
+	return p1->val > p2->val;
+}
+
+void
+doswit(Node *n)
+{
+	Case *c;
+	C1 *q, *iq, *iqh, *iql;
+	long def, nc, i, j, isv, nh;
+	Prog *hsb;
+	Node *vr[2];
+	int dup;
+
+	def = 0;
+	nc = 0;
+	isv = 0;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def) {
+			if(def)
+				diag(n, "more than one default in switch");
+			def = c->label;
+			continue;
+		}
+		isv |= c->isv;
+		nc++;
+	}
+	if(typev[n->type->etype])
+		isv = 1;
+	else if(isv){
+		warn(n, "32-bit switch expression with 64-bit case constant");
+		isv = 0;
+	}
+
+	iq = alloc(nc*sizeof(C1));
+	q = iq;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def)
+			continue;
+		if(c->isv && !isv)
+			continue;	/* can never match */
+		q->label = c->label;
+		if(isv)
+			q->val = c->val;
+		else
+			q->val = (long)c->val;	/* cast ensures correct value for 32-bit switch on 64-bit architecture */
+		q++;
+	}
+	qsort(iq, nc, sizeof(C1), swcmp);
+	if(debug['K'])
+	for(i=0; i<nc; i++)
+		print("case %2ld: = %.8llux\n", i, (vlong)iq[i].val);
+	dup = 0;
+	for(i=0; i<nc-1; i++)
+		if(iq[i].val == iq[i+1].val) {
+			diag(n, "duplicate cases in switch %lld", (vlong)iq[i].val);
+			dup = 1;
+		}
+	if(dup)
+		return;
+	if(def == 0) {
+		def = breakpc;
+		nbreak++;
+	}
+	if(!isv || ewidth[TIND] > ewidth[TLONG] || n->op == OREGISTER) {
+		swit1(iq, nc, def, n);
+		return;
+	}
+
+	/*
+	 * 64-bit case on 32-bit machine:
+	 * switch on high-order words, and
+	 * in each of those, switch on low-order words
+	 */
+	if(n->op != OREGPAIR)
+		fatal(n, "internal: expected register pair");
+	if(thechar == '8'){	/* TO DO: need an enquiry function */
+		vr[0] = n->left;	/* low */
+		vr[1] = n->right;	/* high */
+	}else{
+		vr[0] = n->right;
+		vr[1] = n->left;
+	}
+	vr[0]->type = types[TLONG];
+	vr[1]->type = types[TLONG];
+	gbranch(OGOTO);
+	hsb = p;
+	iqh = alloc(nc*sizeof(C1));
+	iql = alloc(nc*sizeof(C1));
+	nh = 0;
+	for(i=0; i<nc;){
+		iqh[nh].val = iq[i].val >> 32;
+		q = iql;
+		/* iq is sorted, so equal top halves are adjacent */
+		for(j = i; j < nc; j++){
+			if((iq[j].val>>32) != iqh[nh].val)
+				break;
+			q->val = (long)iq[j].val;
+			q->label = iq[j].label;
+			q++;
+		}
+		qsort(iql,  q-iql, sizeof(C1), swcmp);
+		iqh[nh].label = pc;
+		nh++;
+		swit1(iql, q-iql, def, vr[0]);
+		i = j;
+	}
+	patch(hsb, pc);
+	swit1(iqh, nh, def, vr[1]);
+}
+
+void
+casf(void)
+{
+	Case *c;
+
+	c = alloc(sizeof(*c));
+	c->link = cases;
+	cases = c;
+}
+
+long
+outlstring(TRune *s, long n)
+{
+	char buf[sizeof(TRune)];
+	uint c;
+	int i;
+	long r;
+
+	if(suppress)
+		return nstring;
+	while(nstring & (sizeof(TRune)-1))
+		outstring("", 1);
+	r = nstring;
+	while(n > 0) {
+		c = *s++;
+		if(align(0, types[TCHAR], Aarg1)) {
+			for(i = 0; i < sizeof(TRune); i++)
+				buf[i] = c>>(8*(sizeof(TRune) - i - 1));
+		} else {
+			for(i = 0; i < sizeof(TRune); i++)
+				buf[i] = c>>(8*i);
+		}
+		outstring(buf, sizeof(TRune));
+		n -= sizeof(TRune);
+	}
+	return r;
+}
+
+void
+nullwarn(Node *l, Node *r)
+{
+	warn(Z, "result of operation not used");
+	if(l != Z)
+		cgen(l, Z);
+	if(r != Z)
+		cgen(r, Z);
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}
--- /dev/null
+++ b/utils/cc/scon.c
@@ -1,0 +1,606 @@
+#include "cc.h"
+
+static Node*
+acast(Type *t, Node *n)
+{
+	if(n->type->etype != t->etype || n->op == OBIT) {
+		n = new1(OCAST, n, Z);
+		if(nocast(n->left->type, t))
+			*n = *n->left;
+		n->type = t;
+	}
+	return n;
+}
+
+
+void
+evconst(Node *n)
+{
+	Node *l, *r;
+	int et, isf;
+	vlong v;
+	double d;
+
+	if(n == Z || n->type == T)
+		return;
+
+	et = n->type->etype;
+	isf = typefd[et];
+
+	l = n->left;
+	r = n->right;
+
+	d = 0;
+	v = 0;
+
+	switch(n->op) {
+	default:
+		return;
+
+	case ONEG:
+		if(isf)
+			d = -l->fconst;
+		else
+			v = -l->vconst;
+		break;
+
+	case OCOM:
+		v = ~l->vconst;
+		break;
+
+	case OCAST:
+		if(et == TVOID)
+			return;
+		et = l->type->etype;
+		if(isf) {
+			if(typefd[et])
+				d = l->fconst;
+			else
+				d = l->vconst;
+		} else {
+			if(typefd[et])
+				v = l->fconst;
+			else
+				v = convvtox(l->vconst, n->type->etype);
+		}
+		break;
+
+	case OCONST:
+		break;
+
+	case OADD:
+		if(isf)
+			d = l->fconst + r->fconst;
+		else {
+			v = l->vconst + r->vconst;
+		}
+		break;
+
+	case OSUB:
+		if(isf)
+			d = l->fconst - r->fconst;
+		else
+			v = l->vconst - r->vconst;
+		break;
+
+	case OMUL:
+		if(isf)
+			d = l->fconst * r->fconst;
+		else {
+			v = l->vconst * r->vconst;
+		}
+		break;
+
+	case OLMUL:
+		v = (uvlong)l->vconst * (uvlong)r->vconst;
+		break;
+
+
+	case ODIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		if(isf)
+			d = l->fconst / r->fconst;
+		else
+			v = l->vconst / r->vconst;
+		break;
+
+	case OLDIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		v = (uvlong)l->vconst / (uvlong)r->vconst;
+		break;
+
+	case OMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = l->vconst % r->vconst;
+		break;
+
+	case OLMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = (uvlong)l->vconst % (uvlong)r->vconst;
+		break;
+
+	case OAND:
+		v = l->vconst & r->vconst;
+		break;
+
+	case OOR:
+		v = l->vconst | r->vconst;
+		break;
+
+	case OXOR:
+		v = l->vconst ^ r->vconst;
+		break;
+
+	case OLSHR:
+		v = (uvlong)l->vconst >> r->vconst;
+		break;
+
+	case OASHR:
+		v = l->vconst >> r->vconst;
+		break;
+
+	case OASHL:
+		v = l->vconst << r->vconst;
+		break;
+
+	case OLO:
+		v = (uvlong)l->vconst < (uvlong)r->vconst;
+		break;
+
+	case OLT:
+		if(typefd[l->type->etype])
+			v = l->fconst < r->fconst;
+		else
+			v = l->vconst < r->vconst;
+		break;
+
+	case OHI:
+		v = (uvlong)l->vconst > (uvlong)r->vconst;
+		break;
+
+	case OGT:
+		if(typefd[l->type->etype])
+			v = l->fconst > r->fconst;
+		else
+			v = l->vconst > r->vconst;
+		break;
+
+	case OLS:
+		v = (uvlong)l->vconst <= (uvlong)r->vconst;
+		break;
+
+	case OLE:
+		if(typefd[l->type->etype])
+			v = l->fconst <= r->fconst;
+		else
+			v = l->vconst <= r->vconst;
+		break;
+
+	case OHS:
+		v = (uvlong)l->vconst >= (uvlong)r->vconst;
+		break;
+
+	case OGE:
+		if(typefd[l->type->etype])
+			v = l->fconst >= r->fconst;
+		else
+			v = l->vconst >= r->vconst;
+		break;
+
+	case OEQ:
+		if(typefd[l->type->etype])
+			v = l->fconst == r->fconst;
+		else
+			v = l->vconst == r->vconst;
+		break;
+
+	case ONE:
+		if(typefd[l->type->etype])
+			v = l->fconst != r->fconst;
+		else
+			v = l->vconst != r->vconst;
+		break;
+
+	case ONOT:
+		if(typefd[l->type->etype])
+			v = !l->fconst;
+		else
+			v = !l->vconst;
+		break;
+
+	case OANDAND:
+		if(typefd[l->type->etype])
+			v = l->fconst && r->fconst;
+		else
+			v = l->vconst && r->vconst;
+		break;
+
+	case OOROR:
+		if(typefd[l->type->etype])
+			v = l->fconst || r->fconst;
+		else
+			v = l->vconst || r->vconst;
+		break;
+	}
+	if(isf) {
+		n->fconst = d;
+	} else {
+		n->vconst = convvtox(v, n->type->etype);
+	}
+	n->oldop = n->op;
+	n->op = OCONST;
+}
+
+void
+acom(Node *n)
+{
+	Type *t;
+	Node *l, *r;
+	int i;
+
+	switch(n->op)
+	{
+
+	case ONAME:
+	case OCONST:
+	case OSTRING:
+	case OINDREG:
+	case OREGISTER:
+		return;
+
+	case ONEG:
+		l = n->left;
+		if(addo(n) && addo(l))
+			break;
+		acom(l);
+		return;
+
+	case OADD:
+	case OSUB:
+	case OMUL:
+		l = n->left;
+		r = n->right;
+		if(addo(n)) {
+			if(addo(r))
+				break;
+			if(addo(l))
+				break;
+		}
+		acom(l);
+		acom(r);
+		return;
+
+	default:
+		l = n->left;
+		r = n->right;
+		if(l != Z)
+			acom(l);
+		if(r != Z)
+			acom(r);
+		return;
+	}
+
+	/* bust terms out */
+	t = n->type;
+	term[0].mult = 0;
+	term[0].node = Z;
+	nterm = 1;
+	acom1(1, n);
+	if(debug['m'])
+	for(i=0; i<nterm; i++) {
+		print("%d %3lld ", i, term[i].mult);
+		prtree1(term[i].node, 1, 0);
+	}
+	if(nterm < NTERM)
+		acom2(n, t);
+	n->type = t;
+}
+
+int
+acomcmp1(void *a1, void *a2)
+{
+	vlong c1, c2;
+	Term *t1, *t2;
+
+	t1 = (Term*)a1;
+	t2 = (Term*)a2;
+	c1 = t1->mult;
+	if(c1 < 0)
+		c1 = -c1;
+	c2 = t2->mult;
+	if(c2 < 0)
+		c2 = -c2;
+	if(c1 > c2)
+		return 1;
+	if(c1 < c2)
+		return -1;
+	c1 = 1;
+	if(t1->mult < 0)
+		c1 = 0;
+	c2 = 1;
+	if(t2->mult < 0)
+		c2 = 0;
+	if(c2 -= c1)
+		return c2;
+	if(t2 > t1)
+		return 1;
+	return -1;
+}
+
+int
+acomcmp2(void *a1, void *a2)
+{
+	vlong c1, c2;
+	Term *t1, *t2;
+
+	t1 = (Term*)a1;
+	t2 = (Term*)a2;
+	c1 = t1->mult;
+	c2 = t2->mult;
+	if(c1 > c2)
+		return 1;
+	if(c1 < c2)
+		return -1;
+	if(t2 > t1)
+		return 1;
+	return -1;
+}
+
+void
+acom2(Node *n, Type *t)
+{
+	Node *l, *r;
+	Term trm[NTERM];
+	int et, nt, i, j;
+	vlong c1, c2;
+
+	/*
+	 * copy into automatic
+	 */
+	c2 = 0;
+	nt = nterm;
+	for(i=0; i<nt; i++)
+		trm[i] = term[i];
+	/*
+	 * recur on subtrees
+	 */
+	j = 0;
+	for(i=1; i<nt; i++) {
+		c1 = trm[i].mult;
+		if(c1 == 0)
+			continue;
+		l = trm[i].node;
+		if(l != Z) {
+			j = 1;
+			acom(l);
+		}
+	}
+	c1 = trm[0].mult;
+	if(j == 0) {
+		n->oldop = n->op;
+		n->op = OCONST;
+		n->vconst = c1;
+		return;
+	}
+	et = t->etype;
+
+	/*
+	 * prepare constant term,
+	 * combine it with an addressing term
+	 */
+	if(c1 != 0) {
+		l = new1(OCONST, Z, Z);
+		l->type = t;
+		l->vconst = c1;
+		trm[0].mult = 1;
+		for(i=1; i<nt; i++) {
+			if(trm[i].mult != 1)
+				continue;
+			r = trm[i].node;
+			if(r->op != OADDR)
+				continue;
+			r->type = t;
+			l = new1(OADD, r, l);
+			l->type = t;
+			trm[i].mult = 0;
+			break;
+		}
+		trm[0].node = l;
+	}
+	/*
+	 * look for factorable terms
+	 * c1*i + c1*c2*j -> c1*(i + c2*j)
+	 */
+	qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp1);
+	for(i=nt-1; i>=0; i--) {
+		c1 = trm[i].mult;
+		if(c1 < 0)
+			c1 = -c1;
+		if(c1 <= 1)
+			continue;
+		for(j=i+1; j<nt; j++) {
+			c2 = trm[j].mult;
+			if(c2 < 0)
+				c2 = -c2;
+			if(c2 <= 1)
+				continue;
+			if(c2 % c1)
+				continue;
+			r = trm[j].node;
+			if(r->type->etype != et)
+				r = acast(t, r);
+			c2 = trm[j].mult/trm[i].mult;
+			if(c2 != 1 && c2 != -1) {
+				r = new1(OMUL, r, new(OCONST, Z, Z));
+				r->type = t;
+				r->right->type = t;
+				r->right->vconst = c2;
+			}
+			l = trm[i].node;
+			if(l->type->etype != et)
+				l = acast(t, l);
+			r = new1(OADD, l, r);
+			r->type = t;
+			if(c2 == -1)
+				r->op = OSUB;
+			trm[i].node = r;
+			trm[j].mult = 0;
+		}
+	}
+	if(debug['m']) {
+		print("\n");
+		for(i=0; i<nt; i++) {
+			print("%d %3lld ", i, trm[i].mult);
+			prtree1(trm[i].node, 1, 0);
+		}
+	}
+
+	/*
+	 * put it all back together
+	 */
+	qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp2);
+	l = Z;
+	for(i=nt-1; i>=0; i--) {
+		c1 = trm[i].mult;
+		if(c1 == 0)
+			continue;
+		r = trm[i].node;
+		if(r->type->etype != et || r->op == OBIT)
+			r = acast(t, r);
+		if(c1 != 1 && c1 != -1) {
+			r = new1(OMUL, r, new(OCONST, Z, Z));
+			r->type = t;
+			r->right->type = t;
+			if(c1 < 0) {
+				r->right->vconst = -c1;
+				c1 = -1;
+			} else {
+				r->right->vconst = c1;
+				c1 = 1;
+			}
+		}
+		if(l == Z) {
+			l = r;
+			c2 = c1;
+			continue;
+		}
+		if(c1 < 0)
+			if(c2 < 0)
+				l = new1(OADD, l, r);
+			else
+				l = new1(OSUB, l, r);
+		else
+			if(c2 < 0) {
+				l = new1(OSUB, r, l);
+				c2 = 1;
+			} else
+				l = new1(OADD, l, r);
+		l->type = t;
+	}
+	if(c2 < 0) {
+		r = new1(OCONST, 0, 0);
+		r->vconst = 0;
+		r->type = t;
+		l = new1(OSUB, r, l);
+		l->type = t;
+	}
+	*n = *l;
+}
+
+void
+acom1(vlong v, Node *n)
+{
+	Node *l, *r;
+
+	if(v == 0 || nterm >= NTERM)
+		return;
+	if(!addo(n)) {
+		if(n->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			term[0].mult += v*n->vconst;
+			return;
+		}
+		term[nterm].mult = v;
+		term[nterm].node = n;
+		nterm++;
+		return;
+	}
+	switch(n->op) {
+
+	case OCAST:
+		acom1(v, n->left);
+		break;
+
+	case ONEG:
+		acom1(-v, n->left);
+		break;
+
+	case OADD:
+		acom1(v, n->left);
+		acom1(v, n->right);
+		break;
+
+	case OSUB:
+		acom1(v, n->left);
+		acom1(-v, n->right);
+		break;
+
+	case OMUL:
+		l = n->left;
+		r = n->right;
+		if(l->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			acom1(v*l->vconst, r);
+			break;
+		}
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			acom1(v*r->vconst, l);
+			break;
+		}
+		break;
+
+	default:
+		diag(n, "not addo");
+	}
+}
+
+int
+addo(Node *n)
+{
+
+	if(n != Z)
+	if(!typefd[n->type->etype])
+	if(!typev[n->type->etype] || ewidth[TVLONG] == ewidth[TIND])
+	switch(n->op) {
+
+	case OCAST:
+		if(nilcast(n->left->type, n->type))
+			return 1;
+		break;
+
+	case ONEG:
+	case OADD:
+	case OSUB:
+		return 1;
+
+	case OMUL:
+		if(n->left->op == OCONST)
+			return 1;
+		if(n->right->op == OCONST)
+			return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/cc/sub.c
@@ -1,0 +1,2054 @@
+#include	"cc.h"
+
+Node*
+new(int t, Node *l, Node *r)
+{
+	Node *n;
+
+	n = alloc(sizeof(*n));
+	n->op = t;
+	n->left = l;
+	n->right = r;
+	if(l && t != OGOTO)
+		n->lineno = l->lineno;
+	else if(r)
+		n->lineno = r->lineno;
+	else
+		n->lineno = lineno;
+	newflag = 1;
+	return n;
+}
+
+Node*
+new1(int o, Node *l, Node *r)
+{
+	Node *n;
+
+	n = new(o, l, r);
+	n->lineno = nearln;
+	return n;
+}
+
+void
+prtree(Node *n, char *s)
+{
+
+	print(" == %s ==\n", s);
+	prtree1(n, 0, 0);
+	print("\n");
+}
+
+void
+prtree1(Node *n, int d, int f)
+{
+	int i;
+
+	if(f)
+	for(i=0; i<d; i++)
+		print("   ");
+	if(n == Z) {
+		print("Z\n");
+		return;
+	}
+	if(n->op == OLIST) {
+		prtree1(n->left, d, 0);
+		prtree1(n->right, d, 1);
+		return;
+	}
+	d++;
+	print("%O", n->op);
+	i = 3;
+	switch(n->op)
+	{
+	case ONAME:
+		print(" \"%F\"", n);
+		print(" %ld", n->xoffset);
+		i = 0;
+		break;
+
+	case OINDREG:
+		print(" %ld(R%d)", n->xoffset, n->reg);
+		i = 0;
+		break;
+
+	case OREGISTER:
+		if(n->xoffset)
+			print(" %ld+R%d", n->xoffset, n->reg);
+		else
+			print(" R%d", n->reg);
+		i = 0;
+		break;
+
+	case OSTRING:
+		print(" \"%s\"", n->cstring);
+		i = 0;
+		break;
+
+	case OLSTRING:
+		if(sizeof(TRune) == sizeof(Rune))
+			print(" \"%S\"", (Rune*)n->rstring);
+		else
+			print(" \"...\"");
+		i = 0;
+		break;
+
+	case ODOT:
+	case OELEM:
+		print(" \"%F\"", n);
+		break;
+
+	case OCONST:
+		if(typefd[n->type->etype])
+			print(" \"%.8e\"", n->fconst);
+		else
+			print(" \"%lld\"", n->vconst);
+		i = 0;
+		break;
+	}
+	if(n->addable != 0)
+		print(" <%d>", n->addable);
+	if(n->type != T)
+		print(" %T", n->type);
+	if(n->complex != 0)
+		print(" (%d)", n->complex);
+	print(" %L\n", n->lineno);
+	if(i & 2)
+		prtree1(n->left, d, 1);
+	if(i & 1)
+		prtree1(n->right, d, 1);
+}
+
+Type*
+typ(int et, Type *d)
+{
+	Type *t;
+
+	t = alloc(sizeof(*t));
+	t->etype = et;
+	t->link = d;
+	t->down = T;
+	t->sym = S;
+	t->width = ewidth[et];
+	t->offset = 0;
+	t->shift = 0;
+	t->nbits = 0;
+	t->garb = 0;
+	return t;
+}
+
+Type*
+copytyp(Type *t)
+{
+	Type *nt;
+
+	nt = typ(TXXX, T);
+	*nt = *t;
+	return nt;
+}
+
+Type*
+garbt(Type *t, long b)
+{
+	Type *t1;
+
+	if(b & BGARB) {
+		t1 = copytyp(t);
+		t1->garb = simpleg(b);
+		return t1;
+	}
+	return t;
+}
+
+int
+simpleg(long b)
+{
+
+	b &= BGARB;
+	switch(b) {
+	case BCONSTNT:
+		return GCONSTNT;
+	case BVOLATILE:
+		return GVOLATILE;
+	case BVOLATILE|BCONSTNT:
+		return GCONSTNT|GVOLATILE;
+	}
+	return GXXX;
+}
+
+int
+simplec(long b)
+{
+
+	b &= BCLASS;
+	switch(b) {
+	case 0:
+	case BREGISTER:
+		return CXXX;
+	case BAUTO:
+	case BAUTO|BREGISTER:
+		return CAUTO;
+	case BEXTERN:
+		return CEXTERN;
+	case BEXTERN|BREGISTER:
+		return CEXREG;
+	case BSTATIC:
+		return CSTATIC;
+	case BTYPEDEF:
+		return CTYPEDEF;
+	case BTYPESTR:
+		return CTYPESTR;
+	}
+	diag(Z, "illegal combination of classes %Q", b);
+	return CXXX;
+}
+
+Type*
+simplet(long b)
+{
+
+	b &= ~BCLASS & ~BGARB;
+	switch(b) {
+	case BCHAR:
+	case BCHAR|BSIGNED:
+		return types[TCHAR];
+
+	case BCHAR|BUNSIGNED:
+		return types[TUCHAR];
+
+	case BSHORT:
+	case BSHORT|BINT:
+	case BSHORT|BSIGNED:
+	case BSHORT|BINT|BSIGNED:
+		return types[TSHORT];
+
+	case BUNSIGNED|BSHORT:
+	case BUNSIGNED|BSHORT|BINT:
+		return types[TUSHORT];
+
+	case 0:
+	case BINT:
+	case BINT|BSIGNED:
+	case BSIGNED:
+		return types[TINT];
+
+	case BUNSIGNED:
+	case BUNSIGNED|BINT:
+		return types[TUINT];
+
+	case BLONG:
+	case BLONG|BINT:
+	case BLONG|BSIGNED:
+	case BLONG|BINT|BSIGNED:
+		return types[TLONG];
+
+	case BUNSIGNED|BLONG:
+	case BUNSIGNED|BLONG|BINT:
+		return types[TULONG];
+
+	case BVLONG|BLONG:
+	case BVLONG|BLONG|BINT:
+	case BVLONG|BLONG|BSIGNED:
+	case BVLONG|BLONG|BINT|BSIGNED:
+		return types[TVLONG];
+
+	case BVLONG|BLONG|BUNSIGNED:
+	case BVLONG|BLONG|BINT|BUNSIGNED:
+		return types[TUVLONG];
+
+	case BFLOAT:
+		return types[TFLOAT];
+
+	case BDOUBLE:
+	case BDOUBLE|BLONG:
+	case BFLOAT|BLONG:
+		return types[TDOUBLE];
+
+	case BVOID:
+		return types[TVOID];
+	}
+
+	diag(Z, "illegal combination of types %Q", b);
+	return types[TINT];
+}
+
+int
+stcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+	int i;
+	ulong b;
+
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1L << i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ttab[i]) {
+		if(ttab == tasign)
+			if(b == BSTRUCT || b == BUNION)
+				if(!sametype(t1, t2))
+					return 1;
+		if(n->op != OCAST)
+		 	if(b == BIND && i == TIND)
+				if(!sametype(t1, t2))
+					return 1;
+		return 0;
+	}
+	return 1;
+}
+
+int
+tcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+
+	if(stcompat(n, t1, t2, ttab)) {
+		if(t1 == T)
+			diag(n, "incompatible type: \"%T\" for op \"%O\"",
+				t2, n->op);
+		else
+			diag(n, "incompatible types: \"%T\" and \"%T\" for op \"%O\"",
+				t1, t2, n->op);
+		return 1;
+	}
+	return 0;
+}
+
+void
+makedot(Node *n, Type *t, long o)
+{
+	Node *n1, *n2;
+
+	if(t->nbits) {
+		n1 = new(OXXX, Z, Z);
+		*n1 = *n;
+		n->op = OBIT;
+		n->left = n1;
+		n->right = Z;
+		n->type = t;
+		n->addable = n1->left->addable;
+		n = n1;
+	}
+	n->addable = n->left->addable;
+	if(n->addable == 0) {
+		n1 = new1(OCONST, Z, Z);
+		n1->vconst = o;
+		n1->type = types[TLONG];
+		n->right = n1;
+		n->type = t;
+		return;
+	}
+	n->left->type = t;
+	if(o == 0) {
+		*n = *n->left;
+		return;
+	}
+	n->type = t;
+	n1 = new1(OCONST, Z, Z);
+	n1->vconst = o;
+	t = typ(TIND, t);
+	t->width = types[TIND]->width;
+	n1->type = t;
+
+	n2 = new1(OADDR, n->left, Z);
+	n2->type = t;
+
+	n1 = new1(OADD, n1, n2);
+	n1->type = t;
+
+	n->op = OIND;
+	n->left = n1;
+	n->right = Z;
+}
+
+Type*
+dotsearch(Sym *s, Type *t, Node *n, long *off)
+{
+	Type *t1, *xt, *rt;
+
+	xt = T;
+
+	/*
+	 * look it up by name
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == s) {
+			if(xt != T)
+				goto ambig;
+			xt = t1;
+		}
+
+	/*
+	 * look it up by type
+	 */
+	if(s->class == CTYPEDEF || s->class == CTYPESTR)
+		for(t1 = t; t1 != T; t1 = t1->down)
+			if(t1->sym == S && typesu[t1->etype])
+				if(sametype(s->type, t1)) {
+					if(xt != T)
+						goto ambig;
+					xt = t1;
+				}
+	if(xt != T) {
+		*off = xt->offset;
+		return xt;
+	}
+
+	/*
+	 * look it up in unnamed substructures
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == S && typesu[t1->etype]){
+			rt = dotsearch(s, t1->link, n, off);
+			if(rt != T) {
+				if(xt != T)
+					goto ambig;
+				xt = rt;
+				*off += t1->offset;
+			}
+		}
+	return xt;
+
+ambig:
+	diag(n, "ambiguous structure element: %s", s->name);
+	return xt;
+}
+
+long
+dotoffset(Type *st, Type *lt, Node *n)
+{
+	Type *t;
+	Sym *g;
+	long o, o1;
+
+	o = -1;
+	/*
+	 * first try matching at the top level
+	 * for matching tag names
+	 */
+	g = st->tag;
+	if(g != S)
+		for(t=lt->link; t!=T; t=t->down)
+			if(t->sym == S)
+				if(g == t->tag) {
+					if(o >= 0)
+						goto ambig;
+					o = t->offset;
+				}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * second try matching at the top level
+	 * for similar types
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+			if(sametype(st, t)) {
+				if(o >= 0)
+					goto ambig;
+				o = t->offset;
+			}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * last try matching sub-levels
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+		if(typesu[t->etype]) {
+			o1 = dotoffset(st, t, n);
+			if(o1 >= 0) {
+				if(o >= 0)
+					goto ambig;
+				o = o1 + t->offset;
+			}
+		}
+	return o;
+
+ambig:
+	diag(n, "ambiguous unnamed structure element");
+	return o;
+}
+
+/*
+ * look into tree for floating point constant expressions
+ */
+int
+allfloat(Node *n, int flag)
+{
+
+	if(n != Z) {
+		if(n->type->etype != TDOUBLE)
+			return 1;
+		switch(n->op) {
+		case OCONST:
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		case OADD:	/* no need to get more exotic than this */
+		case OSUB:
+		case OMUL:
+		case ODIV:
+			if(!allfloat(n->right, flag))
+				break;
+		case OCAST:
+			if(!allfloat(n->left, flag))
+				break;
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+constas(Node *n, Type *il, Type *ir)
+{
+	Type *l, *r;
+
+	l = il;
+	r = ir;
+
+	if(l == T)
+		return;
+	if(l->garb & GCONSTNT) {
+		warn(n, "assignment to a constant type (%T)", il);
+		return;
+	}
+	if(r == T)
+		return;
+	for(;;) {
+		if(l->etype != TIND || r->etype != TIND)
+			break;
+		l = l->link;
+		r = r->link;
+		if(l == T || r == T)
+			break;
+		if(r->garb & GCONSTNT)
+			if(!(l->garb & GCONSTNT)) {
+				warn(n, "assignment of a constant pointer type (%T)", ir);
+				break;
+			}
+	}
+}
+
+void
+typeext1(Type *st, Node *l)
+{
+	if(st->etype == TFLOAT && allfloat(l, 0))
+		allfloat(l, 1);
+}
+
+void
+typeext(Type *st, Node *l)
+{
+	Type *lt;
+	Node *n1, *n2;
+	long o;
+
+	lt = l->type;
+	if(lt == T)
+		return;
+	if(st->etype == TIND && vconst(l) == 0) {
+		l->type = st;
+		l->vconst = 0;
+		return;
+	}
+	typeext1(st, l);
+
+	/*
+	 * extension of C
+	 * if assign of struct containing unnamed sub-struct
+	 * to type of sub-struct, insert the DOT.
+	 * if assign of *struct containing unnamed substruct
+	 * to type of *sub-struct, insert the add-offset
+	 */
+	if(typesu[st->etype] && typesu[lt->etype]) {
+		o = dotoffset(st, lt, l);
+		if(o >= 0) {
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *l;
+			l->op = ODOT;
+			l->left = n1;
+			l->right = Z;
+			makedot(l, st, o);
+		}
+		return;
+	}
+	if(st->etype == TIND && typesu[st->link->etype])
+	if(lt->etype == TIND && typesu[lt->link->etype]) {
+		o = dotoffset(st->link, lt->link, l);
+		if(o >= 0) {
+			l->type = st;
+			if(o == 0)
+				return;
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *l;
+			n2 = new1(OCONST, Z, Z);
+			n2->vconst = o;
+			n2->type = st;
+			l->op = OADD;
+			l->left = n1;
+			l->right = n2;
+		}
+		return;
+	}
+}
+
+/*
+ * a cast that generates no code
+ * (same size move)
+ */
+int
+nocast(Type *t1, Type *t2)
+{
+	int i, b;
+
+	if(t1->nbits)
+		return 0;
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1<<i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ncast[i])
+		return 1;
+	return 0;
+}
+
+/*
+ * a cast that has a noop semantic
+ * (small to large, convert)
+ */
+int
+nilcast(Type *t1, Type *t2)
+{
+	int et1, et2;
+
+	if(t1 == T)
+		return 0;
+	if(t1->nbits)
+		return 0;
+	if(t2 == T)
+		return 0;
+	et1 = t1->etype;
+	et2 = t2->etype;
+	if(et1 == et2)
+		return 1;
+	if(typefd[et1] && typefd[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	if(typechlp[et1] && typechlp[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	return 0;
+}
+
+/*
+ * "the usual arithmetic conversions are performed"
+ */
+void
+arith(Node *n, int f)
+{
+	Type *t1, *t2;
+	int i, j, k;
+	Node *n1;
+	long w;
+
+	t1 = n->left->type;
+	if(n->right == Z)
+		t2 = t1;
+	else
+		t2 = n->right->type;
+	i = TXXX;
+	if(t1 != T)
+		i = t1->etype;
+	j = TXXX;
+	if(t2 != T)
+		j = t2->etype;
+	k = tab[i][j];
+	if(k == TIND) {
+		if(i == TIND)
+			n->type = t1;
+		else
+		if(j == TIND)
+			n->type = t2;
+	} else {
+		/* convert up to at least int */
+		if(f == 1)
+		while(k < TINT)
+			k += 2;
+		n->type = types[k];
+	}
+	if(n->op == OSUB)
+	if(i == TIND && j == TIND) {
+		w = n->right->type->link->width;
+		if(w < 1 || n->left->type->link == T || n->left->type->link->width < 1)
+			goto bad;
+		n->type = types[ewidth[TIND] <= ewidth[TLONG]? TLONG: TVLONG];
+		if(1 && ewidth[TIND] > ewidth[TLONG]){
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *n;
+			n->op = OCAST;
+			n->left = n1;
+			n->right = Z;
+			n->type = types[TLONG];
+		}
+		if(w > 1) {
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *n;
+			n->op = ODIV;
+			n->left = n1;
+			n1 = new1(OCONST, Z, Z);
+			n1->vconst = w;
+			n1->type = n->type;
+			n->right = n1;
+			w = vlog(n1);
+			if(w >= 0) {
+				n->op = OASHR;
+				n1->vconst = w;
+			}
+		}
+		return;
+	}
+	if(!sametype(n->type, n->left->type)) {
+		n->left = new1(OCAST, n->left, Z);
+		n->left->type = n->type;
+		if(n->type->etype == TIND) {
+			w = n->type->link->width;
+			if(w < 1) {
+				snap(n->type->link);
+				w = n->type->link->width;
+				if(w < 1)
+					goto bad;
+			}
+			if(w > 1) {
+				n1 = new1(OCONST, Z, Z);
+				n1->vconst = w;
+				n1->type = n->type;
+				n->left = new1(OMUL, n->left, n1);
+				n->left->type = n->type;
+			}
+		}
+	}
+	if(n->right != Z)
+	if(!sametype(n->type, n->right->type)) {
+		n->right = new1(OCAST, n->right, Z);
+		n->right->type = n->type;
+		if(n->type->etype == TIND) {
+			w = n->type->link->width;
+			if(w < 1) {
+				snap(n->type->link);
+				w = n->type->link->width;
+				if(w < 1)
+					goto bad;
+			}
+			if(w != 1) {
+				n1 = new1(OCONST, Z, Z);
+				n1->vconst = w;
+				n1->type = n->type;
+				n->right = new1(OMUL, n->right, n1);
+				n->right->type = n->type;
+			}
+		}
+	}
+	return;
+bad:
+	diag(n, "pointer addition not fully declared: %T", n->type->link);
+}
+
+/*
+ * try to rewrite shift & mask
+ */
+void
+simplifyshift(Node *n)
+{
+	ulong c3;
+	int o, s1, s2, c1, c2;
+
+	if(!typechlp[n->type->etype])
+		return;
+	switch(n->op) {
+	default:
+		return;
+	case OASHL:
+		s1 = 0;
+		break;
+	case OLSHR:
+		s1 = 1;
+		break;
+	case OASHR:
+		s1 = 2;
+		break;
+	}
+	if(n->right->op != OCONST)
+		return;
+	if(n->left->op != OAND)
+		return;
+	if(n->left->right->op != OCONST)
+		return;
+	switch(n->left->left->op) {
+	default:
+		return;
+	case OASHL:
+		s2 = 0;
+		break;
+	case OLSHR:
+		s2 = 1;
+		break;
+	case OASHR:
+		s2 = 2;
+		break;
+	}
+	if(n->left->left->right->op != OCONST)
+		return;
+
+	c1 = n->right->vconst;
+	c2 = n->left->left->right->vconst;
+	c3 = n->left->right->vconst;
+
+/*
+	if(debug['h'])
+		print("%.3o %ld %ld %d #%.lux\n",
+			(s1<<3)|s2, c1, c2, topbit(c3), c3);
+*/
+
+	o = n->op;
+	switch((s1<<3)|s2) {
+	case 000:	/* (((e <<u c2) & c3) <<u c1) */
+		c3 >>= c2;
+		c1 += c2;
+		if(c1 >= 32)
+			break;
+		goto rewrite1;
+
+	case 002:	/* (((e >>s c2) & c3) <<u c1) */
+		if(topbit(c3) >= (32-c2))
+			break;
+	case 001:	/* (((e >>u c2) & c3) <<u c1) */
+		if(c1 > c2) {
+			c3 <<= c2;
+			c1 -= c2;
+			o = OASHL;
+			goto rewrite1;
+		}
+		c3 <<= c1;
+		if(c1 == c2)
+			goto rewrite0;
+		c1 = c2-c1;
+		o = OLSHR;
+		goto rewrite2;
+
+	case 022:	/* (((e >>s c2) & c3) >>s c1) */
+		if(c2 <= 0)
+			break;
+	case 012:	/* (((e >>s c2) & c3) >>u c1) */
+		if(topbit(c3) >= (32-c2))
+			break;
+		goto s11;
+	case 021:	/* (((e >>u c2) & c3) >>s c1) */
+		if(topbit(c3) >= 31 && c2 <= 0)
+			break;
+		goto s11;
+	case 011:	/* (((e >>u c2) & c3) >>u c1) */
+	s11:
+		c3 <<= c2;
+		c1 += c2;
+		if(c1 >= 32)
+			break;
+		o = OLSHR;
+		goto rewrite1;
+
+	case 020:	/* (((e <<u c2) & c3) >>s c1) */
+		if(topbit(c3) >= 31)
+			break;
+	case 010:	/* (((e <<u c2) & c3) >>u c1) */
+		c3 >>= c1;
+		if(c1 == c2)
+			goto rewrite0;
+		if(c1 > c2) {
+			c1 -= c2;
+			goto rewrite2;
+		}
+		c1 = c2 - c1;
+		o = OASHL;
+		goto rewrite2;
+	}
+	return;
+
+rewrite0:	/* get rid of both shifts */
+if(debug['<'])prtree(n, "rewrite0");
+	*n = *n->left;
+	n->left = n->left->left;
+	n->right->vconst = c3;
+	return;
+rewrite1:	/* get rid of lower shift */
+if(debug['<'])prtree(n, "rewrite1");
+	n->left->left = n->left->left->left;
+	n->left->right->vconst = c3;
+	n->right->vconst = c1;
+	n->op = o;
+	return;
+rewrite2:	/* get rid of upper shift */
+if(debug['<'])prtree(n, "rewrite2");
+	*n = *n->left;
+	n->right->vconst = c3;
+	n->left->right->vconst = c1;
+	n->left->op = o;
+}
+
+int
+side(Node *n)
+{
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	case OCAST:
+	case ONOT:
+	case OADDR:
+	case OIND:
+	case OCOM:
+	case ONEG:
+	case OPOS:
+	case OTST:
+		n = n->left;
+		goto loop;
+
+	case OCOND:
+		if(side(n->left))
+			break;
+		n = n->right;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OADD:
+	case OSUB:
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OMOD:
+	case OLMOD:
+	case OANDAND:
+	case OOROR:
+	case OCOMMA:
+	case ODOT:
+		if(side(n->left))
+			break;
+		n = n->right;
+		goto loop;
+
+	case OSIGN:
+	case OSIZE:
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case ONAME:
+		return 0;
+	}
+	return 1;
+}
+
+int
+vconst(Node *n)
+{
+	int i;
+
+	if(n == Z)
+		goto no;
+	if(n->op != OCONST)
+		goto no;
+	if(n->type == T)
+		goto no;
+	switch(n->type->etype)
+	{
+	case TFLOAT:
+	case TDOUBLE:
+		i = 100;
+		if(n->fconst > i || n->fconst < -i)
+			goto no;
+		i = n->fconst;
+		if(i != n->fconst)
+			goto no;
+		return i;
+
+	case TVLONG:
+	case TUVLONG:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+	}
+no:
+	return -159;	/* first uninteresting constant */
+}
+
+/*
+ * return log(n) if n is a power of 2 constant
+ */
+int
+log2(uvlong v)
+{
+	int s, i;
+	uvlong m;
+
+	s = 0;
+	m = MASK(8*sizeof(uvlong));
+	for(i=32; i; i>>=1) {
+		m >>= i;
+		if(!(v & m)) {
+			v >>= i;
+			s += i;
+		}
+	}
+	if(v == 1)
+		return s;
+	return -1;
+}
+
+int
+vlog(Node *n)
+{
+	if(n->op != OCONST)
+		goto bad;
+	if(typefd[n->type->etype])
+		goto bad;
+
+	return log2(n->vconst);
+
+bad:
+	return -1;
+}
+
+int
+topbit(ulong v)
+{
+	int i;
+
+	for(i = -1; v; i++)
+		v >>= 1;
+	return i;
+}
+
+/*
+ * try to cast a constant down
+ * rather than cast a variable up
+ * example:
+ *	if(c == 'a')
+ */
+void
+relcon(Node *l, Node *r)
+{
+	vlong v;
+
+	if(l->op != OCONST)
+		return;
+	if(r->op != OCAST)
+		return;
+	if(!nilcast(r->left->type, r->type))
+		return;
+	switch(r->type->etype) {
+	default:
+		return;
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+		v = convvtox(l->vconst, r->type->etype);
+		if(v != l->vconst)
+			return;
+		break;
+	}
+	l->type = r->left->type;
+	*r = *r->left;
+}
+
+int
+relindex(int o)
+{
+
+	switch(o) {
+	default:
+		diag(Z, "bad in relindex: %O", o);
+	case OEQ: return 0;
+	case ONE: return 1;
+	case OLE: return 2;
+	case OLS: return 3;
+	case OLT: return 4;
+	case OLO: return 5;
+	case OGE: return 6;
+	case OHS: return 7;
+	case OGT: return 8;
+	case OHI: return 9;
+	}
+}
+
+Node*
+invert(Node *n)
+{
+	Node *i;
+
+	if(n == Z || n->op != OLIST)
+		return n;
+	i = n;
+	for(n = n->left; n != Z; n = n->left) {
+		if(n->op != OLIST)
+			break;
+		i->left = n->right;
+		n->right = i;
+		i = n;
+	}
+	i->left = n;
+	return i;
+}
+
+int
+bitno(long b)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(b & (1L<<i))
+			return i;
+	diag(Z, "bad in bitno");
+	return 0;
+}
+
+long
+typebitor(long a, long b)
+{
+	long c;
+
+	c = a | b;
+	if(a & b)
+		if((a & b) == BLONG)
+			c |= BVLONG;		/* long long => vlong */
+		else
+			warn(Z, "once is enough: %Q", a & b);
+	return c;
+}
+
+void
+diag(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(debug['X']){
+		Bflush(&diagbuf);
+		abort();
+	}
+	if(n != Z)
+	if(debug['v'])
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	if(nerrors > 10) {
+		Bprint(&diagbuf, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+warn(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	if(debug['w'] || debug['W']) {
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		if(debug['W']) {
+			diag(n, "%s", buf);
+			return;
+		}
+		Bprint(&diagbuf, "warning: %L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+		if(n != Z)
+		if(debug['v'])
+			prtree(n, "warning");
+	}
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", lineno, buf);
+	nerrors++;
+	if(nerrors > 10) {
+		Bprint(&diagbuf, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+fatal(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(debug['X']){
+		Bflush(&diagbuf);
+		abort();
+	}
+	if(n != Z)
+	if(debug['v'])
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	errorexit();
+}
+
+ulong	thash1	= 0x2edab8c9;
+ulong	thash2	= 0x1dc74fb8;
+ulong	thash3	= 0x1f241331;
+ulong	thash[NALLTYPES];
+Init	thashinit[] =
+{
+	TXXX,		0x17527bbd,	0,
+	TCHAR,		0x5cedd32b,	0,
+	TUCHAR,		0x552c4454,	0,
+	TSHORT,		0x63040b4b,	0,
+	TUSHORT,	0x32a45878,	0,
+	TINT,		0x4151d5bd,	0,
+	TUINT,		0x5ae707d6,	0,
+	TLONG,		0x5ef20f47,	0,
+	TULONG,		0x36d8eb8f,	0,
+	TVLONG,		0x6e5e9590,	0,
+	TUVLONG,	0x75910105,	0,
+	TFLOAT,		0x25fd7af1,	0,
+	TDOUBLE,	0x7c40a1b2,	0,
+	TIND,		0x1b832357,	0,
+	TFUNC,		0x6babc9cb,	0,
+	TARRAY,		0x7c50986d,	0,
+	TVOID,		0x44112eff,	0,
+	TSTRUCT,	0x7c2da3bf,	0,
+	TUNION,		0x3eb25e98,	0,
+	TENUM,		0x44b54f61,	0,
+	TFILE,		0x19242ac3,	0,
+	TOLD,		0x22b15988,	0,
+	TDOT,		0x0204f6b3,	0,
+	-1,		0,		0,
+};
+
+char*	bnames[NALIGN];
+Init	bnamesinit[] =
+{
+	Axxx,	0,	"Axxx",
+	Ael1,	0,	"el1",
+	Ael2,	0,	"el2",
+	Asu2,	0,	"su2",
+	Aarg0,	0,	"arg0",
+	Aarg1,	0,	"arg1",
+	Aarg2,	0,	"arg2",
+	Aaut3,	0,	"aut3",
+	-1,	0,	0,
+};
+
+char*	tnames[NALLTYPES];
+Init	tnamesinit[] =
+{
+	TXXX,		0,	"TXXX",
+	TCHAR,		0,	"CHAR",
+	TUCHAR,		0,	"UCHAR",
+	TSHORT,		0,	"SHORT",
+	TUSHORT,	0,	"USHORT",
+	TINT,		0,	"INT",
+	TUINT,		0,	"UINT",
+	TLONG,		0,	"LONG",
+	TULONG,		0,	"ULONG",
+	TVLONG,		0,	"VLONG",
+	TUVLONG,	0,	"UVLONG",
+	TFLOAT,		0,	"FLOAT",
+	TDOUBLE,	0,	"DOUBLE",
+	TIND,		0,	"IND",
+	TFUNC,		0,	"FUNC",
+	TARRAY,		0,	"ARRAY",
+	TVOID,		0,	"VOID",
+	TSTRUCT,	0,	"STRUCT",
+	TUNION,		0,	"UNION",
+	TENUM,		0,	"ENUM",
+	TFILE,		0,	"FILE",
+	TOLD,		0,	"OLD",
+	TDOT,		0,	"DOT",
+	-1,		0,	0,
+};
+
+char*	gnames[NGTYPES];
+Init	gnamesinit[] =
+{
+	GXXX,			0,	"GXXX",
+	GCONSTNT,		0,	"CONST",
+	GVOLATILE,		0,	"VOLATILE",
+	GVOLATILE|GCONSTNT,	0,	"CONST-VOLATILE",
+	-1,			0,	0,
+};
+
+char*	qnames[NALLTYPES];
+Init	qnamesinit[] =
+{
+	TXXX,		0,	"TXXX",
+	TCHAR,		0,	"CHAR",
+	TUCHAR,		0,	"UCHAR",
+	TSHORT,		0,	"SHORT",
+	TUSHORT,	0,	"USHORT",
+	TINT,		0,	"INT",
+	TUINT,		0,	"UINT",
+	TLONG,		0,	"LONG",
+	TULONG,		0,	"ULONG",
+	TVLONG,		0,	"VLONG",
+	TUVLONG,	0,	"UVLONG",
+	TFLOAT,		0,	"FLOAT",
+	TDOUBLE,	0,	"DOUBLE",
+	TIND,		0,	"IND",
+	TFUNC,		0,	"FUNC",
+	TARRAY,		0,	"ARRAY",
+	TVOID,		0,	"VOID",
+	TSTRUCT,	0,	"STRUCT",
+	TUNION,		0,	"UNION",
+	TENUM,		0,	"ENUM",
+
+	TAUTO,		0,	"AUTO",
+	TEXTERN,	0,	"EXTERN",
+	TSTATIC,	0,	"STATIC",
+	TTYPEDEF,	0,	"TYPEDEF",
+	TTYPESTR,	0,	"TYPESTR",
+	TREGISTER,	0,	"REGISTER",
+	TCONSTNT,	0,	"CONSTNT",
+	TVOLATILE,	0,	"VOLATILE",
+	TUNSIGNED,	0,	"UNSIGNED",
+	TSIGNED,	0,	"SIGNED",
+	TDOT,		0,	"DOT",
+	TFILE,		0,	"FILE",
+	TOLD,		0,	"OLD",
+	-1,		0,	0,
+};
+char*	cnames[NCTYPES];
+Init	cnamesinit[] =
+{
+	CXXX,		0,	"CXXX",
+	CAUTO,		0,	"AUTO",
+	CEXTERN,	0,	"EXTERN",
+	CGLOBL,		0,	"GLOBL",
+	CSTATIC,	0,	"STATIC",
+	CLOCAL,		0,	"LOCAL",
+	CTYPEDEF,	0,	"TYPEDEF",
+	CTYPESTR,	0,	"TYPESTR",
+	CPARAM,		0,	"PARAM",
+	CSELEM,		0,	"SELEM",
+	CLABEL,		0,	"LABEL",
+	CEXREG,		0,	"EXREG",
+	-1,		0,	0,
+};
+
+char*	onames[OEND+1];
+Init	onamesinit[] =
+{
+	OXXX,		0,	"OXXX",
+	OADD,		0,	"ADD",
+	OADDR,		0,	"ADDR",
+	OAND,		0,	"AND",
+	OANDAND,	0,	"ANDAND",
+	OARRAY,		0,	"ARRAY",
+	OAS,		0,	"AS",
+	OASI,		0,	"ASI",
+	OASADD,		0,	"ASADD",
+	OASAND,		0,	"ASAND",
+	OASASHL,	0,	"ASASHL",
+	OASASHR,	0,	"ASASHR",
+	OASDIV,		0,	"ASDIV",
+	OASHL,		0,	"ASHL",
+	OASHR,		0,	"ASHR",
+	OASLDIV,	0,	"ASLDIV",
+	OASLMOD,	0,	"ASLMOD",
+	OASLMUL,	0,	"ASLMUL",
+	OASLSHR,	0,	"ASLSHR",
+	OASMOD,		0,	"ASMOD",
+	OASMUL,		0,	"ASMUL",
+	OASOR,		0,	"ASOR",
+	OASSUB,		0,	"ASSUB",
+	OASXOR,		0,	"ASXOR",
+	OBIT,		0,	"BIT",
+	OBREAK,		0,	"BREAK",
+	OCASE,		0,	"CASE",
+	OCAST,		0,	"CAST",
+	OCOMMA,		0,	"COMMA",
+	OCOND,		0,	"COND",
+	OCONST,		0,	"CONST",
+	OCONTINUE,	0,	"CONTINUE",
+	ODIV,		0,	"DIV",
+	ODOT,		0,	"DOT",
+	ODOTDOT,	0,	"DOTDOT",
+	ODWHILE,	0,	"DWHILE",
+	OENUM,		0,	"ENUM",
+	OEQ,		0,	"EQ",
+	OFOR,		0,	"FOR",
+	OFUNC,		0,	"FUNC",
+	OGE,		0,	"GE",
+	OGOTO,		0,	"GOTO",
+	OGT,		0,	"GT",
+	OHI,		0,	"HI",
+	OHS,		0,	"HS",
+	OIF,		0,	"IF",
+	OIND,		0,	"IND",
+	OINDREG,	0,	"INDREG",
+	OINIT,		0,	"INIT",
+	OLABEL,		0,	"LABEL",
+	OLDIV,		0,	"LDIV",
+	OLE,		0,	"LE",
+	OLIST,		0,	"LIST",
+	OLMOD,		0,	"LMOD",
+	OLMUL,		0,	"LMUL",
+	OLO,		0,	"LO",
+	OLS,		0,	"LS",
+	OLSHR,		0,	"LSHR",
+	OLT,		0,	"LT",
+	OMOD,		0,	"MOD",
+	OMUL,		0,	"MUL",
+	ONAME,		0,	"NAME",
+	ONE,		0,	"NE",
+	ONOT,		0,	"NOT",
+	OOR,		0,	"OR",
+	OOROR,		0,	"OROR",
+	OPOSTDEC,	0,	"POSTDEC",
+	OPOSTINC,	0,	"POSTINC",
+	OPREDEC,	0,	"PREDEC",
+	OPREINC,	0,	"PREINC",
+	OPROTO,		0,	"PROTO",
+	OREGISTER,	0,	"REGISTER",
+	ORETURN,	0,	"RETURN",
+	OSET,		0,	"SET",
+	OSIGN,		0,	"SIGN",
+	OSIZE,		0,	"SIZE",
+	OSTRING,	0,	"STRING",
+	OLSTRING,	0,	"LSTRING",
+	OSTRUCT,	0,	"STRUCT",
+	OSUB,		0,	"SUB",
+	OSWITCH,	0,	"SWITCH",
+	OUNION,		0,	"UNION",
+	OUSED,		0,	"USED",
+	OWHILE,		0,	"WHILE",
+	OXOR,		0,	"XOR",
+	OPOS,		0,	"POS",
+	ONEG,		0,	"NEG",
+	OCOM,		0,	"COM",
+	OELEM,		0,	"ELEM",
+	OTST,		0,	"TST",
+	OINDEX,		0,	"INDEX",
+	OFAS,		0,	"FAS",
+	OREGPAIR,	0,	"REGPAIR",
+	OEXREG,		0,	"EXREG",
+	OEND,		0,	"END",
+	-1,		0,	0,
+};
+
+/*	OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
+char	comrel[12] =
+{
+	ONE, OEQ, OGT, OHI, OGE, OHS, OLT, OLO, OLE, OLS,
+};
+char	invrel[12] =
+{
+	OEQ, ONE, OGE, OHS, OGT, OHI, OLE, OLS, OLT, OLO,
+};
+char	logrel[12] =
+{
+	OEQ, ONE, OLS, OLS, OLO, OLO, OHS, OHS, OHI, OHI,
+};
+
+char	typei[NTYPE];
+int	typeiinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TVLONG, TUVLONG, -1,
+};
+char	typeu[NTYPE];
+int	typeuinit[] =
+{
+	TUCHAR, TUSHORT, TUINT, TULONG, TUVLONG, TIND, -1,
+};
+
+char	typesuv[NTYPE];
+int	typesuvinit[] =
+{
+	TVLONG, TUVLONG, TSTRUCT, TUNION, -1,
+};
+
+char	typeilp[NTYPE];
+int	typeilpinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, TIND, -1
+};
+
+char	typechl[NTYPE];
+char	typechlv[NTYPE];
+char typechlvp[NTYPE];
+int	typechlinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typechlp[NTYPE];
+int	typechlpinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TIND, -1,
+};
+
+char	typechlpfd[NTYPE];
+int	typechlpfdinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TFLOAT, TDOUBLE, TIND, -1,
+};
+
+char	typec[NTYPE];
+int	typecinit[] =
+{
+	TCHAR, TUCHAR, -1
+};
+
+char	typeh[NTYPE];
+int	typehinit[] =
+{
+	TSHORT, TUSHORT, -1,
+};
+
+char	typeil[NTYPE];
+int	typeilinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typev[NTYPE];
+int	typevinit[] =
+{
+	TVLONG,	TUVLONG, -1,
+};
+
+char	typefd[NTYPE];
+int	typefdinit[] =
+{
+	TFLOAT, TDOUBLE, -1,
+};
+
+char	typeaf[NTYPE];
+int	typeafinit[] =
+{
+	TFUNC, TARRAY, -1,
+};
+
+char	typesu[NTYPE];
+int	typesuinit[] =
+{
+	TSTRUCT, TUNION, -1,
+};
+
+long	tasign[NTYPE];
+Init	tasigninit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BIND,		0,
+	TSTRUCT,	BSTRUCT,	0,
+	TUNION,		BUNION,		0,
+	-1,		0,		0,
+};
+
+long	tasadd[NTYPE];
+Init	tasaddinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	tcast[NTYPE];
+Init	tcastinit[] =
+{
+	TCHAR,		BNUMBER|BIND|BVOID,	0,
+	TUCHAR,		BNUMBER|BIND|BVOID,	0,
+	TSHORT,		BNUMBER|BIND|BVOID,	0,
+	TUSHORT,	BNUMBER|BIND|BVOID,	0,
+	TINT,		BNUMBER|BIND|BVOID,	0,
+	TUINT,		BNUMBER|BIND|BVOID,	0,
+	TLONG,		BNUMBER|BIND|BVOID,	0,
+	TULONG,		BNUMBER|BIND|BVOID,	0,
+	TVLONG,		BNUMBER|BIND|BVOID,	0,
+	TUVLONG,	BNUMBER|BIND|BVOID,	0,
+	TFLOAT,		BNUMBER|BVOID,		0,
+	TDOUBLE,	BNUMBER|BVOID,		0,
+	TIND,		BINTEGER|BIND|BVOID,	0,
+	TVOID,		BVOID,			0,
+	TSTRUCT,	BSTRUCT|BVOID,		0,
+	TUNION,		BUNION|BVOID,		0,
+	-1,		0,			0,
+};
+
+long	tadd[NTYPE];
+Init	taddinit[] =
+{
+	TCHAR,		BNUMBER|BIND,	0,
+	TUCHAR,		BNUMBER|BIND,	0,
+	TSHORT,		BNUMBER|BIND,	0,
+	TUSHORT,	BNUMBER|BIND,	0,
+	TINT,		BNUMBER|BIND,	0,
+	TUINT,		BNUMBER|BIND,	0,
+	TLONG,		BNUMBER|BIND,	0,
+	TULONG,		BNUMBER|BIND,	0,
+	TVLONG,		BNUMBER|BIND,	0,
+	TUVLONG,	BNUMBER|BIND,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	tsub[NTYPE];
+Init	tsubinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BINTEGER|BIND,	0,
+	-1,		0,		0,
+};
+
+long	tmul[NTYPE];
+Init	tmulinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	-1,		0,		0,
+};
+
+long	tand[NTYPE];
+Init	tandinit[] =
+{
+	TCHAR,		BINTEGER,	0,
+	TUCHAR,		BINTEGER,	0,
+	TSHORT,		BINTEGER,	0,
+	TUSHORT,	BINTEGER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BINTEGER,	0,
+	TULONG,		BINTEGER,	0,
+	TVLONG,		BINTEGER,	0,
+	TUVLONG,	BINTEGER,	0,
+	-1,		0,		0,
+};
+
+long	trel[NTYPE];
+Init	trelinit[] =
+{
+	TCHAR,		BNUMBER,	0,
+	TUCHAR,		BNUMBER,	0,
+	TSHORT,		BNUMBER,	0,
+	TUSHORT,	BNUMBER,	0,
+	TINT,		BNUMBER,	0,
+	TUINT,		BNUMBER,	0,
+	TLONG,		BNUMBER,	0,
+	TULONG,		BNUMBER,	0,
+	TVLONG,		BNUMBER,	0,
+	TUVLONG,	BNUMBER,	0,
+	TFLOAT,		BNUMBER,	0,
+	TDOUBLE,	BNUMBER,	0,
+	TIND,		BIND,		0,
+	-1,		0,		0,
+};
+
+long	tfunct[1] =
+{
+	BFUNC,
+};
+
+long	tindir[1] =
+{
+	BIND,
+};
+
+long	tdot[1] =
+{
+	BSTRUCT|BUNION,
+};
+
+long	tnot[1] =
+{
+	BNUMBER|BIND,
+};
+
+long	targ[1] =
+{
+	BNUMBER|BIND|BSTRUCT|BUNION,
+};
+
+char	tab[NTYPE][NTYPE] =
+{
+/*TXXX*/	{ 0,
+		},
+
+/*TCHAR*/	{ 0,	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUCHAR*/	{ 0,	TUCHAR, TUCHAR, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TSHORT*/	{ 0,	TSHORT, TUSHORT, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUSHORT*/	{ 0,	TUSHORT, TUSHORT, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TINT*/	{ 0,	TINT, TUINT, TINT, TUINT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUINT*/	{ 0,	TUINT, TUINT, TUINT, TUINT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TLONG*/	{ 0,	TLONG, TULONG, TLONG, TULONG, TLONG, TULONG, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TULONG*/	{ 0,	TULONG, TULONG, TULONG, TULONG, TULONG, TULONG, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TVLONG*/	{ 0,	TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG,
+			TUVLONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUVLONG*/	{ 0,	TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG,
+			TUVLONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TFLOAT*/	{ 0,	TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT,
+			TFLOAT, TFLOAT, TFLOAT, TFLOAT, TDOUBLE, TIND,
+		},
+/*TDOUBLE*/	{ 0,	TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE,
+			TDOUBLE, TDOUBLE, TDOUBLE, TFLOAT, TDOUBLE, TIND,
+		},
+/*TIND*/	{ 0,	TIND, TIND, TIND, TIND, TIND, TIND, TIND,
+			 TIND, TIND, TIND, TIND, TIND, TIND,
+		},
+};
+
+void
+urk(char *name, int max, int i)
+{
+	if(i >= max) {
+		fprint(2, "bad tinit: %s %d>=%d\n", name, i, max);
+		exits("init");
+	}
+}
+
+void
+tinit(void)
+{
+	int *ip;
+	Init *p;
+
+	for(p=thashinit; p->code >= 0; p++) {
+		urk("thash", nelem(thash), p->code);
+		thash[p->code] = p->value;
+	}
+	for(p=bnamesinit; p->code >= 0; p++) {
+		urk("bnames", nelem(bnames), p->code);
+		bnames[p->code] = p->s;
+	}
+	for(p=tnamesinit; p->code >= 0; p++) {
+		urk("tnames", nelem(tnames), p->code);
+		tnames[p->code] = p->s;
+	}
+	for(p=gnamesinit; p->code >= 0; p++) {
+		urk("gnames", nelem(gnames), p->code);
+		gnames[p->code] = p->s;
+	}
+	for(p=qnamesinit; p->code >= 0; p++) {
+		urk("qnames", nelem(qnames), p->code);
+		qnames[p->code] = p->s;
+	}
+	for(p=cnamesinit; p->code >= 0; p++) {
+		urk("cnames", nelem(cnames), p->code);
+		cnames[p->code] = p->s;
+	}
+	for(p=onamesinit; p->code >= 0; p++) {
+		urk("onames", nelem(onames), p->code);
+		onames[p->code] = p->s;
+	}
+	for(ip=typeiinit; *ip>=0; ip++) {
+		urk("typei", nelem(typei), *ip);
+		typei[*ip] = 1;
+	}
+	for(ip=typeuinit; *ip>=0; ip++) {
+		urk("typeu", nelem(typeu), *ip);
+		typeu[*ip] = 1;
+	}
+	for(ip=typesuvinit; *ip>=0; ip++) {
+		urk("typesuv", nelem(typesuv), *ip);
+		typesuv[*ip] = 1;
+	}
+	for(ip=typeilpinit; *ip>=0; ip++) {
+		urk("typeilp", nelem(typeilp), *ip);
+		typeilp[*ip] = 1;
+	}
+	for(ip=typechlinit; *ip>=0; ip++) {
+		urk("typechl", nelem(typechl), *ip);
+		typechl[*ip] = 1;
+		typechlv[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typechlpinit; *ip>=0; ip++) {
+		urk("typechlp", nelem(typechlp), *ip);
+		typechlp[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typechlpfdinit; *ip>=0; ip++) {
+		urk("typechlpfd", nelem(typechlpfd), *ip);
+		typechlpfd[*ip] = 1;
+	}
+	for(ip=typecinit; *ip>=0; ip++) {
+		urk("typec", nelem(typec), *ip);
+		typec[*ip] = 1;
+	}
+	for(ip=typehinit; *ip>=0; ip++) {
+		urk("typeh", nelem(typeh), *ip);
+		typeh[*ip] = 1;
+	}
+	for(ip=typeilinit; *ip>=0; ip++) {
+		urk("typeil", nelem(typeil), *ip);
+		typeil[*ip] = 1;
+	}
+	for(ip=typevinit; *ip>=0; ip++) {
+		urk("typev", nelem(typev), *ip);
+		typev[*ip] = 1;
+		typechlv[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typefdinit; *ip>=0; ip++) {
+		urk("typefd", nelem(typefd), *ip);
+		typefd[*ip] = 1;
+	}
+	for(ip=typeafinit; *ip>=0; ip++) {
+		urk("typeaf", nelem(typeaf), *ip);
+		typeaf[*ip] = 1;
+	}
+	for(ip=typesuinit; *ip >= 0; ip++) {
+		urk("typesu", nelem(typesu), *ip);
+		typesu[*ip] = 1;
+	}
+	for(p=tasigninit; p->code >= 0; p++) {
+		urk("tasign", nelem(tasign), p->code);
+		tasign[p->code] = p->value;
+	}
+	for(p=tasaddinit; p->code >= 0; p++) {
+		urk("tasadd", nelem(tasadd), p->code);
+		tasadd[p->code] = p->value;
+	}
+	for(p=tcastinit; p->code >= 0; p++) {
+		urk("tcast", nelem(tcast), p->code);
+		tcast[p->code] = p->value;
+	}
+	for(p=taddinit; p->code >= 0; p++) {
+		urk("tadd", nelem(tadd), p->code);
+		tadd[p->code] = p->value;
+	}
+	for(p=tsubinit; p->code >= 0; p++) {
+		urk("tsub", nelem(tsub), p->code);
+		tsub[p->code] = p->value;
+	}
+	for(p=tmulinit; p->code >= 0; p++) {
+		urk("tmul", nelem(tmul), p->code);
+		tmul[p->code] = p->value;
+	}
+	for(p=tandinit; p->code >= 0; p++) {
+		urk("tand", nelem(tand), p->code);
+		tand[p->code] = p->value;
+	}
+	for(p=trelinit; p->code >= 0; p++) {
+		urk("trel", nelem(trel), p->code);
+		trel[p->code] = p->value;
+	}
+	
+	/* 32-bit defaults */
+	typeword = typechlp;
+	typeswitch = typechl;
+	typecmplx = typesuv;
+}
+
+/*
+ * return 1 if it is impossible to jump into the middle of n.
+ */
+static int
+deadhead(Node *n, int caseok)
+{
+loop:
+	if(n == Z)
+		return 1;
+	switch(n->op) {
+	case OLIST:
+		if(!deadhead(n->left, caseok))
+			return 0;
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		break;
+
+	case OLABEL:
+		return 0;
+
+	case OGOTO:
+		break;
+
+	case OCASE:
+		if(!caseok)
+			return 0;
+		goto rloop;
+
+	case OSWITCH:
+		return deadhead(n->right, 1);
+
+	case OWHILE:
+	case ODWHILE:
+		goto rloop;
+
+	case OFOR:
+		goto rloop;
+
+	case OCONTINUE:
+		break;
+
+	case OBREAK:
+		break;
+
+	case OIF:
+		return deadhead(n->right->left, caseok) && deadhead(n->right->right, caseok);
+
+	case OSET:
+	case OUSED:
+		break;
+	}
+	return 1;
+}
+
+int
+deadheads(Node *c)
+{
+	return deadhead(c->left, 0) && deadhead(c->right, 0);
+}
+
+int
+mixedasop(Type *l, Type *r)
+{
+	return !typefd[l->etype] && typefd[r->etype];
+}
+
+
+/*
+ * (uvlong)~ul creates a ul mask with top bits zero, which is usually wrong
+ * an explicit cast to ulong after ~ suppresses the diagnostic
+ */
+int
+castucom(Node *r)
+{
+	Node *rl;
+
+	if(r->op == OCAST &&
+	   (rl = r->left)->op == OCOM &&
+	   (r->type->etype == TVLONG || r->type->etype == TUVLONG) &&
+	   typeu[rl->type->etype] && typechl[rl->type->etype])
+		return 1;
+	return 0;
+}
--- /dev/null
+++ b/utils/cp/cp.c
@@ -1,0 +1,118 @@
+#include <lib9.h>
+
+#define	DEFB	(8*1024)
+
+void	copy(char *from, char *to, int todir);
+void	copy1(int fdf, int fdt, char *from, char *to);
+void	fixbackslash(char *file);
+
+void
+main(int argc, char *argv[])
+{
+	Dir *dirb;
+	int todir, i;
+
+	if(argc<3){
+		fprint(2, "usage:\tcp fromfile tofile\n");
+		fprint(2, "\tcp fromfile ... todir\n");
+		exits("usage");
+	}
+
+	for(i=0; i<argc; i++)
+		fixbackslash(argv[i]);
+
+
+	todir=0;
+	if((dirb = dirstat(argv[argc-1]))!=nil && (dirb->mode&DMDIR))
+		todir=1;
+	if(argc>3 && !todir){
+		fprint(2, "cp: %s not a directory\n", argv[argc-1]);
+		exits("bad usage");
+	}
+	for(i=1; i<argc-1; i++)
+		copy(argv[i], argv[argc-1], todir);
+	exits(0);
+}
+
+void
+copy(char *from, char *to, int todir)
+{
+	Dir *dirb, *dirt;
+	char name[256];
+	int fdf, fdt;
+
+	if(todir){
+		char *s, *elem;
+		elem=s=from;
+		while(*s++)
+			if(s[-1]=='/')
+				elem=s;
+		sprint(name, "%s/%s", to, elem);
+		to=name;
+	}
+	if((dirb = dirstat(from))==nil){
+		fprint(2,"cp: can't stat %s: %r\n", from);
+		return;
+	}
+	if(dirb->mode&DMDIR){
+		fprint(2, "cp: %s is a directory\n", from);
+		return;
+	}
+	dirb->mode &= 0777;
+	if((dirt = dirstat(to))!=nil)
+	if(dirb->qid.path==dirt->qid.path && dirb->qid.vers==dirt->qid.vers)
+	if(dirb->dev==dirt->dev && dirb->type==dirt->type){
+		fprint(2, "cp: %s and %s are the same file\n", from, to);
+		return;
+	}
+	fdf=open(from, OREAD);
+	if(fdf<0){
+		fprint(2, "cp: can't open %s: %r\n", from);
+		return;
+	}
+	fdt=create(to, OWRITE, dirb->mode);
+	if(fdt<0){
+		fprint(2, "cp: can't create %s: %r\n", to);
+		close(fdf);
+		return;
+	}
+	copy1(fdf, fdt, from, to);
+	close(fdf);
+	close(fdt);
+}
+
+void
+copy1(int fdf, int fdt, char *from, char *to)
+{
+	char *buf;
+	long n, n1, rcount;
+	char err[ERRMAX];
+
+	buf = malloc(DEFB);
+	/* clear any residual error */
+	err[0] = '\0';
+	errstr(err, ERRMAX);
+	for(rcount=0;; rcount++) {
+		n = read(fdf, buf, DEFB);
+		if(n <= 0)
+			break;
+		n1 = write(fdt, buf, n);
+		if(n1 != n) {
+			fprint(2, "cp: error writing %s: %r\n", to);
+			break;
+		}
+	}
+	if(n < 0) 
+		fprint(2, "cp: error reading %s: %r\n", from);
+	free(buf);
+}
+
+void
+fixbackslash(char *file)
+{
+	char *p;
+	
+	for(p=file; *p; p++)
+		if(*p == '\\')
+			*p = '/';
+}
--- /dev/null
+++ b/utils/cp/mkfile
@@ -1,0 +1,22 @@
+<../../mkconfig
+
+TARG=cp
+
+OFILES=	cp.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+#
+#	override install so that cp doesn't try to copy onto itself
+#
+
+$BIN/%:	$O.out
+	cp $target cpx.exe
+	./cpx $O.out $target
+	rm cpx.exe
--- /dev/null
+++ b/utils/cvbit/cvbit.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <pool.h>
+
+static int invert = 0;
+
+void
+main(int argc, char **argv)
+{
+	Memimage *im, *om;
+	char *s;
+	ulong ofmt;
+
+	ofmt = 0;
+	ARGBEGIN{
+	case 'i':
+		invert = 1;
+		break;
+	case 'c':
+		s = ARGF();
+		if(s==nil)
+			break;
+		ofmt = strtochan(s);
+		if(ofmt == 0){
+			fprint(2, "cvbit: bad chan: %s\n", s);
+			exits("chan");
+		}
+		break;
+	}ARGEND
+
+	memimageinit();
+	im = readmemimage(0);
+	if(im == nil){
+		fprint(2, "cvbit: can't read image: %r\n");
+		exits("read");
+	}
+	if(ofmt){
+		om = allocmemimage(im->r, ofmt);
+		if(om == nil){
+			fprint(2, "cvbit: can't allocate new image: %r\n");
+			exits("alloc");
+		}
+		memimagedraw(om, om->r, im, im->r.min, nil, ZP, S);
+	}else
+		om = im;
+	if(invert){
+		uchar *buf;
+		int bpl, y, x;
+
+		bpl = bytesperline(om->r, om->depth);
+		buf = malloc(bpl);
+		for(y=om->r.min.y; y<om->r.max.y; y++){
+			if(unloadmemimage(om, Rpt(Pt(om->r.min.x,y), Pt(om->r.max.x,y+1)), buf, bpl) != bpl){
+				fprint(2, "cvbit: can't unload image line\n");
+				exits("unload");
+			}
+			for(x=0; x<bpl; x++)
+				buf[x] ^= 0xFF;
+			if(loadmemimage(om, Rpt(Pt(om->r.min.x,y), Pt(om->r.max.x,y+1)), buf, bpl) != bpl){
+				fprint(2, "cvbit: can't load image line\n");
+				exits("load");
+			}
+		}
+	}
+	if(writememimage(1, om) < 0){
+		fprint(2, "cvbit: can't write image: %r\n");
+		exits("write");
+	}
+	exits(nil);
+}
+
+char*
+poolname(Pool *p)
+{
+	USED(p);
+	return "none";
+}
+
+void
+poolsetcompact(Pool *p, void (*f)(void*, void*))
+{
+	USED(p);
+	USED(f);
+}
--- /dev/null
+++ b/utils/cvbit/mkfile
@@ -1,0 +1,15 @@
+<../../mkconfig
+
+TARG=cvbit
+
+OFILES=	cvbit.$O\
+
+HFILES=	\
+	$ROOT/include/draw.h\
+	$ROOT/include/memdraw.h\
+
+LIBS=	draw memdraw draw bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/data2c/data2c.c
@@ -1,0 +1,33 @@
+#include <lib9.h>
+#include <bio.h>
+
+void
+main(int argc, char *argv[])
+{
+	Biobuf bin, bout;
+	long len;
+	int n;
+	uchar block[16], *c;
+
+	if(argc != 2){
+		fprint(2, "usage: data2s name\n");
+		exits("usage");
+	}
+	setbinmode();
+	Binit(&bin, 0, OREAD);
+	Binit(&bout, 1, OWRITE);
+	Bprint(&bout, "unsigned char %scode[] = {\n", argv[1]);
+	for(len=0; (n=Bread(&bin, block, sizeof(block))) > 0; len += n){
+		for(c=block; c < block+n; c++)
+			if(*c)
+				Bprint(&bout, "0x%ux,", *c);
+			else
+				Bprint(&bout, "0,");
+		Bprint(&bout, "\n");
+	}
+	if(len == 0)
+		Bprint(&bout, "0\n");	/* avoid empty initialiser */
+	Bprint(&bout, "};\n");
+	Bprint(&bout, "int %slen = %ld;\n", argv[1], len);
+	exits(0);
+}
--- /dev/null
+++ b/utils/data2c/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=data2c
+
+OFILES=	data2c.$O\
+
+HFILES=../../include/bio.h
+
+LIBS=bio 9			#order matters
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/data2s/data2s.c
@@ -1,0 +1,35 @@
+#include <lib9.h>
+#include <bio.h>
+
+void
+main(int argc, char *argv[])
+{
+	Biobuf bin, bout;
+	long len;
+	int n;
+	uchar block[8], *c;
+
+	if(argc != 2){
+		fprint(2, "usage: data2s name\n");
+		exits("usage");
+	}
+	setbinmode();
+	Binit(&bin, 0, OREAD);
+	Binit(&bout, 1, OWRITE);
+	for(len=0; (n=Bread(&bin, block, sizeof(block))) > 0; len += n){
+		Bprint(&bout, "DATA %scode+%ld(SB)/%d, $\"", argv[1], len, n);
+		for(c=block; c < block+n; c++)
+			if(*c)
+				Bprint(&bout, "\\%uo", *c);
+			else
+				Bprint(&bout, "\\z");
+		Bprint(&bout, "\"\n");
+	}
+	if(len == 0)
+		Bprint(&bout, "GLOBL %scode+0(SB), $1\n", argv[1]);
+	else
+		Bprint(&bout, "GLOBL %scode+0(SB), $%ld\n", argv[1], len);
+	Bprint(&bout, "GLOBL %slen+0(SB), $4\n", argv[1]);
+	Bprint(&bout, "DATA %slen+0(SB)/4, $%ld\n", argv[1], len);
+	exits(0);
+}
--- /dev/null
+++ b/utils/data2s/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=data2s
+
+OFILES=	data2s.$O\
+
+HFILES=../../include/bio.h
+
+LIBS=bio 9			#order matters
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/echo/echo.c
@@ -1,0 +1,33 @@
+#include <lib9.h>
+
+void
+main(int argc, char *argv[])
+{
+	int nflag;
+	int i, len;
+	char *buf, *p;
+
+	nflag = 0;
+	if(argc > 1 && strcmp(argv[1], "-n") == 0)
+		nflag = 1;
+
+	len = 1;
+	for(i = 1+nflag; i < argc; i++)
+		len += strlen(argv[i])+1;
+
+	buf = malloc(len);
+	if(buf == 0)
+		exits("no memory");
+
+	p = buf;
+	for(i = 1+nflag; i < argc; i++)
+		p += sprint(p, i == argc-1 ? "%s":"%s ", argv[i]);
+		
+	if(!nflag)
+		sprint(p, "\n");
+
+	if(write(1, buf, strlen(buf)) < 0)
+		fprint(2, "echo: write error: %r\n");
+
+	exits((char *)0);
+}
--- /dev/null
+++ b/utils/echo/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=echo
+
+OFILES=	echo.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/format/Nt.c
@@ -1,0 +1,47 @@
+#include <lib9.h>
+
+int
+initfflag()
+{
+	return 1;
+}
+
+Tm *
+getlocaltime()
+{
+	static Tm tmstruct;
+
+	time_t t = time((time_t *)0);
+	struct tm *ts = localtime(&t);
+	Tm *tt = &tmstruct;
+
+	tt->hour = ts->tm_hour;
+	tt->min = ts->tm_min;
+	tt->sec = ts->tm_sec;
+	tt->year = ts->tm_year;
+	tt->mon = ts->tm_mon;
+	tt->mday = ts->tm_mday;
+	tt->wday = ts->tm_wday;
+	tt->yday = ts->tm_yday;
+	return tt;
+}
+
+int
+openfloppy(char *dev)
+{
+	char buf[16];
+
+	/* if dev is of the form "x:" use "\\.\x:" instead */
+	if (strlen(dev) == 2 && dev[1] == ':') {
+		if (dev[0] == 'a' || dev[0] == 'A') {
+			strcpy(buf, "\\\\.\\");
+			strcat(buf, dev);
+			return open(buf, ORDWR);
+		}
+		else {
+			print("can only open A: drive\n");
+			return -1;
+		}
+	}
+	return open(dev, ORDWR);
+}
--- /dev/null
+++ b/utils/format/Plan9.c
@@ -1,0 +1,19 @@
+#include <lib9.h>
+
+int
+initfflag()
+{
+	return 0;
+}
+
+Tm *
+getlocaltime()
+{
+	return localtime(time(0));
+}
+
+int
+openfloppy(char *dev)
+{
+	return create(dev, ORDWR, 0666);
+}
--- /dev/null
+++ b/utils/format/format.c
@@ -1,0 +1,574 @@
+#include <lib9.h>
+
+/*
+ *  floppy types (all MFM encoding)
+ */
+typedef struct Type	Type;
+struct Type
+{
+	char	*name;
+	int	bytes;		/* bytes/sector */
+	int	sectors;	/* sectors/track */
+	int	heads;		/* number of heads */
+	int	tracks;		/* tracks/disk */
+	int	media;		/* media descriptor byte */
+	int	cluster;	/* default cluster size */
+};
+Type floppytype[] =
+{
+ { "3½HD",	512, 18, 2, 80,	0xf0, 1, },
+ { "3½DD",	512,  9, 2, 80,	0xf9, 2, },
+ { "5¼HD",	512, 15, 2, 80,	0xf9, 1, },
+ { "5¼DD",	512,  9, 2, 40,	0xfd, 2, },
+};
+#define NTYPES (sizeof(floppytype)/sizeof(Type))
+
+typedef struct Dosboot	Dosboot;
+struct Dosboot{
+	uchar	magic[3];	/* really an xx86 JMP instruction */
+	uchar	version[8];
+	uchar	sectsize[2];
+	uchar	clustsize;
+	uchar	nresrv[2];
+	uchar	nfats;
+	uchar	rootsize[2];
+	uchar	volsize[2];
+	uchar	mediadesc;
+	uchar	fatsize[2];
+	uchar	trksize[2];
+	uchar	nheads[2];
+	uchar	nhidden[4];
+	uchar	bigvolsize[4];
+	uchar	driveno;
+	uchar	reserved0;
+	uchar	bootsig;
+	uchar	volid[4];
+	uchar	label[11];
+	uchar	type[8];
+};
+#define	PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
+#define	PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
+
+typedef struct Dosdir	Dosdir;
+struct Dosdir
+{
+	uchar	name[8];
+	uchar	ext[3];
+	uchar	attr;
+	uchar	reserved[10];
+	uchar	time[2];
+	uchar	date[2];
+	uchar	start[2];
+	uchar	length[4];
+};
+
+#define	DRONLY	0x01
+#define	DHIDDEN	0x02
+#define	DSYSTEM	0x04
+#define	DVLABEL	0x08
+#define	DDIR	0x10
+#define	DARCH	0x20
+
+/*
+ *  the boot program for the boot sector.
+ */
+uchar bootprog[512];
+uchar bootprog1[16] =
+{
+	0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+uchar bootprog2[128] =
+{
+	0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
+	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
+	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
+	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
+	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
+	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
+	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
+	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
+	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
+	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
+	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
+	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
+	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
+	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
+	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
+	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
+};
+uchar bootprog3[16] =
+{
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
+};
+
+static void
+mkbootprog(void)
+{
+	int i;
+
+	for (i = 0; i < 512; i++)
+		bootprog[i] = 0;
+	for (i = 0; i < 16; i++)
+		bootprog[i+0x000] = bootprog1[i];
+	for (i = 0; i < 128; i++)
+		bootprog[i+0x03E] = bootprog2[i];
+	for (i = 0; i < 16; i++)
+		bootprog[i+0x1F0] = bootprog3[i];
+}
+
+char *dev;
+int clustersize;
+uchar *fat;	/* the fat */
+int fatbits;
+int fatsecs;
+int fatlast;	/* last cluster allocated */
+int clusters;
+int fatsecs;
+int volsecs;
+uchar *root;	/* first block of root */
+int rootsecs;
+int rootfiles;
+int rootnext;
+Type *t;
+int fflag;
+char file[64];	/* output file name */
+char *bootfile;
+char *type;
+
+enum
+{
+	Sof = 1,	/* start of file */
+	Eof = 2,	/* end of file */
+};
+
+
+void	dosfs(int, char*, int, char*[]);
+ulong	clustalloc(int);
+void	addrname(uchar*, Dir*, ulong, char *);
+
+int initfflag(void);
+Tm *getlocaltime(void);
+int openfloppy(char *);
+
+void
+usage(void)
+{
+	fprint(2, "usage: format [-b bfile] [-c csize] [-df] [-l label] [-t type] file [args ...]\n");
+	exits("usage");
+}
+
+void
+fatal(char *fmt, ...)
+{
+	char err[128];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(err, err+sizeof(err), fmt, arg);
+	va_end(arg);
+	fprint(2, "format: %s\n", err);
+	if(fflag && file[0])
+		remove(file);
+	exits(err);
+}
+
+void
+main(int argc, char **argv)
+{
+	int n, dos;
+	int cfd;
+	char buf[512];
+	char label[11];
+	char *a;
+
+	fflag = initfflag();
+	mkbootprog();
+	dos = 0;
+	type = 0;
+	clustersize = 0;
+	memmove(label, "CYLINDRICAL", sizeof(label));
+	ARGBEGIN {
+	case 'b':
+		bootfile = ARGF();
+		break;
+	case 'd':
+		dos = 1;
+		break;
+	case 'c':
+		clustersize = atoi(ARGF());
+		break;
+	case 'f':
+		fflag = 1;
+		break;
+	case 'l':
+		a = ARGF();
+		n = strlen(a);
+		if(n > sizeof(label))
+			n = sizeof(label);
+		memmove(label, a, n);
+		while(n < sizeof(label))
+			label[n++] = ' ';
+		break;
+	case 't':
+		type = ARGF();
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if(argc < 1)
+		usage();
+
+	dev = argv[0];
+	cfd = -1;
+	if(fflag == 0){
+		n = strlen(argv[0]);
+		if(n > 4 && strcmp(argv[0]+n-4, "disk") == 0)
+			*(argv[0]+n-4) = 0;
+		else if(n > 3 && strcmp(argv[0]+n-3, "ctl") == 0)
+			*(argv[0]+n-3) = 0;
+
+		sprint(buf, "%sctl", dev);
+		cfd = open(buf, ORDWR);
+		if(cfd < 0)
+			fatal("opening %s: %r", buf);
+		print("Formatting floppy %s\n", dev);
+		if(type)
+			sprint(buf, "format %s", type);
+		else
+			strcpy(buf, "format");
+		if(write(cfd, buf, strlen(buf)) < 0)
+			fatal("formatting tracks: %r");
+	}
+
+	if(dos)
+		dosfs(cfd, label, argc-1, argv+1);
+	if(cfd >= 0)
+		close(cfd);
+	exits(0);
+}
+
+void
+dosfs(int cfd, char *label, int argc, char *argv[])
+{
+	char r[16];
+	Dosboot *b;
+	uchar *buf;
+	Dir *d;
+	int n, fd, sysfd;
+	ulong length, x;
+	uchar *p;
+
+	print("Initialising MS-DOS file system\n");
+
+	if(fflag){
+		sprint(file, "%s", dev);
+		if ((fd = openfloppy(dev)) < 0)
+			fatal("create %s: %r", file);
+		t = floppytype;
+		if(type){
+			for(t = floppytype; t < &floppytype[NTYPES]; t++)
+				if(strcmp(type, t->name) == 0)
+					break;
+			if(t == &floppytype[NTYPES])
+				fatal("unknown floppy type %s", type);
+		}
+		length = t->bytes*t->sectors*t->heads*t->tracks;
+	}
+	else{
+		sprint(file, "%sdisk", dev);
+		fd = open(file, ORDWR);
+		if(fd < 0)
+			fatal("open %s: %r", file);
+		d = dirfstat(fd);
+		if(d == nil)
+			fatal("stat %s: %r", file);
+		length = d->length;
+		free(d);
+	
+		t = 0;
+		seek(cfd, 0, 0);
+		n = read(cfd, file, sizeof(file)-1);
+		if(n < 0)
+			fatal("reading floppy type");
+		else {
+			file[n] = 0;
+			for(t = floppytype; t < &floppytype[NTYPES]; t++)
+				if(strcmp(file, t->name) == 0)
+					break;
+			if(t == &floppytype[NTYPES])
+				fatal("unknown floppy type %s", file);
+		}
+	}
+	print("floppy type %s, %d tracks, %d heads, %d sectors/track, %d bytes/sec\n",
+		t->name, t->tracks, t->heads, t->sectors, t->bytes);
+
+	if(clustersize == 0)
+		clustersize = t->cluster;
+	clusters = length/(t->bytes*clustersize);
+	if(clusters < 4087)
+		fatbits = 12;
+	else
+		fatbits = 16;
+	volsecs = length/t->bytes;
+	fatsecs = (fatbits*clusters + 8*t->bytes - 1)/(8*t->bytes);
+	rootsecs = volsecs/200;
+	rootfiles = rootsecs * (t->bytes/sizeof(Dosdir));
+	buf = malloc(t->bytes);
+	if(buf == 0)
+		fatal("out of memory");
+
+	/*
+	 *  write bootstrap & parameter block
+	 */
+	if(bootfile){
+		if((sysfd = open(bootfile, OREAD)) < 0)
+			fatal("open %s: %r", bootfile);
+		if(read(sysfd, buf, t->bytes) < 0)
+			fatal("read %s: %r", bootfile);
+		close(sysfd);
+	}
+	else
+		memmove(buf, bootprog, sizeof(bootprog));
+	b = (Dosboot*)buf;
+	b->magic[0] = 0xEB;
+	b->magic[1] = 0x3C;
+	b->magic[2] = 0x90;
+	memmove(b->version, "Plan9.00", sizeof(b->version));
+	PUTSHORT(b->sectsize, t->bytes);
+	b->clustsize = clustersize;
+	PUTSHORT(b->nresrv, 1);
+	b->nfats = 2;
+	PUTSHORT(b->rootsize, rootfiles);
+	if(volsecs < (1<<16)){
+		PUTSHORT(b->volsize, volsecs);
+	}
+	PUTLONG(b->bigvolsize, volsecs);
+	b->mediadesc = t->media;
+	PUTSHORT(b->fatsize, fatsecs);
+	PUTSHORT(b->trksize, t->sectors);
+	PUTSHORT(b->nheads, t->heads);
+	PUTLONG(b->nhidden, 0);
+	b->driveno = 0;
+	b->bootsig = 0x29;
+	x = time(0);
+	PUTLONG(b->volid, x);
+	memmove(b->label, label, sizeof(b->label));
+	sprint(r, "FAT%d    ", fatbits);
+	memmove(b->type, r, sizeof(b->type));
+	buf[t->bytes-2] = 0x55;
+	buf[t->bytes-1] = 0xAA;
+	if(seek(fd, 0, 0) < 0)
+		fatal("seek to boot sector: %r\n");
+	if(write(fd, buf, t->bytes) != t->bytes)
+		fatal("writing boot sector: %r");
+	free(buf);
+
+	/*
+	 *  allocate an in memory fat
+	 */
+	fat = malloc(fatsecs*t->bytes);
+	if(fat == 0)
+		fatal("out of memory");
+	memset(fat, 0, fatsecs*t->bytes);
+	fat[0] = t->media;
+	fat[1] = 0xff;
+	fat[2] = 0xff;
+	if(fatbits == 16)
+		fat[3] = 0xff;
+	fatlast = 1;
+	if (seek(fd, 2*fatsecs*t->bytes, 1) < 0)
+		fatal("seek to 2 fats: %r");	/* 2 fats */
+
+	/*
+	 *  allocate an in memory root
+	 */
+	root = malloc(rootsecs*t->bytes);
+	if(root == 0)
+		fatal("out of memory");
+	memset(root, 0, rootsecs*t->bytes);
+	if (seek(fd, rootsecs*t->bytes, 1) < 0)
+		fatal("seek to root: %r");		/* rootsecs */
+
+	/*
+	 * Now positioned at the Files Area.
+	 * If we have any arguments, process 
+	 * them and write out.
+	 */
+	for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
+		if(p >= (root+(rootsecs*t->bytes)))
+			fatal("too many files in root");
+		/*
+		 * Open the file and get its length.
+		 */
+		if((sysfd = open(*argv, OREAD)) < 0)
+			fatal("open %s: %r", *argv);
+		d = dirfstat(sysfd);
+		if(d == nil)
+			fatal("stat %s: %r", *argv);
+		print("Adding file %s, length %lld\n", *argv, d->length);
+
+		length = d->length;
+		if(length){
+			/*
+			 * Allocate a buffer to read the entire file into.
+			 * This must be rounded up to a cluster boundary.
+			 *
+			 * Read the file and write it out to the Files Area.
+			 */
+			length += t->bytes*clustersize - 1;
+			length /= t->bytes*clustersize;
+			length *= t->bytes*clustersize;
+			if((buf = malloc(length)) == 0)
+				fatal("out of memory");
+	
+			if(read(sysfd, buf, d->length) < 0)
+				fatal("read %s: %r", *argv);
+			memset(buf+d->length, 0, length-d->length);
+			/* if(write(fd, buf, length) < 0) */
+			if (write(fd, buf, length) != length)
+				fatal("write %s: %r", *argv);
+			free(buf);
+
+			close(sysfd);
+	
+			/*
+			 * Allocate the FAT clusters.
+			 * We're assuming here that where we
+			 * wrote the file is in sync with
+			 * the cluster allocation.
+			 * Save the starting cluster.
+			 */
+			length /= t->bytes*clustersize;
+			x = clustalloc(Sof);
+			for(n = 0; n < length-1; n++)
+				clustalloc(0);
+			clustalloc(Eof);
+		}
+		else
+			x = 0;
+
+		/*
+		 * Add the filename to the root.
+		 */
+		addrname(p, d, x, *argv);
+		free(d);
+	}
+
+	/*
+	 *  write the fats and root
+	 */
+	if (seek(fd, t->bytes, 0) < 0)
+		fatal("seek to fat1: %r");
+	/* if(write(fd, fat, fatsecs*t->bytes) < 0) */
+	if (write(fd, fat, fatsecs*t->bytes) != fatsecs*t->bytes)
+		fatal("writing fat #1: %r");
+	/* if(write(fd, fat, fatsecs*t->bytes) < 0) */
+	if (write(fd, fat, fatsecs*t->bytes) != fatsecs*t->bytes)
+		fatal("writing fat #2: %r");
+	/* if(write(fd, root, rootsecs*t->bytes) < 0) */
+	if (write(fd, root, rootsecs*t->bytes) != rootsecs*t->bytes)
+		fatal("writing root: %r");
+
+	if(fflag){
+		if (seek(fd, t->bytes*t->sectors*t->heads*t->tracks-1, 0) < 0)
+			/* fatal("seek to 9: %r") */ {}
+		if (write(fd, "9", 1) != 1)
+			/* fatal("writing 9: %r") */ {}
+	}
+}
+
+/*
+ *  allocate a cluster
+ */
+ulong
+clustalloc(int flag)
+{
+	ulong o, x;
+
+	if(flag != Sof){
+		x = (flag == Eof) ? 0xffff : (fatlast+1);
+		if(fatbits == 12){
+			x &= 0xfff;
+			o = (3*fatlast)/2;
+			if(fatlast & 1){
+				fat[o] = (fat[o]&0x0f) | (x<<4);
+				fat[o+1] = (x>>4);
+			} else {
+				fat[o] = x;
+				fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
+			}
+		} else {
+			o = 2*fatlast;
+			fat[o] = x;
+			fat[o+1] = x>>8;
+		}
+	}
+		
+	if(flag == Eof)
+		return 0;
+	else
+		return ++fatlast;
+}
+
+void
+putname(char *p, Dosdir *d)
+{
+	int i;
+	char *q;
+
+	q = strrchr(p, '/');
+	if (q)
+		p = q+1;
+	q = strrchr(p, '\\');
+	if (q)
+		p = q+1;
+	memset(d->name, ' ', sizeof d->name+sizeof d->ext);
+	for(i = 0; i< sizeof(d->name); i++){
+		if(*p == 0 || *p == '.')
+			break;
+		d->name[i] = toupper(*p++);
+	}
+	p = strrchr(p, '.');
+	if(p){
+		for(i = 0; i < sizeof d->ext; i++){
+			if(*++p == 0)
+				break;
+			d->ext[i] = toupper(*p);
+		}
+	}
+}
+
+void
+puttime(Dosdir *d)
+{
+	Tm *t = getlocaltime();
+	ushort x;
+
+	x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
+	d->time[0] = x;
+	d->time[1] = x>>8;
+	x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
+	d->date[0] = x;
+	d->date[1] = x>>8;
+}
+
+void
+addrname(uchar *entry, Dir *dir, ulong start, char *nm)
+{
+	Dosdir *d;
+
+	d = (Dosdir*)entry;
+	/* putname(dir->name, d); */
+	putname(nm, d);
+	d->attr = DRONLY;
+	puttime(d);
+	d->start[0] = start;
+	d->start[1] = start>>8;
+	d->length[0] = dir->length;
+	d->length[1] = dir->length>>8;
+	d->length[2] = dir->length>>16;
+	d->length[3] = dir->length>>24;
+}
--- /dev/null
+++ b/utils/format/mkfile
@@ -1,0 +1,14 @@
+<../../mkconfig
+
+TARG=format
+
+OFILES=	format.$O\
+		$TARGMODEL.$O\
+
+HFILES=
+
+LIBS=9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/ftl/ftl.c
@@ -1,0 +1,867 @@
+/*
+ * basic Flash Translation Layer driver
+ *	see for instance the Intel technical paper
+ *	``Understanding the Flash Translation Layer (FTL) Specification''
+ *	Order number 297816-001 (online at www.intel.com)
+ *
+ * a public driver by David Hinds, dhinds@allegro.stanford.edu
+ * further helps with some details.
+ *
+ * this driver uses the common simplification of never storing
+ * the VBM on the medium (a waste of precious flash!) but
+ * rather building it on the fly as the block maps are read.
+ *
+ * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@caldo.demon.co.uk)
+ *	This driver may be used or adapted by anyone for any non-commercial purpose.
+ *
+ * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (charles@vitanuova.com)
+ *
+ * C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies
+ * to use this driver freely for any Inferno-related purposes whatever,
+ * including commercial applications.
+ *
+ * TO DO:
+ *	check error handling details for get/put flash
+ *	bad block handling
+ *	reserved space in formatted size
+ *	possibly block size as parameter
+ *	fetch parameters from header on init
+ *
+ * Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited
+ * 	usage : ftl flashsize secsize inputfile outputfile
+ * outputfile will then be a ftl image of inputfile
+ * nb assumes the base address is zero
+ *
+ */
+
+#include <lib9.h>
+
+ulong flashsize, secsize;
+char *flashm;
+int trace = 0;
+
+#ifndef offsetof
+#define offsetof(T,X) ((ulong)&(((T*)0)->X))
+#endif
+
+typedef struct Ftl Ftl;
+typedef struct Merase Merase;
+typedef struct Terase Terase;
+
+enum {
+	Eshift = 18,	/* 2^18=256k; log2(eraseunit) */
+	Flashseg = 1<<Eshift,
+	Bshift = 9,		/* 2^9=512 */
+	Bsize = 1<<Bshift,
+	BAMoffset = 0x100,
+	Nolimit = ~0,
+	USABLEPCT = 95,	/* release only this % to client */
+
+	FTLDEBUG = 0
+};
+
+/* erase unit header (defined by FTL specification) */
+struct Merase {
+	uchar	linktuple[5];
+	uchar	orgtuple[10];
+	uchar	nxfer;
+	uchar	nerase[4];
+	uchar	id[2];
+	uchar	bshift;
+	uchar	eshift;
+	uchar	pstart[2];
+	uchar	nunits[2];
+	uchar	psize[4];
+	uchar	vbmbase[4];
+	uchar	nvbm[2];
+	uchar	flags;
+	uchar	code;
+	uchar	serial[4];
+	uchar	altoffset[4];
+	uchar	bamoffset[4];
+	uchar	rsv2[12];
+};
+#define	ERASEHDRLEN	64
+
+enum {
+	/* special unit IDs */
+	XferID = 0xffff,
+	XferBusy = 0x7fff,
+
+	/* special BAM addresses */
+	Bfree = 0xffffffff,
+	Bwriting = 0xfffffffe,
+	Bdeleted = 0,
+
+	/* block types */
+	TypeShift = 7,
+	BlockType = (1<<TypeShift)-1,
+	ControlBlock = 0x30,
+	DataBlock = 0x40,
+	ReplacePage = 0x60,
+	BadBlock = 0x70,
+};
+
+#define	BTYPE(b)	((b) & BlockType)
+#define	BADDR(b)	((b) & ~BlockType)
+#define	BNO(va)	(((ulong)(va))>>Bshift)
+#define	MKBAM(b,t)	(((b)<<Bshift)|(t))
+
+struct Terase {
+	int	x;
+	int	id;
+	ulong	offset;
+	ulong	bamoffset;
+	ulong	nbam;
+	ulong*	bam;
+	ulong	bamx;
+	ulong	nfree;
+	ulong	nused;
+	ulong	ndead;
+	ulong	nbad;
+	ulong	nerase;
+};
+
+struct Ftl {
+	ulong	base;	/* base of flash region */
+	ulong	size;	/* size of flash region */
+	ulong	segsize;	/* size of flash segment (erase unit) */
+	int	eshift;	/* log2(erase-unit-size) */
+	int	bshift;	/* log2(bsize) */
+	int	bsize;
+	int	nunit;	/* number of segments (erase units) */
+	Terase**	unit;
+	int	lastx;	/* index in unit of last allocation */
+	int	xfer;		/* index in unit of current transfer unit (-1 if none) */
+	ulong	nfree;	/* total free space in blocks */
+	ulong	nblock;	/* total space in blocks */
+	ulong	rwlimit;	/* user-visible block limit (`formatted size') */
+	ulong*	vbm;		/* virtual block map */
+	ulong	fstart;	/* address of first block of data in a segment */
+	int	trace;	/* (debugging) trace of read/write actions */
+	int	detach;	/* free Ftl on last close */
+ 
+	/* scavenging variables */
+	int	needspace;
+	int	hasproc;
+};
+
+enum {
+	/* Ftl.detach */
+	Detached = 1,	/* detach on close */
+	Deferred	/* scavenger must free it */
+};
+
+/* little endian */
+#define	GET2(p)	(((p)[1]<<8)|(p)[0])
+#define	GET4(p)	(((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
+#define	PUT2(p,v)	(((p)[1]=(v)>>8),((p)[0]=(v)))
+#define	PUT4(p,v)	(((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v)))
+
+static	Ftl	*ftls;
+
+static	ulong	allocblk(Ftl*);
+static	void	eraseflash(Ftl*, ulong);
+static	void	erasefree(Terase*);
+static	void	eraseinit(Ftl*, ulong, int, int);
+static	Terase*	eraseload(Ftl*, int, ulong);
+static	void	ftlfree(Ftl*);
+static	void	getflash(Ftl*, void*, ulong, long);
+static	int	mapblk(Ftl*, ulong, Terase**, ulong*);
+static	Ftl*	mkftl(char*, ulong, ulong, int, char*);
+static	void	putbam(Ftl*, Terase*, int, ulong);
+static	void	putflash(Ftl*, ulong, void*, long);
+static	int	scavenge(Ftl*);
+
+static void	 
+ftlstat(int sz)
+{
+	print("0x%lux:0x%ux:0x%lux\n", ftls->rwlimit*Bsize, sz, flashsize);
+	print("%lud:%d:%lud in 512b blocks\n", ftls->rwlimit, sz>>Bshift, flashsize>>Bshift);
+}
+
+static long	 
+ftlread(void *buf, long n, ulong offset)
+{
+	Ftl *ftl;
+	Terase *e;
+	int nb;
+	uchar *a;
+	ulong pb;
+
+		if(n <= 0 || n%Bsize || offset%Bsize) {
+			fprint(2, "bad read\n");
+			exits("1");
+		}
+		ftl = ftls;
+		nb = n/Bsize;
+		offset /= Bsize;
+		if(offset >= ftl->rwlimit)
+			return 0;
+		if(offset+nb > ftl->rwlimit)
+			nb = ftl->rwlimit - offset;
+		a = buf;
+		for(n = 0; n < nb; n++){
+			if(mapblk(ftl, offset+n, &e, &pb))
+				getflash(ftl, a, e->offset + pb*Bsize, Bsize);
+			else
+				memset(a, 0, Bsize);
+			a += Bsize;
+		}
+		return a-(uchar*)buf;
+	/* not reached */
+}
+
+static long	 
+ftlwrite(void *buf, long n, ulong offset)
+{
+	int ns, nb;
+	uchar *a;
+	Terase *e, *oe;
+	ulong ob, v;
+	Ftl *ftl;
+
+	if(n <= 0)
+		return 0;
+		ftl = ftls;
+		if(n <= 0 || n%Bsize || offset%Bsize) {
+			fprint(2, "bad write\n");
+			exits("1");
+		}
+		nb = n/Bsize;
+		offset /= Bsize;
+		if(offset >= ftl->rwlimit)
+			return 0;
+		if(offset+nb > ftl->rwlimit)
+			nb = ftl->rwlimit - offset;
+		a = buf;
+		for(n = 0; n < nb; n++){
+			ns = 0;
+			while((v = allocblk(ftl)) == 0)
+				if(!scavenge(ftl) || ++ns > 3){
+					print("ftl: flash memory full\n");
+				}
+			if(!mapblk(ftl, offset+n, &oe, &ob))
+				oe = nil;
+			e = ftl->unit[v>>16];
+			v &= 0xffff;
+			putflash(ftl, e->offset + v*Bsize, a, Bsize);
+			putbam(ftl, e, v, MKBAM(offset+n, DataBlock));
+			/* both old and new block references exist in this window (can't be closed?) */
+			ftl->vbm[offset+n] = (e->x<<16) | v;
+			if(oe != nil){
+				putbam(ftl, oe, ob, Bdeleted);
+				oe->ndead++;
+			}
+			a += Bsize;
+		}
+		return a-(uchar*)buf;
+	/* not reached */
+}
+
+static Ftl *
+mkftl(char *fname, ulong base, ulong size, int eshift, char *op)
+{
+	int i, j, nov, segblocks;
+	ulong limit;
+	Terase *e;
+	Ftl *ftl;
+
+	ftl = malloc(sizeof(*ftl));
+	if(ftl == nil) {
+		fprint(2, "out of memory\n");
+		exits("1");
+	}
+	ftl->lastx = 0;
+	ftl->detach = 0;
+	ftl->needspace = 0;
+	ftl->hasproc = 0;
+	ftl->trace = 0;
+	limit = flashsize;
+	if(size == Nolimit)
+		size = limit-base;
+	if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) {
+		fprint(2, "bad flash space parameters");
+		exits("1");
+	}
+	if(FTLDEBUG || ftl->trace || trace)
+		print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit);
+	ftl->base = base;
+	ftl->size = size;
+	ftl->bshift = Bshift;
+	ftl->bsize = Bsize;
+	ftl->eshift = eshift;
+	ftl->segsize = 1<<eshift;
+	ftl->nunit = size>>eshift;
+	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks per segment (header, and BAM itself) */
+	ftl->fstart = nov;
+	segblocks = ftl->segsize/Bsize - nov;
+	ftl->nblock = ftl->nunit*segblocks;
+	if(ftl->nblock >= 0x10000)
+		ftl->nblock = 0x10000;
+	ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm));
+	ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit));
+	if(ftl->vbm == nil || ftl->unit == nil) {
+		fprint(2, "out of mem");
+		exits("1");
+	}
+	for(i=0; i<ftl->nblock; i++)
+		ftl->vbm[i] = 0;
+	if(strcmp(op, "format") == 0){
+		for(i=0; i<ftl->nunit-1; i++)
+			eraseinit(ftl, i*ftl->segsize, i, 1);
+		eraseinit(ftl, i*ftl->segsize, XferID, 1);
+	}
+	ftl->xfer = -1;
+	for(i=0; i<ftl->nunit; i++){
+		e = eraseload(ftl, i, i*ftl->segsize);
+		if(e == nil){
+			print("ftl: logical segment %d: bad format\n", i);
+			continue;
+		}
+		if(e->id == XferBusy){
+			e->nerase++;
+			eraseinit(ftl, e->offset, XferID, e->nerase);
+			e->id = XferID;
+		}
+		for(j=0; j<ftl->nunit; j++)
+			if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){
+				print("ftl: duplicate erase unit #%x\n", e->id);
+				erasefree(e);
+				e = nil;
+				break;
+			}
+		if(e){
+			ftl->unit[e->x] = e;
+			if(e->id == XferID)
+				ftl->xfer = e->x;
+			if (FTLDEBUG || ftl->trace || trace)
+				print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n",
+					e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase);
+		}
+	}
+	if(ftl->xfer < 0 && ftl->nunit <= 0 || ftl->xfer >= 0 && ftl->nunit <= 1) {
+		fprint(2, "no valid flash data units");
+		exits("1");
+	}
+	if(ftl->xfer < 0)
+		print("ftl: no transfer unit: device is WORM\n");
+	else
+		ftl->nblock -= segblocks;	/* discount transfer segment */
+	if(ftl->nblock >= 1000)
+		ftl->rwlimit = ftl->nblock-100;	/* TO DO: variable reserve */
+	else
+		ftl->rwlimit = ftl->nblock*USABLEPCT/100;
+	return ftl;
+}
+
+static void
+ftlfree(Ftl *ftl)
+{
+	if(ftl != nil){
+		free(ftl->unit);
+		free(ftl->vbm);
+		free(ftl);
+	}
+}
+
+/*
+ * this simple greedy algorithm weighted by nerase does seem to lead
+ * to even wear of erase units (cf. the eNVy file system)
+ */
+static Terase *
+bestcopy(Ftl *ftl)
+{
+	Terase *e, *be;
+	int i;
+
+	be = nil;
+	for(i=0; i<ftl->nunit; i++)
+		if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad &&
+		    (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead))
+			be = e;
+	return be;
+}
+
+static int
+copyunit(Ftl *ftl, Terase *from, Terase *to)
+{
+	int i, nb;
+	uchar id[2];
+	ulong *bam;
+	uchar *buf;
+	ulong v, bno;
+
+	if(FTLDEBUG || ftl->trace || trace)
+		print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset);
+	to->nbam = 0;
+	free(to->bam);
+	to->bam = nil;
+	buf = malloc(Bsize);
+	if(buf == nil)
+		return 0;
+	PUT2(id, XferBusy);
+	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
+	/* make new BAM */
+	nb = from->nbam*sizeof(*to->bam);
+	bam = malloc(nb);
+	if(bam == nil) {
+		fprint(2, "nomem\n");
+		exits("1");
+	}
+	memmove(bam, from->bam, nb);
+	to->nused = 0;
+	to->nbad = 0;
+	to->nfree = 0;
+	to->ndead = 0;
+	for(i = 0; i < from->nbam; i++)
+		switch(bam[i]){
+		case Bwriting:
+		case Bdeleted:
+		case Bfree:
+			bam[i] = Bfree;
+			to->nfree++;
+			break;
+		default:
+			switch(bam[i]&BlockType){
+			default:
+			case BadBlock:	/* it isn't necessarily bad in this unit */
+				to->nfree++;
+				bam[i] = Bfree;
+				break;
+			case DataBlock:
+			case ReplacePage:
+				v = bam[i];
+				bno = BNO(v & ~BlockType);
+				if(i < ftl->fstart || bno >= ftl->nblock){
+					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v);
+					to->nfree++;
+					bam[i] = Bfree;
+					break;
+				}
+				getflash(ftl, buf, from->offset+i*Bsize, Bsize);
+				putflash(ftl, to->offset+i*Bsize, buf, Bsize);
+				to->nused++;
+				break;
+			case ControlBlock:
+				to->nused++;
+				break;
+			}
+		}
+	for(i=0; i<from->nbam; i++){
+		uchar *p = (uchar*)&bam[i];
+		v = bam[i];
+		if(v != Bfree && ftl->trace > 1)
+			print("to[%d]=#%lux\n", i, v);
+		PUT4(p, v);
+	}
+	putflash(ftl, to->bamoffset, bam, nb);	/* BUG: PUT4 */
+	for(i=0; i<from->nbam; i++){
+		uchar *p = (uchar*)&bam[i];
+		v = bam[i];
+		PUT4(p, v);
+	}
+	to->id = from->id;
+	PUT2(id, to->id);
+	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
+	to->nbam = from->nbam;
+	to->bam = bam;
+	ftl->nfree += to->nfree - from->nfree;
+	free(buf);
+	return 1;
+}
+
+static int
+mustscavenge(void *a)
+{
+	return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred;
+}
+
+static int
+donescavenge(void *a)
+{
+	return ((Ftl*)a)->needspace == 0;
+}
+
+static void
+scavengeproc(void *arg)
+{
+	Ftl *ftl;
+	int i;
+	Terase *e, *ne;
+
+	ftl = arg;
+	if(mustscavenge(ftl)){
+		if(ftl->detach == Deferred){
+			ftlfree(ftl);
+			fprint(2, "scavenge out of memory\n");
+			exits("1");
+		}
+		if(FTLDEBUG || ftl->trace || trace)
+			print("ftl: scavenge %ld\n", ftl->nfree);
+		e = bestcopy(ftl);
+		if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne)
+			goto Fail;
+		if(copyunit(ftl, e, ne)){
+			i = ne->x; ne->x = e->x; e->x = i;
+			ftl->unit[ne->x] = ne;
+			ftl->unit[e->x] = e;
+			ftl->xfer = e->x;
+			e->id = XferID;
+			e->nbam = 0;
+			free(e->bam);
+			e->bam = nil;
+			e->bamx = 0;
+			e->nerase++;
+			eraseinit(ftl, e->offset, XferID, e->nerase);
+		}
+	Fail:
+		if(FTLDEBUG || ftl->trace || trace)
+			print("ftl: end scavenge %ld\n", ftl->nfree);
+		ftl->needspace = 0;
+	}
+}
+
+static int
+scavenge(Ftl *ftl)
+{
+	if(ftl->xfer < 0 || bestcopy(ftl) == nil)
+		return 0;	/* you worm! */
+
+	if(!ftl->hasproc){
+		ftl->hasproc = 1;
+	}
+	ftl->needspace = 1;
+
+	scavengeproc(ftls);
+
+	return ftl->nfree;
+}
+
+static void
+putbam(Ftl *ftl, Terase *e, int n, ulong entry)
+{
+	uchar b[4];
+
+	e->bam[n] = entry;
+	PUT4(b, entry);
+	putflash(ftl, e->bamoffset + n*4, b, 4);
+}
+
+static ulong
+allocblk(Ftl *ftl)
+{
+	Terase *e;
+	int i, j;
+
+	i = ftl->lastx;
+	do{
+		e = ftl->unit[i];
+		if(e != nil && e->id != XferID && e->nfree){
+			ftl->lastx = i;
+			for(j=e->bamx; j<e->nbam; j++)
+				if(e->bam[j] == Bfree){
+					putbam(ftl, e, j, Bwriting);
+					ftl->nfree--;
+					e->nfree--;
+					e->bamx = j+1;
+					return (e->x<<16) | j;
+				}
+			e->nfree = 0;
+			print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree);
+		}
+		if(++i >= ftl->nunit)
+			i = 0;
+	}while(i != ftl->lastx);
+	return 0;
+}
+
+static int
+mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp)
+{
+	ulong v;
+	int x;
+
+	if(bno < ftl->nblock){
+		v = ftl->vbm[bno];
+		if(v == 0 || v == ~0)
+			return 0;
+		x = v>>16;
+		if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){
+			print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x);
+			return 0;
+		}
+		*ep = ftl->unit[x];
+		*bp = v & 0xFFFF;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+eraseinit(Ftl *ftl, ulong offset, int id, int nerase)
+{
+	union {
+		Merase	m;
+		uchar	block[ERASEHDRLEN];
+	} *m;
+	uchar *bam, *p;
+	int i, nov;
+
+	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks (header, and BAM itself) */
+	if(nov*Bsize >= ftl->segsize) {
+		fprint(2, "ftl -- too small for files");
+		exits("1");
+	}
+	eraseflash(ftl, offset);
+	m = malloc(sizeof(*m));
+	if(m == nil) {
+		fprint(2, "nomem\n");
+		exits("1");
+	}
+	memset(m, 0xFF, sizeof(*m));
+	m->m.linktuple[0] = 0x13;
+	m->m.linktuple[1] = 0x3;
+	memmove(m->m.linktuple+2, "CIS", 3);
+	m->m.orgtuple[0] = 0x46;
+	m->m.orgtuple[1] = 0x57;
+	m->m.orgtuple[2] = 0x00;
+	memmove(m->m.orgtuple+3, "FTL100", 7);
+	m->m.nxfer = 1;
+	PUT4(m->m.nerase, nerase);
+	PUT2(m->m.id, id);
+	m->m.bshift = ftl->bshift;
+	m->m.eshift = ftl->eshift;
+	PUT2(m->m.pstart, 0);
+	PUT2(m->m.nunits, ftl->nunit);
+	PUT4(m->m.psize, ftl->size - nov*Bsize);
+	PUT4(m->m.vbmbase, 0xffffffff);	/* we always calculate the VBM */
+	PUT2(m->m.nvbm, 0);
+	m->m.flags = 0;
+	m->m.code = 0xFF;
+	memmove(m->m.serial, "Inf1", 4);
+	PUT4(m->m.altoffset, 0);
+	PUT4(m->m.bamoffset, BAMoffset);
+	putflash(ftl, offset, m, ERASEHDRLEN);
+	free(m);
+	if(id == XferID)
+		return;
+	nov *= 4;	/* now bytes of BAM */
+	bam = malloc(nov);
+	if(bam == nil) {
+		fprint(2, "nomem");
+		exits("1");
+	}
+	for(i=0; i<nov; i += 4){
+		p = bam+i;
+		PUT4(p, ControlBlock);	/* reserve them */
+	}
+	putflash(ftl, offset+BAMoffset, bam, nov);
+	free(bam);
+}
+
+static Terase *
+eraseload(Ftl *ftl, int x, ulong offset)
+{
+	union {
+		Merase	m;
+		uchar	block[ERASEHDRLEN];
+	} *m;
+	Terase *e;
+	uchar *p;
+	int i, nbam;
+	ulong bno, v;
+
+	m = malloc(sizeof(*m));
+	if(m == nil) {
+		fprint(2, "nomem");
+		exits("1");
+	}
+	getflash(ftl, m, offset, ERASEHDRLEN);
+	if(memcmp(m->m.orgtuple+3, "FTL100", 7) != 0 ||
+	   memcmp(m->m.serial, "Inf1", 4) != 0){
+		free(m);
+		return nil;
+	}
+	e = malloc(sizeof(*e));
+	if(e == nil){
+		free(m);
+		fprint(2, "nomem");
+		exits("1");
+	}
+	e->x = x;
+	e->id = GET2(m->m.id);
+	e->offset = offset;
+	e->bamoffset = GET4(m->m.bamoffset);
+	e->nerase = GET4(m->m.nerase);
+	e->bamx = 0;
+	e->nfree = 0;
+	e->nused = 0;
+	e->ndead = 0;
+	e->nbad = 0;
+	free(m);
+	if(e->bamoffset != BAMoffset){
+		free(e);
+		return nil;
+	}
+	e->bamoffset += offset;
+	if(e->id == XferID || e->id == XferBusy){
+		e->bam = nil;
+		e->nbam = 0;
+		return e;
+	}
+	nbam = ftl->segsize/Bsize;
+	e->bam = malloc(nbam*sizeof(*e->bam));
+	e->nbam = nbam;
+	getflash(ftl, e->bam, e->bamoffset, nbam*4);
+	/* scan BAM to build VBM */
+	e->bamx = 0;
+	for(i=0; i<nbam; i++){
+		p = (uchar*)&e->bam[i];
+		e->bam[i] = v = GET4(p);
+		if(v == Bwriting || v == Bdeleted)
+			e->ndead++;
+		else if(v == Bfree){
+			if(e->bamx == 0)
+				e->bamx = i;
+			e->nfree++;
+			ftl->nfree++;
+		}else{
+			switch(v & BlockType){
+			case ControlBlock:
+				break;
+			case DataBlock:
+				/* add to VBM */
+				if(v & (1<<31))
+					break;	/* negative => VBM page, ignored */
+				bno = BNO(v & ~BlockType);
+				if(i < ftl->fstart || bno >= ftl->nblock){
+					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v);
+					e->nbad++;
+					break;
+				}
+				ftl->vbm[bno] = (e->x<<16) | i;
+				e->nused++;
+				break;
+			case ReplacePage:
+				/* replacement VBM page; ignored */
+				break;
+			default:
+				print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v);
+			case BadBlock:
+				e->nbad++;
+				break;
+			}
+		}
+	}
+	return e;
+}
+
+static void
+erasefree(Terase *e)
+{
+	free(e->bam);
+	free(e);
+}
+
+static void
+eraseflash(Ftl *ftl, ulong offset)
+{
+	offset += ftl->base;
+	if(FTLDEBUG || ftl->trace || trace)
+		print("ftl: erase seg @#%lux\n", offset);
+	memset(flashm+offset, 0xff, secsize);
+}
+
+static void
+putflash(Ftl *ftl, ulong offset, void *buf, long n)
+{
+	offset += ftl->base;
+	if(ftl->trace || trace)
+		print("ftl: write(#%lux, %ld)\n", offset, n);
+	memcpy(flashm+offset, buf, n);
+}
+
+static void
+getflash(Ftl *ftl, void *buf, ulong offset, long n)
+{
+	offset += ftl->base;
+	if(ftl->trace || trace)
+		print("ftl: read(#%lux, %ld)\n", offset, n);
+	memcpy(buf, flashm+offset, n);
+}
+
+#define BUFSIZE 8192
+
+void
+main(int argc, char **argv)
+{
+	int k, r, sz, offset = 0;
+	char *buf, *buf1;
+	int fd1, fd2;
+
+	if (argc != 5) {
+		fprint(2, "usage: %s flashsize secsize kfsfile flashfile\n", argv[0]);
+		exits("1");
+	}
+	flashsize = strtol(argv[1], nil, 0);
+	secsize = strtol(argv[2], nil , 0);
+	fd1 = open(argv[3], OREAD);
+	fd2 = create(argv[4], OWRITE, 0644);
+	if (fd1 < 0 || fd2 < 0) {
+		fprint(2, "bad io files\n");
+		exits("1");
+	}
+	if(secsize == 0 || secsize > flashsize || secsize&(secsize-1) || 0&(secsize-1) || flashsize == 0 || flashsize != Nolimit && flashsize&(secsize-1)) {
+		fprint(2, "bad sizes\n");
+		exits("1");
+	}
+	for(k=0; k<32 && (1<<k) != secsize; k++)
+			;
+	flashm = malloc(flashsize);
+	buf = malloc(BUFSIZE);
+	if (flashm == nil) {
+		fprint(2, "no mem for flash\n");
+		exits("1");
+	}
+	ftls = mkftl("FLASH", 0, Nolimit, k, "format");
+	for (;;) {
+		r = read(fd1, buf, BUFSIZE);
+		if (r <= 0)
+			break;
+		if (ftlwrite(buf, r, offset) != r) {
+			fprint(2, "ftlwrite failed - input file too big\n");
+			exits("1");
+		}
+		offset += r;
+	}
+	write(fd2, flashm, flashsize);
+	close(fd1);
+	close(fd2);
+	ftlstat(offset);
+	/* ftls = mkftl("FLASH", 0, Nolimit, k, "init"); */
+	sz = offset;
+	offset = 0;
+	buf1 = malloc(BUFSIZE);
+	fd1 = open(argv[3], OREAD);
+	for (;;) {
+		r = read(fd1, buf1, BUFSIZE);
+		if (r <= 0)
+			break;
+		if (ftlread(buf, r, offset) != r) {
+			fprint(2, "ftlread failed\n");
+			exits("1");
+		}
+		if (memcmp(buf, buf1, r) != 0) {
+			fprint(2, "bad read\n");
+			exits("1");
+		}
+		offset += r;
+	}
+	close(fd1);
+	if (offset != sz) {
+		fprint(2, "bad final offset\n");
+		exits("1");
+	}
+	exits("0");
+}
--- /dev/null
+++ b/utils/ftl/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=ftl
+
+OFILES=	ftl.$O\
+
+HFILES=
+
+LIBS=9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/iar/Nt.c
@@ -1,0 +1,10 @@
+#include	<lib9.h>
+
+char *
+myctime(long x)
+{
+	time_t t;
+
+	t = x;
+	return ctime(&t);
+}
--- /dev/null
+++ b/utils/iar/Plan9.c
@@ -1,0 +1,7 @@
+#include	<lib9.h>
+
+char *
+myctime(long x)
+{
+	return ctime(x);
+}
--- /dev/null
+++ b/utils/iar/Posix.c
@@ -1,0 +1,10 @@
+#include	<lib9.h>
+
+char *
+myctime(long x)
+{
+	time_t t;
+
+	t = x;
+	return ctime(&t);
+}
--- /dev/null
+++ b/utils/iar/ar.c
@@ -1,0 +1,1197 @@
+/*
+ * ar - portable (ascii) format version
+ */
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+#include <ar.h>
+
+/*
+ *	The algorithm uses up to 3 temp files.  The "pivot member" is the
+ *	archive member specified by and a, b, or i option.  The temp files are
+ *	astart - contains existing members up to and including the pivot member.
+ *	amiddle - contains new files moved or inserted behind the pivot.
+ *	aend - contains the existing members that follow the pivot member.
+ *	When all members have been processed, function 'install' streams the
+ * 	temp files, in order, back into the archive.
+ */
+
+typedef struct	Arsymref
+{
+	char	*name;
+	int	type;
+	int	len;
+	long	offset;
+	struct	Arsymref *next;
+} Arsymref;
+
+typedef struct	Armember	/* Temp file entry - one per archive member */
+{
+	struct Armember	*next;
+	struct ar_hdr	hdr;
+	long		size;
+	long		date;
+	void		*member;
+} Armember;
+
+typedef	struct Arfile		/* Temp file control block - one per tempfile */
+{
+	int	paged;		/* set when some data paged to disk */
+	char	*fname;		/* paging file name */
+	int	fd;		/* paging file descriptor */
+	long	size;
+	Armember *head;		/* head of member chain */
+	Armember *tail;		/* tail of member chain */
+	Arsymref *sym;		/* head of defined symbol chain */
+} Arfile;
+
+typedef struct Hashchain
+{
+	char	*name;
+	struct Hashchain *next;
+} Hashchain;
+
+#define	NHASH	1024
+
+/*
+ *	macro to portably read/write archive header.
+ *	'cmd' is read/write/Bread/Bwrite, etc.
+ */
+#define	HEADER_IO(cmd, f, h)	cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\
+				|| cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\
+				|| cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\
+				|| cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\
+				|| cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\
+				|| cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\
+				|| cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag)
+
+		/* constants and flags */
+char	*man =		"mrxtdpq";
+char	*opt =		"uvnbailo";
+char	artemp[] =	"/tmp/vXXXXX";
+char	movtemp[] =	"/tmp/v1XXXXX";
+char	tailtemp[] =	"/tmp/v2XXXXX";
+char	symdef[] =	"__.SYMDEF";
+
+int	aflag;				/* command line flags */
+int	bflag;
+int	cflag;
+int	oflag;
+int	uflag;
+int	vflag;
+
+Arfile *astart, *amiddle, *aend;	/* Temp file control block pointers */
+int	allobj = 1;			/* set when all members are object files of the same type */
+int	symdefsize;			/* size of symdef file */
+int	dupfound;			/* flag for duplicate symbol */
+Hashchain	*hash[NHASH];		/* hash table of text symbols */
+	
+#define	ARNAMESIZE	sizeof(astart->tail->hdr.name)
+
+char	poname[ARNAMESIZE+1];		/* name of pivot member */
+char	*file;				/* current file or member being worked on */
+Biobuf	bout;
+Biobuf bar;
+
+void	arcopy(Biobuf*, Arfile*, Armember*);
+int	arcreate(char*);
+void	arfree(Arfile*);
+void	arinsert(Arfile*, Armember*);
+char	*armalloc(int);
+void	armove(Biobuf*, Arfile*, Armember*);
+void	arread(Biobuf*, Armember*, int);
+void	arstream(int, Arfile*);
+int	arwrite(int, Armember*);
+int	bamatch(char*, char*);
+int	duplicate(char*);
+Armember *getdir(Biobuf*);
+int	getspace(void);
+void	install(char*, Arfile*, Arfile*, Arfile*, int);
+void	longt(Armember*);
+int	match(int, char**);
+void	mesg(int, char*);
+char *myctime(long);	/* $TARGMODEL.c */
+Arfile	*newtempfile(char*);
+Armember *newmember(void);
+void	objsym(Sym*, void*);
+int	openar(char*, int, int);
+int	page(Arfile*);
+void	pmode(long);
+void	rl(int);
+void	scanobj(Biobuf*, Arfile*, int);
+void	ar_select(int*, long);
+void	setcom(void(*)(char*, int, char**));
+void	skip(Biobuf*, long);
+int	symcomp(void*, void*);
+void	trim(char*, char*, int);
+void	usage(void);
+void	wrerr(void);
+void	wrsym(Biobuf*, int, Arsymref*);
+
+void	arcmd(char*, int, char**);		/* command processing */
+void	dcmd(char*, int, char**);
+void	xcmd(char*, int, char**);
+void	tcmd(char*, int, char**);
+void	pcmd(char*, int, char**);
+void	mcmd(char*, int, char**);
+void	qcmd(char*, int, char**);
+void	(*comfun)(char*, int, char**);
+
+void
+main(int argc, char *argv[])
+{
+	char *cp;
+
+	Binit(&bout, 1, OWRITE);
+	if(argc < 3)
+		usage();
+	for (cp = argv[1]; *cp; cp++) {
+		switch(*cp) {
+		case 'a':	aflag = 1;	break;
+		case 'b':	bflag = 1;	break;
+		case 'c':	cflag = 1;	break;
+		case 'd':	setcom(dcmd);	break;
+		case 'i':	bflag = 1;	break;
+		case 'l':
+				strcpy(artemp, "vXXXXX");
+				strcpy(movtemp, "v1XXXXX");
+				strcpy(tailtemp, "v2XXXXX");
+				break;
+		case 'm':	setcom(mcmd);	break;
+		case 'o':	oflag = 1;	break;
+		case 'p':	setcom(pcmd);	break;
+		case 'q':	setcom(qcmd);	break;
+		case 'r':	setcom(arcmd);	break;
+		case 't':	setcom(tcmd);	break;
+		case 'u':	uflag = 1;	break;
+		case 'v':	vflag = 1;	break;
+		case 'x':	setcom(xcmd);	break;
+		default:
+			fprint(2, "ar: bad option `%c'\n", *cp);
+			exits("error");
+		}
+	}
+	if (aflag && bflag) {
+		fprint(2, "ar: only one of 'a' and 'b' can be specified\n");
+		usage();
+	}
+	if(aflag || bflag) {
+		trim(argv[2], poname, sizeof(poname));
+		argv++;
+		argc--;
+		if(argc < 3)
+			usage();
+	}
+	if(comfun == 0) {
+		if(uflag == 0) {
+			fprint(2, "ar: one of [%s] must be specified\n", man);
+			usage();
+		}
+		setcom(arcmd);
+	}
+	cp = argv[2];
+	argc -= 3;
+	argv += 3;
+	(*comfun)(cp, argc, argv);	/* do the command */
+	cp = 0;
+	while (argc--) {
+		if (*argv) {
+			fprint(2, "ar: %s not found\n", *argv);
+			cp = "error";
+		}
+		argv++;
+	}
+	if(allobj && dupfound)
+		exits("dup found");
+	exits(cp);
+}
+/*
+ *	select a command
+ */
+void
+setcom(void (*fun)(char *, int, char**))
+{
+
+	if(comfun != 0) {
+		fprint(2, "ar: only one of [%s] allowed\n", man);
+		usage();
+	}
+	comfun = fun;
+}
+/*
+ *	perform the 'r' and 'u' commands
+ */
+void
+arcmd(char *arname, int count, char **files)
+{
+	int fd;
+	int i;
+	Arfile *ap;
+	Armember *bp;
+	Dir *d;
+	Biobuf *bfile;
+
+	fd = openar(arname, ORDWR, 1);
+	if (fd >= 0) {
+		Binit(&bar, fd, OREAD);
+		Bseek(&bar,seek(fd,0,1), 1);
+	}
+	astart = newtempfile(artemp);
+	ap = astart;
+	aend = 0;
+	for(i = 0; fd >= 0; i++) {
+		bp = getdir(&bar);
+		if (!bp)
+			break;
+		if (bamatch(file, poname)) {		/* check for pivot */
+			aend = newtempfile(tailtemp);
+			ap = aend;
+		}
+			/* pitch symdef file */
+		if (i == 0 && strcmp(file, symdef) == 0) {
+			skip(&bar, bp->size);
+			continue;
+		}
+		if (count && !match(count, files)) {
+			scanobj(&bar, ap, bp->size);
+			arcopy(&bar, ap, bp);
+			continue;
+		}
+		bfile = Bopen(file, OREAD);
+		if (!bfile) {
+			if (count != 0)
+				fprint(2, "ar: cannot open %s\n", file);
+			scanobj(&bar, ap, bp->size);
+			arcopy(&bar, ap, bp);
+			continue;
+		}
+		d = dirfstat(Bfildes(bfile));
+		if (d == nil)
+			fprint(2, "ar: cannot stat %s: %r\n", file);
+		if (uflag && (d == nil || d->mtime <= bp->date)) {
+			scanobj(&bar, ap, bp->size);
+			arcopy(&bar, ap, bp);
+			Bterm(bfile);
+			free(d);
+			continue;
+		}
+		mesg('r', file);
+		skip(&bar, bp->size);
+		scanobj(bfile, ap, d->length);
+		free(d);
+		armove(bfile, ap, bp);
+		Bterm(bfile);
+	}
+	if(fd >= 0)
+		close(fd);
+		/* copy in remaining files named on command line */
+	for (i = 0; i < count; i++) {
+		file = files[i];
+		if(file == 0)
+			continue;
+		files[i] = 0;
+		bfile = Bopen(file, OREAD);
+		if (!bfile)
+			fprint(2, "ar: %s cannot open\n", file);
+		else {
+			mesg('a', file);
+			d = dirfstat(Bfildes(bfile));
+			if (d == nil)
+				fprint(2, "ar: can't stat %s: %r\n", file);
+			else {
+				scanobj(bfile, astart, d->length);
+				armove(bfile, astart, newmember());
+				free(d);
+			}
+			Bterm(bfile);
+		}
+	}
+	if(fd < 0 && !cflag)
+		install(arname, astart, 0, aend, 1);	/* issue 'creating' msg */
+	else
+		install(arname, astart, 0, aend, 0);
+}
+
+void
+dcmd(char *arname, int count, char **files)
+{
+	Armember *bp;
+	int fd, i;
+
+
+	if (!count)
+		return;
+	fd = openar(arname, ORDWR, 0);
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	astart = newtempfile(artemp);
+	for (i = 0; bp = getdir(&bar); i++) {
+		if(match(count, files)) {
+			mesg('d', file);
+			skip(&bar, bp->size);
+			if (strcmp(file, symdef) == 0)
+				allobj = 0;
+		} else if (i == 0 && strcmp(file, symdef) == 0)
+				skip(&bar, bp->size);
+		else {
+			scanobj(&bar, astart, bp->size);
+			arcopy(&bar, astart, bp);
+		}
+	}
+	close(fd);
+	install(arname, astart, 0, 0, 0);
+}
+
+void
+xcmd(char *arname, int count, char **files)
+{
+	int fd, f, mode, i;
+	Armember *bp;
+	Dir dx;
+
+	fd = openar(arname, OREAD, 0);
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	i = 0;
+	while (bp = getdir(&bar)) {
+		if(count == 0 || match(count, files)) {
+			mode = strtoul(bp->hdr.mode, 0, 8) & 0777;
+			f = create(file, OWRITE, mode);
+			if(f < 0) {
+				fprint(2, "ar: %s cannot create\n", file);
+				skip(&bar, bp->size);
+			} else {
+				mesg('x', file);
+				arcopy(&bar, 0, bp);
+				if (write(f, bp->member, bp->size) < 0)
+					wrerr();
+				if(oflag) {
+					nulldir(&dx);
+					dx.atime = bp->date;
+					dx.mtime = bp->date;
+					if(dirwstat(file, &dx) < 0)
+						perror(file);
+				}
+				free(bp->member);
+				close(f);
+			}
+			free(bp);
+			if (count && ++i >= count)
+				break;
+		} else {
+			skip(&bar, bp->size);
+			free(bp);
+		}
+	}
+	close(fd);
+}
+void
+pcmd(char *arname, int count, char **files)
+{
+	int fd;
+	Armember *bp;
+
+	fd = openar(arname, OREAD, 0);
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	while(bp = getdir(&bar)) {
+		if(count == 0 || match(count, files)) {
+			if(vflag)
+				print("\n<%s>\n\n", file);
+			arcopy(&bar, 0, bp);
+			if (write(1, bp->member, bp->size) < 0)
+				wrerr();
+		} else
+			skip(&bar, bp->size);
+		free(bp);
+	}
+	close(fd);
+}
+void
+mcmd(char *arname, int count, char **files)
+{
+	int fd, i;
+	Arfile *ap;
+	Armember *bp;
+
+	if (count == 0)
+		return;
+	fd = openar(arname, ORDWR, 0);
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	astart = newtempfile(artemp);
+	amiddle = newtempfile(movtemp);
+	aend = 0;
+	ap = astart;
+	for (i = 0; bp = getdir(&bar); i++) {
+		if (bamatch(file, poname)) {
+			aend = newtempfile(tailtemp);
+			ap = aend;
+		}
+		if(match(count, files)) {
+			mesg('m', file);
+			scanobj(&bar, amiddle, bp->size);
+			arcopy(&bar, amiddle, bp);
+		} else
+			/*
+			 * pitch the symdef file if it is at the beginning
+			 * of the archive and we aren't inserting in front
+			 * of it (ap == astart).
+			 */
+		if (ap == astart && i == 0 && strcmp(file, symdef) == 0)
+			skip(&bar, bp->size);
+		else {
+			scanobj(&bar, ap, bp->size);
+			arcopy(&bar, ap, bp);
+		}
+	}
+	close(fd);
+	if (poname[0] && aend == 0)
+		fprint(2, "ar: %s not found - files moved to end.\n", poname);
+	install(arname, astart, amiddle, aend, 0);
+}
+void
+tcmd(char *arname, int count, char **files)
+{
+	int fd;
+	Armember *bp;
+	char name[ARNAMESIZE+1];
+
+	fd = openar(arname, OREAD, 0);
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	while(bp = getdir(&bar)) {
+		if(count == 0 || match(count, files)) {
+			if(vflag)
+				longt(bp);
+			trim(file, name, ARNAMESIZE);
+			Bprint(&bout, "%s\n", name);
+		}
+		skip(&bar, bp->size);
+		free(bp);
+	}
+	close(fd);
+}
+void
+qcmd(char *arname, int count, char **files)
+{
+	int fd, i;
+	Armember *bp;
+	Biobuf *bfile;
+
+	if(aflag || bflag) {
+		fprint(2, "ar: abi not allowed with q\n");
+		exits("error");
+	}
+	fd = openar(arname, ORDWR, 1);
+	if (fd < 0) {
+		if(!cflag)
+			fprint(2, "ar: creating %s\n", arname);
+		fd = arcreate(arname);
+	}
+	Binit(&bar, fd, OREAD);
+	Bseek(&bar,seek(fd,0,1), 1);
+	Bseek(&bar, 0, 2);
+	bp = newmember();
+	for(i=0; i<count && files[i]; i++) {
+		file = files[i];
+		files[i] = 0;
+		bfile = Bopen(file, OREAD);
+		if(!bfile)
+			fprint(2, "ar: %s cannot open\n", file);
+		else {
+			mesg('q', file);
+			armove(bfile, 0, bp);
+			if (!arwrite(fd, bp))
+				wrerr();
+			free(bp->member);
+			bp->member = 0;
+			Bterm(bfile);
+		}
+	}
+	free(bp);
+	close(fd);
+}
+
+/*
+ *	extract the symbol references from an object file
+ */
+void
+scanobj(Biobuf *b, Arfile *ap, int size)
+{
+	int obj;
+	long offset;
+	Dir *d;
+	static int lastobj = -1;
+
+	if (!allobj)			/* non-object file encountered */
+		return;
+	offset = Boffset(b);
+	obj = objtype(b, 0);
+	if (obj < 0) {			/* not an object file */
+		allobj = 0;
+		d = dirfstat(Bfildes(b));
+		if (d != nil && d->length == 0)
+			fprint(2, "ar: zero length file %s\n", file);
+		free(d);
+		Bseek(b, offset, 0);
+		return;
+	}
+	if (lastobj >= 0 && obj != lastobj) {
+		fprint(2, "ar: inconsistent object file %s\n", file);
+		allobj = 0;
+		Bseek(b, offset, 0);
+		return;
+	}
+	lastobj = obj;
+	if (!readar(b, obj, offset+size, 0)) {
+		fprint(2, "ar: invalid symbol reference in file %s\n", file);
+		allobj = 0;
+		Bseek(b, offset, 0);
+		return;
+	}
+	Bseek(b, offset, 0);
+	objtraverse(objsym, ap);
+}
+
+/*
+ *	add text and data symbols to the symbol list
+ */
+void
+objsym(Sym *s, void *p)
+{
+	int n;
+	Arsymref *as;
+	Arfile *ap;
+
+	if (s->type != 'T' &&  s->type != 'D')
+		return;
+	ap = (Arfile*)p;
+	as = (Arsymref*)armalloc(sizeof(Arsymref));
+	as->offset = ap->size;
+	n = strlen(s->name);
+	as->name = armalloc(n+1);
+	strcpy(as->name, s->name);
+	if(s->type == 'T' && duplicate(as->name)) {
+		dupfound = 1;
+		fprint(2, "duplicate text symbol: %s\n", as->name);
+		free(as->name);
+		free(as);
+		return;
+	}
+	as->type = s->type;
+	symdefsize += 4+(n+1)+1;
+	as->len = n;
+	as->next = ap->sym;
+	ap->sym = as;
+}
+
+/*
+ *	Check the symbol table for duplicate text symbols
+ */
+int
+duplicate(char *name)
+{
+	Hashchain *p;
+	char *cp;
+	int h;
+
+	h = 0;
+	for(cp = name; *cp; h += *cp++)
+		h *= 1119;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+
+	for(p = hash[h]; p; p = p->next)
+		if(strcmp(p->name, name) == 0)
+			return 1;
+	p = (Hashchain*) armalloc(sizeof(Hashchain));
+	p->next = hash[h];
+	p->name = name;
+	hash[h] = p;
+	return 0;
+}
+
+/*
+ *	open an archive and validate its header
+ */
+int
+openar(char *arname, int mode, int errok)
+{
+	int fd;
+	char mbuf[SARMAG];
+
+	fd = open(arname, mode);
+	if(fd >= 0){
+		if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) {
+			fprint(2, "ar: %s not in archive format\n", arname);
+			exits("error");
+		}
+	}else if(!errok){
+		fprint(2, "ar: cannot open %s: %r\n", arname);
+		exits("error");
+	}
+	return fd;
+}
+/*
+ *	create an archive and set its header
+ */
+int
+arcreate(char *arname)
+{
+	int fd;
+
+	fd = create(arname, OWRITE, 0664);
+	if(fd < 0){
+		fprint(2, "ar: cannot create %s: %r\n", arname);
+		exits("error");
+	}
+	if(write(fd, ARMAG, SARMAG) != SARMAG)
+		wrerr();
+	return fd;
+}
+/*
+ *		error handling
+ */
+void
+wrerr(void)
+{
+	perror("ar: write error");
+	exits("error");
+}
+
+void
+rderr(void)
+{
+	perror("ar: read error");
+	exits("error");
+}
+
+void
+phaseerr(int offset)
+{
+	fprint(2, "ar: phase error at offset %d\n", offset);
+	exits("error");
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: ar [%s][%s] archive files ...\n", opt, man);
+	exits("error");
+}
+/*
+ *	read the header for the next archive member
+ */
+Armember *
+getdir(Biobuf *b)
+{
+	Armember *bp;
+	char *cp;
+	static char name[ARNAMESIZE+1];
+
+	bp = newmember();
+	if(HEADER_IO(Bread, b, bp->hdr)) {
+		free(bp);
+		return 0;
+	}
+	if(strncmp(bp->hdr.fmag, ARFMAG, sizeof(bp->hdr.fmag)))
+		phaseerr(Boffset(b));
+	strncpy(name, bp->hdr.name, sizeof(bp->hdr.name));
+	cp = name+sizeof(name)-1;
+	while(*--cp==' ')
+		;
+	cp[1] = '\0';
+	file = name;
+	bp->date = atol(bp->hdr.date);
+	bp->size = atol(bp->hdr.size);
+	return bp;
+}
+/*
+ *	Copy the file referenced by fd to the temp file
+ */
+void
+armove(Biobuf *b, Arfile *ap, Armember *bp)
+{
+	char *cp;
+	Dir *d;
+
+	if ((d = dirfstat(Bfildes(b))) == nil) {
+		fprint(2, "ar: cannot stat %s: %r\n", file);
+		return;
+	}
+	trim(file, bp->hdr.name, sizeof(bp->hdr.name));
+	for (cp = strchr(bp->hdr.name, 0);		/* blank pad on right */
+		cp < bp->hdr.name+sizeof(bp->hdr.name); cp++)
+			*cp = ' ';
+	sprint(bp->hdr.date, "%-12ld", d->mtime);
+	sprint(bp->hdr.uid, "%-6d", 0);
+	sprint(bp->hdr.gid, "%-6d", 0);
+	sprint(bp->hdr.mode, "%-8lo", d->mode);
+	sprint(bp->hdr.size, "%-10lld", (vlong)d->length);
+	strncpy(bp->hdr.fmag, ARFMAG, 2);
+	bp->size = d->length;
+	bp->date = d->mtime;
+	arread(b, bp, bp->size);
+	if (d->length&0x01)
+		d->length++;
+	if (ap) {
+		arinsert(ap, bp);
+		ap->size += d->length+SAR_HDR;
+	}
+	free(d);
+}
+/*
+ *	Copy the archive member at the current offset into the temp file.
+ */
+void
+arcopy(Biobuf *b, Arfile *ap, Armember *bp)
+{
+	int n;
+
+	n = bp->size;
+	if (n & 01)
+		n++;
+	arread(b, bp, n);
+	if (ap) {
+		arinsert(ap, bp);
+		ap->size += n+SAR_HDR;
+	}
+}
+/*
+ *	Skip an archive member
+ */
+void
+skip(Biobuf *bp, long len)
+{
+	if (len & 01)
+		len++;
+	Bseek(bp, len, 1);
+}
+/*
+ *	Stream the three temp files to an archive
+ */
+void
+install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createflag)
+{
+	int fd;
+
+	if(allobj && dupfound) {
+		fprint(2, "%s not changed\n", arname);
+		return;
+	}
+
+	if(createflag)
+		fprint(2, "ar: creating %s\n", arname);
+	fd = arcreate(arname);
+
+	if(allobj)
+		rl(fd);
+
+	if (astart) {
+		arstream(fd, astart);
+		arfree(astart);
+	}
+	if (amiddle) {
+		arstream(fd, amiddle);
+		arfree(amiddle);
+	}
+	if (aend) {
+		arstream(fd, aend);
+		arfree(aend);
+	}
+	close(fd);
+}
+
+void
+rl(int fd)
+{
+
+	Biobuf b;
+	char *cp;
+	struct ar_hdr a;
+	long len;
+
+	Binit(&b, fd, OWRITE);
+	Bseek(&b,seek(fd,0,1), 0);
+
+	len = symdefsize;
+	if(len&01)
+		len++;
+	sprint(a.date, "%-12ld", time(0));
+	sprint(a.uid, "%-6d", 0);
+	sprint(a.gid, "%-6d", 0);
+	sprint(a.mode, "%-8o", 0644);
+	sprint(a.size, "%-10ld", len);
+	strncpy(a.fmag, ARFMAG, 2);
+	strcpy(a.name, symdef);
+	for (cp = strchr(a.name, 0);		/* blank pad on right */
+		cp < a.name+sizeof(a.name); cp++)
+			*cp = ' ';
+	if(HEADER_IO(Bwrite, &b, a))
+			wrerr();
+
+	len += Boffset(&b);
+	if (astart) {
+		wrsym(&b, len, astart->sym);
+		len += astart->size;
+	}
+	if(amiddle) {
+		wrsym(&b, len, amiddle->sym);
+		len += amiddle->size;
+	}
+	if(aend)
+		wrsym(&b, len, aend->sym);
+
+	if(symdefsize&0x01)
+		Bputc(&b, 0);
+	Bterm(&b);
+}
+/*
+ *	Write the defined symbols to the symdef file
+ */
+void
+wrsym(Biobuf *bp, int offset, Arsymref *as)
+{
+	int off;
+
+	while(as) {
+		Bputc(bp, as->type);
+		off = as->offset+offset;
+		Bputc(bp, off);
+		Bputc(bp, off>>8);
+		Bputc(bp, off>>16);
+		Bputc(bp, off>>24);
+		if (Bwrite(bp, as->name, as->len+1) != as->len+1)
+			wrerr();
+		as = as->next;
+	}
+}
+/*
+ *	Check if the archive member matches an entry on the command line.
+ */
+int
+match(int count, char **files)
+{
+	int i;
+	char name[ARNAMESIZE+1];
+
+	for(i=0; i<count; i++) {
+		if(files[i] == 0)
+			continue;
+		trim(files[i], name, ARNAMESIZE);
+		if(strncmp(name, file, ARNAMESIZE) == 0) {
+			file = files[i];
+			files[i] = 0;
+			return 1;
+		}
+	}
+	return 0;
+}
+/*
+ *	compare the current member to the name of the pivot member
+ */
+int
+bamatch(char *file, char *pivot)
+{
+	static int state = 0;
+
+	switch(state)
+	{
+	case 0:			/* looking for position file */
+		if (aflag) {
+			if (strncmp(file, pivot, ARNAMESIZE) == 0)
+				state = 1;
+		} else if (bflag) {
+			if (strncmp(file, pivot, ARNAMESIZE) == 0) {
+				state = 2;	/* found */
+				return 1;
+			}
+		}
+		break;
+	case 1:			/* found - after previous file */
+		state = 2;
+		return 1;
+	case 2:			/* already found position file */
+		break;
+	}
+	return 0;
+}
+/*
+ *	output a message, if 'v' option was specified
+ */
+void
+mesg(int c, char *file)
+{
+
+	if(vflag)
+		Bprint(&bout, "%c - %s\n", c, file);
+}
+/*
+ *	isolate file name by stripping leading directories and trailing slashes
+ */
+void
+trim(char *s, char *buf, int n)
+{
+	char *p;
+
+	for(;;) {
+		p = strrchr(s, '/');
+		if (!p) {		/* no slash in name */
+			strncpy(buf, s, n);
+			return;
+		}
+		if (p[1] != 0) {	/* p+1 is first char of file name */
+			strncpy(buf, p+1, n);
+			return;
+		}
+		*p = 0;			/* strip trailing slash */
+	}
+}
+/*
+ *	utilities for printing long form of 't' command
+ */
+#define	SUID	04000
+#define	SGID	02000
+#define	ROWN	0400
+#define	WOWN	0200
+#define	XOWN	0100
+#define	RGRP	040
+#define	WGRP	020
+#define	XGRP	010
+#define	ROTH	04
+#define	WOTH	02
+#define	XOTH	01
+#define	STXT	01000
+
+void
+longt(Armember *bp)
+{
+	char *cp;
+
+	pmode(strtoul(bp->hdr.mode, 0, 8));
+	Bprint(&bout, "%3ld/%1ld", atol(bp->hdr.uid), atol(bp->hdr.gid));
+	Bprint(&bout, "%7ld", bp->size);
+	cp = myctime(bp->date);
+	Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
+}
+
+int	m1[] = { 1, ROWN, 'r', '-' };
+int	m2[] = { 1, WOWN, 'w', '-' };
+int	m3[] = { 2, SUID, 's', XOWN, 'x', '-' };
+int	m4[] = { 1, RGRP, 'r', '-' };
+int	m5[] = { 1, WGRP, 'w', '-' };
+int	m6[] = { 2, SGID, 's', XGRP, 'x', '-' };
+int	m7[] = { 1, ROTH, 'r', '-' };
+int	m8[] = { 1, WOTH, 'w', '-' };
+int	m9[] = { 2, STXT, 't', XOTH, 'x', '-' };
+
+int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
+
+void
+pmode(long mode)
+{
+	int **mp;
+
+	for(mp = &m[0]; mp < &m[9];)
+		ar_select(*mp++, mode);
+}
+
+void
+ar_select(int *ap, long mode)
+{
+	int n;
+
+	n = *ap++;
+	while(--n>=0 && (mode&*ap++)==0)
+		ap++;
+	Bputc(&bout, *ap);
+}
+/*
+ *	Temp file I/O subsystem.  We attempt to cache all three temp files in
+ *	core.  When we run out of memory we spill to disk.
+ *	The I/O model assumes that temp files:
+ *		1) are only written on the end
+ *		2) are only read from the beginning
+ *		3) are only read after all writing is complete.
+ *	The architecture uses one control block per temp file.  Each control
+ *	block anchors a chain of buffers, each containing an archive member.
+ */
+Arfile *
+newtempfile(char *name)		/* allocate a file control block */
+{
+	Arfile *ap;
+
+	ap = (Arfile *) armalloc(sizeof(Arfile));
+	ap->fname = name;
+	return ap;
+}
+
+Armember *
+newmember(void)			/* allocate a member buffer */
+{
+	return (Armember *)armalloc(sizeof(Armember));
+}
+
+void
+arread(Biobuf *b, Armember *bp, int n)	/* read an image into a member buffer */
+{
+	int i;
+
+	bp->member = armalloc(n);
+	i = Bread(b, bp->member, n);
+	if (i < 0) {
+		free(bp->member);
+		bp->member = 0;
+		rderr();
+	}
+}
+/*
+ * insert a member buffer into the member chain
+ */
+void
+arinsert(Arfile *ap, Armember *bp)
+{
+	bp->next = 0;
+	if (!ap->tail)
+		ap->head = bp;
+	else
+		ap->tail->next = bp;
+	ap->tail = bp;
+}
+/*
+ *	stream the members in a temp file to the file referenced by 'fd'.
+ */
+void
+arstream(int fd, Arfile *ap)
+{
+	Armember *bp;
+	int i;
+	char buf[8192];
+
+	if (ap->paged) {		/* copy from disk */
+		seek(ap->fd, 0, 0);
+		for (;;) {
+			i = read(ap->fd, buf, sizeof(buf));
+			if (i < 0)
+				rderr();
+			if (i == 0)
+				break;
+			if (write(fd, buf, i) != i)
+				wrerr();
+		}
+		close(ap->fd);
+		ap->paged = 0;
+	}
+		/* dump the in-core buffers */
+	for (bp = ap->head; bp; bp = bp->next) {
+		if (!arwrite(fd, bp))
+			wrerr();
+	}
+}
+/*
+ *	write a member to 'fd'.
+ */
+int
+arwrite(int fd, Armember *bp)
+{
+	int len;
+
+	if(HEADER_IO(write, fd, bp->hdr))
+		return 0;
+	len = bp->size;
+	if (len & 01)
+		len++;
+	if (write(fd, bp->member, len) != len)
+		return 0;
+	return 1;
+}
+/*
+ *	Spill a member to a disk copy of a temp file
+ */
+int
+page(Arfile *ap)
+{
+	Armember *bp;
+
+	bp = ap->head;
+	if (!ap->paged) {		/* not yet paged - create file */
+		ap->fname = mktemp(ap->fname);
+		ap->fd = create(ap->fname, ORDWR|ORCLOSE, 0600);
+		if (ap->fd < 0) {
+			fprint(2,"ar: can't create temp file\n");
+			return 0;
+		}
+		ap->paged = 1;
+	}
+	if (!arwrite(ap->fd, bp))	/* write member and free buffer block */
+		return 0;
+	ap->head = bp->next;
+	if (ap->tail == bp)
+		ap->tail = bp->next;
+	free(bp->member);
+	free(bp);
+	return 1;
+}
+/*
+ *	try to reclaim space by paging.  we try to spill the start, middle,
+ *	and end files, in that order.  there is no particular reason for the
+ *	ordering.
+ */
+int
+getspace(void)
+{
+	if (astart && astart->head && page(astart))
+			return 1;
+	if (amiddle && amiddle->head && page(amiddle))
+			return 1;
+	if (aend && aend->head && page(aend))
+			return 1;
+	return 0;
+}
+
+void
+arfree(Arfile *ap)		/* free a member buffer */
+{
+	Armember *bp, *next;
+
+	for (bp = ap->head; bp; bp = next) {
+		next = bp->next;
+		if (bp->member)
+			free(bp->member);
+		free(bp);
+	}
+	free(ap);
+}
+/*
+ *	allocate space for a control block or member buffer.  if the malloc
+ *	fails we try to reclaim space by spilling previously allocated
+ *	member buffers.
+ */
+char *
+armalloc(int n)
+{
+	char *cp;
+
+	do {
+		cp = malloc(n);
+		if (cp) {
+			memset(cp, 0, n);
+			return cp;
+		}
+	} while (getspace());
+	fprint(2, "ar: out of memory\n");
+	exits("malloc");
+	return 0;
+}
+/*
+ * Nt stub for GetNameFromID() in lib9\dirstat-Nt.c.
+ * Other architectures never call it.
+ */
+char *
+GetNameFromID(int id)
+{
+	USED(id);
+ 	return "unknown";
+}
--- /dev/null
+++ b/utils/iar/mkfile
@@ -1,0 +1,18 @@
+<../../mkconfig
+
+TARG=iar	# inferno archiver
+
+OFILES=	ar.$O\
+	$TARGMODEL.$O\
+
+HFILES=	../include/a.out.h\
+	../../include/bio.h\
+	../include/mach.h\
+
+LIBS=mach bio 9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
--- /dev/null
+++ b/utils/idea/NOTICE
@@ -1,0 +1,27 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 2002 Vita Nuova Holdings Limited (www.vitanuova.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/idea/idea.c
@@ -1,0 +1,254 @@
+/*
+ * Copyright © 2002 Vita Nuova Holdings Limited.  All rights reserved.
+ */
+#include <lib9.h>
+#include <bio.h>
+
+/* CBC, ECB, integrate, SSL */
+
+#define KEYLEN	52
+
+#define	MODA	0x10000
+#define	MODM	0x10001
+#define	MASKA	(MODA-1)
+
+#define 	OP1(x, y)		((x) ^ (y))
+#define	OP2(x, y)		(((x) + (y)) & MASKA)
+#define 	OP3(x, y)		mod(x, y)
+
+#define	OP2INV(x)	(-(x))
+#define	OP3INV(x)	inv(x)
+
+#define BIGEND(k, i)	((k[i]<<8)|k[i+1])
+#define MSB(x)		((x)>>8)
+#define LSB(x)		((x)&0xff)
+
+static ushort
+mod(ushort x, ushort y)
+{
+	ushort q, r;
+	uint z;
+
+	if (x == 0)
+		return 1-y;
+	if (y == 0)
+		return 1-x;
+	z = (uint)x*(uint)y;
+	q = z >> 16;
+	r = z & MASKA;
+	return r-q+(r<q);
+}
+
+static ushort 
+inv(ushort x)
+{
+	int q, r0, r1, r2, v0, v1, v2;
+
+	if (x <= 1)
+		return x;
+	r0 = MODM;
+	r1 = x;
+	v0 = 0;
+	v1 = 1;
+	while (r1 != 0) {
+		q = r0/r1;
+		r2 = r0-q*r1;
+		v2 = v0-q*v1;
+		r0 = r1;
+		r1 = r2;
+		v0 = v1;
+		v1 = v2;
+	}
+	if (v0 < 0)
+		v0 += MODM;
+	return v0 & MASKA;
+}
+
+static void
+idea_key_setup_decrypt(ushort ek[KEYLEN], ushort dk[KEYLEN])
+{
+	int i;
+
+	for (i = 0; i < 54; i += 6) {
+		dk[i] = OP3INV(ek[48-i]);
+		dk[i+1] = OP2INV(ek[50-i]);
+		dk[i+2] = OP2INV(ek[49-i]);
+		dk[i+3] = OP3INV(ek[51-i]);
+		if (i < 48) {
+			dk[i+4] = ek[46-i];
+			dk[i+5] = ek[47-i];
+		}
+	}
+}
+
+void
+idea_key_setup(uchar key[16], ushort ek[2*KEYLEN])
+{
+	int i, j;
+	ushort tmp, *e = ek;
+
+	for (i = 0; i < 8; i++)
+		ek[i] = BIGEND(key, 2*i);
+	for (i = 8, j = 1; i < KEYLEN; i++, j++) {
+		ek[i] = (e[j&7]<<9)|(e[(j+1)&7]>>7);
+		if (((i+1) & 7) == 0)
+			e += 8;
+	}
+	tmp = ek[49];
+	ek[49] = ek[50];
+	ek[50] = tmp;
+	idea_key_setup_decrypt(ek, &ek[KEYLEN]);
+}
+
+void
+idea_cipher(ushort key[2*KEYLEN], uchar text[8], int decrypting)
+{
+	int i;
+	ushort *k;
+	ushort x[4];
+	ushort tmp, yout, zout;
+
+	k = decrypting ? &key[KEYLEN] : key;
+	for (i = 0; i < 4; i++)
+		x[i] = BIGEND(text, 2*i);
+	for (i = 0; i < 17; i++) {
+		if (!(i&1)) {		/* odd round */
+			x[0] = OP3(x[0], k[3*i]);
+			tmp = OP2(x[2], k[3*i+2]);
+			x[2] = OP2(x[1], k[3*i+1]);
+			x[3] = OP3(x[3], k[3*i+3]);
+			x[1] = tmp;
+		}
+		else {
+			tmp = OP3(k[3*i+1], OP1(x[0], x[1]));
+			yout = OP3(OP2(tmp, OP1(x[2], x[3])), k[3*i+2]);
+			zout = OP2(tmp, yout);
+			x[0] = OP1(x[0], yout);
+			x[1] = OP1(x[1], yout);
+			x[2] = OP1(x[2], zout);
+			x[3] = OP1(x[3], zout);
+		}
+	}
+	for (i = 0; i < 4; i++) {
+		text[2*i] = MSB(x[i]); 
+		text[2*i+1] = LSB(x[i]);
+	}
+}
+
+static void
+decerr(char *s)
+{
+	fprint(2, "decrypt error: %s (wrong password ?)\n", s);
+	exits("decrypt");
+}
+
+void
+main(int argc, char **argv)
+{
+/*
+	uchar key[] = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+			        0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 };
+	uchar plain[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03 };
+	uchar cipher[] = { 0x11, 0xFB, 0xED, 0x2B, 0x01, 0x98, 0x6D, 0xE5 };
+	uchar tmp[8];
+*/
+	ushort edkey[2*KEYLEN];
+	uchar obuf[8], buf[8], key[16];
+	long i, m, n, om;
+	int dec, stdin = 0, stdout = 1;
+	Biobuf *bin, *bout;
+
+/*
+	memcpy(tmp, plain, 8);
+	idea_key_setup(key, edkey);
+	idea_cipher(edkey, tmp, 0);
+	if (memcmp(tmp, cipher, 8)) {
+		print("encrypt wrong\n");
+		exits("");
+	}
+	idea_cipher(edkey, tmp, 1);
+	if (memcmp(tmp, plain, 8)) {
+		print("decrypt wrong\n");
+		exits("");
+	}
+*/
+
+	if((argc != 3 && argc != 4) || (strcmp(argv[1], "-e") != 0 && strcmp(argv[1], "-d") != 0) || strlen(argv[2]) != 16){
+		fprint(2, "usage: idea -[e | d] <16 char key> [inputfile]\n");
+		exits("usage");
+	}
+	dec = strcmp(argv[1], "-d") == 0;
+	if(argc == 4){
+		char s[128];
+
+		stdin = open(argv[3], OREAD);
+		if(stdin < 0){
+			fprint(2, "cannot open %s\n", argv[3]);
+			exits("");
+		}
+		strcpy(s, argv[3]);
+		if(dec){
+			if(strcmp(s+strlen(s)-3, ".id") != 0){
+				fprint(2, "input file not a .id file\n");
+				exits("");
+			}
+			s[strlen(s)-3] = '\0';
+		}
+		else
+			strcat(s, ".id");
+		stdout = create(s, OWRITE, 0666);
+		if(stdout < 0){
+			fprint(2, "cannot create %s\n", s);
+			exits("");
+		}
+	}
+	for(i = 0; i < 16; i++)
+		key[i] = argv[2][i];
+	idea_key_setup(key, edkey);
+	m = om = 0;
+	bin = (Biobuf*)malloc(sizeof(Biobuf));
+	bout = (Biobuf*)malloc(sizeof(Biobuf));
+	Binit(bin, stdin, OREAD);
+	Binit(bout, stdout, OWRITE);
+	for(;;){
+		n = Bread(bin, &buf[m], 8-m);
+		if(n <= 0)
+			break;
+		m += n;
+		if(m == 8){
+			idea_cipher(edkey, buf, dec);
+			if(dec){	/* leave last block around */
+				if(om > 0)
+					Bwrite(bout, obuf, 8);
+				memcpy(obuf, buf, 8);
+				om = 8;
+			}
+			else
+				Bwrite(bout, buf, 8);
+			m = 0;
+		}
+	}
+	if(dec){
+		if(om != 8)
+			decerr("no last block");
+		if(m != 0)
+			decerr("last block not 8 bytes long");
+		m = obuf[7];
+		if(m < 0 || m > 7)
+			decerr("bad modulus");
+		for(i = m; i < 8-1; i++)
+			if(obuf[i] != 0)
+				decerr("byte not 0");
+		Bwrite(bout, obuf, m);
+	}
+	else{
+		for(i = m; i < 8; i++)
+			buf[i] = 0;
+		buf[7] = m;
+		idea_cipher(edkey, buf, dec);
+		Bwrite(bout, buf, 8);
+	}
+	Bflush(bout);
+	Bterm(bin);
+	Bterm(bout);
+}	
--- /dev/null
+++ b/utils/idea/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=idea
+
+OFILES=	idea.$O\
+
+HFILES=
+
+LIBS=bio 9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/include/a.out.h
@@ -1,0 +1,47 @@
+typedef	struct	Exec	Exec;
+struct	Exec
+{
+	long	magic;		/* magic number */
+	long	text;	 	/* size of text segment */
+	long	data;	 	/* size of initialized data */
+	long	bss;	  	/* size of uninitialized data */
+	long	syms;	 	/* size of symbol table */
+	long	entry;	 	/* entry point */
+	long	spsz;		/* size of pc/sp offset table */
+	long	pcsz;		/* size of pc/line number table */
+};
+
+#define HDR_MAGIC	0x00008000		/* header expansion */
+
+#define	_MAGIC(f, b)	((f)|((((4*(b))+0)*(b))+7))
+#define	A_MAGIC		_MAGIC(0, 8)		/* 68020 */
+#define	I_MAGIC		_MAGIC(0, 11)		/* intel 386 */
+#define	J_MAGIC		_MAGIC(0, 12)		/* intel 960 (retired) */
+#define	K_MAGIC		_MAGIC(0, 13)		/* sparc */
+#define	V_MAGIC		_MAGIC(0, 16)		/* mips 3000 BE */
+#define	X_MAGIC		_MAGIC(0, 17)		/* att dsp 3210 (retired) */
+#define	M_MAGIC		_MAGIC(0, 18)		/* mips 4000 BE */
+#define	D_MAGIC		_MAGIC(0, 19)		/* amd 29000 (retired) */
+#define	E_MAGIC		_MAGIC(0, 20)		/* arm */
+#define	Q_MAGIC		_MAGIC(0, 21)		/* powerpc */
+#define	N_MAGIC		_MAGIC(0, 22)		/* mips 4000 LE */
+#define	L_MAGIC		_MAGIC(0, 23)		/* dec alpha (retired) */
+#define	P_MAGIC		_MAGIC(0, 24)		/* mips 3000 LE */
+#define	U_MAGIC		_MAGIC(0, 25)		/* sparc64 (retired) */
+#define	S_MAGIC		_MAGIC(HDR_MAGIC, 26)	/* amd64 */
+#define	T_MAGIC		_MAGIC(HDR_MAGIC, 27)	/* powerpc64 */
+#define	R_MAGIC		_MAGIC(HDR_MAGIC, 28)	/* arm64 */
+
+#define	MIN_MAGIC	8
+#define	MAX_MAGIC	28			/* <= 90 */
+
+#define	DYN_MAGIC	0x80000000		/* dlm */
+
+typedef	struct	Sym	Sym;
+struct	Sym
+{
+	vlong	value;
+	uint	sig;
+	char	type;
+	char	*name;
+};
--- /dev/null
+++ b/utils/include/ar.h
@@ -1,0 +1,17 @@
+#define	ARMAG	"!<arch>\n"
+#define	SARMAG	8
+
+#define	ARFMAG	"`\n"
+#define SARNAME	16
+
+struct	ar_hdr
+{
+	char	name[SARNAME];
+	char	date[12];
+	char	uid[6];
+	char	gid[6];
+	char	mode[8];
+	char	size[10];
+	char	fmag[2];
+};
+#define	SAR_HDR	(SARNAME+44)
--- /dev/null
+++ b/utils/include/mach.h
@@ -1,0 +1,327 @@
+/*
+ *	Architecture-dependent application data
+ */
+#include "a.out.h"
+#pragma	src	"/sys/src/libmach"
+#pragma	lib	"libmach.a"
+/*
+ *	Supported architectures:
+ *		mips,
+ *		68020,
+ *		i386,
+ *		amd64,
+ *		sparc,
+ *		mips2 (R4000)
+ *		arm
+ *		powerpc,
+ *		powerpc64
+ *		arm64
+ */
+enum
+{
+	MMIPS,			/* machine types */
+	MSPARC,
+	M68020,
+	MI386,
+	MI960,			/* retired */
+	M3210,			/* retired */
+	MMIPS2,
+	NMIPS2,
+	M29000,			/* retired */
+	MARM,
+	MPOWER,
+	MALPHA,			/* retired */
+	NMIPS,
+	MSPARC64,		/* retired */
+	MAMD64,
+	MPOWER64,
+	MARM64,
+				/* types of executables */
+	FNONE = 0,		/* unidentified */
+	FMIPS,			/* v.out */
+	FMIPSB,			/* mips bootable */
+	FSPARC,			/* k.out */
+	FSPARCB,		/* Sparc bootable */
+	F68020,			/* 2.out */
+	F68020B,		/* 68020 bootable */
+	FNEXTB,			/* Next bootable */
+	FI386,			/* 8.out */
+	FI386B,			/* I386 bootable */
+	FI960,			/* retired */
+	FI960B,			/* retired */
+	F3210,			/* retired */
+	FMIPS2BE,		/* 4.out */
+	F29000,			/* retired */
+	FARM,			/* 5.out */
+	FARMB,			/* ARM bootable */
+	FPOWER,			/* q.out */
+	FPOWERB,		/* power pc bootable */
+	FMIPS2LE,		/* 0.out */
+	FALPHA,			/* retired */
+	FALPHAB,		/* retired DEC Alpha bootable */
+	FMIPSLE,		/* 3k little endian */
+	FSPARC64,		/* retired */
+	FAMD64,			/* 6.out */
+	FAMD64B,		/* 6.out bootable */
+	FPOWER64,		/* 9.out */
+	FPOWER64B,		/* 9.out bootable */
+	FARM64,			/* arm64 */
+	FARM64B,		/* arm64 bootable */
+
+	ANONE = 0,		/* dissembler types */
+	AMIPS,
+	AMIPSCO,		/* native mips */
+	ASPARC,
+	ASUNSPARC,		/* native sun */
+	A68020,
+	AI386,
+	AI8086,			/* oh god */
+	AI960,			/* retired */
+	A29000,			/* retired */
+	AARM,
+	APOWER,
+	AALPHA,			/* retired */
+	ASPARC64,		/* retired */
+	AAMD64,
+	APOWER64,
+	AARM64,
+				/* object file types */
+	Obj68020 = 0,		/* .2 */
+	ObjSparc,		/* .k */
+	ObjMips,		/* .v */
+	Obj386,			/* .8 */
+	Obj960,			/* retired */
+	Obj3210,		/* retired */
+	ObjMips2,		/* .4 */
+	Obj29000,		/* retired */
+	ObjArm,			/* .5 */
+	ObjPower,		/* .q */
+	ObjMips2le,		/* .0 */
+	ObjAlpha,		/* retired */
+	ObjSparc64,		/* retired */
+	ObjAmd64,		/* .6 */
+	ObjSpim,		/* .0 */
+	ObjPower64,		/* .9 */
+	ObjArm64,		/* .4? */
+	Maxobjtype,
+
+	CNONE  = 0,		/* symbol table classes */
+	CAUTO,
+	CPARAM,
+	CSTAB,
+	CTEXT,
+	CDATA,
+	CANY,			/* to look for any class */
+};
+
+typedef	struct	Map	Map;
+typedef struct	Symbol	Symbol;
+typedef	struct	Reglist	Reglist;
+typedef struct	segment	segment;
+
+typedef	struct	Mach	Mach;
+typedef	struct	Machdata Machdata;
+typedef	int	(*Rsegio)(segment*, ulong, long, char*, int);
+
+/*
+ * 	Structure to map a segment to a position in a file
+ */
+struct Map {
+	int	nsegs;			/* number of segments */
+	struct segment {		/* per-segment map */
+		char	*name;		/* the segment name */
+		int	fd;		/* file descriptor */
+		int	inuse;		/* in use - not in use */
+		int	cache;		/* should cache reads? */
+		uvlong	b;		/* base */
+		uvlong	e;		/* end */
+		vlong	f;		/* offset within file */
+		Rsegio	mget;	/* special get if not nil */
+		Rsegio	mput;	/* special put if not nil */
+	} seg[1];			/* actually n of these */
+};
+
+/*
+ *	Internal structure describing a symbol table entry
+ */
+struct Symbol {
+	void 	*handle;		/* used internally - owning func */
+	struct {
+		char	*name;
+		vlong	value;		/* address or stack offset */
+		char	type;		/* as in a.out.h */
+		char	class;		/* as above */
+		int	index;		/* in findlocal, globalsym, textsym */
+	};
+};
+
+/*
+ *	machine register description
+ */
+struct Reglist {
+	char	*rname;			/* register name */
+	short	roffs;			/* offset in u-block */
+	char	rflags;			/* INTEGER/FLOAT, WRITABLE */
+	char	rformat;		/* print format: 'x', 'X', 'f', '8', '3', 'Y', 'W' */
+};
+
+enum {					/* bits in rflags field */
+	RINT	= (0<<0),
+	RFLT	= (1<<0),
+	RRDONLY	= (1<<1)
+};
+
+/*
+ *	Machine-dependent data is stored in two structures:
+ *		Mach  - miscellaneous general parameters
+ *		Machdata - jump vector of service functions used by debuggers
+ *
+ *	Mach is defined in ?.c and set in executable.c
+ *
+ *	Machdata is defined in ?db.c
+ *		and set in the debugger startup.
+ */
+struct Mach{
+	char	*name;
+	int	mtype;			/* machine type code */
+	Reglist *reglist;		/* register set */
+	ulong	regsize;		/* sizeof registers in bytes */
+	ulong	fpregsize;		/* sizeof fp registers in bytes */
+	char	*pc;			/* pc name */
+	char	*sp;			/* sp name */
+	char	*link;			/* link register name */
+	char	*sbreg;			/* static base register name */
+	uvlong	sb;			/* static base register value */
+	int	pgsize;			/* page size */
+	uvlong	kbase;			/* kernel base address */
+	uvlong	ktmask;			/* ktzero = kbase & ~ktmask */
+	uvlong	utop;			/* user stack top */
+	int	pcquant;		/* quantization of pc */
+	int	szaddr;			/* sizeof(void*) */
+	int	szreg;			/* sizeof(register) */
+	int	szfloat;		/* sizeof(float) */
+	int	szdouble;		/* sizeof(double) */
+};
+
+extern	Mach	*mach;			/* Current machine */
+
+typedef uvlong	(*Rgetter)(Map*, char*);
+typedef	void	(*Tracer)(Map*, uvlong, uvlong, Symbol*);
+
+struct	Machdata {		/* Machine-dependent debugger support */
+	uchar	bpinst[4];			/* break point instr. */
+	short	bpsize;				/* size of break point instr. */
+
+	ushort	(*swab)(ushort);		/* ushort to local byte order */
+	ulong	(*swal)(ulong);			/* ulong to local byte order */
+	uvlong	(*swav)(uvlong);		/* uvlong to local byte order */
+	int	(*ctrace)(Map*, uvlong, uvlong, uvlong, Tracer); /* C traceback */
+	uvlong	(*findframe)(Map*, uvlong, uvlong, uvlong, uvlong);/* frame finder */
+	char*	(*excep)(Map*, Rgetter);	/* last exception */
+	ulong	(*bpfix)(uvlong);		/* breakpoint fixup */
+	int	(*sftos)(char*, int, void*);	/* single precision float */
+	int	(*dftos)(char*, int, void*);	/* double precision float */
+	int	(*foll)(Map*, uvlong, Rgetter, uvlong*);/* follow set */
+	int	(*das)(Map*, uvlong, char, char*, int);	/* symbolic disassembly */
+	int	(*hexinst)(Map*, uvlong, char*, int); 	/* hex disassembly */
+	int	(*instsize)(Map*, uvlong);	/* instruction size */
+};
+
+/*
+ *	Common a.out header describing all architectures
+ */
+typedef struct Fhdr
+{
+	char	*name;		/* identifier of executable */
+	uchar	type;		/* file type - see codes above */
+	uchar	hdrsz;		/* header size */
+	uchar	_magic;		/* _MAGIC() magic */
+	uchar	spare;
+	long	magic;		/* magic number */
+	uvlong	txtaddr;	/* text address */
+	vlong	txtoff;		/* start of text in file */
+	uvlong	dataddr;	/* start of data segment */
+	vlong	datoff;		/* offset to data seg in file */
+	vlong	symoff;		/* offset of symbol table in file */
+	uvlong	entry;		/* entry point */
+	vlong	sppcoff;	/* offset of sp-pc table in file */
+	vlong	lnpcoff;	/* offset of line number-pc table in file */
+	long	txtsz;		/* text size */
+	long	datsz;		/* size of data seg */
+	long	bsssz;		/* size of bss */
+	long	symsz;		/* size of symbol table */
+	long	sppcsz;		/* size of sp-pc table */
+	long	lnpcsz;		/* size of line number-pc table */
+} Fhdr;
+
+extern	int	asstype;	/* dissembler type - machdata.c */
+extern	Machdata *machdata;	/* jump vector - machdata.c */
+
+Map*		attachproc(int, int, int, Fhdr*);
+Map*		attachremt(int, Fhdr*);
+int		beieee80ftos(char*, int, void*);
+int		beieeesftos(char*, int, void*);
+int		beieeedftos(char*, int, void*);
+ushort		beswab(ushort);
+ulong		beswal(ulong);
+uvlong		beswav(uvlong);
+uvlong		ciscframe(Map*, uvlong, uvlong, uvlong, uvlong);
+int		cisctrace(Map*, uvlong, uvlong, uvlong, Tracer);
+int		crackhdr(int fd, Fhdr*);
+uvlong		file2pc(char*, long);
+int		fileelem(Sym**, uchar *, char*, int);
+long		fileline(char*, int, uvlong);
+int		filesym(int, char*, int);
+int		findlocal(Symbol*, char*, Symbol*);
+int		findseg(Map*, char*);
+int		findsym(uvlong, int, Symbol *);
+int		fnbound(uvlong, uvlong*);
+int		fpformat(Map*, Reglist*, char*, int, int);
+int		get1(Map*, uvlong, uchar*, int);
+int		get2(Map*, uvlong, ushort*);
+int		get4(Map*, uvlong, ulong*);
+int		get8(Map*, uvlong, uvlong*);
+int		geta(Map*, uvlong, uvlong*);
+int		getauto(Symbol*, int, int, Symbol*);
+Sym*		getsym(int);
+int		globalsym(Symbol *, int);
+char*		_hexify(char*, ulong, int);
+int		ieeesftos(char*, int, ulong);
+int		ieeedftos(char*, int, ulong, ulong);
+int		isar(Biobuf*);
+int		leieee80ftos(char*, int, void*);
+int		leieeesftos(char*, int, void*);
+int		leieeedftos(char*, int, void*);
+ushort		leswab(ushort);
+ulong		leswal(ulong);
+uvlong		leswav(uvlong);
+uvlong		line2addr(long, uvlong, uvlong);
+Map*		loadmap(Map*, int, Fhdr*);
+int		localaddr(Map*, char*, char*, uvlong*, Rgetter);
+int		localsym(Symbol*, int);
+int		lookup(char*, char*, Symbol*);
+void		machbytype(int);
+int		machbyname(char*);
+int		nextar(Biobuf*, int, char*);
+Map*		newmap(Map*, int);
+void		objtraverse(void(*)(Sym*, void*), void*);
+int		objtype(Biobuf*, char**);
+uvlong		pc2sp(uvlong);
+long		pc2line(uvlong);
+int		put1(Map*, uvlong, uchar*, int);
+int		put2(Map*, uvlong, ushort);
+int		put4(Map*, uvlong, ulong);
+int		put8(Map*, uvlong, uvlong);
+int		puta(Map*, uvlong, uvlong);
+int		readar(Biobuf*, int, vlong, int);
+int		readobj(Biobuf*, int);
+uvlong		riscframe(Map*, uvlong, uvlong, uvlong, uvlong);
+int		risctrace(Map*, uvlong, uvlong, uvlong, Tracer);
+int		setmap(Map*, int, uvlong, uvlong, vlong, char*);
+void		setmapio(Map*, int, Rsegio, Rsegio);
+Sym*		symbase(long*);
+int		syminit(int, Fhdr*);
+int		symoff(char*, int, uvlong, int);
+void		textseg(uvlong, Fhdr*);
+int		textsym(Symbol*, int);
+void		unusemap(Map*, int);
--- /dev/null
+++ b/utils/include/regexp.h
@@ -1,0 +1,66 @@
+#pragma	src	"/usr/inferno/libregexp"
+#pragma	lib	"libregexp.a"
+
+typedef struct Resub		Resub;
+typedef struct Reclass		Reclass;
+typedef struct Reinst		Reinst;
+typedef struct Reprog		Reprog;
+
+/*
+ *	Sub expression matches
+ */
+struct Resub{
+	union
+	{
+		char *sp;
+		Rune *rsp;
+	}s;
+	union
+	{
+		char *ep;
+		Rune *rep;
+	}e;
+};
+
+/*
+ *	character class, each pair of rune's defines a range
+ */
+struct Reclass{
+	Rune	*end;
+	Rune	spans[64];
+};
+
+/*
+ *	Machine instructions
+ */
+struct Reinst{
+	int	type;
+	union	{
+		Reclass	*cp;		/* class pointer */
+		Rune	r;		/* character */
+		int	subid;		/* sub-expression id for RBRA and LBRA */
+		Reinst	*right;		/* right child of OR */
+	}u1;
+	union {	/* regexp relies on these two being in the same union */
+		Reinst *left;		/* left child of OR */
+		Reinst *next;		/* next instruction for CAT & LBRA */
+	}u2;
+};
+
+/*
+ *	Reprogram definition
+ */
+struct Reprog{
+	Reinst	*startinst;	/* start pc */
+	Reclass	class[16];	/* .data */
+	Reinst	firstinst[5];	/* .text */
+};
+
+extern Reprog	*regcomp(char*);
+extern Reprog	*regcomplit(char*);
+extern Reprog	*regcompnl(char*);
+extern void	regerror(char*);
+extern int	regexec(Reprog*, char*, Resub*, int);
+extern void	regsub(char*, char*, Resub*, int);
+extern int	rregexec(Reprog*, Rune*, Resub*, int);
+extern void	rregsub(Rune*, Rune*, Resub*, int);
--- /dev/null
+++ b/utils/iyacc/mkfile
@@ -1,0 +1,20 @@
+<../../mkconfig
+
+TARG=iyacc
+
+OFILES=	yacc.$O\
+
+HFILES= ../../include/bio.h\
+
+LIBS=bio 9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include '-DROOT="'$ROOT'"' '-DPARSER="yaccpar"' '-DPARSERS="yaccpar"'
+
+install:V:	$ROOT/utils/lib/yaccpar
+
+$ROOT/utils/lib/yaccpar:	yaccpar
+	rm -f $target && cp $prereq $target
--- /dev/null
+++ b/utils/iyacc/yacc.c
@@ -1,0 +1,2933 @@
+#include <lib9.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define	Bungetrune	Bungetc		/* ok for now. */
+
+/*
+ * all these are 32 bit
+ */
+#define TBITSET		((32+NTERMS)/32)	/* BOTCH?? +31 */
+#define BIT(a,i)	((a)[(i)>>5] & (1<<((i)&037)))
+#define SETBIT(a,i)	((a)[(i)>>5] |= (1<<((i)&037)))
+#define NWORDS(n)	(((n)+32)/32)
+
+#ifndef PARSER
+#define PARSER		"yaccpar"
+#define PARSERS		"yaccpars"
+#endif
+#define TEMPNAME	"y.tmp.XXXXXX"
+#define ACTNAME		"y.acts.XXXXXX"
+#define OFILE		"tab.c"
+#define FILEU		"output"
+#define FILED		"tab.h"
+#define FILEDEBUG	"debug"
+
+enum
+{
+/*
+ * the following are adjustable
+ * according to memory size
+ */
+	ACTSIZE		= 30000,
+	MEMSIZE		= 30000,
+	NSTATES		= 2000,
+	NTERMS		= 255,
+	NPROD		= 800,
+	NNONTERM	= 300,
+	TEMPSIZE	= 2000,
+	CNAMSZ		= 5000,
+	LSETSIZE	= 2400,
+	WSETSIZE	= 350,
+
+	NAMESIZE	= 50,
+	NTYPES		= 63,
+	ISIZE		= 400,
+
+	PRIVATE		= 0xE000,	/* unicode private use */
+
+	/* relationships which must hold:
+		TBITSET ints must hold NTERMS+1 bits...
+		WSETSIZE >= NNONTERM
+		LSETSIZE >= NNONTERM
+		TEMPSIZE >= NTERMS + NNONTERM + 1
+		TEMPSIZE >= NSTATES
+	*/
+
+	NTBASE		= 010000,
+	ERRCODE		= 8190,
+	ACCEPTCODE	= 8191,
+
+	NOASC		= 0,	/* no assoc. */
+	LASC		= 1,	/* left assoc. */
+	RASC		= 2,	/* right assoc. */
+	BASC		= 3,	/* binary assoc. */
+
+	/* flags for state generation */
+
+	DONE		= 0,
+	MUSTDO		= 1,
+	MUSTLOOKAHEAD	= 2,
+
+	/* flags for a rule having an action, and being reduced */
+
+	ACTFLAG		= 04,
+	REDFLAG		= 010,
+
+	/* output parser flags */
+	YYFLAG1		= -1000,
+
+	/* parse tokens */
+	IDENTIFIER	= PRIVATE,
+	MARK,
+	TERM,
+	LEFT,
+	RIGHT,
+	BINARY,
+	PREC,
+	LCURLY,
+	IDENTCOLON,
+	NUMBER,
+	START,
+	TYPEDEF,
+	TYPENAME,
+	UNION,
+
+	ENDFILE		= 0,
+
+	EMPTY		= 1,
+	WHOKNOWS	= 0,
+	OK		= 1,
+	NOMORE		= -1000,
+};
+
+	/* macros for getting associativity and precedence levels */
+
+#define ASSOC(i)	((i)&03)
+#define PLEVEL(i)	(((i)>>4)&077)
+#define TYPE(i)		(((i)>>10)&077)
+
+	/* macros for setting associativity and precedence levels */
+
+#define SETASC(i,j)	i |= j
+#define SETPLEV(i,j)	i |= (j<<4)
+#define SETTYPE(i,j)	i |= (j<<10)
+
+	/* looping macros */
+
+#define TLOOP(i)	for(i=1; i<=ntokens; i++)
+#define NTLOOP(i)	for(i=0; i<=nnonter; i++)
+#define PLOOP(s,i)	for(i=s; i<nprod; i++)
+#define SLOOP(i)	for(i=0; i<nstate; i++)
+#define WSBUMP(x)	x++
+#define WSLOOP(s,j)	for(j=s; j<cwp; j++)
+#define ITMLOOP(i,p,q)	for(q=pstate[i+1], p=pstate[i]; p<q; p++)
+#define SETLOOP(i)	for(i=0; i<tbitset; i++)
+
+	/* command to clobber tempfiles after use */
+
+#define	ZAPFILE(x)	if(x) remove(x)
+
+	/* I/O descriptors */
+
+Biobuf*	faction;	/* file for saving actions */
+Biobuf*	fdefine;	/* file for #defines */
+Biobuf*	fdebug;		/* y.debug for strings for debugging */
+Biobuf*	ftable;		/* y.tab.c file */
+Biobuf*	ftemp;		/* tempfile to pass 2 */
+Biobuf*	finput;		/* input file */
+Biobuf*	foutput;	/* y.output file */
+
+	/* communication variables between various I/O routines */
+
+char*	infile;			/* input file name */
+int	numbval;		/* value of an input number */
+char	tokname[NAMESIZE+4];	/* input token name, slop for runes and 0 */
+
+	/* structure declarations */
+
+typedef
+struct
+{
+	int	lset[TBITSET];
+} Lkset;
+
+typedef
+struct
+{
+	int*	pitem;
+	Lkset*	look;
+} Item;
+
+typedef
+struct
+{
+	char*	name;
+	int	value;
+} Symb;
+
+typedef
+struct
+{
+	int*	pitem;
+	int	flag;
+	Lkset	ws;
+} Wset;
+
+	/* storage of names */
+
+char	cnames[CNAMSZ];		/* place where token and nonterminal names are stored */
+int	cnamsz = CNAMSZ;	/* size of cnames */
+char*	cnamp = cnames;		/* place where next name is to be put in */
+int	ndefout = 4;		/* number of defined symbols output */
+char*	tempname;
+char*	actname;
+char	ttempname[] = TEMPNAME;
+char	tactname[] = ACTNAME;
+char*	parser = PARSER;
+char*	yydebug;
+char	par[256];		/* full path of parser */
+
+	/* storage of types */
+int	ntypes;			/* number of types defined */
+char*	typeset[NTYPES];	/* pointers to type tags */
+
+	/* token information */
+
+int	ntokens = 0 ;		/* number of tokens */
+Symb	tokset[NTERMS];
+int	toklev[NTERMS];		/* vector with the precedence of the terminals */
+
+	/* nonterminal information */
+
+int	nnonter = -1;		/* the number of nonterminals */
+Symb	nontrst[NNONTERM];
+int	start;			/* start symbol */
+
+	/* assigned token type values */
+int	extval = 0;
+
+char*	ytabc = OFILE;	/* name of y.tab.c */
+
+	/* grammar rule information */
+
+int	mem0[MEMSIZE] ;		/* production storage */
+int*	mem = mem0;
+int	nprod = 1;		/* number of productions */
+int*	prdptr[NPROD];		/* pointers to descriptions of productions */
+int	levprd[NPROD];		/* precedence levels for the productions */
+int	rlines[NPROD];		/* line number for this rule */
+
+	/* state information */
+
+int	nstate = 0;		/* number of states */
+Item*	pstate[NSTATES+2];	/* pointers to the descriptions of the states */
+int	tystate[NSTATES];	/* contains type information about the states */
+int	defact[NSTATES];	/* the default actions of states */
+int	tstates[NTERMS];	/* states generated by terminal gotos */
+int	ntstates[NNONTERM]; 	/* states generated by nonterminal gotos */
+int	mstates[NSTATES];	/* chain of overflows of term/nonterm generation lists  */
+int	lastred; 		/* the number of the last reduction of a state */
+
+	/* lookahead set information */
+
+Lkset	lkst[LSETSIZE];
+int	nolook;			/* flag to turn off lookahead computations */
+int	tbitset;		/* size of lookahead sets */
+int	nlset = 0;		/* next lookahead set index */
+int	nolook = 0;		/* flag to suppress lookahead computations */
+Lkset	clset;  		/* temporary storage for lookahead computations */
+
+	/* working set information */
+
+Wset	wsets[WSETSIZE];
+Wset*	cwp;
+
+	/* storage for action table */
+
+int	amem[ACTSIZE];		/* action table storage */
+int*	memp = amem;		/* next free action table position */
+int	indgo[NSTATES];		/* index to the stored goto table */
+
+	/* temporary vector, indexable by states, terms, or ntokens */
+
+int	temp1[TEMPSIZE];	/* temporary storage, indexed by terms + ntokens or states */
+int	lineno = 1;		/* current input line number */
+int	fatfl = 1;  		/* if on, error is fatal */
+int	nerrors = 0;		/* number of errors */
+
+	/* statistics collection variables */
+
+int	zzgoent;
+int	zzgobest;
+int	zzacent;
+int	zzexcp;
+int	zzclose;
+int	zzrrconf;
+int	zzsrconf;
+
+int*	ggreed = lkst[0].lset;
+int*	pgo = wsets[0].ws.lset;
+int*	yypgo = &nontrst[0].value;
+
+int	maxspr = 0;  		/* maximum spread of any entry */
+int	maxoff = 0;  		/* maximum offset into a array */
+int*	pmem = mem0;
+int*	maxa;
+int	nxdb = 0;
+int	adb = 0;
+
+
+	/* storage for information about the nonterminals */
+
+int**	pres[NNONTERM+2];  	/* vector of pointers to productions yielding each nonterminal */
+Lkset*	pfirst[NNONTERM+2];	/* vector of pointers to first sets for each nonterminal */
+int	pempty[NNONTERM+1];	/* vector of nonterminals nontrivially deriving e */
+
+	/* random stuff picked out from between functions */
+
+int	indebug = 0;
+Wset*	zzcwp = wsets;
+int	zzgoent = 0;
+int	zzgobest = 0;
+int	zzacent = 0;
+int	zzexcp = 0;
+int	zzclose = 0;
+int	zzsrconf = 0;
+int*	zzmemsz = mem0;
+int	zzrrconf = 0;
+int	pidebug = 0;		/* debugging flag for putitem */
+int	gsdebug = 0;
+int	cldebug = 0;		/* debugging flag for closure */
+int	pkdebug = 0;
+int	g2debug = 0;
+
+struct
+{
+	char*	name;
+	long	value;
+} resrv[] =
+{
+	"binary",	BINARY,
+	"left",		LEFT,
+	"nonassoc",	BINARY,
+	"prec",		PREC,
+	"right",	RIGHT,
+	"start",	START,
+	"term",		TERM,
+	"token",	TERM,
+	"type",		TYPEDEF,
+	"union",	UNION,
+	0,
+};
+
+	/* define functions */
+
+void	main(int, char**);
+void	others(void);
+char*	chcopy(char*, char*);
+char*	writem(int*);
+char*	symnam(int);
+void	summary(void);
+void	error(char*, ...);
+void	aryfil(int*, int, int);
+int	setunion(int*, int*);
+void	prlook(Lkset*);
+void	cpres(void);
+void	cpfir(void);
+int	state(int);
+void	putitem(int*, Lkset*);
+void	cempty(void);
+void	stagen(void);
+void	closure(int);
+Lkset*	flset(Lkset*);
+void	cleantmp(void);
+void	intr(void);
+void	setup(int, char**);
+void	finact(void);
+int	defin(int, char*);
+void	defout(int);
+char*	cstash(char*);
+long	gettok(void);
+int	fdtype(int);
+int	chfind(int, char*);
+void	cpyunion(void);
+void	cpycode(void);
+int	skipcom(void);
+void	cpyact(int);
+void	openup(char*, int, int, int, char*);
+void	output(void);
+int	apack(int*, int);
+void	go2out(void);
+void	go2gen(int);
+void	precftn(int, int, int);
+void	wract(int);
+void	wrstate(int);
+void	warray(char*, int*, int);
+void	hideprod(void);
+void	callopt(void);
+void	gin(int);
+void	stin(int);
+int	nxti(void);
+void	osummary(void);
+void	aoutput(void);
+void	arout(char*, int*, int);
+int	gtnm(void);
+
+void
+main(int argc, char *argv[])
+{
+
+	setup(argc, argv);	/* initialize and read productions */
+	tbitset = NWORDS(ntokens);
+	cpres();		/* make table of which productions yield a given nonterminal */
+	cempty();		/* make a table of which nonterminals can match the empty string */
+	cpfir();		/* make a table of firsts of nonterminals */
+	stagen();		/* generate the states */
+	output();		/* write the states and the tables */
+	go2out();
+	hideprod();
+	summary();
+	callopt();
+	others();
+	exits(0);
+}
+
+/*
+ * put out other arrays, copy the parsers
+ */
+void
+others(void)
+{
+	int c, i, j;
+
+	finput = Bopen(parser, OREAD);
+	if(finput == 0)
+		error("cannot find parser %s", parser);
+	warray("yyr1", levprd, nprod);
+	aryfil(temp1, nprod, 0);
+	PLOOP(1, i)
+		temp1[i] = prdptr[i+1]-prdptr[i]-2;
+	warray("yyr2", temp1, nprod);
+
+	aryfil(temp1, nstate, -1000);
+	TLOOP(i)
+		for(j=tstates[i]; j!=0; j=mstates[j])
+			temp1[j] = i;
+	NTLOOP(i)
+		for(j=ntstates[i]; j!=0; j=mstates[j])
+			temp1[j] = -i;
+	warray("yychk", temp1, nstate);
+	warray("yydef", defact, nstate);
+
+	/* put out token translation tables */
+	/* table 1 has 0-256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok1", temp1, c+1);
+
+	/* table 2 has PRIVATE-PRIVATE+256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value - PRIVATE;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok2", temp1, c+1);
+
+	/* table 3 has everything else */
+	Bprint(ftable, "long	yytok3[] =\n{\n");
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256)
+			continue;
+		if(j >= PRIVATE && j < 256+PRIVATE)
+			continue;
+
+		Bprint(ftable, "%4d,%4d,", j, i);
+		c++;
+		if(c%5 == 0)
+			Bprint(ftable, "\n");
+	}
+	Bprint(ftable, "%4d\n};\n", 0);
+
+	/* copy parser text */
+	while((c=Bgetrune(finput)) != Beof) {
+		if(c == '$') {
+			if((c = Bgetrune(finput)) != 'A')
+				Bputrune(ftable, '$');
+			else { /* copy actions */
+				faction = Bopen(actname, OREAD);
+				if(faction == 0)
+					error("cannot reopen action tempfile");
+				while((c=Bgetrune(faction)) != Beof)
+					Bputrune(ftable, c);
+				Bterm(faction);
+				ZAPFILE(actname);
+				c = Bgetrune(finput);
+			}
+		}
+		Bputrune(ftable, c);
+	}
+	Bterm(ftable);
+}
+
+/*
+ * copies string q into p, returning next free char ptr
+ */
+char*
+chcopy(char* p, char* q)
+{
+	int c;
+
+	while(c = *q) {
+		if(c == '"')
+			*p++ = '\\';
+		*p++ = c;
+		q++;
+	}
+	*p = 0;
+	return p;
+}
+
+/*
+ * creates output string for item pointed to by pp
+ */
+char*
+writem(int *pp)
+{
+	int i,*p;
+	static char sarr[ISIZE];
+	char* q;
+
+	for(p=pp; *p>0; p++)
+		;
+	p = prdptr[-*p];
+	q = chcopy(sarr, nontrst[*p-NTBASE].name);
+	q = chcopy(q, ": ");
+	for(;;) {
+		*q = ' ';
+		p++;
+		if(p == pp)
+			*q = '.';
+		q++;
+		*q = '\0';
+		i = *p;
+		if(i <= 0)
+			break;
+		q = chcopy(q, symnam(i));
+		if(q > &sarr[ISIZE-30])
+			error("item too big");
+	}
+
+	/* an item calling for a reduction */
+	i = *pp;
+	if(i < 0 ) {
+		q = chcopy(q, "    (");
+		sprint(q, "%d)", -i);
+	}
+	return sarr;
+}
+
+/*
+ * return a pointer to the name of symbol i
+ */
+char*
+symnam(int i)
+{
+	char* cp;
+
+	cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name;
+	if(*cp == ' ')
+		cp++;
+	return cp;
+}
+
+/*
+ * output the summary on y.output
+ */
+void
+summary(void)
+{
+
+	if(foutput != 0) {
+		Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n",
+			ntokens, NTERMS, nnonter, NNONTERM);
+		Bprint(foutput, "%d/%d grammar rules, %d/%d states\n",
+			nprod, NPROD, nstate, NSTATES);
+		Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n",
+			zzsrconf, zzrrconf);
+		Bprint(foutput, "%d/%d working sets used\n",
+			(int)(zzcwp-wsets), WSETSIZE);
+		Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
+			(int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE);
+		Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE);
+		Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate);
+		Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp);
+		Bprint(foutput, "%d goto entries\n", zzgoent);
+		Bprint(foutput, "%d entries saved by goto default\n", zzgobest);
+	}
+	if(zzsrconf != 0 || zzrrconf != 0) {
+		print("\nconflicts: ");
+		if(zzsrconf)
+			print("%d shift/reduce", zzsrconf);
+		if(zzsrconf && zzrrconf)
+			print(", ");
+		if(zzrrconf)
+			print("%d reduce/reduce", zzrrconf);
+		print("\n");
+	}
+	if(ftemp != 0) {
+		Bterm(ftemp);
+		ftemp = 0;
+	}
+	if(fdefine != 0) {
+		Bterm(fdefine);
+		fdefine = 0;
+	}
+}
+
+/*
+ * write out error comment -- NEEDS WORK
+ */
+void
+error(char *s, ...)
+{
+
+	nerrors++;
+	fprint(2, "\n fatal error:");
+	fprint(2, s, (&s)[1]);
+	fprint(2, ", %s:%d\n", infile, lineno);
+	if(!fatfl)
+		return;
+	summary();
+	cleantmp();
+	exits("error");
+}
+
+/*
+ * set elements 0 through n-1 to c
+ */
+void
+aryfil(int *v, int n, int c)
+{
+	int i;
+
+	for(i=0; i<n; i++)
+		v[i] = c;
+}
+
+/*
+ * set a to the union of a and b
+ * return 1 if b is not a subset of a, 0 otherwise
+ */
+int
+setunion(int *a, int *b)
+{
+	int i, x, sub;
+
+	sub = 0;
+	SETLOOP(i) {
+		x = *a;
+		*a |= *b;
+		if(*a != x)
+			sub = 1;
+		a++;
+		b++;
+	}
+	return sub;
+}
+
+void
+prlook(Lkset* p)
+{
+	int j, *pp;
+
+	pp = p->lset;
+	if(pp == 0)
+		Bprint(foutput, "\tNULL");
+	else {
+		Bprint(foutput, " { ");
+		TLOOP(j)
+			if(BIT(pp,j))
+				Bprint(foutput, "%s ", symnam(j));
+		Bprint(foutput, "}");
+	}
+}
+
+/*
+ * compute an array with the beginnings of  productions yielding given nonterminals
+ * The array pres points to these lists
+ * the array pyield has the lists: the total size is only NPROD+1
+ */
+void
+cpres(void)
+{
+	int c, j, i, **pmem;
+	static int *pyield[NPROD];
+
+	pmem = pyield;
+	NTLOOP(i) {
+		c = i+NTBASE;
+		pres[i] = pmem;
+		fatfl = 0;  	/* make undefined  symbols  nonfatal */
+		PLOOP(0, j)
+			if(*prdptr[j] == c)
+				*pmem++ =  prdptr[j]+1;
+		if(pres[i] == pmem)
+			error("nonterminal %s not defined!", nontrst[i].name);
+	}
+	pres[i] = pmem;
+	fatfl = 1;
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+	if(pmem != &pyield[nprod])
+		error("internal Yacc error: pyield %d", pmem-&pyield[nprod]);
+}
+
+/*
+ * compute an array with the first of nonterminals
+ */
+void
+cpfir(void)
+{
+	int *p, **s, i, **t, ch, changes;
+
+	zzcwp = &wsets[nnonter];
+	NTLOOP(i) {
+		aryfil(wsets[i].ws.lset, tbitset, 0);
+		t = pres[i+1];
+		/* initially fill the sets */
+		for(s=pres[i]; s<t; ++s)
+			for(p = *s; (ch = *p) > 0; ++p) {
+				if(ch < NTBASE) {
+					SETBIT(wsets[i].ws.lset, ch);
+					break;
+				}
+				if(!pempty[ch-NTBASE])
+					break;
+			}
+	}
+
+	/* now, reflect transitivity */
+	changes = 1;
+	while(changes) {
+		changes = 0;
+		NTLOOP(i) {
+			t = pres[i+1];
+			for(s = pres[i]; s < t; ++s)
+				for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
+					changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset);
+					if(!pempty[ch])
+						break;
+				}
+		}
+	}
+
+	NTLOOP(i)
+		pfirst[i] = flset(&wsets[i].ws);
+	if(!indebug)
+		return;
+	if(foutput != 0)
+		NTLOOP(i) {
+			Bprint(foutput, "\n%s: ", nontrst[i].name);
+			prlook(pfirst[i]);
+			Bprint(foutput, " %d\n", pempty[i]);
+		}
+}
+
+/*
+ * sorts last state,and sees if it equals earlier ones. returns state number
+ */
+int
+state(int c)
+{
+	Item *p1, *p2, *k, *l, *q1, *q2;
+	int size1, size2, i;
+
+	p1 = pstate[nstate];
+	p2 = pstate[nstate+1];
+	if(p1 == p2)
+		return 0;	/* null state */
+	/* sort the items */
+	for(k = p2-1; k > p1; k--)	/* make k the biggest */
+		for(l = k-1; l >= p1; --l)
+			if(l->pitem > k->pitem) {
+				int *s;
+				Lkset *ss;
+
+				s = k->pitem;
+				k->pitem = l->pitem;
+				l->pitem = s;
+				ss = k->look;
+				k->look = l->look;
+				l->look = ss;
+			}
+	size1 = p2 - p1;	/* size of state */
+
+	for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) {
+		/* get ith state */
+		q1 = pstate[i];
+		q2 = pstate[i+1];
+		size2 = q2 - q1;
+		if(size1 != size2)
+			continue;
+		k = p1;
+		for(l = q1; l < q2; l++) {
+			if(l->pitem != k->pitem)
+				break;
+			k++;
+		}
+		if(l != q2)
+			continue;
+		/* found it */
+		pstate[nstate+1] = pstate[nstate];	/* delete last state */
+		/* fix up lookaheads */
+		if(nolook)
+			return i;
+		for(l = q1, k = p1; l < q2; ++l, ++k ) {
+			int s;
+
+			SETLOOP(s)
+				clset.lset[s] = l->look->lset[s];
+			if(setunion(clset.lset, k->look->lset)) {
+				tystate[i] = MUSTDO;
+				/* register the new set */
+				l->look = flset( &clset );
+			}
+		}
+		return i;
+	}
+	/* state is new */
+	if(nolook)
+		error("yacc state/nolook error");
+	pstate[nstate+2] = p2;
+	if(nstate+1 >= NSTATES)
+		error("too many states");
+	if(c >= NTBASE) {
+		mstates[nstate] = ntstates[c-NTBASE];
+		ntstates[c-NTBASE] = nstate;
+	} else {
+		mstates[nstate] = tstates[c];
+		tstates[c] = nstate;
+	}
+	tystate[nstate] = MUSTDO;
+	return nstate++;
+}
+
+void
+putitem(int *ptr, Lkset *lptr)
+{
+	Item *j;
+
+	if(pidebug && foutput != 0)
+		Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate);
+	j = pstate[nstate+1];
+	j->pitem = ptr;
+	if(!nolook)
+		j->look = flset(lptr);
+	pstate[nstate+1] = ++j;
+	if((int*)j > zzmemsz) {
+		zzmemsz = (int*)j;
+		if(zzmemsz >=  &mem0[MEMSIZE])
+			error("out of state space");
+	}
+}
+
+/*
+ * mark nonterminals which derive the empty string
+ * also, look for nonterminals which don't derive any token strings
+ */
+void
+cempty(void)
+{
+
+	int i, *p;
+
+	/* first, use the array pempty to detect productions that can never be reduced */
+	/* set pempty to WHONOWS */
+	aryfil(pempty, nnonter+1, WHOKNOWS);
+
+	/* now, look at productions, marking nonterminals which derive something */
+more:
+	PLOOP(0, i) {
+		if(pempty[*prdptr[i] - NTBASE])
+			continue;
+		for(p = prdptr[i]+1; *p >= 0; ++p)
+			if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
+				break;
+		/* production can be derived */
+		if(*p < 0) {
+			pempty[*prdptr[i]-NTBASE] = OK;
+			goto more;
+		}
+	}
+
+	/* now, look at the nonterminals, to see if they are all OK */
+	NTLOOP(i) {
+		/* the added production rises or falls as the start symbol ... */
+		if(i == 0)
+			continue;
+		if(pempty[i] != OK) {
+			fatfl = 0;
+			error("nonterminal %s never derives any token string", nontrst[i].name);
+		}
+	}
+
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+
+	/* now, compute the pempty array, to see which nonterminals derive the empty string */
+	/* set pempty to WHOKNOWS */
+	aryfil( pempty, nnonter+1, WHOKNOWS);
+
+	/* loop as long as we keep finding empty nonterminals */
+
+again:
+	PLOOP(1, i) {
+		/* not known to be empty */
+		if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
+			for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p)
+				;
+			/* we have a nontrivially empty nonterminal */
+			if(*p < 0) {
+				pempty[*prdptr[i]-NTBASE] = EMPTY;
+				/* got one ... try for another */
+				goto again;
+			}
+		}
+	}
+}
+
+/*
+ * generate the states
+ */
+void
+stagen(void)
+{
+
+	int c, i, j, more;
+	Wset *p, *q;
+
+	/* initialize */
+	nstate = 0;
+
+	/* THIS IS FUNNY from the standpoint of portability
+	 * it represents the magic moment when the mem0 array, which has
+	 * been holding the productions, starts to hold item pointers, of a
+	 * different type...
+	 * someday, alloc should be used to allocate all this stuff... for now, we
+	 * accept that if pointers don't fit in integers, there is a problem...
+	 */
+
+	pstate[0] = pstate[1] = (Item*)mem;
+	aryfil(clset.lset, tbitset, 0);
+	putitem(prdptr[0]+1, &clset);
+	tystate[0] = MUSTDO;
+	nstate = 1;
+	pstate[2] = pstate[1];
+
+	aryfil(amem, ACTSIZE, 0);
+
+	/* now, the main state generation loop */
+	for(more=1; more;) {
+		more = 0;
+		SLOOP(i) {
+			if(tystate[i] != MUSTDO)
+				continue;
+			tystate[i] = DONE;
+			aryfil(temp1, nnonter+1, 0);
+			/* take state i, close it, and do gotos */
+			closure(i);
+			/* generate goto's */
+			WSLOOP(wsets, p) {
+				if(p->flag)
+					continue;
+				p->flag = 1;
+				c = *(p->pitem);
+				if(c <= 1) {
+					if(pstate[i+1]-pstate[i] <= p-wsets)
+						tystate[i] = MUSTLOOKAHEAD;
+					continue;
+				}
+				/* do a goto on c */
+				WSLOOP(p, q)
+					/* this item contributes to the goto */
+					if(c == *(q->pitem)) {
+						putitem(q->pitem+1, &q->ws);
+						q->flag = 1;
+					}
+				if(c < NTBASE)
+					state(c);	/* register new state */
+				else
+					temp1[c-NTBASE] = state(c);
+			}
+			if(gsdebug && foutput != 0) {
+				Bprint(foutput, "%d: ", i);
+				NTLOOP(j)
+					if(temp1[j])
+						Bprint(foutput, "%s %d, ",
+						nontrst[j].name, temp1[j]);
+				Bprint(foutput, "\n");
+			}
+			indgo[i] = apack(&temp1[1], nnonter-1) - 1;
+			/* do some more */
+			more = 1;
+		}
+	}
+}
+
+/*
+ * generate the closure of state i
+ */
+void
+closure(int i)
+{
+
+	Wset *u, *v;
+	Item *p, *q;
+	int c, ch, work, k, *pi, **s, **t;
+
+	zzclose++;
+
+	/* first, copy kernel of state i to wsets */
+	cwp = wsets;
+	ITMLOOP(i, p, q) {
+		cwp->pitem = p->pitem;
+		cwp->flag = 1;			/* this item must get closed */
+		SETLOOP(k)
+			cwp->ws.lset[k] = p->look->lset[k];
+		WSBUMP(cwp);
+	}
+
+	/* now, go through the loop, closing each item */
+	work = 1;
+	while(work) {
+		work = 0;
+		WSLOOP(wsets, u) {
+			if(u->flag == 0)
+				continue;
+			/* dot is before c */
+			c = *(u->pitem);
+			if(c < NTBASE) {
+				u->flag = 0;
+				/* only interesting case is where . is before nonterminal */
+				continue;
+			}
+
+			/* compute the lookahead */
+			aryfil(clset.lset, tbitset, 0);
+
+			/* find items involving c */
+			WSLOOP(u, v)
+				if(v->flag == 1 && *(pi=v->pitem) == c) {
+					v->flag = 0;
+					if(nolook)
+						continue;
+					while((ch = *++pi) > 0) {
+						/* terminal symbol */
+						if(ch < NTBASE) {
+							SETBIT(clset.lset, ch);
+							break;
+						}
+						/* nonterminal symbol */
+						setunion(clset.lset, pfirst[ch-NTBASE]->lset);
+						if(!pempty[ch-NTBASE])
+							break;
+					}
+					if(ch <= 0)
+						setunion(clset.lset, v->ws.lset);
+				}
+
+			/*
+			 * now loop over productions derived from c
+			 * c is now nonterminal number
+			 */
+			c -= NTBASE;
+			t = pres[c+1];
+			for(s = pres[c]; s < t; ++s) {
+				/*
+				 * put these items into the closure
+				 * is the item there
+				 */
+				WSLOOP(wsets, v)
+					/* yes, it is there */
+					if(v->pitem == *s) {
+						if(nolook)
+							goto nexts;
+						if(setunion(v->ws.lset, clset.lset))
+							v->flag = work = 1;
+						goto nexts;
+					}
+
+				/*  not there; make a new entry */
+				if(cwp-wsets+1 >= WSETSIZE)
+					error( "working set overflow");
+				cwp->pitem = *s;
+				cwp->flag = 1;
+				if(!nolook) {
+					work = 1;
+					SETLOOP(k) cwp->ws.lset[k] = clset.lset[k];
+				}
+				WSBUMP(cwp);
+
+			nexts:;
+			}
+		}
+	}
+
+	/* have computed closure; flags are reset; return */
+	if(cwp > zzcwp)
+		zzcwp = cwp;
+	if(cldebug && foutput != 0) {
+		Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook);
+		WSLOOP(wsets, u) {
+			if(u->flag)
+				Bprint(foutput, "flag set!\n");
+			u->flag = 0;
+			Bprint(foutput, "\t%s", writem(u->pitem));
+			prlook(&u->ws);
+			Bprint(foutput, "\n");
+		}
+	}
+}
+
+/*
+ * decide if the lookahead set pointed to by p is known
+ * return pointer to a perminent location for the set
+ */
+Lkset*
+flset(Lkset *p)
+{
+	Lkset *q;
+	int *u, *v, *w, j;
+
+	for(q = &lkst[nlset]; q-- > lkst;) {
+		u = p->lset;
+		v = q->lset;
+		w = &v[tbitset];
+		while(v < w)
+			if(*u++ != *v++)
+				goto more;
+		/* we have matched */
+		return q;
+	more:;
+	}
+	/* add a new one */
+	q = &lkst[nlset++];
+	if(nlset >= LSETSIZE)
+		error("too many lookahead sets");
+	SETLOOP(j)
+		q->lset[j] = p->lset[j];
+	return q;
+}
+
+void
+cleantmp(void)
+{
+	ZAPFILE(actname);
+	ZAPFILE(tempname);
+}
+
+void
+intr(void)
+{
+	cleantmp();
+	exits("interrupted");
+}
+
+void
+setup(int argc, char *argv[])
+{
+	long c, t;
+	int i, j, lev, ty, ytab, *p;
+	int vflag, dflag, stem;
+	char actnm[8], *stemc, *cp;
+
+	ytab = 0;
+	vflag = 0;
+	dflag = 0;
+	stem = 0;
+	stemc = "y";
+	foutput = 0;
+	fdefine = 0;
+	fdebug = 0;
+	ARGBEGIN{
+	case 'v':
+	case 'V':
+		vflag++;
+		break;
+	case 'D':
+		yydebug = ARGF();
+		break;
+	case 'd':
+		dflag++;
+		break;
+	case 'o':
+		ytab++;
+		ytabc = ARGF();
+		break;
+	case 's':
+		stem++;
+		stemc = ARGF();
+		break;
+	case 'S':
+		parser = PARSERS;
+		break;
+	default:
+		error("illegal option: %c", ARGC());
+	}ARGEND
+
+	cp = getenv("ROOT");
+	if(cp == 0)
+		cp = ROOT;
+	snprint(par, sizeof(par), "%s/utils/lib/%s", cp, parser);
+	parser = par;
+
+	openup(stemc, dflag, vflag, ytab, ytabc);
+
+	ftemp = Bopen(tempname = mktemp(ttempname), OWRITE);
+	faction = Bopen(actname = mktemp(tactname), OWRITE);
+	if(ftemp == 0 || faction == 0)
+		error("cannot open temp file");
+	if(argc < 1)
+		error("no input file");
+	infile = argv[0];
+	finput = Bopen(infile, OREAD);
+	if(finput == 0)
+		error("cannot open '%s'", argv[0]);
+	cnamp = cnames;
+
+	defin(0, "$end");
+	extval = PRIVATE;	/* tokens start in unicode 'private use' */
+	defin(0, "error");
+	defin(1, "$accept");
+	defin(0, "$unk");
+	mem = mem0;
+	i = 0;
+
+	for(t = gettok(); t != MARK && t != ENDFILE;)
+	switch(t) {
+	case ';':
+		t = gettok();
+		break;
+
+	case START:
+		if(gettok() != IDENTIFIER)
+			error("bad %%start construction");
+		start = chfind(1, tokname);
+		t = gettok();
+		continue;
+
+	case TYPEDEF:
+		if(gettok() != TYPENAME)
+			error("bad syntax in %%type");
+		ty = numbval;
+		for(;;) {
+			t = gettok();
+			switch(t) {
+			case IDENTIFIER:
+				if((t=chfind(1, tokname)) < NTBASE) {
+					j = TYPE(toklev[t]);
+					if(j != 0 && j != ty)
+						error("type redeclaration of token %s",
+							tokset[t].name);
+					else
+						SETTYPE(toklev[t], ty);
+				} else {
+					j = nontrst[t-NTBASE].value;
+					if(j != 0 && j != ty)
+						error("type redeclaration of nonterminal %s",
+							nontrst[t-NTBASE].name );
+					else
+						nontrst[t-NTBASE].value = ty;
+				}
+			case ',':
+				continue;
+			case ';':
+				t = gettok();
+			default:
+				break;
+			}
+			break;
+		}
+		continue;
+
+	case UNION:
+		/* copy the union declaration to the output */
+		cpyunion();
+		t = gettok();
+		continue;
+
+	case LEFT:
+	case BINARY:
+	case RIGHT:
+		i++;
+
+	case TERM:
+		/* nonzero means new prec. and assoc. */
+		lev = t-TERM;
+		ty = 0;
+
+		/* get identifiers so defined */
+		t = gettok();
+
+		/* there is a type defined */
+		if(t == TYPENAME) {
+			ty = numbval;
+			t = gettok();
+		}
+		for(;;) {
+			switch(t) {
+			case ',':
+				t = gettok();
+				continue;
+
+			case ';':
+				break;
+
+			case IDENTIFIER:
+				j = chfind(0, tokname);
+				if(j >= NTBASE)
+					error("%s defined earlier as nonterminal", tokname);
+				if(lev) {
+					if(ASSOC(toklev[j]))
+						error("redeclaration of precedence of %s", tokname);
+					SETASC(toklev[j], lev);
+					SETPLEV(toklev[j], i);
+				}
+				if(ty) {
+					if(TYPE(toklev[j]))
+						error("redeclaration of type of %s", tokname);
+					SETTYPE(toklev[j],ty);
+				}
+				t = gettok();
+				if(t == NUMBER) {
+					tokset[j].value = numbval;
+					if(j < ndefout && j > 3)
+						error("please define type number of %s earlier",
+							tokset[j].name);
+					t = gettok();
+				}
+				continue;
+			}
+			break;
+		}
+		continue;
+
+	case LCURLY:
+		defout(0);
+		cpycode();
+		t = gettok();
+		continue;
+
+	default:
+		error("syntax error");
+	}
+	if(t == ENDFILE)
+		error("unexpected EOF before %%");
+
+	/* t is MARK */
+	Bprint(ftable, "extern	int	yyerrflag;\n");
+	Bprint(ftable, "#ifndef	YYMAXDEPTH\n");
+	Bprint(ftable, "#define	YYMAXDEPTH	150\n");
+	Bprint(ftable, "#endif\n" );
+	if(!ntypes) {
+		Bprint(ftable, "#ifndef	YYSTYPE\n");
+		Bprint(ftable, "#define	YYSTYPE	int\n");
+		Bprint(ftable, "#endif\n");
+	}
+	Bprint(ftable, "YYSTYPE	yylval;\n");
+	Bprint(ftable, "YYSTYPE	yyval;\n");
+
+	prdptr[0] = mem;
+
+	/* added production */
+	*mem++ = NTBASE;
+
+	/* if start is 0, we will overwrite with the lhs of the first rule */
+	*mem++ = start;
+	*mem++ = 1;
+	*mem++ = 0;
+	prdptr[1] = mem;
+	while((t=gettok()) == LCURLY)
+		cpycode();
+	if(t != IDENTCOLON)
+		error("bad syntax on first rule");
+
+	if(!start)
+		prdptr[0][1] = chfind(1, tokname);
+
+	/* read rules */
+	while(t != MARK && t != ENDFILE) {
+		/* process a rule */
+		rlines[nprod] = lineno;
+		if(t == '|')
+			*mem++ = *prdptr[nprod-1];
+		else
+			if(t == IDENTCOLON) {
+				*mem = chfind(1, tokname);
+				if(*mem < NTBASE)
+					error("token illegal on LHS of grammar rule");
+				mem++;
+			} else
+				error("illegal rule: missing semicolon or | ?");
+		/* read rule body */
+		t = gettok();
+
+	more_rule:
+		while(t == IDENTIFIER) {
+			*mem = chfind(1, tokname);
+			if(*mem < NTBASE)
+				levprd[nprod] = toklev[*mem];
+			mem++;
+			t = gettok();
+		}
+		if(t == PREC) {
+			if(gettok() != IDENTIFIER)
+				error("illegal %%prec syntax");
+			j = chfind(2, tokname);
+			if(j >= NTBASE)
+				error("nonterminal %s illegal after %%prec",
+					nontrst[j-NTBASE].name);
+			levprd[nprod] = toklev[j];
+			t = gettok();
+		}
+		if(t == '=') {
+			levprd[nprod] |= ACTFLAG;
+			Bprint(faction, "\ncase %d:", nprod);
+			cpyact(mem-prdptr[nprod]-1);
+			Bprint(faction, " break;");
+			if((t=gettok()) == IDENTIFIER) {
+
+				/* action within rule... */
+				sprint(actnm, "$$%d", nprod);
+
+				/* make it a nonterminal */
+				j = chfind(1, actnm);
+
+				/*
+				 * the current rule will become rule number nprod+1
+				 * move the contents down, and make room for the null
+				 */
+				for(p = mem; p >= prdptr[nprod]; --p)
+					p[2] = *p;
+				mem += 2;
+
+				/* enter null production for action */
+				p = prdptr[nprod];
+				*p++ = j;
+				*p++ = -nprod;
+
+				/* update the production information */
+				levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
+				levprd[nprod] = ACTFLAG;
+				if(++nprod >= NPROD)
+					error("more than %d rules", NPROD);
+				prdptr[nprod] = p;
+
+				/* make the action appear in the original rule */
+				*mem++ = j;
+
+				/* get some more of the rule */
+				goto more_rule;
+			}
+		}
+
+		while(t == ';')
+			t = gettok();
+		*mem++ = -nprod;
+
+		/* check that default action is reasonable */
+		if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) {
+
+			/* no explicit action, LHS has value */
+			int tempty;
+
+			tempty = prdptr[nprod][1];
+			if(tempty < 0)
+				error("must return a value, since LHS has a type");
+			else
+				if(tempty >= NTBASE)
+					tempty = nontrst[tempty-NTBASE].value;
+				else
+					tempty = TYPE(toklev[tempty]);
+			if(tempty != nontrst[*prdptr[nprod]-NTBASE].value)
+				error("default action causes potential type clash");
+		}
+		nprod++;
+		if(nprod >= NPROD)
+			error("more than %d rules", NPROD);
+		prdptr[nprod] = mem;
+		levprd[nprod] = 0;
+	}
+
+	/* end of all rules */
+	defout(1);
+
+	finact();
+	if(t == MARK) {
+		Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+		while((c=Bgetrune(finput)) != Beof)
+			Bputrune(ftable, c);
+	}
+	Bterm(finput);
+}
+
+/*
+ * finish action routine
+ */
+void
+finact(void)
+{
+
+	Bterm(faction);
+	Bprint(ftable, "#define YYEOFCODE %d\n", 1);
+	Bprint(ftable, "#define YYERRCODE %d\n", 2);
+}
+
+/*
+ * define s to be a terminal if t=0
+ * or a nonterminal if t=1
+ */
+int
+defin(int nt, char *s)
+{
+	int val;
+	Rune rune;
+
+	val = 0;
+	if(nt) {
+		nnonter++;
+		if(nnonter >= NNONTERM)
+			error("too many nonterminals, limit %d",NNONTERM);
+		nontrst[nnonter].name = cstash(s);
+		return NTBASE + nnonter;
+	}
+
+	/* must be a token */
+	ntokens++;
+	if(ntokens >= NTERMS)
+		error("too many terminals, limit %d", NTERMS);
+	tokset[ntokens].name = cstash(s);
+
+	/* establish value for token */
+	/* single character literal */
+	if(s[0] == ' ') {
+		val = chartorune(&rune, &s[1]);
+		if(s[val+1] == 0) {
+			val = rune;
+			goto out;
+		}
+	}
+
+	/* escape sequence */
+	if(s[0] == ' ' && s[1] == '\\') {
+		if(s[3] == 0) {
+			/* single character escape sequence */
+			switch(s[2]) {
+			case 'n':	val = '\n'; break;
+			case 'r':	val = '\r'; break;
+			case 'b':	val = '\b'; break;
+			case 't':	val = '\t'; break;
+			case 'f':	val = '\f'; break;
+			case '\'':	val = '\''; break;
+			case '"':	val = '"'; break;
+			case '\\':	val = '\\'; break;
+			default:	error("invalid escape");
+			}
+			goto out;
+		}
+
+		/* \nnn sequence */
+		if(s[2] >= '0' && s[2] <= '7') {
+			if(s[3] < '0' ||
+			   s[3] > '7' ||
+			   s[4] < '0' ||
+			   s[4] > '7' ||
+			   s[5] != 0)
+				error("illegal \\nnn construction");
+			val = 64*s[2] + 8*s[3] + s[4] - 73*'0';
+			if(val == 0)
+				error("'\\000' is illegal");
+			goto out;
+		}
+		error("unknown escape");
+	}
+	val = extval++;
+
+out:
+	tokset[ntokens].value = val;
+	toklev[ntokens] = 0;
+	return ntokens;
+}
+
+/*
+ * write out the defines (at the end of the declaration section)
+ */
+void
+defout(int last)
+{
+	int i, c;
+	char sar[NAMESIZE+10];
+
+	for(i=ndefout; i<=ntokens; i++) {
+		/* non-literals */
+		c = tokset[i].name[0];
+		if(c != ' ' && c != '$') {
+			Bprint(ftable, "#define	%s	%d\n",
+				tokset[i].name, tokset[i].value);
+			if(fdefine)
+				Bprint(fdefine, "#define\t%s\t%d\n",
+					tokset[i].name, tokset[i].value);
+		}
+	}
+	ndefout = ntokens+1;
+	if(last && fdebug) {
+		Bprint(fdebug, "char*	yytoknames[] =\n{\n");
+		TLOOP(i) {
+			if(tokset[i].name) {
+				chcopy(sar, tokset[i].name);
+				Bprint(fdebug, "\t\"%s\",\n", sar);
+				continue;
+			}
+			Bprint(fdebug, "\t0,\n");
+		}
+		Bprint(fdebug, "};\n");
+	}
+}
+
+char*
+cstash(char *s)
+{
+	char *temp;
+
+	temp = cnamp;
+	do {
+		if(cnamp >= &cnames[cnamsz])
+			error("too many characters in id's and literals");
+		else
+			*cnamp++ = *s;
+	} while(*s++);
+	return temp;
+}
+
+long
+gettok(void)
+{
+	long c;
+	Rune rune;
+	int i, base, match, reserve;
+	static int peekline;
+
+begin:
+	reserve = 0;
+	lineno += peekline;
+	peekline = 0;
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\n' || c == '\t' || c == '\f' || c == '\r') {
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+
+	/* skip comment */
+	if(c == '/') {
+		lineno += skipcom();
+		goto begin;
+	}
+	switch(c) {
+	case Beof:
+		return ENDFILE;
+
+	case '{':
+		Bungetrune(finput);
+		return '=';
+
+	case '<':
+		/* get, and look up, a type name (union member name) */
+		i = 0;
+		while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n' && c != '\r') {
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		if(c != '>')
+			error("unterminated < ... > clause");
+		tokname[i] = 0;
+		for(i=1; i<=ntypes; i++)
+			if(!strcmp(typeset[i], tokname)) {
+				numbval = i;
+				return TYPENAME;
+			}
+		ntypes++;
+		numbval = ntypes;
+		typeset[numbval] = cstash(tokname);
+		return TYPENAME;
+
+	case '"':
+	case '\'':
+		match = c;
+		tokname[0] = ' ';
+		i = 1;
+		for(;;) {
+			c = Bgetrune(finput);
+			if(c == '\n' || c == '\r' || c <= 0)
+				error("illegal or missing ' or \"" );
+			if(c == '\\') {
+				tokname[i] = '\\';
+				if(i < NAMESIZE)
+					i++;
+				c = Bgetrune(finput);
+			} else
+				if(c == match)
+					break;
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		break;
+
+	case '%':
+	case '\\':
+		switch(c = Bgetrune(finput)) {
+		case '0':	return TERM;
+		case '<':	return LEFT;
+		case '2':	return BINARY;
+		case '>':	return RIGHT;
+		case '%':
+		case '\\':	return MARK;
+		case '=':	return PREC;
+		case '{':	return LCURLY;
+		default:	reserve = 1;
+		}
+
+	default:
+		/* number */
+		if(isdigit(c)) {
+			numbval = c-'0';
+			base = (c=='0')? 8: 10;
+			for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput))
+				numbval = numbval*base + (c-'0');
+			Bungetrune(finput);
+			return NUMBER;
+		}
+		if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$')  {
+			i = 0;
+			while(islower(c) || isupper(c) || isdigit(c) ||
+			    c == '-' || c=='_' || c=='.' || c=='$') {
+				if(reserve && isupper(c))
+					c += 'a'-'A';
+				rune = c;
+				c = runetochar(&tokname[i], &rune);
+				if(i < NAMESIZE)
+					i += c;
+				c = Bgetrune(finput);
+			}
+		} else
+			return c;
+		Bungetrune(finput);
+	}
+	tokname[i] = 0;
+
+	/* find a reserved word */
+	if(reserve) {
+		for(c=0; resrv[c].name; c++)
+			if(strcmp(tokname, resrv[c].name) == 0)
+				return resrv[c].value;
+		error("invalid escape, or illegal reserved word: %s", tokname);
+	}
+
+	/* look ahead to distinguish IDENTIFIER from IDENTCOLON */
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\t'|| c == '\n' || c == '\r' || c == '\f' || c == '/') {
+		if(c == '\n')
+			peekline++;
+		/* look for comments */
+		if(c == '/')
+			peekline += skipcom();
+		c = Bgetrune(finput);
+	}
+	if(c == ':')
+		return IDENTCOLON;
+	Bungetrune(finput);
+	return IDENTIFIER;
+}
+
+/*
+ * determine the type of a symbol
+ */
+int
+fdtype(int t)
+{
+	int v;
+
+	if(t >= NTBASE)
+		v = nontrst[t-NTBASE].value;
+	else
+		v = TYPE(toklev[t]);
+	if(v <= 0)
+		error("must specify type for %s", (t>=NTBASE)?
+			nontrst[t-NTBASE].name: tokset[t].name);
+	return v;
+}
+
+int
+chfind(int t, char *s)
+{
+	int i;
+
+	if(s[0] == ' ')
+		t = 0;
+	TLOOP(i)
+		if(!strcmp(s, tokset[i].name))
+			return i;
+	NTLOOP(i)
+		if(!strcmp(s, nontrst[i].name))
+			return NTBASE+i;
+
+	/* cannot find name */
+	if(t > 1)
+		error("%s should have been defined earlier", s);
+	return defin(t, s);
+}
+
+/*
+ * copy the union declaration to the output, and the define file if present
+ */
+void
+cpyunion(void)
+{
+	long c;
+	int level;
+
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	Bprint(ftable, "typedef union ");
+	if(fdefine != 0)
+		Bprint(fdefine, "\ntypedef union ");
+
+	level = 0;
+	for(;;) {
+		if((c=Bgetrune(finput)) == Beof)
+			error("EOF encountered while processing %%union");
+		Bputrune(ftable, c);
+		if(fdefine != 0)
+			Bputrune(fdefine, c);
+		switch(c) {
+		case '\n':
+			lineno++;
+			break;
+		case '{':
+			level++;
+			break;
+		case '}':
+			level--;
+
+			/* we are finished copying */
+			if(level == 0) {
+				Bprint(ftable, " YYSTYPE;\n");
+				if(fdefine != 0)
+					Bprint(fdefine, "\tYYSTYPE;\nextern\tYYSTYPE\tyylval;\n");
+				return;
+			}
+		}
+	}
+}
+
+/*
+ * copies code between \{ and \}
+ */
+void
+cpycode(void)
+{
+
+	long c;
+
+	c = Bgetrune(finput);
+	if(c == '\n') {
+		c = Bgetrune(finput);
+		lineno++;
+	}
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	while(c != Beof) {
+		if(c == '\\') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '\\');
+		}
+		if(c == '%') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '%');
+		}
+		Bputrune(ftable, c);
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+	error("eof before %%}");
+}
+
+/*
+ * skip over comments
+ * skipcom is called after reading a '/'
+ */
+int
+skipcom(void)
+{
+	long c;
+	int i;
+
+	/* i is the number of lines skipped */
+	i = 0;
+	if(Bgetrune(finput) != '*')
+		error("illegal comment");
+	c = Bgetrune(finput);
+	while(c != Beof) {
+		while(c == '*')
+			if((c=Bgetrune(finput)) == '/')
+				return i;
+		if(c == '\n')
+			i++;
+		c = Bgetrune(finput);
+	}
+	error("EOF inside comment");
+	return 0;
+}
+
+/*
+ * copy C action to the next ; or closing }
+ */
+void
+cpyact(int offset)
+{
+	long c;
+	int brac, match, j, s, fnd, tok;
+
+	Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	brac = 0;
+
+loop:
+	c = Bgetrune(finput);
+swt:
+	switch(c) {
+	case ';':
+		if(brac == 0) {
+			Bputrune(faction, c);
+			return;
+		}
+		goto lcopy;
+
+	case '{':
+		brac++;
+		goto lcopy;
+
+	case '$':
+		s = 1;
+		tok = -1;
+		c = Bgetrune(finput);
+
+		/* type description */
+		if(c == '<') {
+			Bungetrune(finput);
+			if(gettok() != TYPENAME)
+				error("bad syntax on $<ident> clause");
+			tok = numbval;
+			c = Bgetrune(finput);
+		}
+		if(c == '$') {
+			Bprint(faction, "yyval");
+
+			/* put out the proper tag... */
+			if(ntypes) {
+				if(tok < 0)
+					tok = fdtype(*prdptr[nprod]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(c == '-') {
+			s = -s;
+			c = Bgetrune(finput);
+		}
+		if(isdigit(c)) {
+			j = 0;
+			while(isdigit(c)) {
+				j = j*10 + (c-'0');
+				c = Bgetrune(finput);
+			}
+			Bungetrune(finput);
+			j = j*s - offset;
+			if(j > 0)
+				error("Illegal use of $%d", j+offset);
+
+		dollar:
+			Bprint(faction, "yypt[-%d].yyv", -j);
+
+			/* put out the proper tag */
+			if(ntypes) {
+				if(j+offset <= 0 && tok < 0)
+					error("must specify type of $%d", j+offset);
+				if(tok < 0)
+					tok = fdtype(prdptr[nprod][j+offset]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(isupper(c) || islower(c) || c == '_' || c == '.') {
+			int tok; /* tok used oustide for type info */
+
+			/* look for $name */
+			Bungetrune(finput);
+			if(gettok() != IDENTIFIER)
+				error("$ must be followed by an identifier");
+			tok = chfind(2, tokname);
+			if((c = Bgetrune(finput)) != '#') {
+				Bungetrune(finput);
+				fnd = -1;
+			} else
+				if(gettok() != NUMBER) {
+					error("# must be followed by number");
+					fnd = -1;
+				} else
+					fnd = numbval;
+			for(j=1; j<=offset; ++j)
+				if(tok == prdptr[nprod][j]) {
+					if(--fnd <= 0) {
+						j -= offset;
+						goto dollar;
+					}
+				}
+			error("$name or $name#number not found");
+		}
+		Bputc(faction, '$');
+		if(s < 0 )
+			Bputc(faction, '-');
+		goto swt;
+
+	case '}':
+		brac--;
+		if(brac)
+			goto lcopy;
+		Bputrune(faction, c);
+		return;
+
+	case '/':
+		/* look for comments */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		if(c != '*')
+			goto swt;
+
+		/* it really is a comment */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		while(c >= 0) {
+			while(c == '*') {
+				Bputrune(faction, c);
+				if((c=Bgetrune(finput)) == '/')
+					goto lcopy;
+			}
+			Bputrune(faction, c);
+			if(c == '\n')
+				lineno++;
+			c = Bgetrune(finput);
+		}
+		error("EOF inside comment");
+
+	case '\'':
+		/* character constant */
+		match = '\'';
+		goto string;
+
+	case '"':
+		/* character string */
+		match = '"';
+
+	string:
+		Bputrune(faction, c);
+		while(c = Bgetrune(finput)) {
+			if(c == '\\') {
+				Bputrune(faction, c);
+				c = Bgetrune(finput);
+				if(c == '\n')
+					lineno++;
+			} else
+				if(c == match)
+					goto lcopy;
+				if(c == '\n')
+					error("newline in string or char. const.");
+			Bputrune(faction, c);
+		}
+		error("EOF in string or character constant");
+
+	case Beof:
+		error("action does not terminate");
+
+	case '\n':
+		lineno++;
+		goto lcopy;
+	}
+
+lcopy:
+	Bputrune(faction, c);
+	goto loop;
+}
+
+void
+openup(char *stem, int dflag, int vflag, int ytab, char *ytabc)
+{
+	char buf[256];
+
+	if(vflag) {
+		sprint(buf, "%s.%s", stem, FILEU);
+		foutput = Bopen(buf, OWRITE);
+		if(foutput == 0)
+			error("cannot open %s", buf);
+	}
+	if(yydebug) {
+		sprint(buf, "%s.%s", stem, FILEDEBUG);
+		if((fdebug = Bopen(buf, OWRITE)) == 0)
+			error("can't open %s", buf);
+	}
+	if(dflag) {
+		sprint(buf, "%s.%s", stem, FILED);
+		fdefine = Bopen(buf, OWRITE);
+		if(fdefine == 0)
+			error("can't create %s", buf);
+	}
+	if(ytab == 0)
+		sprint(buf, "%s.%s", stem, OFILE);
+	else
+		strcpy(buf, ytabc);
+	ftable = Bopen(buf, OWRITE);
+	if(ftable == 0)
+		error("cannot open table file %s", buf);
+}
+
+/*
+ * print the output for the states
+ */
+void
+output(void)
+{
+	int i, k, c;
+	Wset *u, *v;
+
+	Bprint(ftable, "short	yyexca[] =\n{");
+	if(fdebug)
+		Bprint(fdebug, "char*	yystates[] =\n{\n");
+
+	/* output the stuff for state i */
+	SLOOP(i) {
+		nolook = tystate[i]!=MUSTLOOKAHEAD;
+		closure(i);
+
+		/* output actions */
+		nolook = 1;
+		aryfil(temp1, ntokens+nnonter+1, 0);
+		WSLOOP(wsets, u) {
+			c = *(u->pitem);
+			if(c > 1 && c < NTBASE && temp1[c] == 0) {
+				WSLOOP(u, v)
+					if(c == *(v->pitem))
+						putitem(v->pitem+1, (Lkset*)0);
+				temp1[c] = state(c);
+			} else
+				if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0)
+					temp1[c+ntokens] = amem[indgo[i]+c];
+		}
+		if(i == 1)
+			temp1[1] = ACCEPTCODE;
+
+		/* now, we have the shifts; look at the reductions */
+		lastred = 0;
+		WSLOOP(wsets, u) {
+			c = *u->pitem;
+
+			/* reduction */
+			if(c <= 0) {
+				lastred = -c;
+				TLOOP(k)
+					if(BIT(u->ws.lset, k)) {
+						if(temp1[k] == 0)
+							temp1[k] = c;
+						else
+						if(temp1[k] < 0) { /* reduce/reduce conflict */
+							if(foutput)
+								Bprint(foutput,
+									"\n%d: reduce/reduce conflict"
+									" (red'ns %d and %d ) on %s",
+									i, -temp1[k], lastred,
+									symnam(k));
+							if(-temp1[k] > lastred)
+								temp1[k] = -lastred;
+							zzrrconf++;
+						} else
+							/* potential shift/reduce conflict */
+							precftn( lastred, k, i );
+					}
+				}
+		}
+		wract(i);
+	}
+
+	if(fdebug)
+		Bprint(fdebug, "};\n");
+	Bprint(ftable, "};\n");
+	Bprint(ftable, "#define	YYNPROD	%d\n", nprod);
+	Bprint(ftable, "#define	YYPRIVATE %d\n", PRIVATE);
+	if(yydebug)
+		Bprint(ftable, "#define	yydebug	%s\n", yydebug);
+}
+
+/*
+ * pack state i from temp1 into amem
+ */
+int
+apack(int *p, int n)
+{
+	int *pp, *qq, *rr, off, *q, *r;
+
+	/* we don't need to worry about checking because
+	 * we will only look at entries known to be there...
+	 * eliminate leading and trailing 0's
+	 */
+
+	q = p+n;
+	for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
+		;
+ 	/* no actions */
+	if(pp > q)
+		return 0;
+	p = pp;
+
+	/* now, find a place for the elements from p to q, inclusive */
+	r = &amem[ACTSIZE-1];
+	for(rr = amem; rr <= r; rr++, off++) {
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp != 0)
+				if(*pp != *qq && *qq != 0)
+					goto nextk;
+
+		/* we have found an acceptable k */
+		if(pkdebug && foutput != 0)
+			Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem));
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp) {
+				if(qq > r)
+					error("action table overflow");
+				if(qq > memp)
+					memp = qq;
+				*qq = *pp;
+			}
+		if(pkdebug && foutput != 0)
+			for(pp = amem; pp <= memp; pp += 10) {
+				Bprint(foutput, "\t");
+				for(qq = pp; qq <= pp+9; qq++)
+					Bprint(foutput, "%d ", *qq);
+				Bprint(foutput, "\n");
+			}
+		return(off);
+	nextk:;
+	}
+	error("no space in action table");
+	return 0;
+}
+
+/*
+ * output the gotos for the nontermninals
+ */
+void
+go2out(void)
+{
+	int i, j, k, best, count, cbest, times;
+
+	/* mark begining of gotos */
+	Bprint(ftemp, "$\n");
+	for(i = 1; i <= nnonter; i++) {
+		go2gen(i);
+
+		/* find the best one to make default */
+		best = -1;
+		times = 0;
+
+		/* is j the most frequent */
+		for(j = 0; j <= nstate; j++) {
+			if(tystate[j] == 0)
+				continue;
+			if(tystate[j] == best)
+				continue;
+
+			/* is tystate[j] the most frequent */
+			count = 0;
+			cbest = tystate[j];
+			for(k = j; k <= nstate; k++)
+				if(tystate[k] == cbest)
+					count++;
+			if(count > times) {
+				best = cbest;
+				times = count;
+			}
+		}
+
+		/* best is now the default entry */
+		zzgobest += times-1;
+		for(j = 0; j <= nstate; j++)
+			if(tystate[j] != 0 && tystate[j] != best) {
+				Bprint(ftemp, "%d,%d,", j, tystate[j]);
+				zzgoent++;
+			}
+
+		/* now, the default */
+		if(best == -1)
+			best = 0;
+		zzgoent++;
+		Bprint(ftemp, "%d\n", best);
+	}
+}
+
+/*
+ * output the gotos for nonterminal c
+ */
+void
+go2gen(int c)
+{
+	int i, work, cc;
+	Item *p, *q;
+
+
+	/* first, find nonterminals with gotos on c */
+	aryfil(temp1, nnonter+1, 0);
+	temp1[c] = 1;
+	work = 1;
+	while(work) {
+		work = 0;
+		PLOOP(0, i)
+
+			/* cc is a nonterminal */
+			if((cc=prdptr[i][1]-NTBASE) >= 0)
+				/* cc has a goto on c */
+				if(temp1[cc] != 0) {
+
+					/* thus, the left side of production i does too */
+					cc = *prdptr[i]-NTBASE;
+					if(temp1[cc] == 0) {
+						  work = 1;
+						  temp1[cc] = 1;
+					}
+				}
+	}
+
+	/* now, we have temp1[c] = 1 if a goto on c in closure of cc */
+	if(g2debug && foutput != 0) {
+		Bprint(foutput, "%s: gotos on ", nontrst[c].name);
+		NTLOOP(i)
+			if(temp1[i])
+				Bprint(foutput, "%s ", nontrst[i].name);
+		Bprint(foutput, "\n");
+	}
+
+	/* now, go through and put gotos into tystate */
+	aryfil(tystate, nstate, 0);
+	SLOOP(i)
+		ITMLOOP(i, p, q)
+			if((cc = *p->pitem) >= NTBASE)
+				/* goto on c is possible */
+				if(temp1[cc-NTBASE]) {
+					tystate[i] = amem[indgo[i]+c];
+					break;
+				}
+}
+
+/*
+ * decide a shift/reduce conflict by precedence.
+ * r is a rule number, t a token number
+ * the conflict is in state s
+ * temp1[t] is changed to reflect the action
+ */
+void
+precftn(int r, int t, int s)
+{
+	int lp, lt, action;
+
+	lp = levprd[r];
+	lt = toklev[t];
+	if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
+
+		/* conflict */
+		if(foutput != 0)
+			Bprint(foutput,
+				"\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s",
+				s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t));
+		zzsrconf++;
+		return;
+	}
+	if(PLEVEL(lt) == PLEVEL(lp))
+		action = ASSOC(lt);
+	else
+		if(PLEVEL(lt) > PLEVEL(lp))
+			action = RASC;  /* shift */
+		else
+			action = LASC;  /* reduce */
+	switch(action) {
+	case BASC:  /* error action */
+		temp1[t] = ERRCODE;
+		break;
+	case LASC:  /* reduce */
+		temp1[t] = -r;
+		break;
+	}
+}
+
+/*
+ * output state i
+ * temp1 has the actions, lastred the default
+ */
+void
+wract(int i)
+{
+	int p, p0, p1, ntimes, tred, count, j, flag;
+
+	/* find the best choice for lastred */
+	lastred = 0;
+	ntimes = 0;
+	TLOOP(j) {
+		if(temp1[j] >= 0)
+			continue;
+		if(temp1[j]+lastred == 0)
+			continue;
+		/* count the number of appearances of temp1[j] */
+		count = 0;
+		tred = -temp1[j];
+		levprd[tred] |= REDFLAG;
+		TLOOP(p)
+			if(temp1[p]+tred == 0)
+				count++;
+		if(count > ntimes) {
+			lastred = tred;
+			ntimes = count;
+		}
+	}
+
+	/*
+	 * for error recovery, arrange that, if there is a shift on the
+	 * error recovery token, `error', that the default be the error action
+	 */
+	if(temp1[2] > 0)
+		lastred = 0;
+
+	/* clear out entries in temp1 which equal lastred */
+	TLOOP(p)
+		if(temp1[p]+lastred == 0)
+			temp1[p] = 0;
+
+	wrstate(i);
+	defact[i] = lastred;
+	flag = 0;
+	TLOOP(p0)
+		if((p1=temp1[p0]) != 0) {
+			if(p1 < 0) {
+				p1 = -p1;
+				goto exc;
+			}
+			if(p1 == ACCEPTCODE) {
+				p1 = -1;
+				goto exc;
+			}
+			if(p1 == ERRCODE) {
+				p1 = 0;
+			exc:
+				if(flag++ == 0)
+					Bprint(ftable, "-1, %d,\n", i);
+				Bprint(ftable, "\t%d, %d,\n", p0, p1);
+				zzexcp++;
+				continue;
+			}
+			Bprint(ftemp, "%d,%d,", p0, p1);
+			zzacent++;
+		}
+	if(flag) {
+		defact[i] = -2;
+		Bprint(ftable, "\t-2, %d,\n", lastred);
+	}
+	Bprint(ftemp, "\n");
+}
+
+/*
+ * writes state i
+ */
+void
+wrstate(int i)
+{
+	int j0, j1;
+	Item *pp, *qq;
+	Wset *u;
+
+	if(fdebug) {
+		if(lastred) {
+			Bprint(fdebug, "	0, /*%d*/\n", i);
+		} else {
+			Bprint(fdebug, "	\"");
+			ITMLOOP(i, pp, qq)
+				Bprint(fdebug, "%s\\n", writem(pp->pitem));
+			if(tystate[i] == MUSTLOOKAHEAD)
+				WSLOOP(wsets + (pstate[i+1] - pstate[i]), u)
+					if(*u->pitem < 0)
+						Bprint(fdebug, "%s\\n", writem(u->pitem));
+			Bprint(fdebug, "\", /*%d*/\n", i);
+		}
+	}
+	if(foutput == 0)
+		return;
+	Bprint(foutput, "\nstate %d\n", i);
+	ITMLOOP(i, pp, qq)
+		Bprint(foutput, "\t%s\n", writem(pp->pitem));
+	if(tystate[i] == MUSTLOOKAHEAD)
+		/* print out empty productions in closure */
+		WSLOOP(wsets+(pstate[i+1]-pstate[i]), u)
+			if(*u->pitem < 0)
+				Bprint(foutput, "\t%s\n", writem(u->pitem));
+
+	/* check for state equal to another */
+	TLOOP(j0)
+		if((j1=temp1[j0]) != 0) {
+			Bprint(foutput, "\n\t%s  ", symnam(j0));
+			/* shift, error, or accept */
+			if(j1 > 0) {
+				if(j1 == ACCEPTCODE)
+					Bprint(foutput,  "accept");
+				else
+					if(j1 == ERRCODE)
+						Bprint(foutput, "error");
+					else
+						Bprint(foutput, "shift %d", j1);
+			} else
+				Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]);
+		}
+
+	/* output the final production */
+	if(lastred)
+		Bprint(foutput, "\n\t.  reduce %d (src line %d)\n\n",
+			lastred, rlines[lastred]);
+	else
+		Bprint(foutput, "\n\t.  error\n\n");
+
+	/* now, output nonterminal actions */
+	j1 = ntokens;
+	for(j0 = 1; j0 <= nnonter; j0++) {
+		j1++;
+		if(temp1[j1])
+			Bprint(foutput, "\t%s  goto %d\n", symnam(j0+NTBASE), temp1[j1]);
+	}
+}
+
+void
+warray(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i=0;;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i >= n) {
+			Bprint(ftable, "\n};\n");
+			break;
+		}
+		Bprint(ftable, ",");
+	}
+}
+
+/*
+ * in order to free up the mem and amem arrays for the optimizer,
+ * and still be able to output yyr1, etc., after the sizes of
+ * the action array is known, we hide the nonterminals
+ * derived by productions in levprd.
+ */
+
+void
+hideprod(void)
+{
+	int i, j;
+
+	j = 0;
+	levprd[0] = 0;
+	PLOOP(1, i) {
+		if(!(levprd[i] & REDFLAG)) {
+			j++;
+			if(foutput != 0)
+				Bprint(foutput, "Rule not reduced:   %s\n", writem(prdptr[i]));
+		}
+		levprd[i] = *prdptr[i] - NTBASE;
+	}
+	if(j)
+		print("%d rules never reduced\n", j);
+}
+
+void
+callopt(void)
+{
+	int i, *p, j, k, *q;
+
+	/* read the arrays from tempfile and set parameters */
+	finput = Bopen(tempname, OREAD);
+	if(finput == 0)
+		error("optimizer cannot open tempfile");
+	pgo[0] = 0;
+	temp1[0] = 0;
+	nstate = 0;
+	nnonter = 0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nstate++;
+			pmem--;
+			temp1[nstate] = pmem - mem0;
+		case ',':
+			continue;
+		case '$':
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+
+	pmem--;
+	temp1[nstate] = yypgo[0] = pmem - mem0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nnonter++;
+			yypgo[nnonter] = pmem-mem0;
+		case ',':
+			continue;
+		case -1:
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+	pmem--;
+	yypgo[nnonter--] = pmem - mem0;
+	for(i = 0; i < nstate; i++) {
+		k = 32000;
+		j = 0;
+		q = mem0 + temp1[i+1];
+		for(p = mem0 + temp1[i]; p < q ; p += 2) {
+			if(*p > j)
+				j = *p;
+			if(*p < k)
+				k = *p;
+		}
+		/* nontrivial situation */
+		if(k <= j) {
+			/* j is now the range */
+/*			j -= k;			/* call scj */
+			if(k > maxoff)
+				maxoff = k;
+		}
+		tystate[i] = (temp1[i+1]-temp1[i]) + 2*j;
+		if(j > maxspr)
+			maxspr = j;
+	}
+
+	/* initialize ggreed table */
+	for(i = 1; i <= nnonter; i++) {
+		ggreed[i] = 1;
+		j = 0;
+
+		/* minimum entry index is always 0 */
+		q = mem0 + yypgo[i+1] - 1;
+		for(p = mem0+yypgo[i]; p < q ; p += 2) {
+			ggreed[i] += 2;
+			if(*p > j)
+				j = *p;
+		}
+		ggreed[i] = ggreed[i] + 2*j;
+		if(j > maxoff)
+			maxoff = j;
+	}
+
+	/* now, prepare to put the shift actions into the amem array */
+	for(i = 0; i < ACTSIZE; i++)
+		amem[i] = 0;
+	maxa = amem;
+	for(i = 0; i < nstate; i++) {
+		if(tystate[i] == 0 && adb > 1)
+			Bprint(ftable, "State %d: null\n", i);
+		indgo[i] = YYFLAG1;
+	}
+	while((i = nxti()) != NOMORE)
+		if(i >= 0)
+			stin(i);
+		else
+			gin(-i);
+
+	/* print amem array */
+	if(adb > 2 )
+		for(p = amem; p <= maxa; p += 10) {
+			Bprint(ftable, "%4d  ", (int)(p-amem));
+			for(i = 0; i < 10; ++i)
+				Bprint(ftable, "%4d  ", p[i]);
+			Bprint(ftable, "\n");
+		}
+
+	/* write out the output appropriate to the language */
+	aoutput();
+	osummary();
+	Bterm(finput);
+	ZAPFILE(tempname);
+}
+
+void
+gin(int i)
+{
+	int *p, *r, *s, *q1, *q2;
+
+	/* enter gotos on nonterminal i into array amem */
+	ggreed[i] = 0;
+
+	q2 = mem0+ yypgo[i+1] - 1;
+	q1 = mem0 + yypgo[i];
+
+	/* now, find amem place for it */
+	for(p = amem; p < &amem[ACTSIZE]; p++) {
+		if(*p)
+			continue;
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			if(*s)
+				goto nextgp;
+			if(s > maxa)
+				if((maxa = s) > &amem[ACTSIZE])
+					error("a array overflow");
+		}
+		/* we have found amem spot */
+		*p = *q2;
+		if(p > maxa)
+			if((maxa = p) > &amem[ACTSIZE])
+				error("a array overflow");
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			*s = r[1];
+		}
+		pgo[i] = p-amem;
+		if(adb > 1)
+			Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]);
+		return;
+
+	nextgp:;
+	}
+	error("cannot place goto %d\n", i);
+}
+
+void
+stin(int i)
+{
+	int *r, *s, n, flag, j, *q1, *q2;
+
+	tystate[i] = 0;
+
+	/* enter state i into the amem array */
+	q2 = mem0+temp1[i+1];
+	q1 = mem0+temp1[i];
+	/* find an acceptable place */
+	for(n = -maxoff; n < ACTSIZE; n++) {
+		flag = 0;
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r + n + amem) < amem)
+				goto nextn;
+			if(*s == 0)
+				flag++;
+			else
+				if(*s != r[1])
+					goto nextn;
+		}
+
+		/* check that the position equals another only if the states are identical */
+		for(j=0; j<nstate; j++) {
+			if(indgo[j] == n) {
+
+				/* we have some disagreement */
+				if(flag)
+					goto nextn;
+				if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) {
+
+					/* states are equal */
+					indgo[i] = n;
+					if(adb > 1)
+						Bprint(ftable,
+						"State %d: entry at %d equals state %d\n",
+						i, n, j);
+					return;
+				}
+
+				/* we have some disagreement */
+				goto nextn;
+			}
+		}
+
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r+n+amem) >= &amem[ACTSIZE])
+				error("out of space in optimizer a array");
+			if(s > maxa)
+				maxa = s;
+			if(*s != 0 && *s != r[1])
+				error("clobber of a array, pos'n %d, by %d", s-amem, r[1]);
+			*s = r[1];
+		}
+		indgo[i] = n;
+		if(adb > 1)
+			Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]);
+		return;
+	nextn:;
+	}
+	error("Error; failure to place state %d\n", i);
+}
+
+/*
+ * finds the next i
+ */
+int
+nxti(void)
+{
+	int i, max, maxi;
+
+	max = 0;
+	maxi = 0;
+	for(i = 1; i <= nnonter; i++)
+		if(ggreed[i] >= max) {
+			max = ggreed[i];
+			maxi = -i;
+		}
+	for(i = 0; i < nstate; ++i)
+		if(tystate[i] >= max) {
+			max = tystate[i];
+			maxi = i;
+		}
+	if(nxdb)
+		Bprint(ftable, "nxti = %d, max = %d\n", maxi, max);
+	if(max == 0)
+		return NOMORE;
+	return maxi;
+}
+
+/*
+ * write summary
+ */
+void
+osummary(void)
+{
+
+	int i, *p;
+
+	if(foutput == 0)
+		return;
+	i = 0;
+	for(p = maxa; p >= amem; p--)
+		if(*p == 0)
+			i++;
+
+	Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
+		(int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE);
+	Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i);
+	Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);
+}
+
+/*
+ * this version is for C
+ * write out the optimized parser
+ */
+void
+aoutput(void)
+{
+	Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1));
+	arout("yyact", amem, (maxa-amem)+1);
+	arout("yypact", indgo, nstate);
+	arout("yypgo", pgo, nnonter+1);
+}
+
+void
+arout(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i = 0; i < n;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i == n)
+			Bprint(ftable, "\n};\n");
+		else
+			Bprint(ftable, ",");
+	}
+}
+
+/*
+ * read and convert an integer from the standard input
+ * return the terminating character
+ * blanks, tabs, and newlines are ignored
+ */
+int
+gtnm(void)
+{
+	int sign, val, c;
+
+	sign = 0;
+	val = 0;
+	while((c=Bgetrune(finput)) != Beof) {
+		if(isdigit(c)) {
+			val = val*10 + c-'0';
+			continue;
+		}
+		if(c == '-') {
+			sign = 1;
+			continue;
+		}
+		break;
+	}
+	if(sign)
+		val = -val;
+	*pmem++ = val;
+	if(pmem >= &mem0[MEMSIZE])
+		error("out of space");
+	return c;
+}
--- /dev/null
+++ b/utils/iyacc/yaccpar
@@ -1,0 +1,241 @@
+#define YYFLAG 		-1000
+#define	yyclearin	yychar = -1
+#define	yyerrok		yyerrflag = 0
+
+#ifdef	yydebug
+#include	"y.debug"
+#else
+#define	yydebug		0
+char*	yytoknames[1];		/* for debugging */
+char*	yystates[1];		/* for debugging */
+#endif
+
+/*	parser for yacc output	*/
+
+int	yynerrs = 0;		/* number of errors */
+int	yyerrflag = 0;		/* error recovery flag */
+
+extern	int	fprint(int, char*, ...);
+extern	int	sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+	static char x[16];
+
+	if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+	if(yytoknames[yyc-1])
+		return yytoknames[yyc-1];
+	sprint(x, "<%d>", yyc);
+	return x;
+}
+
+char*
+yystatname(int yys)
+{
+	static char x[16];
+
+	if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+	if(yystates[yys])
+		return yystates[yys];
+	sprint(x, "<%d>\n", yys);
+	return x;
+}
+
+long
+yylex1(void)
+{
+	long yychar;
+	long *t3p;
+	int c;
+
+	yychar = yylex();
+	if(yychar <= 0) {
+		c = yytok1[0];
+		goto out;
+	}
+	if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+		c = yytok1[yychar];
+		goto out;
+	}
+	if(yychar >= YYPRIVATE)
+		if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+			c = yytok2[yychar-YYPRIVATE];
+			goto out;
+		}
+	for(t3p=yytok3;; t3p+=2) {
+		c = t3p[0];
+		if(c == yychar) {
+			c = t3p[1];
+			goto out;
+		}
+		if(c == 0)
+			break;
+	}
+	c = 0;
+
+out:
+	if(c == 0)
+		c = yytok2[1];	/* unknown char */
+	if(yydebug >= 3)
+		fprint(2, "lex %.4lux %s\n", yychar, yytokname(c));
+	return c;
+}
+
+int
+yyparse(void)
+{
+	struct
+	{
+		YYSTYPE	yyv;
+		int	yys;
+	} yys[YYMAXDEPTH], *yyp, *yypt;
+	short *yyxi;
+	int yyj, yym, yystate, yyn, yyg;
+	long yychar;
+	YYSTYPE save1, save2;
+	int save3, save4;
+
+	save1 = yylval;
+	save2 = yyval;
+	save3 = yynerrs;
+	save4 = yyerrflag;
+
+	yystate = 0;
+	yychar = -1;
+	yynerrs = 0;
+	yyerrflag = 0;
+	yyp = &yys[-1];
+	goto yystack;
+
+ret0:
+	yyn = 0;
+	goto ret;
+
+ret1:
+	yyn = 1;
+	goto ret;
+
+ret:
+	yylval = save1;
+	yyval = save2;
+	yynerrs = save3;
+	yyerrflag = save4;
+	return yyn;
+
+yystack:
+	/* put a state and value onto the stack */
+	if(yydebug >= 4)
+		fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+	yyp++;
+	if(yyp >= &yys[YYMAXDEPTH]) {
+		yyerror("yacc stack overflow");
+		goto ret1;
+	}
+	yyp->yys = yystate;
+	yyp->yyv = yyval;
+
+yynewstate:
+	yyn = yypact[yystate];
+	if(yyn <= YYFLAG)
+		goto yydefault; /* simple state */
+	if(yychar < 0)
+		yychar = yylex1();
+	yyn += yychar;
+	if(yyn < 0 || yyn >= YYLAST)
+		goto yydefault;
+	yyn = yyact[yyn];
+	if(yychk[yyn] == yychar) { /* valid shift */
+		yychar = -1;
+		yyval = yylval;
+		yystate = yyn;
+		if(yyerrflag > 0)
+			yyerrflag--;
+		goto yystack;
+	}
+
+yydefault:
+	/* default state action */
+	yyn = yydef[yystate];
+	if(yyn == -2) {
+		if(yychar < 0)
+			yychar = yylex1();
+
+		/* look through exception table */
+		for(yyxi=yyexca;; yyxi+=2)
+			if(yyxi[0] == -1 && yyxi[1] == yystate)
+				break;
+		for(yyxi += 2;; yyxi += 2) {
+			yyn = yyxi[0];
+			if(yyn < 0 || yyn == yychar)
+				break;
+		}
+		yyn = yyxi[1];
+		if(yyn < 0)
+			goto ret0;
+	}
+	if(yyn == 0) {
+		/* error ... attempt to resume parsing */
+		switch(yyerrflag) {
+		case 0:   /* brand new error */
+			yyerror("syntax error");
+			yynerrs++;
+			if(yydebug >= 1) {
+				fprint(2, "%s", yystatname(yystate));
+				fprint(2, "saw %s\n", yytokname(yychar));
+			}
+
+		case 1:
+		case 2: /* incompletely recovered error ... try again */
+			yyerrflag = 3;
+
+			/* find a state where "error" is a legal shift action */
+			while(yyp >= yys) {
+				yyn = yypact[yyp->yys] + YYERRCODE;
+				if(yyn >= 0 && yyn < YYLAST) {
+					yystate = yyact[yyn];  /* simulate a shift of "error" */
+					if(yychk[yystate] == YYERRCODE)
+						goto yystack;
+				}
+
+				/* the current yyp has no shift onn "error", pop stack */
+				if(yydebug >= 2)
+					fprint(2, "error recovery pops state %d, uncovers %d\n",
+						yyp->yys, (yyp-1)->yys );
+				yyp--;
+			}
+			/* there is no state on the stack with an error shift ... abort */
+			goto ret1;
+
+		case 3:  /* no shift yet; clobber input char */
+			if(yydebug >= 2)
+				fprint(2, "error recovery discards %s\n", yytokname(yychar));
+			if(yychar == YYEOFCODE)
+				goto ret1;
+			yychar = -1;
+			goto yynewstate;   /* try again in the same state */
+		}
+	}
+
+	/* reduction by production yyn */
+	if(yydebug >= 2)
+		fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+	yypt = yyp;
+	yyp -= yyr2[yyn];
+	yyval = (yyp+1)->yyv;
+	yym = yyn;
+
+	/* consult goto table to find next state */
+	yyn = yyr1[yyn];
+	yyg = yypgo[yyn];
+	yyj = yyg + yyp->yys + 1;
+
+	if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+		yystate = yyact[yyg];
+	switch(yym) {
+		$A
+	}
+	goto yystack;  /* stack new state and value */
+}
--- /dev/null
+++ b/utils/ka/a.h
@@ -1,0 +1,181 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../kc/k.out.h"
+
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		500
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+EXTERN	struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN	struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym	*sym;
+	long	offset;
+	short	type;
+	short	reg;
+	short	xreg;
+	short	name;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	int	nosched;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	alloc(long);
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+void	outcode(int, Gen*, int, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	macprag(void);
+void	maclin(void);
+void	macif(int);
+void	macend(void);
+void	dodefine(char*);
+void	prfile(long);
+void	outhist(void);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/ka/a.y
@@ -1,0 +1,734 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	long	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LMOVW LMOVD LMOVB LSWAP LADDW LCMP
+%token	<lval>	LBRA LFMOV LFCONV LFADD LCPOP LTRAP LJMPL LXORW
+%token	<lval>	LNOP LEND LRETT LUNIMP LTEXT LDATA LRETRN
+%token	<lval>	LCONST LSP LSB LFP LPC LCREG LFLUSH
+%token	<lval>	LREG LFREG LR LC LF
+%token	<lval>	LFSR LFPQ LPSR LSCHED
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg
+%type	<gen>	addr rreg name psr creg freg
+%type	<gen>	imm ximm fimm rel fsr fpq
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LSCHED ';'
+	{
+		nosched = $1;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * B.1 load integer instructions
+ */
+	LMOVW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.2 load floating instructions
+ *	includes CSR
+ */
+|	LMOVD addr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV addr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV fimm ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV freg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW addr ',' fsr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.3 load coprocessor instructions
+ *	excludes CSR
+ */
+|	LMOVW addr ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD addr ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.4 store integer instructions
+ */
+|	LMOVW rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW imm ',' addr
+	{
+		if($2.offset != 0)
+			yyerror("constant must be zero");
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB imm ',' addr
+	{
+		if($2.offset != 0)
+			yyerror("constant must be zero");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.5 store floating instructions
+ *	includes CSR and CQ
+ */
+|	LMOVW freg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD freg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW fsr ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD fpq ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.6 store coprocessor instructions
+ *	excludes CSR and CQ
+ */
+|	LMOVW creg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD creg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.7 atomic load unsigned byte (TAS)
+ * B.8 swap
+ */
+|	LSWAP addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.9  add instructions
+ * B.10 tagged add instructions
+ * B.11 subtract instructions
+ * B.12 tagged subtract instructions
+ * B.13 multiply step instruction
+ * B.14 logical instructions
+ * B.15 shift instructions
+ * B.17 save/restore
+ */
+|	LADDW rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LADDW imm ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LADDW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LADDW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXORW rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LXORW imm ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LXORW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXORW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.16 set hi
+ *	other pseudo moves
+ */
+|	LMOVW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW ximm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVD ximm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.18 branch on integer condition
+ * B.19 floating point branch on condition
+ * B.20 coprocessor branch on condition
+ */
+|	LBRA comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * B.21 call instruction
+ * B.22 jump and link instruction
+ */
+|	LJMPL comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LJMPL comma addr
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LJMPL comma sreg ',' rel
+	{
+		outcode($1, &nullgen, $3, &$5);
+	}
+|	LJMPL comma sreg ',' addr
+	{
+		outcode($1, &nullgen, $3, &$5);
+	}
+/*
+ * B.23 return from trap
+ */
+|	LRETT rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.28 instruction cache flush
+ */
+|	LFLUSH rel comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LFLUSH addr comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * B.24 trap on condition
+ */
+|	LTRAP rreg ',' sreg
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTRAP imm ',' sreg
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTRAP rreg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LTRAP comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * B.25 read state register instructions
+ */
+|	LMOVW psr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * B.26 write state register instructions BOTCH XOR
+ */
+|	LMOVW rreg ',' psr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW imm ',' psr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXORW rreg ',' sreg ',' psr
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LXORW imm ',' sreg ',' psr
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * B.27 unimplemented trap
+ */
+|	LUNIMP	comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LUNIMP imm comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * B.29 floating point operate
+ */
+|	LFCONV freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFADD freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFADD freg ',' freg ',' freg
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * B.30 coprocessor operate
+ */
+|	LCPOP creg ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LCPOP creg ',' creg ',' creg
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * CMP
+ */
+|	LCMP rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LCMP rreg ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * NOP
+ */
+|	LNOP comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LNOP rreg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LNOP freg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LNOP ',' rreg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LNOP ',' freg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * END
+ */
+|	LEND comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTEXT name ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTEXT name ',' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * DATA
+ */
+|	LDATA name '/' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LDATA name '/' con ',' ximm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LDATA name '/' con ',' fimm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * RETURN
+ */
+|	LRETRN	comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+rreg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+fsr:
+	LFSR
+	{
+		$$ = nullgen;
+		$$.type = D_PREG;
+		$$.reg = $1;
+	}
+
+fpq:
+	LFPQ
+	{
+		$$ = nullgen;
+		$$.type = D_PREG;
+		$$.reg = $1;
+	}
+
+psr:
+	LPSR
+	{
+		$$ = nullgen;
+		$$.type = D_PREG;
+		$$.reg = $1;
+	}
+
+creg:
+	LCREG
+	{
+		$$ = nullgen;
+		$$.type = D_CREG;
+		$$.reg = $1;
+	}
+|	LC '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_CREG;
+		$$.reg = $3;
+	}
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+ximm:
+	'$' addr
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+
+fimm:
+	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+sreg:
+	LREG
+|	LR '(' con ')'
+	{
+		if($$ < 0 || $$ >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+addr:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	'(' sreg ',' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_ASI;
+		$$.reg = $2;
+		$$.offset = $4;
+	}
+|	'(' sreg '+' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.xreg = $4;
+		$$.offset = 0;
+	}
+|	name
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+comma:
+|	','
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/ka/l.s
@@ -1,0 +1,696 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	HZ		20			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+#define	TK2MS(t)	((((ulong)(t))*1000)/HZ)	/* ticks to milliseconds */
+#define	MS2TK(t)	((((ulong)(t))*HZ)/1000)	/* milliseconds to ticks */
+
+/*
+ * PSR bits
+ */
+#define	PSREC		0x00002000
+#define	PSREF		0x00001000
+#define PSRSUPER	0x00000080
+#define PSRPSUPER	0x00000040
+#define	PSRET		0x00000020
+#define SPL(n)		(n<<8)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		6		/* R6 is m-> */
+#define	USER		5		/* R5 is u-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	USERADDR	0xE0000000
+#define	UREGADDR	(USERADDR+BY2PG-((32+6)*BY2WD))
+#define	BOOTSTACK	(KTZERO-0*BY2PG)
+#define	TRAPS		(KTZERO-2*BY2PG)
+
+/*
+ * MMU
+ */
+
+#define	VAMASK		0x3FFFFFFF
+#define	NPMEG		(1<<12)
+#define	BY2SEGM		(1<<18)
+#define	PG2SEGM		(1<<6)
+#define	NTLBPID		(1+NCONTEXT)	/* TLBPID 0 is unallocated */
+#define	NCONTEXT	8
+#define	CONTEXT		0x30000000	/* in ASI 2 */
+
+/*
+ * MMU regions
+ */
+#define	INVALIDSEGM	0xFFFC0000	/* highest seg of VA reserved as invalid */
+#define	INVALIDPMEG	0x7F
+#define	SCREENSEGM	0xFFF80000
+#define	SCREENPMEG	0x7E
+#define	ROMSEGM		0xFFE80000
+#define	ROMEND		0xFFEA0000
+#define	PG2ROM		((ROMEND-ROMSEGM)/BY2PG)
+#define	IOSEGM0		ROMSEGM		/* see mmuinit() */
+#define	NIOSEGM		((SCREENSEGM-ROMSEGM)/BY2SEGM)
+#define	IOPMEG0		(SCREENPMEG-NIOSEGM)
+#define	IOSEGM		ROMEND
+#define	IOEND		SCREENSEGM
+#define	TOPPMEG		IOPMEG0
+
+/*
+ * MMU entries
+ */
+#define	PTEVALID	(1<<31)
+#define	PTERONLY	(0<<30)
+#define	PTEWRITE	(1<<30)
+#define	PTEKERNEL	(1<<29)
+#define	PTENOCACHE	(1<<28)
+#define	PTEMAINMEM	(0<<26)
+#define	PTEIO		(1<<26)
+#define	PTEACCESS	(1<<25)
+#define	PTEMODIFY	(1<<24)
+#define PTEUNCACHED	0
+#define PTEMAPMEM	(1024*1024)	
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE	16
+
+#define	INVALIDPTE	0
+#define	PPN(pa)		((pa>>12)&0xFFFF)
+
+/*
+ * Weird addresses in various ASI's
+ */
+#define	CACHETAGS	0x80000000		/* ASI 2 */
+#define	CACHEDATA	0x90000000		/* ASI 2 */
+#define	SER		0x60000000		/* ASI 2 */
+#define	SEVAR		0x60000004		/* ASI 2 */
+#define	ASER		0x60000008		/* ASI 2 */
+#define	ASEVAR		0x6000000C		/* ASI 2 */
+#define	ENAB		0x40000000		/* ASI 2 */
+#define	ENABCACHE	0x10
+#define	ENABRESET	0x04
+
+/*
+ * Virtual addresses
+ */
+#define	VTAG(va)	((va>>22)&0x03F)
+#define	VPN(va)		((va>>13)&0x1FF)
+
+#define	PARAM		((char*)0x40500000)
+#define	TLBFLUSH_	0x01
+
+/*
+ * Address spaces
+ */
+
+#define	UZERO	0x00000000		/* base of user address space */
+#define	UTZERO	(UZERO+BY2PG)		/* first address in user text */
+#define	TSTKTOP	0x10000000		/* end of new stack in sysexec */
+#define TSTKSIZ 32
+#define	USTKTOP	(TSTKTOP-TSTKSIZ*BY2PG)	/* byte just beyond user stack */
+#define	KZERO	0xE0000000		/* base of kernel address space */
+#define	KTZERO	(KZERO+4*BY2PG)		/* first address in kernel text */
+#define	USTKSIZE	(4*1024*1024)	/* size of user stack */
+
+#define	MACHSIZE	4096
+
+#define isphys(x) (((ulong)(x)&0xF0000000) == KZERO)
+
+#define	SYSPSR	(SPL(0x0)|PSREF|PSRSUPER|0)
+#define	NOOP	OR R0, R0; OR R0, R0; OR R0, R0
+
+TEXT	start(SB), $-4
+
+	/* get virtual, fast */
+	/* we are executing in segment 0, mapped to pmeg 0. stack is there too */
+	/* get virtual by mapping segment(KZERO) to pmeg 0., and next to 1 */
+	MOVW	$KZERO, R7
+	MOVB	R0, (R7, 3)
+	MOVW	$(KZERO+BY2SEGM), R7
+	MOVW	$1, R8
+	MOVB	R8, (R7, 3)
+	/* now mapped correctly.  jmpl to where we want to be */
+	MOVW	$setSB(SB), R2
+	MOVW	$startvirt(SB), R7
+	JMPL	(R7)
+	MOVW	$_mul(SB), R0	/* touch _mul etc.; doesn't need to execute */
+	RETURN			/* can't get here */
+
+TEXT	startvirt(SB), $-4
+
+	MOVW	$BOOTSTACK, R1
+
+	MOVW	$(SPL(0xF)|PSREF|PSRSUPER), R7
+	MOVW	R7, PSR
+
+	MOVW	$(0x35<<22), R7		/* NVM OFM DZM AU */
+	MOVW	R7, fsr+0(SB)
+	MOVW	fsr+0(SB), FSR
+	FMOVD	$0.5, F26		/* 0.5 -> F26 */
+	FSUBD	F26, F26, F24		/* 0.0 -> F24 */
+	FADDD	F26, F26, F28		/* 1.0 -> F28 */
+	FADDD	F28, F28, F30		/* 2.0 -> F30 */
+
+	FMOVD	F24, F0
+	FMOVD	F24, F2
+	FMOVD	F24, F4
+	FMOVD	F24, F6
+	FMOVD	F24, F8
+	FMOVD	F24, F10
+	FMOVD	F24, F12
+	FMOVD	F24, F14
+	FMOVD	F24, F16
+	FMOVD	F24, F18
+	FMOVD	F24, F20
+	FMOVD	F24, F22
+
+	MOVW	$mach0(SB), R(MACH)
+/*	MOVW	$0x8, R7 /**/
+	MOVW	R0, WIM
+	JMPL	main(SB)
+	MOVW	(R0), R0
+	RETURN
+
+TEXT	swap1(SB), $0
+
+	TAS	(R7), R7		/* LDSTUB, thank you ken */
+	RETURN
+
+TEXT	swap1_should_work(SB), $0
+
+	MOVW	R7, R8
+	MOVW	$1, R7
+	SWAP	(R8), R7
+	RETURN
+
+TEXT	swap1x(SB), $0
+
+	MOVW	PSR, R9
+	MOVW	R9, R10
+	AND	$~PSRET, R10		/* BUG: book says this is buggy */
+	MOVW	R10, PSR
+	NOOP
+	MOVW	(R7), R7
+	CMP	R7, R0
+	BNE	was1
+	MOVW	$1, R10
+	MOVW	R10, (R8)
+was1:
+	MOVW	R9, PSR
+	RETURN
+
+TEXT	spllo(SB), $0
+
+	MOVW	PSR, R7
+	MOVW	R7, R10
+	OR	$PSRET, R10
+	MOVW	R10, PSR
+	NOOP
+	RETURN
+
+TEXT	splhi(SB), $0
+
+	MOVW	R15, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	PSR, R7
+	MOVW	R7, R10
+	AND	$~PSRET, R10	/* BUG: book says this is buggy */
+	MOVW	R10, PSR
+	NOOP
+	RETURN
+
+TEXT	splx(SB), $0
+
+	MOVW	R15, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	R7, PSR		/* BUG: book says this is buggy */
+	NOOP
+	RETURN
+
+TEXT	spldone(SB), $0
+
+	RETURN
+
+TEXT	touser(SB), $0
+	MOVW	$(SYSPSR&~PSREF), R8
+	MOVW	R8, PSR
+	NOOP
+
+	MOVW	R7, R1
+	SAVE	R0, R0			/* RETT is implicit RESTORE */
+	MOVW	$(UTZERO+32), R7	/* PC; header appears in text */
+	MOVW	$(UTZERO+32+4), R8	/* nPC */
+	RETT	R7, R8
+
+TEXT	rfnote(SB), $0
+
+	MOVW	R7, R1			/* 1st arg is &uregpointer */
+	ADD	$4, R1			/* point at ureg */
+	JMP	restore
+
+TEXT	traplink(SB), $-4
+
+	/* R8 to R23 are free to play with */
+	/* R17 contains PC, R18 contains nPC */
+	/* R19 has PSR loaded from vector code */
+
+	ANDCC	$PSRPSUPER, R19, R0
+	BE	usertrap
+
+kerneltrap:
+	/*
+	 * Interrupt or fault from kernel
+	 */
+	ANDN	$7, R1, R20			/* dbl aligned */
+	MOVW	R1, (0-(4*(32+6))+(4*1))(R20)	/* save R1=SP */
+	/* really clumsy: store these in Ureg so can be restored below */
+	MOVW	R2, (0-(4*(32+6))+(4*2))(R20)	/* SB */
+	MOVW	R5, (0-(4*(32+6))+(4*5))(R20)	/* USER */
+	MOVW	R6, (0-(4*(32+6))+(4*6))(R20)	/* MACH */
+	SUB	$(4*(32+6)), R20, R1
+
+trap1:
+	MOVW	Y, R20
+	MOVW	R20, (4*(32+0))(R1)		/* Y */
+	MOVW	TBR, R20
+	MOVW	R20, (4*(32+1))(R1)		/* TBR */
+	AND	$~0x1F, R19			/* force CWP=0 */
+	MOVW	R19, (4*(32+2))(R1)		/* PSR */
+	MOVW	R18, (4*(32+3))(R1)		/* nPC */
+	MOVW	R17, (4*(32+4))(R1)		/* PC */
+	MOVW	R0, (4*0)(R1)
+	MOVW	R3, (4*3)(R1)
+	MOVW	R4, (4*4)(R1)
+	MOVW	R7, (4*7)(R1)
+	RESTORE	R0, R0
+	/* now our registers R8-R31 are same as before trap */
+	/* save registers two at a time */
+	MOVD	R8, (4*8)(R1)
+	MOVD	R10, (4*10)(R1)
+	MOVD	R12, (4*12)(R1)
+	MOVD	R14, (4*14)(R1)
+	MOVD	R16, (4*16)(R1)
+	MOVD	R18, (4*18)(R1)
+	MOVD	R20, (4*20)(R1)
+	MOVD	R22, (4*22)(R1)
+	MOVD	R24, (4*24)(R1)
+	MOVD	R26, (4*26)(R1)
+	MOVD	R28, (4*28)(R1)
+	MOVD	R30, (4*30)(R1)
+	/* SP and SB and u and m are already set; away we go */
+	MOVW	R1, R7		/* pointer to Ureg */
+	SUB	$8, R1
+	MOVW	$SYSPSR, R8
+	MOVW	R8, PSR
+	NOOP
+	JMPL	trap(SB)
+
+	ADD	$8, R1
+restore:
+	MOVW	(4*(32+2))(R1), R8		/* PSR */
+	MOVW	R8, PSR
+	NOOP
+
+	MOVD	(4*30)(R1), R30
+	MOVD	(4*28)(R1), R28
+	MOVD	(4*26)(R1), R26
+	MOVD	(4*24)(R1), R24
+	MOVD	(4*22)(R1), R22
+	MOVD	(4*20)(R1), R20
+	MOVD	(4*18)(R1), R18
+	MOVD	(4*16)(R1), R16
+	MOVD	(4*14)(R1), R14
+	MOVD	(4*12)(R1), R12
+	MOVD	(4*10)(R1), R10
+	MOVD	(4*8)(R1), R8
+	SAVE	R0, R0
+	MOVD	(4*6)(R1), R6
+	MOVD	(4*4)(R1), R4
+	MOVD	(4*2)(R1), R2
+	MOVW	(4*(32+0))(R1), R20		/* Y */
+	MOVW	R20, Y
+	MOVW	(4*(32+4))(R1), R17		/* PC */
+	MOVW	(4*(32+3))(R1), R18		/* nPC */
+	MOVW	(4*1)(R1), R1	/* restore R1=SP */
+	RETT	R17, R18
+	
+usertrap:
+	/*
+	 * Interrupt or fault from user
+	 */
+	MOVW	R1, R8
+	MOVW	R2, R9
+	MOVW	$setSB(SB), R2
+	MOVW	$(USERADDR+BY2PG), R1
+	MOVW	R8, (0-(4*(32+6))+(4*1))(R1)	/* save R1=SP */
+	MOVW	R9, (0-(4*(32+6))+(4*2))(R1)	/* save R2=SB */
+	MOVW	R5, (0-(4*(32+6))+(4*5))(R1)	/* save R5=USER */
+	MOVW	R6, (0-(4*(32+6))+(4*6))(R1)	/* save R6=MACH */
+	MOVW	$USERADDR, R(USER)
+	MOVW	$mach0(SB), R(MACH)
+	SUB	$(4*(32+6)), R1
+	JMP	trap1
+
+TEXT	syslink(SB), $-4
+
+	/* R8 to R23 are free to play with */
+	/* R17 contains PC, R18 contains nPC */
+	/* R19 has PSR loaded from vector code */
+	/* assume user did it; syscall checks */
+
+	MOVW	R1, R8
+	MOVW	R2, R9
+	MOVW	$setSB(SB), R2
+	MOVW	$(USERADDR+BY2PG), R1
+	MOVW	R8, (0-(4*(32+6))+4)(R1)	/* save R1=SP */
+	SUB	$(4*(32+6)), R1
+	MOVW	R9, (4*2)(R1)			/* save R2=SB */
+	MOVW	R3, (4*3)(R1)			/* global register */
+	MOVD	R4, (4*4)(R1)			/* global register, R5=USER */
+	MOVD	R6, (4*6)(R1)			/* save R6=MACH, R7=syscall# */
+	MOVW	$USERADDR, R(USER)
+	MOVW	$mach0(SB), R(MACH)
+	MOVW	TBR, R20
+	MOVW	R20, (4*(32+1))(R1)		/* TBR */
+	AND	$~0x1F, R19
+	MOVW	R19, (4*(32+2))(R1)		/* PSR */
+	MOVW	R18, (4*(32+3))(R1)		/* nPC */
+	MOVW	R17, (4*(32+4))(R1)		/* PC */
+	RESTORE	R0, R0
+	/* now our registers R8-R31 are same as before trap */
+	MOVW	R15, (4*15)(R1)
+	/* SP and SB and u and m are already set; away we go */
+	MOVW	R1, R7			/* pointer to Ureg */
+	SUB	$8, R1
+	MOVW	$SYSPSR, R8
+	MOVW	R8, PSR
+	JMPL	syscall(SB)
+	/* R7 contains return value from syscall */
+
+	ADD	$8, R1
+	MOVW	(4*(32+2))(R1), R8		/* PSR */
+	MOVW	R8, PSR
+	NOOP
+
+	MOVW	(4*15)(R1), R15
+	SAVE	R0, R0
+	MOVW	(4*6)(R1), R6
+	MOVD	(4*4)(R1), R4
+	MOVD	(4*2)(R1), R2
+	MOVW	(4*(32+4))(R1), R17		/* PC */
+	MOVW	(4*(32+3))(R1), R18		/* nPC */
+	MOVW	(4*1)(R1), R1	/* restore R1=SP */
+	RETT	R17, R18
+
+TEXT	puttbr(SB), $0
+
+	MOVW	R7, TBR
+	NOOP
+	RETURN
+
+TEXT	gettbr(SB), $0
+
+	MOVW	TBR, R7
+	RETURN
+
+TEXT	r1(SB), $0
+
+	MOVW	R1, R7
+	RETURN
+
+TEXT	getwim(SB), $0
+
+	MOVW	WIM, R7
+	RETURN
+
+TEXT	setlabel(SB), $0
+
+	MOVW	R1, (R7)
+	MOVW	R15, 4(R7)
+	MOVW	$0, R7
+	RETURN
+
+TEXT	gotolabel(SB), $0
+
+	MOVW	(R7), R1
+	MOVW	4(R7), R15
+	MOVW	$1, R7
+	RETURN
+
+TEXT	putcxsegm(SB), $0
+
+	MOVW	R7, R8			/* context */
+	MOVW	4(FP), R9		/* segment addr */
+	MOVW	8(FP), R10		/* segment value */
+	MOVW	$0xFFE80118, R7
+	JMPL	(R7)
+	RETURN
+
+TEXT	getpsr(SB), $0
+
+	MOVW	PSR, R7
+	RETURN
+
+TEXT	putcxreg(SB), $0
+
+	MOVW	$CONTEXT, R8
+	MOVB	R7, (R8, 2)
+	RETURN
+
+TEXT	putb2(SB), $0
+
+	MOVW	4(FP), R8
+	MOVB	R8, (R7, 2)
+	RETURN
+
+TEXT	getb2(SB), $0
+
+	MOVB	(R7, 2), R7
+	RETURN
+
+TEXT	getw2(SB), $0
+
+	MOVW	(R7, 2), R7
+	RETURN
+
+TEXT	putw2(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 2)
+	RETURN
+
+TEXT	putw4(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 4)
+	RETURN
+
+TEXT	getw4(SB), $0
+
+	MOVW	(R7, 4), R7
+	RETURN
+
+TEXT	putwC(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 0xC)
+	RETURN
+
+TEXT	putwD(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 0xD)
+	RETURN
+
+TEXT	putwD16(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xD)
+	RETURN
+
+TEXT	putwE(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 0xE)
+	RETURN
+
+TEXT	putwE16(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	ADD	$(1<<4), R7
+	MOVW	R8, (R7, 0xE)
+	RETURN
+
+TEXT	putsegm(SB), $0
+
+	MOVW	4(FP), R8
+	MOVW	R8, (R7, 3)
+	RETURN
+
+/*
+ * in savefpregs and restfpregs, incoming R7 points to doubleword
+ * below where F0 will go; doubleword align in and backfill FSR
+ */
+TEXT	savefpregs(SB), $0
+
+	ADD	$8, R7
+	ANDN	$7, R7		/* now MOVD-aligned */
+	MOVW	FSR, -4(R7)
+
+	MOVD	F0, (0*4)(R7)
+	MOVD	F2, (2*4)(R7)
+	MOVD	F4, (4*4)(R7)
+	MOVD	F6, (6*4)(R7)
+	MOVD	F8, (8*4)(R7)
+	MOVD	F10, (10*4)(R7)
+	MOVD	F12, (12*4)(R7)
+	MOVD	F14, (14*4)(R7)
+	MOVD	F16, (16*4)(R7)
+	MOVD	F18, (18*4)(R7)
+	MOVD	F20, (20*4)(R7)
+	MOVD	F22, (22*4)(R7)
+	MOVD	F24, (24*4)(R7)
+	MOVD	F26, (26*4)(R7)
+	MOVD	F28, (28*4)(R7)
+	MOVD	F30, (30*4)(R7)
+
+	MOVW	PSR, R8
+	ANDN	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	restfpregs(SB), $0
+
+	MOVW	PSR, R8
+	OR	$PSREF, R8
+	MOVW	R8, PSR
+
+	ADD	$8, R7
+	ANDN	$7, R7		/* now MOVD-aligned */
+	OR	R0, R0
+
+	MOVW	-4(R7), FSR
+
+	MOVD	(0*4)(R7), F0
+	MOVD	(2*4)(R7), F2
+	MOVD	(4*4)(R7), F4
+	MOVD	(6*4)(R7), F6
+	MOVD	(8*4)(R7), F8
+	MOVD	(10*4)(R7), F10
+	MOVD	(12*4)(R7), F12
+	MOVD	(14*4)(R7), F14
+	MOVD	(16*4)(R7), F16
+	MOVD	(18*4)(R7), F18
+	MOVD	(20*4)(R7), F20
+	MOVD	(22*4)(R7), F22
+	MOVD	(24*4)(R7), F24
+	MOVD	(26*4)(R7), F26
+	MOVD	(28*4)(R7), F28
+	MOVD	(30*4)(R7), F30
+
+	ANDN	$PSREF, R8
+	MOVW	R8, PSR
+	RETURN
+
+TEXT	clearfpintr(SB), $0
+
+	MOVW	$fpq+BY2WD(SB), R7
+	ANDN	$0x7, R7		/* must be D aligned */
+	MOVW	$fsr+0(SB), R9
+clrq:
+	MOVD	FQ, (R7)
+	MOVW	FSR, (R9)
+	MOVW	(R9), R8
+	AND	$(1<<13), R8		/* queue not empty? */
+	BNE	clrq
+	RETURN
+
+TEXT	getfsr(SB), $0
+	MOVW	$fsr+0(SB), R7
+	MOVW	FSR, (R7)
+	MOVW	(R7), R7
+	RETURN
+
+GLOBL	mach0+0(SB), $MACHSIZE
+GLOBL	fpq+0(SB), $(3*BY2WD)
+GLOBL	fsr+0(SB), $BY2WD
--- /dev/null
+++ b/utils/ka/lex.c
@@ -1,0 +1,722 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = 'k';
+	thestring = "sparc";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"FSR",		LFSR,	D_FSR,
+	"CSR",		LFSR,	D_CSR,
+
+	"FQ",		LFPQ,	D_FPQ,
+	"CQ",		LFPQ,	D_CPQ,
+
+	"Y",		LPSR,	D_Y,
+	"PSR",		LPSR,	D_PSR,
+	"WIM",		LPSR,	D_WIM,
+	"TBR",		LPSR,	D_TBR,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+	"R16",		LREG,	16,
+	"R17",		LREG,	17,
+	"R18",		LREG,	18,
+	"R19",		LREG,	19,
+	"R20",		LREG,	20,
+	"R21",		LREG,	21,
+	"R22",		LREG,	22,
+	"R23",		LREG,	23,
+	"R24",		LREG,	24,
+	"R25",		LREG,	25,
+	"R26",		LREG,	26,
+	"R27",		LREG,	27,
+	"R28",		LREG,	28,
+	"R29",		LREG,	29,
+	"R30",		LREG,	30,
+	"R31",		LREG,	31,
+
+	"C",		LC,	0,
+	"C0",		LCREG,	0,
+	"C1",		LCREG,	1,
+	"C2",		LCREG,	2,
+	"C3",		LCREG,	3,
+	"C4",		LCREG,	4,
+	"C5",		LCREG,	5,
+	"C6",		LCREG,	6,
+	"C7",		LCREG,	7,
+	"C8",		LCREG,	8,
+	"C9",		LCREG,	9,
+	"C10",		LCREG,	10,
+	"C11",		LCREG,	11,
+	"C12",		LCREG,	12,
+	"C13",		LCREG,	13,
+	"C14",		LCREG,	14,
+	"C15",		LCREG,	15,
+	"C16",		LCREG,	16,
+	"C17",		LCREG,	17,
+	"C18",		LCREG,	18,
+	"C19",		LCREG,	19,
+	"C20",		LCREG,	20,
+	"C21",		LCREG,	21,
+	"C22",		LCREG,	22,
+	"C23",		LCREG,	23,
+	"C24",		LCREG,	24,
+	"C25",		LCREG,	25,
+	"C26",		LCREG,	26,
+	"C27",		LCREG,	27,
+	"C28",		LCREG,	28,
+	"C29",		LCREG,	29,
+	"C30",		LCREG,	30,
+	"C31",		LCREG,	31,
+
+	"F",		LF,	0,
+	"F0",		LFREG,	0,
+	"F2",		LFREG,	2,
+	"F4",		LFREG,	4,
+	"F6",		LFREG,	6,
+	"F8",		LFREG,	8,
+	"F10",		LFREG,	10,
+	"F12",		LFREG,	12,
+	"F14",		LFREG,	14,
+	"F16",		LFREG,	16,
+	"F18",		LFREG,	18,
+	"F20",		LFREG,	20,
+	"F22",		LFREG,	22,
+	"F24",		LFREG,	24,
+	"F26",		LFREG,	26,
+	"F28",		LFREG,	28,
+	"F30",		LFREG,	30,
+	"F1",		LFREG,	1,
+	"F3",		LFREG,	3,
+	"F5",		LFREG,	5,
+	"F7",		LFREG,	7,
+	"F9",		LFREG,	9,
+	"F11",		LFREG,	11,
+	"F13",		LFREG,	13,
+	"F15",		LFREG,	15,
+	"F17",		LFREG,	17,
+	"F19",		LFREG,	19,
+	"F21",		LFREG,	21,
+	"F23",		LFREG,	23,
+	"F25",		LFREG,	25,
+	"F27",		LFREG,	27,
+	"F29",		LFREG,	29,
+	"F31",		LFREG,	31,
+
+	"ADD",		LADDW, AADD,
+	"ADDCC",	LADDW, AADDCC,
+	"ADDX",		LADDW, AADDX,
+	"ADDXCC",	LADDW, AADDXCC,
+	"AND",		LADDW, AAND,
+	"ANDCC",	LADDW, AANDCC,
+	"ANDN",		LADDW, AANDN,
+	"ANDNCC",	LADDW, AANDNCC,
+	"BA",		LBRA, ABA,
+	"BCC",		LBRA, ABCC,
+	"BCS",		LBRA, ABCS,
+	"BE",		LBRA, ABE,
+	"BG",		LBRA, ABG,
+	"BGE",		LBRA, ABGE,
+	"BGU",		LBRA, ABGU,
+	"BL",		LBRA, ABL,
+	"BLE",		LBRA, ABLE,
+	"BLEU",		LBRA, ABLEU,
+	"BN",		LBRA, ABN,
+	"BNE",		LBRA, ABNE,
+	"BNEG",		LBRA, ABNEG,
+	"BPOS",		LBRA, ABPOS,
+	"BVC",		LBRA, ABVC,
+	"BVS",		LBRA, ABVS,
+	"CB0",		LBRA, ACB0,
+	"CB01",		LBRA, ACB01,
+	"CB012",	LBRA, ACB012,
+	"CB013",	LBRA, ACB013,
+	"CB02",		LBRA, ACB02,
+	"CB023",	LBRA, ACB023,
+	"CB03",		LBRA, ACB03,
+	"CB1",		LBRA, ACB1,
+	"CB12",		LBRA, ACB12,
+	"CB123",	LBRA, ACB123,
+	"CB13",		LBRA, ACB13,
+	"CB2",		LBRA, ACB2,
+	"CB23",		LBRA, ACB23,
+	"CB3",		LBRA, ACB3,
+	"CBA",		LBRA, ACBA,
+	"CBN",		LBRA, ACBN,
+	"CMP",		LCMP, ACMP,
+	"CPOP1",	LCPOP, ACPOP1,
+	"CPOP2",	LCPOP, ACPOP2,
+	"DATA",		LDATA, ADATA,
+	"DIV",		LADDW, ADIV,
+	"DIVL",		LADDW, ADIVL,
+	"END",		LEND, AEND,
+	"FABSD",	LFCONV, AFABSD,
+	"FABSF",	LFCONV, AFABSF,
+	"FABSX",	LFCONV, AFABSX,
+	"FADDD",	LFADD, AFADDD,
+	"FADDF",	LFADD, AFADDF,
+	"FADDX",	LFADD, AFADDX,
+	"FBA",		LBRA, AFBA,
+	"FBE",		LBRA, AFBE,
+	"FBG",		LBRA, AFBG,
+	"FBGE",		LBRA, AFBGE,
+	"FBL",		LBRA, AFBL,
+	"FBLE",		LBRA, AFBLE,
+	"FBLG",		LBRA, AFBLG,
+	"FBN",		LBRA, AFBN,
+	"FBNE",		LBRA, AFBNE,
+	"FBO",		LBRA, AFBO,
+	"FBU",		LBRA, AFBU,
+	"FBUE",		LBRA, AFBUE,
+	"FBUG",		LBRA, AFBUG,
+	"FBUGE",	LBRA, AFBUGE,
+	"FBUL",		LBRA, AFBUL,
+	"FBULE",	LBRA, AFBULE,
+	"FCMPD",	LFADD, AFCMPD,
+	"FCMPED",	LFADD, AFCMPED,
+	"FCMPEF",	LFADD, AFCMPEF,
+	"FCMPEX",	LFADD, AFCMPEX,
+	"FCMPF",	LFADD, AFCMPF,
+	"FCMPX",	LFADD, AFCMPX,
+	"FDIVD",	LFADD, AFDIVD,
+	"FDIVF",	LFADD, AFDIVF,
+	"FDIVX",	LFADD, AFDIVX,
+	"FMOVD",	LFMOV, AFMOVD,
+	"FMOVDF",	LFCONV, AFMOVDF,
+	"FMOVDW",	LFCONV, AFMOVDW,
+	"FMOVDX",	LFCONV, AFMOVDX,
+	"FMOVF",	LFMOV, AFMOVF,
+	"FMOVFD",	LFCONV, AFMOVFD,
+	"FMOVFW",	LFCONV, AFMOVFW,
+	"FMOVFX",	LFCONV, AFMOVFX,
+	"FMOVWD",	LFCONV, AFMOVWD,
+	"FMOVWF",	LFCONV, AFMOVWF,
+	"FMOVWX",	LFCONV, AFMOVWX,
+	"FMOVX",	LFCONV, AFMOVX,
+	"FMOVXD",	LFCONV, AFMOVXD,
+	"FMOVXF",	LFCONV, AFMOVXF,
+	"FMOVXW",	LFCONV, AFMOVXW,
+	"FMULD",	LFADD, AFMULD,
+	"FMULF",	LFADD, AFMULF,
+	"FMULX",	LFADD, AFMULX,
+	"FNEGD",	LFCONV, AFNEGD,
+	"FNEGF",	LFCONV, AFNEGF,
+	"FNEGX",	LFCONV, AFNEGX,
+	"FSQRTD",	LFCONV, AFSQRTD,
+	"FSQRTF",	LFCONV, AFSQRTF,
+	"FSQRTX",	LFCONV, AFSQRTX,
+	"FSUBD",	LFADD, AFSUBD,
+	"FSUBF",	LFADD, AFSUBF,
+	"FSUBX",	LFADD, AFSUBX,
+	"GLOBL",	LTEXT, AGLOBL,
+	"IFLUSH",	LFLUSH, AIFLUSH,
+	"JMPL",		LJMPL, AJMPL,
+	"JMP",		LJMPL, AJMP,
+	"MOD",		LADDW, AMOD,
+	"MODL",		LADDW, AMODL,
+	"MOVB",		LMOVB, AMOVB,
+	"MOVBU",	LMOVB, AMOVBU,
+	"MOVD",		LMOVD, AMOVD,
+	"MOVH",		LMOVB, AMOVH,
+	"MOVHU",	LMOVB, AMOVHU,
+	"MOVW",		LMOVW, AMOVW,
+	"MUL",		LADDW, AMUL,
+	"MULSCC",	LADDW, AMULSCC,
+	"NOP",		LNOP, ANOP,
+	"OR",		LADDW, AOR,
+	"ORCC",		LADDW, AORCC,
+	"ORN",		LADDW, AORN,
+	"ORNCC",	LADDW, AORNCC,
+	"RESTORE",	LADDW, ARESTORE,
+	"RETT",		LRETT, ARETT,
+	"RETURN",	LRETRN, ARETURN,
+	"SAVE",		LADDW, ASAVE,
+	"SLL",		LADDW, ASLL,
+	"SRA",		LADDW, ASRA,
+	"SRL",		LADDW, ASRL,
+	"SUB",		LADDW, ASUB,
+	"SUBCC",	LADDW, ASUBCC,
+	"SUBX",		LADDW, ASUBX,
+	"SUBXCC",	LADDW, ASUBXCC,
+	"SWAP",		LSWAP, ASWAP,
+	"TA",		LTRAP, ATA,
+	"TADDCC",	LADDW, ATADDCC,
+	"TADDCCTV",	LADDW, ATADDCCTV,
+	"TAS",		LSWAP, ATAS,
+	"TCC",		LTRAP, ATCC,
+	"TCS",		LTRAP, ATCS,
+	"TE",		LTRAP, ATE,
+	"TEXT",		LTEXT, ATEXT,
+	"TG",		LTRAP, ATG,
+	"TGE",		LTRAP, ATGE,
+	"TGU",		LTRAP, ATGU,
+	"TL",		LTRAP, ATL,
+	"TLE",		LTRAP, ATLE,
+	"TLEU",		LTRAP, ATLEU,
+	"TN",		LTRAP, ATN,
+	"TNE",		LTRAP, ATNE,
+	"TNEG",		LTRAP, ATNEG,
+	"TPOS",		LTRAP, ATPOS,
+	"TSUBCC",	LADDW, ATSUBCC,
+	"TSUBCCTV",	LADDW, ATSUBCCTV,
+	"TVC",		LTRAP, ATVC,
+	"TVS",		LTRAP, ATVS,
+	"UNIMP",	LUNIMP, AUNIMP,
+	"WORD",		LUNIMP, AWORD,
+	"XNOR",		LADDW, AXNOR,
+	"XNORCC",	LADDW, AXNORCC,
+	"XOR",		LXORW, AXOR,
+	"XORCC",	LADDW, AXORCC,
+
+	"SCHED",	LSCHED, 0,
+	"NOSCHED",	LSCHED, 0x80,
+
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	nullgen.xreg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+	case D_PREG:
+		break;
+
+	case D_OREG:
+	case D_ASI:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+	if(g1->xreg != NREG) {
+		if(reg != NREG || g2->xreg != NREG)
+			yyerror("bad addressing modes");
+		reg = g1->xreg;
+	} else
+	if(g2->xreg != NREG) {
+		if(reg != NREG)
+			yyerror("bad addressing modes");
+		reg = g2->xreg;
+	}
+jackpot:
+	sf = 0;
+	s = g1->sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g1->name;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->name;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, reg|nosched);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/ka/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=ka
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../kc/k.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/ka/note
@@ -1,0 +1,274 @@
+/* load instructions */
+
+	LDSB		MOVB	x, R
+	LDSBA		MOVB	x, R, asi
+	LDSH		MOVH	x, R
+	LDSHA		MOVH	x, R, asi
+	LDUB		MOVBU	x, R
+	LDUBA		MOVBU	x, R, asi
+	LDUH		MOVHU	x, R
+	LDUHA		MOVHU	x, R, asi
+	LD		MOVW	x, R
+	LDA		MOVW	x, R, asi
+	LDD		MOVD	x, R
+	LDDA		MOVD	x, R, asi
+
+note: x is (R+R) or offset(R)
+note: MOVD is a bad name, means double word
+
+/* load floating */
+
+	LDF		MOVF	x, FR
+	LDDF		MOVD	x, FR
+	LDFSR		MOVW	x, FPSR
+
+note: MOVF maybe is MOVW
+
+/* load coprocessor */
+
+	LDC		MOVW	x, CR
+	LDDC		MOVD	x, CR
+	LDCSR		MOVW	x, CPSR
+
+/* store */
+
+	STB		MOVB	R, x
+	STBA		MOVB	R, x, asi
+	STH		MOVH	R, x
+	STHA		MOVH	R, x, asi
+	ST		MOVW	R, x
+	STA		MOVW	R, x, asi
+	STD		MOVD	R, x
+	STDA		MOVD	R, x, asi
+
+/* store floating *
+
+	STF		MOVF	FR, x
+	STDF		MOVD	FR, x
+	STFSR		MOVW	FPSR, x
+	STDFQ		MOVD	FPQ, x
+
+note: STDFQ gok
+
+/* store coprocessor */
+
+	STC		MOVW	CR, x
+	STDC		MOVD	CR, x
+	STCSR		MOVW	CPSR, x
+	STDCQ		MOVD	CPQ, x
+
+/* atomic load/store */
+
+	LDSTUB		TAS	x
+	LDSTUBA		TAS	x, asi
+
+/* swap */
+
+	SWAP		SWAP	R, x
+	SWAPA		SWAP	R, x, asi
+
+/* calc */
+
+	ADD		ADDW	y,R, R
+	ADDcc		ADDWT	y,R, R
+	ADDX		ADDC	y,R, R
+	ADDXcc		ADDCT	y,R, R
+	TADDcc
+	TADDccTV
+	SUB
+	SUBcc
+	SUBX
+	SUBXcc
+	TSUBcc
+	TSUBccTV
+	MULScc
+	AND
+	ANDcc
+	ANDN
+	ANDNcc
+	OR
+	ORcc
+	ORN
+	ORNcc
+	XOR
+	XORcc
+	XNOR
+	XNORcc
+	SLL
+	SRL
+	SRA
+
+note: y is R or $simm13
+
+/* sethi */
+
+	SETHI		MOVW	$c, R		/* high 22 bits */
+
+/* save/restore (same as add) */
+
+	SAVE		SAVE	y,R, R
+	RESTORE		RESTORE	y,R, R
+
+/* branch on cc */
+
+	BA
+	BN
+	BNE
+	BE
+	BG
+	BLE
+	BGE
+	BL
+	BGU
+	BLEU
+	BCC
+	BCS
+	BPOS
+	BNEG
+	BVC
+	BVS
+
+note: annul bit?
+
+/* branch on fcc */
+
+	FBA
+	FBN
+	FBU
+	FBG
+	FBUG
+	FBL
+	FBUL
+	FBLG
+	FBNE
+	FBE
+	FBUE
+	FBGE
+	FBUGE
+	FBLE
+	FBULE
+	FBO
+
+note: annul bit?
+
+/* branch on coprocecssor cc */
+
+	CBA
+	CBN
+	CB3
+	CB2
+	CB23
+	CB1
+	CB13
+	CB12
+	CB123
+	CB0
+	CB03
+	CB02
+	CB023
+	CB01
+	CB013
+	CB012
+
+note: annul bit?
+
+/* call */
+
+	CALL
+	JAL	x, R
+
+/* return from trap */
+
+	RETT	x
+
+/* trap on integer cc */
+
+	TA
+	TN
+	TNE
+	TE
+	TG
+	TLE
+	TGE
+	TL
+	TGU
+	TLEU
+	TCC
+	TCS
+	TPOS
+	TNEG
+	TVC
+	TVS
+
+/* read state register */
+
+	RDY		MOVW	Y, R
+	RDPSR		MOVW	PSR, R
+	RDWIM		MOVW	WIM, R
+	RDTBR		MOVW	TBR, R
+
+/* write state register */
+
+	WRY		MOVW	R, Y
+	WRPSR		MOVW	R, PSR
+	WRWIM		MOVW	R, WIM
+	WRTBR		MOVW	R, TBR
+
+/* unimplemented */
+
+	UNIMP	$C22
+
+/* instruction cache flush */
+
+	IFLUSH	x
+
+/* floating op */
+
+	FiTOs
+	FiTOd
+	FiTOx
+
+	FsTOi
+	FdTOi
+	FxTOi
+
+	FsTOd
+	FsTOx
+	FdTOs
+	FdTOx
+	FxTOs
+	FxTOd
+
+	FMOVs
+	FNEGs
+	FABSs
+
+	FSQRTs
+	FSQRTd
+	FSQRTx
+
+	FADDs
+	FADDd
+	FADDx
+	FSUBs
+	FSUBd
+	FSUBx
+
+	FMULs
+	FMULd
+	FMULx
+	FDIVs
+	FDIVd
+	FDIVx
+
+	FCMPs
+	FCMPd
+	FCMPx
+	FCMPEs
+	FCMPEd
+	FCMPEx
+
+/* coprocessor op */
+
+	CPop1
+	CPop2
--- /dev/null
+++ b/utils/kc/cgen.c
@@ -1,0 +1,1089 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesuv[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+			if(o != OAND)
+				break;
+			if(nn != Z)
+				gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OMUL:
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case ODIV:
+	case OMOD:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+			if(debug['M'])
+				print("%L multiply\n", n->lineno);
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			gopcode(o, &nod, &nod1, &nod);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, r, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, n, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, n, nn);
+			cgen(r, &nod);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+		regalloc(&nod1, n, Z);
+		gopcode(OAS, &nod2, Z, &nod1);
+		gopcode(o, &nod, &nod1, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		regalloc(&nod3, r, Z);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			cgen(r, &nod3);
+		} else {
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(n->op, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			cgen(l, nn);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+			/* both null, gen l->nn */
+			cgen(l, nn);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		if(nn && l->op == ONAME)	/* in x=++i, emit USED(i) */
+			gins(ANOP, l, Z);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		if(n->op == OCONST) {
+			o = vconst(n);
+			if(!true)
+				o = !o;
+			gbranch(OGOTO);
+			if(o) {
+				p1 = p;
+				gbranch(OGOTO);
+				patch(p1, pc);
+			}
+			goto com;
+		}
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		o = ONE;
+		if(true)
+			o = comrel[relindex(o)];
+		if(typefd[n->type->etype]) {
+			nodreg(&nod1, n, NREG+FREGZERO);
+			gopcode(o, &nod, Z, &nod1);
+		} else
+			gopcode(o, &nod, Z, nodconst(0));
+		regfree(&nod);
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(sconst(r)) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, &nod, Z, r);
+			regfree(&nod);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod1, Z, &nod);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1L), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0L), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			nod1.xoffset += SZ_LONG;
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+
+			regfree(&nod1);
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && nn->complex >= FNX) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		/* BOTCH -- functions can clobber rathole */
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 5) {
+		layout(&nod1, &nod2, w, 0, Z);
+		goto out;
+	}
+
+	/*
+	 * minimize space for unrolling loop
+	 * 3,4,5 times. (6 or more is never minimum)
+	 * if small structure, try 2 also.
+	 */
+	c = 0; /* set */
+	m = 100;
+	i = 3;
+	if(w <= 15)
+		i = 2;
+	for(; i<=5; i++)
+		if(i + w%i <= m) {
+			c = i;
+			m = c + w%c;
+		}
+
+	regalloc(&nod3, &regnode, Z);
+	layout(&nod1, &nod2, w%c, w/c, &nod3);
+	
+	pc1 = pc;
+	layout(&nod1, &nod2, c, 0, Z);
+
+	gopcode(OSUB, nodconst(1L), Z, &nod3);
+	nod1.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1);
+	nod2.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2);
+	
+	gopcode(OGT, &nod3, Z, nodconst(0));
+	patch(p, pc1);
+
+	regfree(&nod3);
+out:
+	regfree(&nod1);
+	regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &regnode, Z);
+	regalloc(&t2, &regnode, Z);
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
--- /dev/null
+++ b/utils/kc/enam.c
@@ -1,0 +1,175 @@
+char	*anames[] =
+{
+	"XXX",
+	"ADD",
+	"ADDCC",
+	"ADDX",
+	"ADDXCC",
+	"AND",
+	"ANDCC",
+	"ANDN",
+	"ANDNCC",
+	"BA",
+	"BCC",
+	"BCS",
+	"BE",
+	"BG",
+	"BGE",
+	"BGU",
+	"BL",
+	"BLE",
+	"BLEU",
+	"BN",
+	"BNE",
+	"BNEG",
+	"BPOS",
+	"BVC",
+	"BVS",
+	"CB0",
+	"CB01",
+	"CB012",
+	"CB013",
+	"CB02",
+	"CB023",
+	"CB03",
+	"CB1",
+	"CB12",
+	"CB123",
+	"CB13",
+	"CB2",
+	"CB23",
+	"CB3",
+	"CBA",
+	"CBN",
+	"CMP",
+	"CPOP1",
+	"CPOP2",
+	"DATA",
+	"DIV",
+	"DIVL",
+	"FABSD",
+	"FABSF",
+	"FABSX",
+	"FADDD",
+	"FADDF",
+	"FADDX",
+	"FBA",
+	"FBE",
+	"FBG",
+	"FBGE",
+	"FBL",
+	"FBLE",
+	"FBLG",
+	"FBN",
+	"FBNE",
+	"FBO",
+	"FBU",
+	"FBUE",
+	"FBUG",
+	"FBUGE",
+	"FBUL",
+	"FBULE",
+	"FCMPD",
+	"FCMPED",
+	"FCMPEF",
+	"FCMPEX",
+	"FCMPF",
+	"FCMPX",
+	"FDIVD",
+	"FDIVF",
+	"FDIVX",
+	"FMOVD",
+	"FMOVDF",
+	"FMOVDW",
+	"FMOVDX",
+	"FMOVF",
+	"FMOVFD",
+	"FMOVFW",
+	"FMOVFX",
+	"FMOVWD",
+	"FMOVWF",
+	"FMOVWX",
+	"FMOVX",
+	"FMOVXD",
+	"FMOVXF",
+	"FMOVXW",
+	"FMULD",
+	"FMULF",
+	"FMULX",
+	"FNEGD",
+	"FNEGF",
+	"FNEGX",
+	"FSQRTD",
+	"FSQRTF",
+	"FSQRTX",
+	"FSUBD",
+	"FSUBF",
+	"FSUBX",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"IFLUSH",
+	"JMPL",
+	"JMP",
+	"MOD",
+	"MODL",
+	"MOVB",
+	"MOVBU",
+	"MOVD",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MUL",
+	"MULSCC",
+	"NAME",
+	"NOP",
+	"OR",
+	"ORCC",
+	"ORN",
+	"ORNCC",
+	"RESTORE",
+	"RETT",
+	"RETURN",
+	"SAVE",
+	"SLL",
+	"SRA",
+	"SRL",
+	"SUB",
+	"SUBCC",
+	"SUBX",
+	"SUBXCC",
+	"SWAP",
+	"TA",
+	"TADDCC",
+	"TADDCCTV",
+	"TAS",
+	"TCC",
+	"TCS",
+	"TE",
+	"TEXT",
+	"TG",
+	"TGE",
+	"TGU",
+	"TL",
+	"TLE",
+	"TLEU",
+	"TN",
+	"TNE",
+	"TNEG",
+	"TPOS",
+	"TSUBCC",
+	"TSUBCCTV",
+	"TVC",
+	"TVS",
+	"UNIMP",
+	"WORD",
+	"XNOR",
+	"XNORCC",
+	"XOR",
+	"XORCC",
+	"END",
+	"DYNT",
+	"INIT",
+	"SIGNAME",
+	"LAST"
+};
--- /dev/null
+++ b/utils/kc/gc.h
@@ -1,0 +1,341 @@
+#include	"../cc/cc.h"
+#include	"../kc/k.out.h"
+
+/*
+ * kc/sparc
+ * Sun sparc
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_VLONG	8
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	char	sval[NSNAME];
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	short	as;
+	char	reg;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	long	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	long	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	int	hintabsize;
+EXTERN	long	maxargsafe;
+EXTERN	Multab	multab[20];
+EXTERN	int	mnstring;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	char	reg[NREG+NREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	5
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+EXTERN	int	suppress;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	usedset(Node*, int);
+void	noretval(int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	swit2(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(Node*, long);
+int	mulcon1(Node*, long, Node*);
+void	nullwarn(Node*, Node*);
+void	sextern(Sym*, Node*, long, long);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+/*
+ * com64.c
+ */
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/kc/k.out.h
@@ -1,0 +1,263 @@
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+enum
+{
+	REGZERO		= 0,	/* always zero */
+	REGSP		= 1,	/* stack pointer */
+	REGSB		= 2,	/* static pointer */
+	REGSB1		= 3,	/* (possible) second static pointer */
+	REGEXT		= 6,	/* first external register, grows-- */
+	REGRET		= 7,	/* return register and first temp, grows++ */
+	REGTMP		= 14,	/* used by the loader */
+	REGLINK		= 15,	/* subroutine linkage */
+	REGARG		= 7,	/* first arg passed in */
+
+	FREGRET		= 0,
+	FREGEXT		= 22,	/* first external register */
+	FREGZERO	= 24,	/* both float and double */
+	FREGHALF	= 26,	/* double */
+	FREGONE		= 28,	/* double */
+	FREGTWO		= 30	/* double */
+/*
+ * GENERAL:
+ *
+ * compiler allocates R7 up as temps
+ * compiler allocates external registers R6 down
+ * compiler allocates register variables F4-F22
+ * compiler allocates external registers F22 down
+ */
+};
+
+enum	as
+{
+	AXXX	= 0,
+	AADD,
+	AADDCC,
+	AADDX,
+	AADDXCC,
+	AAND,
+	AANDCC,
+	AANDN,
+	AANDNCC,
+	ABA,
+	ABCC,
+	ABCS,
+	ABE,
+	ABG,
+	ABGE,
+	ABGU,
+	ABL,
+	ABLE,
+	ABLEU,
+	ABN,
+	ABNE,
+	ABNEG,
+	ABPOS,
+	ABVC,
+	ABVS,
+	ACB0,
+	ACB01,
+	ACB012,
+	ACB013,
+	ACB02,
+	ACB023,
+	ACB03,
+	ACB1,
+	ACB12,
+	ACB123,
+	ACB13,
+	ACB2,
+	ACB23,
+	ACB3,
+	ACBA,
+	ACBN,
+	ACMP,		/* pseudo op */
+	ACPOP1,
+	ACPOP2,
+	ADATA,
+	ADIV,
+	ADIVL,
+	AFABSD,		/* pseudo op */
+	AFABSF,
+	AFABSX,		/* pseudo op */
+	AFADDD,
+	AFADDF,
+	AFADDX,
+	AFBA,
+	AFBE,
+	AFBG,
+	AFBGE,
+	AFBL,
+	AFBLE,
+	AFBLG,
+	AFBN,
+	AFBNE,
+	AFBO,
+	AFBU,
+	AFBUE,
+	AFBUG,
+	AFBUGE,
+	AFBUL,
+	AFBULE,
+	AFCMPD,
+	AFCMPED,
+	AFCMPEF,
+	AFCMPEX,
+	AFCMPF,
+	AFCMPX,
+	AFDIVD,
+	AFDIVF,
+	AFDIVX,
+	AFMOVD,		/* pseudo op */
+	AFMOVDF,
+	AFMOVDW,
+	AFMOVDX,
+	AFMOVF,
+	AFMOVFD,
+	AFMOVFW,
+	AFMOVFX,
+	AFMOVWD,
+	AFMOVWF,
+	AFMOVWX,
+	AFMOVX,		/* pseudo op */
+	AFMOVXD,
+	AFMOVXF,
+	AFMOVXW,
+	AFMULD,
+	AFMULF,
+	AFMULX,
+	AFNEGD,		/* pseudo op */
+	AFNEGF,
+	AFNEGX,		/* pseudo op */
+	AFSQRTD,
+	AFSQRTF,
+	AFSQRTX,
+	AFSUBD,
+	AFSUBF,
+	AFSUBX,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AIFLUSH,
+	AJMPL,
+	AJMP,
+	AMOD,
+	AMODL,
+	AMOVB,
+	AMOVBU,
+	AMOVD,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMUL,
+	AMULSCC,
+	ANAME,
+	ANOP,
+	AOR,
+	AORCC,
+	AORN,
+	AORNCC,
+	ARESTORE,
+	ARETT,
+	ARETURN,
+	ASAVE,
+	ASLL,
+	ASRA,
+	ASRL,
+	ASUB,
+	ASUBCC,
+	ASUBX,
+	ASUBXCC,
+	ASWAP,
+	ATA,
+	ATADDCC,
+	ATADDCCTV,
+	ATAS,
+	ATCC,
+	ATCS,
+	ATE,
+	ATEXT,
+	ATG,
+	ATGE,
+	ATGU,
+	ATL,
+	ATLE,
+	ATLEU,
+	ATN,
+	ATNE,
+	ATNEG,
+	ATPOS,
+	ATSUBCC,
+	ATSUBCCTV,
+	ATVC,
+	ATVS,
+	AUNIMP,
+	AWORD,
+	AXNOR,
+	AXNORCC,
+	AXOR,
+	AXORCC,
+	AEND,
+	ADYNT,
+	AINIT,
+	ASIGNAME,
+	ALAST
+};
+
+/* type/name */
+enum
+{
+	D_GOK	= 0,
+	D_NONE,
+
+/* name */
+	D_EXTERN,
+	D_STATIC,
+	D_AUTO,
+	D_PARAM,
+
+/* type */
+	D_BRANCH,
+	D_OREG,
+	D_ASI,
+	D_CONST,
+	D_FCONST,
+	D_SCONST,
+	D_REG,
+	D_FREG,
+	D_CREG,
+	D_PREG,
+	D_FILE,
+	D_FILE1,
+
+/* reg names iff type is D_PREG */
+	D_CPQ	= 0,
+	D_CSR,
+	D_FPQ,
+	D_FSR,
+	D_PSR,
+	D_TBR,
+	D_WIM,
+	D_Y
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/kc/list.c
@@ -1,0 +1,229 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('B', Bconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		sprint(str, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a <= AEND)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(F%d)(REG)", a, a->reg);
+		break;
+
+	case D_CREG:
+		sprint(str, "C%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(C%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/kc/mkenam
@@ -1,0 +1,17 @@
+ed - ../kc/k.out.h <<'!'
+v/^	A/d
+g/^	AEND/s//&,/
+g/[ 	]*=.*,/s//,/
+v/,/p
+,s/^	A/	"/
+,s/,.*$/",/
+1i
+char	*anames[] =
+{
+.
+,a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/kc/mkfile
@@ -1,0 +1,40 @@
+<../../mkconfig
+
+TARG=kc
+
+OFILES=\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	reg.$O\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+	mul.$O\
+
+HFILES=\
+	gc.h\
+	k.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9			# order is important.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	k.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/kc/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(Node *n, long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("%L: multiply table failure %ld\n", n->lineno, v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=6; g++) {
+		if(g >= 6 && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("%L: multiply table failure (g=%d h=%s) %ld\n",
+				n->lineno, g, hint, v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(n, v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/kc/peep.c
@@ -1,0 +1,694 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == AMOVW || p->as == AFMOVF || p->as == AFMOVD)
+		if(regtyp(&p->to)) {
+			if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			if(regzer(&p->from))
+			if(p->to.type == D_REG) {
+				p->from.type = D_REG;
+				p->from.reg = 0;
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regzer(Adr *a)
+{
+
+	if(a->type == D_CONST)
+		if(a->sym == S)
+			if(a->offset == 0)
+				return 1;
+	if(a->type == D_REG)
+		if(a->reg == 0)
+			return 1;
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG) {
+		if(a->reg != 0)
+			return 1;
+		return 0;
+	}
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case AJMPL:
+			return 0;
+
+		case AADD:
+		case ASUB:
+		case ASLL:
+		case ASRL:
+		case ASRA:
+		case AOR:
+		case AAND:
+		case AXOR:
+		case AMUL:
+		case ADIV:
+		case ADIVL:
+		case AMOD:
+		case AMODL:
+
+		case AFADDD:
+		case AFADDF:
+		case AFSUBD:
+		case AFSUBF:
+		case AFMULD:
+		case AFMULF:
+		case AFDIVD:
+		case AFDIVF:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AFMOVF:
+		case AFMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+
+	case AFMOVF:
+	case AFMOVD:
+	case AFMOVDW:
+	case AFMOVWD:
+	case AFMOVFW:
+	case AFMOVWF:
+	case AFMOVFD:
+	case AFMOVDF:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case AADD:	/* read read write */
+	case ASUB:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case ADIV:
+	case ADIVL:
+	case AMOD:
+	case AMODL:
+
+	case AFADDF:
+	case AFADDD:
+	case AFSUBF:
+	case AFSUBD:
+	case AFMULF:
+	case AFMULD:
+	case AFDIVF:
+	case AFDIVD:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABA:	/* no reference */
+	case ABCC:
+	case ABCS:
+	case ABE:
+	case ABG:
+	case ABGE:
+	case ABGU:
+	case ABL:
+	case ABLE:
+	case ABLEU:
+	case ABN:
+	case ABNE:
+	case ABNEG:
+	case ABPOS:
+	case ABVC:
+	case ABVS:
+	case AFBA:
+	case AFBE:
+	case AFBG:
+	case AFBGE:
+	case AFBL:
+	case AFBLE:
+	case AFBNE:
+	case AFBN:
+	case AFBLG:
+	case AFBO:
+	case AFBU:
+	case AFBUE:
+	case AFBUG:
+	case AFBUGE:
+	case AFBUL:
+	case AFBULE:
+		break;
+
+	case ACMP:	/* read read */
+	case AFCMPD:
+	case AFCMPF:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub(&p->to, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		break;
+
+	case AJMP:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARETURN:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGRET)
+				return 2;
+		if(v->type == D_FREG)
+			if(v->reg == FREGRET)
+				return 2;
+
+	case AJMPL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG) {
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+		}
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+	return 0;
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+	case AADD:
+	case ASUB:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case ADIV:
+	case ADIVL:
+	case AMOD:
+	case AMODL:
+		return D_REG;
+
+	case AFADDF:
+	case AFADDD:
+	case AFSUBF:
+	case AFSUBD:
+	case AFMULF:
+	case AFMULD:
+	case AFDIVF:
+	case AFDIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v))
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG)
+		if(a->type == D_OREG)
+			if(v->reg == a->reg)
+				return 1;
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v))
+		if(p->from.type == v->type || p->to.type == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v))
+		a->reg = s->reg;
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
--- /dev/null
+++ b/utils/kc/reg.c
@@ -1,0 +1,1122 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARETURN:
+		case AJMP:
+		case ARETT:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AFMOVF:
+		case AFMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case AJMPL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+	if(debug['R'] && debug['v']) {
+		print("\nlooping structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->use1.b[z] |
+					r->use2.b[z] | r->set.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARETURN)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+	if(debug['R'] && debug['v'])
+		print("\nprop structure:\n");
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		if(debug['R'] && debug['v'])
+			print("%P\n	set = %B; rah = %B; cal = %B\n",
+				r->prog, r->set, r->refahead, r->calahead);
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set an not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L$%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L$%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L$%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TFLOAT)
+		p1->as = AFMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AFMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case AJMPL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARETURN:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost > 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(p->to.type == D_FREG && p->as == AMOVW)
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(p->from.type == D_FREG && p->as == AMOVW)
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R9
+ *	1	R10
+ *	...	...
+ *	4	R13
+ *	5	R16
+ *	...	...
+ *	20	R31
+ */
+long
+RtoB(int r)
+{
+
+	if(r >= 9 && r <= 13)
+		return 1L << (r-9);
+	if(r >= 16 && r <= 31)
+		return 1L << (r-11);
+	return 0;
+}
+
+int
+BtoR(long b)
+{
+	int r;
+
+	b &= 0x001fffffL;
+	if(b == 0)
+		return 0;
+	r = bitno(b) + 9;
+	if(r >= 14)
+		r += 2;
+	return r;
+}
+
+/*
+ *	bit	reg
+ *	22	F4
+ *	23	F6
+ *	...	...
+ *	31	F22
+ */
+long
+FtoB(int f)
+{
+
+	if(f < 4 || f > 22 || (f&1))
+		return 0;
+	return 1L << (f/2 + 20);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xffc00000L;
+	if(b == 0)
+		return 0;
+	return bitno(b)*2 - 40;
+}
--- /dev/null
+++ b/utils/kc/sgen.c
@@ -1,0 +1,238 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int v;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASASHL;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASHL;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		v = vlog(l);
+		if(v >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASLSHR;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OLSHR;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
+
--- /dev/null
+++ b/utils/kc/swt.c
@@ -1,0 +1,583 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	Node tn;
+	
+	regalloc(&tn, &regnode, Z);
+	swit2(q, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(sval(q->val)) {
+				gopcode(OEQ, n, Z, nodconst(q->val));
+			} else {
+				gopcode(OSUB, nodconst(q->val), n, tn);
+				gopcode(OEQ, tn, Z, nodconst(0));
+			}
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(sval(r->val)) {
+		gopcode(OGT, n, Z, nodconst(r->val));
+		sp = p;
+	} else {
+		gopcode(OSUB, nodconst(r->val), n, tn);
+		gopcode(OGT, tn, Z, nodconst(0));
+		sp = p;
+	}
+	gbranch(OGOTO);
+	p->as = ABE;
+	patch(p, r->label);
+	swit2(q, i, def, n, tn);
+
+	patch(sp, pc);
+	swit2(r+1, nc-i-1, def, n, tn);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(n, v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	if(v < 0)
+		gopcode(OSUB, &nod1, nodconst(0), &nod1);
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+	if(a->op == OCONST && typev[a->type->etype]) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+void	zaddr(Biobuf*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	long l;
+
+	Bputc(b, p->as);
+	Bputc(b, p->reg);
+	l = p->lineno;
+	Bputc(b, l);
+	Bputc(b, l>>8);
+	Bputc(b, l>>16);
+	Bputc(b, l>>24);
+	zaddr(b, &p->from, sf);
+	zaddr(b, &p->to, st);
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n;
+	ulong sig;
+
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		Bputc(b, ASIGNAME);
+		Bputc(b, sig);
+		Bputc(b, sig>>8);
+		Bputc(b, sig>>16);
+		Bputc(b, sig>>24);
+		s->sig = SIGDONE;
+	}
+	else
+		Bputc(b, ANAME);	/* as */
+	Bputc(b, t);		/* type */
+	Bputc(b, s->sym);		/* sym */
+	n = s->name;
+	while(*n) {
+		Bputc(b, *n);
+		n++;
+	}
+	Bputc(b, 0);
+}
+
+void
+zaddr(Biobuf *b, Adr *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(b, a->type);
+	Bputc(b, a->reg);
+	Bputc(b, s);
+	Bputc(b, a->name);
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(b, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		l = e.h;
+		Bputc(b, l);
+		Bputc(b, l>>8);
+		Bputc(b, l>>16);
+		Bputc(b, l>>24);
+		break;
+	}
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		o += SZ_LONG - w;	/* big endian adjustment */
+		w = 1;
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v += SZ_LONG-1;
+	if(v > max)
+		max = round(v, SZ_LONG);
+	return max;
+}
--- /dev/null
+++ b/utils/kc/txt.c
@@ -1,0 +1,1270 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+	int i;
+	Type *t;
+
+	thechar = 'k';
+	thestring = "sparc";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = 0;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	memset(reg, 0, sizeof(reg));
+	reg[REGZERO] = 1;
+	reg[REGLINK] = 1;
+	reg[REGTMP] = 1;
+	for(i=NREG; i<NREG+NREG; i+=2)
+		reg[i+1] = 1;
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(i != REGZERO && i != REGTMP && i != REGLINK)
+		if(reg[i])
+			diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NREG; i+=2)
+		if(reg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesuv[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gopcode(OAS, n, Z, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i > 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+	case TVLONG:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NREG)
+				goto out;
+		}
+		j = lasti*2 + NREG;
+		for(i=NREG; i<NREG+NREG; i+=2) {
+			if(j >= NREG+NREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j += 2;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	i = 0;
+out:
+	if(i)
+		reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_LONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = 0;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod;
+	Prog *p1;
+	double d;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0.0) {
+			a = FREGZERO;
+			goto ffreg;
+		}
+		if(d == 0.5) {
+			a = FREGHALF;
+			goto ffreg;
+		}
+		if(d == 1.0) {
+			a = FREGONE;
+			goto ffreg;
+		}
+		if(d == 2.0) {
+			a = FREGTWO;
+			goto ffreg;
+		}
+		if(d == -.5) {
+			fop(OSUB, FREGHALF, FREGZERO, t);
+			return;
+		}
+		if(d == -1.0) {
+			fop(OSUB, FREGONE, FREGZERO, t);
+			return;
+		}
+		if(d == -2.0) {
+			fop(OSUB, FREGTWO, FREGZERO, t);
+			return;
+		}
+		if(d == 1.5) {
+			fop(OADD, FREGONE, FREGHALF, t);
+			return;
+		}
+		if(d == 2.5) {
+			fop(OADD, FREGTWO, FREGHALF, t);
+			return;
+		}
+		if(d == 3.0) {
+			fop(OADD, FREGTWO, FREGONE, t);
+			return;
+		}
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0) {
+			a = FREGZERO;
+		ffreg:
+			nodreg(&nod, f, NREG+a);
+			gmove(&nod, t);
+			return;
+		}
+	}
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			if(typefd[tt]) {
+				/* special case can load mem to Freg */
+				regalloc(&nod, t, t);
+				gins(AMOVW, f, &nod);
+				a = AFMOVWD;
+				if(tt == TFLOAT)
+					a = AFMOVWF;
+				gins(a, &nod, &nod);
+				gmove(&nod, t);
+				regfree(&nod);
+				return;
+			}
+			a = AMOVW;
+			break;
+		case TFLOAT:
+			a = AFMOVF;
+			break;
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		}
+		regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			if(typefd[ft]) {
+				/* special case can store mem from Freg */
+				regalloc(&nod, f, Z);
+				a = AFMOVDW;
+				if(ft == TFLOAT)
+					a = AFMOVFW;
+				gins(a, f, &nod);
+				gins(AMOVW, &nod, t);
+				regfree(&nod);
+				return;
+			}
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AFMOVF;
+			break;
+		case TVLONG:
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		}
+		if(!typefd[ft] && vconst(f) == 0) {
+			gins(a, f, t);
+			return;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TVLONG:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			a = AFMOVD;
+			if(ft == TFLOAT)
+				a = AFMOVFD;
+			break;
+		case TFLOAT:
+			a = AFMOVDF;
+			if(ft == TFLOAT)
+				a = AFMOVF;
+			break;
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TINT:
+		case TUINT:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			regalloc(&nod, f, Z);	/* should be type float */
+			a = AFMOVDW;
+			if(ft == TFLOAT)
+				a = AFMOVFW;
+			gins(a, f, &nod);
+			gins(AFMOVF, &nod, nodrat);
+			regfree(&nod);
+			gins(AMOVW, nodrat, t);
+			gmove(t, t);
+			if(nrathole < SZ_LONG)
+				nrathole = SZ_LONG;
+			return;
+		}
+		break;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+		case TFLOAT:
+			goto fxtofl;
+		case TLONG:
+		case TULONG:
+		case TINT:
+		case TUINT:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+		case TFLOAT:
+			goto fxtofl;
+		case TUINT:
+		case TINT:
+		case TULONG:
+		case TLONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+		case TFLOAT:
+		fxtofl:
+			regalloc(&nod, t, t);	/* should be type float */
+			gins(AMOVW, f, nodrat);
+			gins(AFMOVF, nodrat, &nod);
+			a = AFMOVWD;
+			if(tt == TFLOAT)
+				a = AFMOVWF;
+			gins(a, &nod, t);
+			regfree(&nod);
+			if(nrathole < SZ_LONG)
+				nrathole = SZ_LONG;
+			if(ft == TULONG) {
+				regalloc(&nod, t, Z);
+				if(tt == TFLOAT) {
+					gins(AFCMPF, t, Z);
+					p->to.type = D_FREG;
+					p->to.reg = FREGZERO;
+					gins(AFBGE, Z, Z);
+					p1 = p;
+					gins(AFMOVF, nodfconst(4294967296.), &nod);
+					gins(AFADDF, &nod, t);
+				} else {
+					gins(AFCMPD, t, Z);
+					p->to.type = D_FREG;
+					p->to.reg = FREGZERO;
+					gins(AFBGE, Z, Z);
+					p1 = p;
+					gins(AFMOVD, nodfconst(4294967296.), &nod);
+					gins(AFADDD, &nod, t);
+				}
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AFMOVF || a == AFMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et;
+	Adr ta;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		et = f1->type->etype;
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADD;
+		if(et == TFLOAT)
+			a = AFADDF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AFADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUB;
+		if(et == TFLOAT)
+			a = AFSUBF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AFSUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		break;
+
+	case OFUNC:
+		a = AJMPL;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT) {
+			a = AFMULF;
+			break;
+		} else
+		if(et == TDOUBLE || et == TVLONG) {
+			a = AFMULD;
+			break;
+		}
+		a = AMUL;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = AFDIVF;
+			break;
+		} else
+		if(et == TDOUBLE || et == TVLONG) {
+			a = AFDIVD;
+			break;
+		}
+		a = ADIV;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = AMOD;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		a = AMODL;
+		break;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVL;
+		break;
+
+	case OEQ:
+		a = ABE;
+		if(typefd[et])
+			a = AFBE;
+		goto cmp;
+
+	case ONE:
+		a = ABNE;
+		if(typefd[et])
+			a = AFBLG;
+		goto cmp;
+
+	case OLT:
+		a = ABL;
+		if(typefd[et])
+			a = AFBL;
+		goto cmp;
+
+	case OLE:
+		a = ABLE;
+		if(typefd[et])
+			a = AFBLE;
+		goto cmp;
+
+	case OGE:
+		a = ABGE;
+		if(typefd[et])
+			a = AFBGE;
+		goto cmp;
+
+	case OGT:
+		a = ABG;
+		if(typefd[et])
+			a = AFBG;
+		goto cmp;
+
+	case OLO:
+		a = ABCS;
+		goto cmp;
+
+	case OLS:
+		a = ABLEU;
+		goto cmp;
+
+	case OHS:
+		a = ABCC;
+		goto cmp;
+
+	case OHI:
+		a = ABGU;
+		goto cmp;
+
+	cmp:
+		nextpc();
+		p->as = ACMP;
+		if(et == TFLOAT)
+			p->as = AFCMPF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			p->as = AFCMPD;
+		if(f1 != Z)
+			naddr(f1, &p->from);
+		if(t != Z)
+			naddr(t, &p->to);
+		if(f1 == Z || t == Z || f2 != Z)
+			diag(Z, "bad cmp in gopcode %O", o);
+		if(debug['g'])
+			print("%P\n", p);
+		f1 = Z;
+		f2 = Z;
+		t = Z;
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+		if(ta.type == D_CONST && ta.offset == 0)
+			p->reg = REGZERO;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARETURN;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	p->from.name = D_EXTERN;
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sval(long v)
+{
+
+	if(v >= -(1<<12) && v < (1<<12))
+		return 1;
+	return 0;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= -(vlong)(1<<12) && vv < (vlong)(1<<12))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= 3)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= 16)
+			return 0;
+		o = exfregoffset + NREG;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
--- /dev/null
+++ b/utils/kl/asm.c
@@ -1,0 +1,1242 @@
+#include	"l.h"
+
+#define	LPUT(c)\
+	{\
+		cbp[0] = (c)>>24;\
+		cbp[1] = (c)>>16;\
+		cbp[2] = (c)>>8;\
+		cbp[3] = (c);\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	CPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp++;\
+		cbc--;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	if(s->type != STEXT && s->type != SLEAF)
+		diag("entry not text: %s", s->name);
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	seek(cout, HEADR, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 4;
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(asmout(p, o, 0)) {
+			p = p->link;
+			pc += 4;
+		}
+		pc += o->size;
+	}
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 3:
+		seek(cout, HEADR+textsize, 0);
+		break;
+	case 1:
+	case 2:
+		seek(cout, HEADR+textsize, 0);
+		break;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100);
+		else
+			datblk(t, datsize-t);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 3:
+			seek(cout, HEADR+textsize+datsize, 0);
+			break;
+		case 2:
+		case 1:
+			seek(cout, HEADR+textsize+datsize, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sp\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		 /* round up file length for boot image */
+		if(HEADTYPE == 0 || HEADTYPE == 3)
+			if((symsize+lcsize) & 1)
+				CPUT(0);
+		cflush();
+	}
+
+	seek(cout, 0L, 0);
+	switch(HEADTYPE) {
+	case 0:
+		lput(0x1030107);		/* magic and sections */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 1:
+		break;
+	case 2:
+		lput(4*13*13+7);		/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 3:
+		lput(0x1030107);		/* magic and sections */
+		lput(0x90100000);
+#define SPARC_NOOP 0x01000000
+		lput(SPARC_NOOP);
+		lput(SPARC_NOOP);
+		lput(SPARC_NOOP);
+		lput(SPARC_NOOP);
+		lput(SPARC_NOOP);
+		lput(SPARC_NOOP);
+		break;
+	}
+	cflush();
+}
+
+void
+lput(long l)
+{
+
+	LPUT(l);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	LPUT(v);
+	if(ver)
+		t += 'a' - 'A';
+	CPUT(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		CPUT(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			CPUT(s[i]);
+			CPUT(s[i+1]);
+		}
+		CPUT(0);
+		CPUT(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			CPUT(s[i]);
+		CPUT(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['L'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['L'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			CPUT(s+128);	/* 129-255 +pc */
+			if(debug['L'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			CPUT(0);	/* 0 vv +lc */
+			CPUT(s>>24);
+			CPUT(s>>16);
+			CPUT(s>>8);
+			CPUT(s);
+			if(debug['L']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			CPUT(0+s);	/* 1-64 +lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			CPUT(64-s);	/* 65-128 -lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		CPUT(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['L'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization\n%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(&p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i+4]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)&p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			if(p->to.sym) {
+				if(p->to.sym->type == STEXT ||
+				   p->to.sym->type == SLEAF)
+					d += p->to.sym->value;
+				if(p->to.sym->type == SDATA)
+					d += p->to.sym->value + INITDAT;
+				if(p->to.sym->type == SBSS)
+					d += p->to.sym->value + INITDAT;
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+#define	OP2(x)	(0x80000000|((x)<<19))
+#define	OP3(x)	(0xc0000000|((x)<<19))
+#define	OPB(x)	(0x00800000|((x)<<25))
+#define	OPT(x)	(0x81d02000|((x)<<25))
+#define	OPF1(x)	(0x81a00000|((x)<<5))
+#define	OPF2(x)	(0x81a80000|((x)<<5))
+#define	OPFB(x)	(0x01800000|((x)<<25))
+
+#define	OP_RRR(op,r1,r2,r3)\
+	(0x00000000 | op |\
+	(((r1)&31L)<<0) |\
+	(((r2)&31L)<<14) |\
+	(((r3)&31L)<<25))
+#define	OP_IRR(op,i,r2,r3)\
+	(0x00002000L | (op) |\
+	(((i)&0x1fffL)<<0) |\
+	(((r2)&31L)<<14) |\
+	(((r3)&31L)<<25))
+#define	OP_BRA(op,pc)\
+	((op) |\
+	(((pc)&0x3fffff)<<0))
+
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+	long o1, o2, o3, o4, o5, v;
+	Prog *ct;
+	int r;
+
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+
+	switch(o->type) {
+	default:
+		if(aflag)
+			return 0;
+		diag("unknown type %d", o->type);
+		if(!debug['a'])
+			prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		if(aflag) {
+			if(p->link) {
+				if(p->as == ATEXT) {
+					ct = curtext;
+					o2 = autosize;
+					curtext = p;
+					autosize = p->to.offset + 4;
+					o1 = asmout(p->link, oplook(p->link), aflag);
+					curtext = ct;
+					autosize = o2;
+				} else
+					o1 = asmout(p->link, oplook(p->link), aflag);
+			}
+			return o1;
+		}
+		break;
+
+	case 1:		/* mov r1,r2 ==> OR r1,r0,r2 */
+		o1 = OP_RRR(opcode(AOR), p->from.reg, REGZERO, p->to.reg);
+		break;
+
+	case 2:		/* mov $c,r ==> add r1,r0,r2 */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = OP_IRR(opcode(AADD), v, r, p->to.reg);
+		break;
+
+	case 3:		/* mov soreg, r */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		if(v == 0 && p->reg != NREG)
+			o1 = OP_RRR(opcode(p->as), p->reg, r, p->to.reg);
+		else
+			o1 = OP_IRR(opcode(p->as), v, r, p->to.reg);
+		break;
+
+	case 4:		/* mov r, soreg */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		if(v == 0 && p->reg != NREG)
+			o1 = OP_RRR(opcode(p->as+AEND), p->reg, r, p->from.reg);
+		else
+			o1 = OP_IRR(opcode(p->as+AEND), v, r, p->from.reg);
+		break;
+
+	case 5:		/* mov $lcon, reg => sethi, add */
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, p->to.reg);
+		break;
+
+	case 6:		/* mov asi, r[+r] */
+		o1 = OP_RRR(opcode(p->as), p->reg, p->from.reg, p->to.reg);
+		o1 |= (1<<23) | ((p->from.offset&0xff)<<5);
+		break;
+
+	case 7:		/* mov [+r]r, asi */
+		o1 = OP_RRR(opcode(p->as+AEND), p->reg, p->to.reg, p->from.reg);
+		o1 |= (1<<23) | ((p->to.offset&0xff)<<5);
+		break;
+
+	case 8:		/* mov r, preg and mov preg, r */
+		if(p->to.type == D_PREG) {
+			r = p->from.reg;
+			switch(p->to.reg)
+			{
+			default:
+				diag("unknown register P%d", p->to.reg);
+			case D_Y:
+				o1 = OP2(48);	/* wry */
+				break;
+			case D_PSR:
+				o1 = OP2(49);	/* wrpsr */
+				break;
+			case D_WIM:
+				o1 = OP2(50);	/* wrwim */
+				break;
+			case D_TBR:
+				o1 = OP2(51);	/* wrtbr */
+				break;
+			}
+			o1 = OP_IRR(o1, 0, r, 0);
+			break;
+		}
+		if(p->from.type == D_PREG) {
+			r = p->to.reg;
+			switch(p->from.reg)
+			{
+			default:
+				diag("unknown register P%d", p->to.reg);
+			case D_Y:
+				o1 = OP2(40);	/* rdy */
+				break;
+			case D_PSR:
+				o1 = OP2(41);	/* rdpsr */
+				break;
+			case D_WIM:
+				o1 = OP2(42);	/* rdwim */
+				break;
+			case D_TBR:
+				o1 = OP2(43);	/* rdtbr */
+				break;
+			}
+			o1 = OP_RRR(o1, 0, 0, r);
+			break;
+		}
+		break;
+
+	case 9:		/* movb r,r */
+		v = 24;
+		if(p->as == AMOVH || p->as == AMOVHU)
+			v = 16;
+		r = ASRA;
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			r = ASRL;
+		o1 = OP_IRR(opcode(ASLL), v, p->from.reg, p->to.reg);
+		o2 = OP_IRR(opcode(r), v, p->to.reg, p->to.reg);
+		break;
+
+	case 10:	/* mov $loreg, reg */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, p->to.reg);
+		break;
+
+	case 11:	/* mov loreg, r */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(opcode(p->as), (v&0x3ff), REGTMP, p->to.reg);
+		break;
+
+	case 12:	/* mov r, loreg */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(opcode(p->as+AEND), (v&0x3ff), REGTMP, p->from.reg);
+		break;
+
+	case 13:	/* mov $ucon, r */
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (p->to.reg<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		break;
+
+	case 20:	/* op $scon,r */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(opcode(p->as), v, r, p->to.reg);
+		break;
+
+	case 21:	/* op r1,r2 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(opcode(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 22:	/* op $lcon,r */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, REGTMP);
+		o3 = OP_RRR(opcode(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 23:	/* cmp r,r */
+		o1 = OP_RRR(opcode(ASUBCC), p->to.reg, p->from.reg, REGZERO);
+		break;
+
+	case 24:	/* cmp r,$c */
+		v = regoff(&p->to);
+		o1 = OP_IRR(opcode(ASUBCC), v, p->from.reg, REGZERO);
+		break;
+
+	case 25:	/* cmp $c,r BOTCH, fix compiler */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opcode(AADD), v, NREG, REGTMP);
+		o2 = OP_RRR(opcode(ASUBCC), p->to.reg, REGTMP, REGZERO);
+		break;
+
+	case 26:	/* op $ucon,r */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 30:	/* jmp/jmpl soreg */
+		if(aflag)
+			return 0;
+		v = regoff(&p->to);
+		r = p->reg;
+		if(r == NREG && p->as == AJMPL)
+			r = 15;
+		o1 = OP_IRR(opcode(AJMPL), v, p->to.reg, r);
+		break;
+
+	case 31:	/* ba jmp */
+		if(aflag)
+			return 0;
+		r = p->as;
+		if(r == AJMP)
+			r = ABA;
+		v = 0;
+		if(p->cond)
+			v = p->cond->pc - p->pc;
+		o1 = OP_BRA(opcode(r), v/4);
+		if(r == ABA && p->link && p->cond && isnop(p->link)) {
+			o2 = asmout(p->cond, oplook(p->cond), 1);
+			if(o2) {
+				o1 += 1;
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
+				LPUT(o1);
+				LPUT(o2);
+				return 1;
+			}
+			/* cant set annul here because pc has already been counted */
+		}
+		break;
+
+	case 32:	/* jmpl lbra */
+		if(aflag)
+			return 0;
+		v = 0;
+		if(p->cond)
+			v = p->cond->pc - p->pc;
+		r = p->reg;
+		if(r != NREG && r != 15)
+			diag("cant jmpl other than R15");
+		o1 = 0x40000000 | ((v/4) & 0x3fffffffL);	/* call */
+		if(p->link && p->cond && isnop(p->link)) {
+			o2 = asmout(p->cond, oplook(p->cond), 1);
+			if(o2) {
+				o1 += 1;
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
+				LPUT(o1);
+				LPUT(o2);
+				return 1;
+			}
+		}
+		break;
+
+	case 33:	/* trap r */
+		if(aflag)
+			return 0;
+		o1 = opcode(p->as) | (p->from.reg<<14);
+		break;
+
+	case 34:	/* rett r1,r2 -> jmpl (r1); rett (r2) */
+		if(aflag)
+			return 0;
+		o1 = OP_IRR(opcode(AJMPL), 0, p->from.reg, REGZERO);
+		o2 = OP_IRR(opcode(ARETT), 0, p->to.reg, REGZERO);
+		break;
+
+	case 40:	/* ldfsr, stfsr, stdq */
+		if(p->to.type == D_PREG) {
+			r = p->from.reg;
+			if(r == NREG)
+				r = o->param;
+			v = regoff(&p->from);
+			if(p->to.reg == D_FSR) {
+				o1 = OP_IRR(OP3(33), v, r, 0);
+				break;
+			}
+			diag("unknown reg load %d", p->to.reg);
+		} else {
+			r = p->to.reg;
+			if(r == NREG)
+				r = o->param;
+			v = regoff(&p->to);
+			if(p->from.reg == D_FSR) {
+				o1 = OP_IRR(OP3(37), v, r, 0);
+				break;
+			}
+			if(p->as == AMOVD && p->from.reg == D_FPQ) {
+				o1 = OP_IRR(OP3(38), v, r, 0);
+				break;
+			}
+			diag("unknown reg store %d", p->from.reg);
+		}
+		break;
+
+	case 41:	/* ldf,ldd */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		if(p->as == AFMOVF || p->as == AMOVW) {
+			o1 = OP_IRR(OP3(32), v, r, p->to.reg);
+			break;
+		}
+		if(p->as == AMOVD || p->as == AFMOVD) {
+			o1 = OP_IRR(OP3(35), v, r, p->to.reg);
+			break;
+		}
+		diag("only MOVD and MOVW to FREG");
+		break;
+
+	case 42:	/* ldd -> ldf,ldf */
+		/* note should be ldd with proper allignment */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = OP_IRR(OP3(32), v, r, p->to.reg);
+		o2 = OP_IRR(OP3(32), v+4, r, p->to.reg+1);
+		break;
+
+	case 43:	/* stf */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		if(p->as == AFMOVF || p->as == AMOVW) {
+			o1 = OP_IRR(OP3(36), v, r, p->from.reg);
+			break;
+		}
+		if(p->as == AMOVD || p->as == AFMOVD) {
+			o1 = OP_IRR(OP3(39), v, r, p->from.reg);
+			break;
+		}
+		diag("only MOVD and MOVW from FREG");
+		break;
+
+	case 44:	/* std -> stf,stf */
+		/* note should be std with proper allignment */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = OP_IRR(OP3(36), v, r, p->from.reg);
+		o2 = OP_IRR(OP3(36), v+4, r, p->from.reg+1);
+		break;
+
+	case 45:	/* ldf lorg */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(OP3(32), v&0x3ff, REGTMP, p->to.reg);
+		break;
+
+	case 46:	/* ldd lorg -> ldf,ldf */
+		/* note should be ldd with proper allignment */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(OP3(32), (v&0x3ff), REGTMP, p->to.reg);
+		o4 = OP_IRR(OP3(32), (v&0x3ff)+4, REGTMP, p->to.reg+1);
+		break;
+
+	case 47:	/* stf lorg */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(OP3(36), v&0x3ff, REGTMP, p->from.reg);
+		break;
+
+	case 48:	/* std lorg -> stf,stf */
+		/* note should be std with proper allignment */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);	/* sethi */
+		o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
+		o3 = OP_IRR(OP3(36), (v&0x3ff), REGTMP, p->from.reg);
+		o4 = OP_IRR(OP3(36), (v&0x3ff)+4, REGTMP, p->from.reg+1);
+		break;
+
+	case 49:	/* fmovd -> fmovf,fmovf */
+		o1 = OP_RRR(opcode(AFMOVF), p->from.reg, 0, p->to.reg);
+		o2 = OP_RRR(opcode(AFMOVF), p->from.reg+1, 0, p->to.reg+1);
+		break;
+
+	case 50:	/* fcmp */
+		o1 = OP_RRR(opcode(p->as), p->to.reg, p->from.reg, 0);
+		break;
+
+	case 51:	/* word */
+		if(aflag)
+			return 0;
+		o1 = regoff(&p->from);
+		break;
+
+	case 52:	/* div */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(opcode(ASRA), 31, r, REGTMP);
+		o2 = OP_IRR(OP2(48), 0, REGTMP, 0);
+		o3 = OP_RRR(opcode(ADIV), p->from.reg, r, p->to.reg);
+		break;
+
+	case 53:	/* divl */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(OP2(48), 0, REGZERO, 0);
+		o2 = OP_RRR(opcode(ADIVL), p->from.reg, r, p->to.reg);
+		break;
+
+	case 54:	/* mod */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(opcode(ASRA), 31, r, REGTMP);
+		o2 = OP_IRR(OP2(48), 0, REGTMP, 0);
+		o3 = OP_RRR(opcode(ADIV), p->from.reg, r, REGTMP);
+		o4 = OP_RRR(opcode(AMUL), p->from.reg, REGTMP, REGTMP);
+		o5 = OP_RRR(opcode(ASUB), REGTMP, r, p->to.reg);
+		break;
+
+	case 55:	/* modl */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(OP2(48), 0, REGZERO, 0);
+		o2 = OP_RRR(opcode(ADIVL), p->from.reg, r, REGTMP);
+		o3 = OP_RRR(opcode(AMUL), p->from.reg, REGTMP, REGTMP);
+		o4 = OP_RRR(opcode(ASUB), REGTMP, r, p->to.reg);
+		break;
+
+	case 56:	/* b(cc) -- annullable */
+		if(aflag)
+			return 0;
+		r = p->as;
+		v = 0;
+		if(p->cond)
+			v = p->cond->pc - p->pc;
+		o1 = OP_BRA(opcode(r), v/4);
+		if(p->link && p->cond && isnop(p->link))
+		if(!debug['A']) {
+			o2 = asmout(p->cond, oplook(p->cond), 2);
+			if(o2) {
+				o1 |= 1<<29;	/* annul */
+				o1 += 1;
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
+				LPUT(o1);
+				LPUT(o2);
+				return 1;
+			}
+		}
+		break;
+
+	case 57:	/* op r1,r2 with reserved rs1 */
+		r = 0;
+		o1 = OP_RRR(opcode(p->as), p->from.reg, r, p->to.reg);
+		break;
+	}
+	if(aflag)
+		return o1;
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		LPUT(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		LPUT(o1);
+		LPUT(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		LPUT(o5);
+		break;
+	}
+	return 0;
+}
+
+int
+isnop(Prog *p)
+{
+	if(p->as != AORN)
+		return 0;
+	if(p->reg != REGZERO && p->reg != NREG)
+		return 0;
+	if(p->from.type != D_REG || p->from.reg != REGZERO)
+		return 0;
+	if(p->to.type != D_REG || p->to.reg != REGZERO)
+		return 0;
+	return 1;
+}
+
+long
+opcode(int a)
+{
+	switch(a) {
+	case AADD:	return OP2(0);
+	case AADDCC:	return OP2(16);
+	case AADDX:	return OP2(8);
+	case AADDXCC:	return OP2(24);
+
+	case AMUL:	return OP2(10);
+	case ADIV:	return OP2(15);
+	case ADIVL:	return OP2(14);
+
+	case ATADDCC:	return OP2(32);
+	case ATADDCCTV:	return OP2(34);
+
+	case ASUB:	return OP2(4);
+	case ASUBCC:	return OP2(20);
+	case ASUBX:	return OP2(12);
+	case ASUBXCC:	return OP2(28);
+
+	case ATSUBCC:	return OP2(33);
+	case ATSUBCCTV:	return OP2(35);
+
+	case AMULSCC:	return OP2(36);
+	case ASAVE:	return OP2(60);
+	case ARESTORE:	return OP2(61);
+
+	case AAND:	return OP2(1);
+	case AANDCC:	return OP2(17);
+	case AANDN:	return OP2(5);
+	case AANDNCC:	return OP2(21);
+
+	case AOR:	return OP2(2);
+	case AORCC:	return OP2(18);
+	case AORN:	return OP2(6);
+	case AORNCC:	return OP2(22);
+
+	case AXOR:	return OP2(3);
+	case AXORCC:	return OP2(19);
+	case AXNOR:	return OP2(7);
+	case AXNORCC:	return OP2(23);
+
+	case ASLL:	return OP2(37);
+	case ASRL:	return OP2(38);
+	case ASRA:	return OP2(39);
+
+	case AJMPL:
+	case AJMP:	return OP2(56);
+	case ARETT:	return OP2(57);
+
+	case AMOVBU:	return OP3(1);	/* ldub */
+	case AMOVB:	return OP3(9);	/* ldsb */
+	case AMOVHU:	return OP3(2);	/* lduh */
+	case AMOVH:	return OP3(10);	/* ldsh */
+	case AMOVW:	return OP3(0);	/* ld */
+	case AMOVD:	return OP3(3);	/* ldd */
+
+	case AMOVBU+AEND:
+	case AMOVB+AEND:return OP3(5);	/* stb */
+
+	case AMOVHU+AEND:
+	case AMOVH+AEND:return OP3(6);	/* sth */
+
+	case AMOVW+AEND:return OP3(4);	/* st */
+
+	case AMOVD+AEND:return OP3(7);	/* std */
+
+	case ASWAP:			/* swap is symmetric */
+	case ASWAP+AEND:return OP3(15);
+
+	case ATAS:	return OP3(13);	/* tas is really ldstub */
+
+	case ABN:	return OPB(0);
+	case ABE:	return OPB(1);
+	case ABLE:	return OPB(2);
+	case ABL:	return OPB(3);
+	case ABLEU:	return OPB(4);
+	case ABCS:	return OPB(5);
+	case ABNEG:	return OPB(6);
+	case ABVS:	return OPB(7);
+	case ABA:	return OPB(8);
+	case ABNE:	return OPB(9);
+	case ABG:	return OPB(10);
+	case ABGE:	return OPB(11);
+	case ABGU:	return OPB(12);
+	case ABCC:	return OPB(13);
+	case ABPOS:	return OPB(14);
+	case ABVC:	return OPB(15);
+
+	case AFBA:	return OPFB(8);
+	case AFBE:	return OPFB(9);
+	case AFBG:	return OPFB(6);
+	case AFBGE:	return OPFB(11);
+	case AFBL:	return OPFB(4);
+	case AFBLE:	return OPFB(13);
+	case AFBLG:	return OPFB(2);
+	case AFBN:	return OPFB(0);
+	case AFBNE:	return OPFB(1);
+	case AFBO:	return OPFB(15);
+	case AFBU:	return OPFB(7);
+	case AFBUE:	return OPFB(10);
+	case AFBUG:	return OPFB(5);
+	case AFBUGE:	return OPFB(12);
+	case AFBUL:	return OPFB(3);
+	case AFBULE:	return OPFB(14);
+
+	case ATN:	return OPT(0);
+	case ATE:	return OPT(1);
+	case ATLE:	return OPT(2);
+	case ATL:	return OPT(3);
+	case ATLEU:	return OPT(4);
+	case ATCS:	return OPT(5);
+	case ATNEG:	return OPT(6);
+	case ATVS:	return OPT(7);
+	case ATA:	return OPT(8);
+	case ATNE:	return OPT(9);
+	case ATG:	return OPT(10);
+	case ATGE:	return OPT(11);
+	case ATGU:	return OPT(12);
+	case ATCC:	return OPT(13);
+	case ATPOS:	return OPT(14);
+	case ATVC:	return OPT(15);
+
+	case AFADDF:	return OPF1(65);
+	case AFADDD:	return OPF1(66);
+	case AFADDX:	return OPF1(67);
+	case AFSUBF:	return OPF1(69);
+	case AFSUBD:	return OPF1(70);
+	case AFSUBX:	return OPF1(71);
+	case AFMULF:	return OPF1(73);
+	case AFMULD:	return OPF1(74);
+	case AFMULX:	return OPF1(75);
+	case AFDIVF:	return OPF1(77);
+	case AFDIVD:	return OPF1(78);
+	case AFDIVX:	return OPF1(79);
+
+	case AFMOVF:	return OPF1(1);
+	case AFNEGF:	return OPF1(5);
+	case AFABSF:	return OPF1(9);
+
+	case AFSQRTF:	return OPF1(41);
+	case AFSQRTD:	return OPF1(42);
+	case AFSQRTX:	return OPF1(43);
+
+	case AFMOVWF:	return OPF1(196);
+	case AFMOVWD:	return OPF1(200);
+	case AFMOVWX:	return OPF1(204);
+	case AFMOVFW:	return OPF1(209);
+	case AFMOVDW:	return OPF1(210);
+	case AFMOVXW:	return OPF1(211);
+	case AFMOVFD:	return OPF1(201);
+	case AFMOVFX:	return OPF1(205);
+	case AFMOVDF:	return OPF1(198);
+	case AFMOVDX:	return OPF1(206);
+	case AFMOVXF:	return OPF1(199);
+	case AFMOVXD:	return OPF1(203);
+
+	case AFCMPF:	return OPF2(81);
+	case AFCMPD:	return OPF2(82);
+	case AFCMPX:	return OPF2(83);
+	case AFCMPEF:	return OPF2(85);
+	case AFCMPED:	return OPF2(86);
+	case AFCMPEX:	return OPF2(87);
+
+	case AUNIMP:	return 0;
+	}
+	diag("bad opcode %A", a);
+	return 0;
+}
--- /dev/null
+++ b/utils/kl/foo.c
@@ -1,0 +1,15 @@
+int foo(void)
+{
+	return 100;
+}
+
+main()
+{
+	int x;
+
+	x = foo();
+}
+
+_main()
+{
+}
--- /dev/null
+++ b/utils/kl/l.h
@@ -1,0 +1,324 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../kc/k.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char	u0sval[NSNAME];
+		Ieee	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog	*forwd;
+	Prog	*cond;
+	Prog	*link;
+	long	pc;
+	long	regused;
+	short	line;
+	short	mark;
+	uchar	optab;
+	uchar	as;
+	char	reg;
+};
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	long	value;
+	Sym	*link;
+};
+struct	Autom
+{
+	Sym	*asym;
+	Auto	*link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	uchar	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+};
+EXTERN	struct
+{
+	Optab*	start;
+	Optab*	stop;
+} oprange[AEND];
+
+enum
+{
+	AXLD		= AEND+1,
+	AXST,
+	FPCHIP		= 1,
+	BIG		= 4096-8,
+	STRINGSZ	= 200,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+	DATBLK		= 1024,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	NSCHED		= 20,
+
+/* mark flags */
+	LABEL		= 1<<0,
+	LEAF		= 1<<1,
+	FLOAT		= 1<<2,
+	BRANCH		= 1<<3,
+	LOAD		= 1<<4,
+	FCMP		= 1<<5,
+	SYNC		= 1<<6,
+	LIST		= 1<<7,
+	FOLL		= 1<<8,
+	NOSCHED		= 1<<9,
+
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+
+	C_NONE		= 0,
+
+	C_REG,
+	C_FREG,
+	C_CREG,
+	C_PREG,
+	C_FSR,
+	C_FQ,
+
+	C_ZCON,		/* 0 */
+	C_SCON,		/* 13 bit signed */
+	C_UCON,		/* low 10 bits 0 */
+	C_LCON,		/* other */
+
+	C_SACON,
+	C_SECON,
+	C_LACON,
+	C_LECON,
+
+	C_SBRA,
+	C_LBRA,
+
+	C_ESAUTO,
+	C_OSAUTO,
+	C_SAUTO,
+	C_OLAUTO,
+	C_ELAUTO,
+	C_LAUTO,
+
+	C_ESEXT,
+	C_OSEXT,
+	C_SEXT,
+	C_ELEXT,
+	C_OLEXT,
+	C_LEXT,
+
+	C_ZOREG,
+	C_SOREG,
+	C_LOREG,
+	C_ASI,
+
+	C_ANY,
+
+	C_GOK,
+
+	C_NCLASS
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	Prog*	prog_mul;
+EXTERN	Prog*	prog_div;
+EXTERN	Prog*	prog_divl;
+EXTERN	Prog*	prog_mod;
+EXTERN	Prog*	prog_modl;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi8[8];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	char*	noname;
+EXTERN	long	instoffset;
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	long	symsize;
+EXTERN	long	staticgen;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	tothunk;
+EXTERN	char	xcmp[C_NCLASS][C_NCLASS];
+EXTERN	int	version;
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+
+extern	Optab	optab[];
+extern	char*	anames[];
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	histtoauto(void);
+void	addnop(Prog*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmlc(void);
+int	asmout(Prog*, Optab*, int);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	cflush(void);
+int	cmp(int, int);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long);
+void	diag(char*, ...);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+int	find1(long, int);
+void	follow(void);
+void	gethunk(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+void	initmuldiv(void);
+Sym*	lookup(char*, int);
+void	lput(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	noops(void);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opcode(int);
+Optab*	oplook(Prog*);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	sched(Prog*, Prog*);
+void	span(void);
+void	undef(void);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
--- /dev/null
+++ b/utils/kl/list.c
@@ -1,0 +1,258 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	if(a == ADATA || a == AINIT || a == ADYNT)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else{
+		s = str;
+		if(p->mark & NOSCHED)
+			s += sprint(s, "*");
+		if(p->reg == NREG)
+			sprint(s, "	%A	%D,%D", a, &p->from, &p->to);
+		else
+		if(p->from.type == D_OREG) {
+			sprint(s, "	%A	%ld(R%d+R%d),%D", a,
+				p->from.offset, p->from.reg, p->reg, &p->to);
+		} else
+		if(p->to.type == D_OREG) {
+			sprint(s, "	%A	%D,%ld(R%d+R%d)", a,
+				&p->from, p->to.offset, p->to.reg, p->reg);
+		} else
+		if(p->from.type == D_FREG)
+			sprint(s, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a <= ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_ASI:
+		if(a->reg != NREG)
+			sprint(str, "(R%d,%ld)", a->reg, a->offset);
+		else
+			sprint(str, "(R%d,%ld)", 0, a->offset);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(F%d)(REG)", a, a->reg);
+		break;
+
+	case D_CREG:
+		sprint(str, "C%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(C%d)(REG)", a, a->reg);
+		break;
+
+	case D_PREG:
+		sprint(str, "P%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(P%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(v >= INITTEXT)
+				v -= INITTEXT-HEADR;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%lux-%lux", a->ieee.h, a->ieee.l);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/kl/mkfile
@@ -1,0 +1,34 @@
+<../../mkconfig
+
+CFLAGS=$CFLAGS -I../include -I.
+
+TARG=kl
+
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	sched.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+
+HFILES=\
+	l.h\
+	../kc/k.out.h\
+	../include/ar.h\
+
+LIBS=bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+enam.$O:	../kc/enam.c
+	$CC $CFLAGS ../kc/enam.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/kl/noop.c
@@ -1,0 +1,650 @@
+#include	"l.h"
+
+void
+noops(void)
+{
+	Prog *p, *p1, *q, *q1;
+	int o, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		/* too hard, just leave alone */
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			q = p;
+			p->mark |= LABEL|LEAF|SYNC;
+			if(p->link)
+				p->link->mark |= LABEL;
+			curtext = p;
+			break;
+
+		case AORN:
+			q = p;
+			if(p->to.type == D_REG)
+				if(p->to.reg == REGZERO)
+					p->mark |= LABEL|SYNC;
+			break;
+
+		case AUNIMP:
+		case ATAS:
+		case ASWAP:
+		case ATA:
+		case ATCC:
+		case ATCS:
+		case ATE:
+		case ATG:
+		case ATGE:
+		case ATGU:
+		case ATL:
+		case ATLE:
+		case ATLEU:
+		case ATN:
+		case ATNE:
+		case ATNEG:
+		case ATPOS:
+		case ATVC:
+		case ATVS:
+		case AWORD:
+			q = p;
+			p->mark |= LABEL|SYNC;
+			continue;
+
+		case AFABSD:
+		case AFABSF:
+		case AFABSX:
+		case AFADDD:
+		case AFADDF:
+		case AFADDX:
+		case AFDIVD:
+		case AFDIVF:
+		case AFDIVX:
+		case AFMOVD:
+		case AFMOVDF:
+		case AFMOVDW:
+		case AFMOVDX:
+		case AFMOVF:
+		case AFMOVFD:
+		case AFMOVFW:
+		case AFMOVFX:
+		case AFMOVWD:
+		case AFMOVWF:
+		case AFMOVWX:
+		case AFMOVX:
+		case AFMOVXD:
+		case AFMOVXF:
+		case AFMOVXW:
+		case AFMULD:
+		case AFMULF:
+		case AFMULX:
+		case AFNEGD:
+		case AFNEGF:
+		case AFNEGX:
+		case AFSQRTD:
+		case AFSQRTF:
+		case AFSQRTX:
+		case AFSUBD:
+		case AFSUBF:
+		case AFSUBX:
+			q = p;
+			p->mark |= FLOAT;
+			continue;
+
+		case AMUL:
+		case ADIV:
+		case ADIVL:
+		case AMOD:
+		case AMODL:
+			q = p;
+			if(!debug['M']) {
+				if(prog_mul == P)
+					initmuldiv();
+				if(curtext != P)
+					curtext->mark &= ~LEAF;
+			}
+			continue;
+
+		case AJMPL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case AJMP:
+
+		case ABA:
+		case ABN:
+		case ABE:
+		case ABNE:
+		case ABLE:
+		case ABG:
+		case ABL:
+		case ABGE:
+		case ABLEU:
+		case ABGU:
+		case ABCS:
+		case ABCC:
+		case ABNEG:
+		case ABPOS:
+		case ABVC:
+		case ABVS:
+
+		case AFBN:
+		case AFBO:
+		case AFBE:
+		case AFBLG:
+		case AFBG:
+		case AFBLE:
+		case AFBGE:
+		case AFBL:
+		case AFBNE:
+		case AFBUE:
+		case AFBA:
+		case AFBU:
+		case AFBUG:
+		case AFBULE:
+		case AFBUGE:
+		case AFBUL:
+			p->mark |= BRANCH;
+			q = p;
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+				if(!(q1->mark & LEAF))
+					q1->mark |= LABEL;
+			} else
+				p->mark |= LABEL;
+			q1 = p->link;
+			if(q1 != P)
+				q1->mark |= LABEL;
+			continue;
+
+		case AFCMPD:
+		case AFCMPED:
+		case AFCMPEF:
+		case AFCMPEX:
+		case AFCMPF:
+		case AFCMPX:
+			q = p;
+			p->mark |= FCMP|FLOAT;
+			continue;
+
+		case ARETURN:
+			/* special form of RETURN is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+
+			q = p;
+			if(p->link != P)
+				p->link->mark |= LABEL;
+			continue;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		default:
+			q = p;
+			continue;
+		}
+	}
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+
+		case AJMPL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	curtext = P;
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if((p->mark & LEAF) && autosize <= 4)
+				autosize = 0;
+			else
+				if(autosize & 4)
+					autosize += 4;
+			p->to.offset = autosize - 4;
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = ASUB;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			} else
+			if(!(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+				break;
+			}
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->from.offset = 0;
+			q1->to.reg = REGSP;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		case AMUL:
+		case ADIV:
+		case ADIVL:
+		case AMOD:
+		case AMODL:
+			if(debug['M'])
+				break;
+			if(p->from.type != D_REG)
+				break;
+			if(p->to.type != D_REG)
+				break;
+			q1 = p;
+
+			/* MOV a,4(SP) */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->from.reg;
+			p->to.type = D_OREG;
+			p->to.reg = REGSP;
+			p->to.offset = 4;
+
+			/* MOV b,REGTMP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->reg;
+			if(q1->reg == NREG)
+				p->from.reg = q1->to.reg;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+			p->to.offset = 0;
+
+			/* CALL appropriate */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AJMPL;
+			p->line = q1->line;
+			p->to.type = D_BRANCH;
+			p->cond = p;
+			p->mark |= BRANCH;
+			switch(o) {
+			case AMUL:
+				p->cond = prog_mul;
+				break;
+			case ADIV:
+				p->cond = prog_div;
+				break;
+			case ADIVL:
+				p->cond = prog_divl;
+				break;
+			case AMOD:
+				p->cond = prog_mod;
+				break;
+			case AMODL:
+				p->cond = prog_modl;
+				break;
+			}
+
+			/* MOV REGTMP, b */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->from.offset = 0;
+			p->to.type = D_REG;
+			p->to.reg = q1->to.reg;
+
+			/* ADD $8,SP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.reg = NREG;
+			p->from.offset = 8;
+			p->reg = NREG;
+			p->to.type = D_REG;
+			p->to.reg = REGSP;
+
+			/* SUB $8,SP */
+			q1->as = ASUB;
+			q1->from.type = D_CONST;
+			q1->from.offset = 8;
+			q1->from.reg = NREG;
+			q1->reg = NREG;
+			q1->to.type = D_REG;
+			q1->to.reg = REGSP;
+			break;
+
+		case ARETURN:
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = AJMP;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 8;
+					p->to.reg = REGLINK;
+					p->mark |= BRANCH;
+					break;
+				}
+
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = AJMP;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 8;
+				q->to.reg = REGLINK;
+				q->mark |= BRANCH;
+
+				q->link = p->link;
+				p->link = q;
+				break;
+			}
+
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = REGRET+1;
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			}
+
+			q1 = prg();
+			q1->as = AJMP;
+			q1->line = p->line;
+			q1->to.type = D_OREG;
+			q1->to.offset = 8;
+			q1->to.reg = REGRET+1;
+			q1->mark |= BRANCH;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				q = prg();
+				q->line = p->line;
+				q->as = AJMP;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				q->mark |= BRANCH;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+			}
+			q = prg();
+			q->line = p->line;
+			q->as = AJMP;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->mark |= BRANCH;
+			q->link = p->link;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AADD;
+			q->from.type = D_CONST;
+			q->from.offset = autosize;
+			q->to.type = D_REG;
+			q->to.reg = REGSP;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVW;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+		}
+	}
+
+	curtext = P;
+	q = P;		/* p - 1 */
+	q1 = firstp;	/* top of block */
+	o = 0;		/* count of instructions */
+	for(p = firstp; p != P; p = p1) {
+		p1 = p->link;
+		o++;
+		if(p->mark & NOSCHED){
+			if(q1 != p){
+				sched(q1, q);
+			}
+			for(; p != P; p = p->link){
+				if(!(p->mark & NOSCHED))
+					break;
+				q = p;
+			}
+			p1 = p;
+			q1 = p;
+			o = 0;
+			continue;
+		}
+		if(p->mark & (LABEL|SYNC)) {
+			if(q1 != p)
+				sched(q1, q);
+			q1 = p;
+			o = 1;
+		}
+		if(p->mark & (BRANCH|SYNC)) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		if(o >= NSCHED) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		q = p;
+	}
+}
+
+void
+addnop(Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = AORN;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGZERO;
+	q->to.type = D_REG;
+	q->to.reg = REGZERO;
+
+	q->link = p->link;
+	p->link = q;
+}
+
+void
+initmuldiv(void)
+{
+	Sym *s1, *s2, *s3, *s4, *s5;
+	Prog *p;
+
+	s1 = lookup("_mul", 0);
+	s2 = lookup("_div", 0);
+	s3 = lookup("_divl", 0);
+	s4 = lookup("_mod", 0);
+	s5 = lookup("_modl", 0);
+	for(p = firstp; p != P; p = p->link)
+		if(p->as == ATEXT) {
+			if(p->from.sym == s1)
+				prog_mul = p;
+			if(p->from.sym == s2)
+				prog_div = p;
+			if(p->from.sym == s3)
+				prog_divl = p;
+			if(p->from.sym == s4)
+				prog_mod = p;
+			if(p->from.sym == s5)
+				prog_modl = p;
+		}
+	if(prog_mul == P) {
+		diag("undefined: %s", s1->name);
+		prog_mul = curtext;
+	}
+	if(prog_div == P) {
+		diag("undefined: %s", s2->name);
+		prog_div = curtext;
+	}
+	if(prog_divl == P) {
+		diag("undefined: %s", s3->name);
+		prog_divl = curtext;
+	}
+	if(prog_mod == P) {
+		diag("undefined: %s", s4->name);
+		prog_mod = curtext;
+	}
+	if(prog_modl == P) {
+		diag("undefined: %s", s5->name);
+		prog_modl = curtext;
+	}
+}
--- /dev/null
+++ b/utils/kl/obj.c
@@ -1,0 +1,1277 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= 'k';
+char	*thestring 	= "sparc";
+
+/*
+ *	-H0 -T0x200000 -R0		is boot
+ *	-H2 -T4128 -R4096		is plan9 format
+ *	-H3 -T0xE0004000 -R4		is javastation boot format
+ */
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		break;
+	} ARGEND
+	USED(argc);
+	if(*argv == 0) {
+		diag("usage: vl [-options] objects");
+		errorexit();
+	}
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+
+	case 0:	/* boot */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x200000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096L;
+		break;
+	case 1:	/* garbage */
+		HEADR = 20L+60L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80020000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* javastation boot */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xE0004020L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	}
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0)
+		outfile = "k.out";
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("%s: cannot create", outfile);
+		errorexit();
+	}
+	nuxiinit();
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", tothunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	errorexit();
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	Bflush(&bso);
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[100], pname[150];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(file[0] == '-' && file[1] == 'l') {
+		if(debug['9'])
+			sprint(name, "/%s/lib/lib", thestring);
+		else
+			sprint(name, "/usr/%clib/lib", thechar);
+		strcat(name, file+2);
+		strcat(name, ".a");
+		file = name;
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open file: %s", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			l = read(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	long l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = AEND+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = AEND+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = AEND+1;
+		return 0;	/* force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+	case D_PREG:
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_ASI:
+	case D_CONST:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		a->ieee.l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee.h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		goto out;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		goto out;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			goto out;
+		}
+
+	u = malloc(sizeof(Auto));
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+out:
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char name[1024], comp[256], *p;
+	int i;
+
+	if(histfrogp <= 0)
+		return;
+
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else
+	if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		if(debug['9'])
+			sprint(name, "/%s/lib", thestring);
+		else
+			sprint(name, "/usr/%clib", thechar);
+		i = 0;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) {
+			diag("library component too long");
+			return;
+		}
+		strcat(name, "/");
+		strcat(name, comp);
+	}
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	Prog *p, *t;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+	long ipc;
+	uchar *bloc, *bsize, *stop;
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	histfrogp = 0;
+	version++;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= 0 || o >= ALAST) {
+		diag("%s: opcode out of range %d", pn, o);
+		print("	probably not a .k file\n");
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		if(o == ASIGNAME) {
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->reg = bloc[1] & 0x7f;
+	if(bloc[1] & 0x80)
+		p->mark = NOSCHED;
+	p->line = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg < 0 || p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case ADATA:
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		curtext = p;
+		autosize = (p->to.offset+3L) & ~3L;
+		p->to.offset = autosize;
+		autosize += 4;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		if(textp != P) {
+			for(t = textp; t->cond != P; t = t->cond)
+				;
+			t->cond = p;
+		} else
+			textp = p;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+
+	case AFMOVF:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(&p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AFMOVD:
+		if(skip)
+			goto casedef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee.l, p->from.ieee.h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l + 1);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+	int n;
+
+	n = (sizeof(Prog) + 3) & ~3;
+	while(nhunk < n)
+		gethunk();
+
+	p = (Prog*)hunk;
+	nhunk -= n;
+	hunk += n;
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(tothunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(tothunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char *)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+
+	hunk = h;
+	nhunk = nh;
+	tothunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	s2 = lookup("_profin", 0);
+	s4 = lookup("_profout", 0);
+	if(s2->type != STEXT || s4->type != STEXT) {
+		diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				p->reg = 1;
+				ps2 = p;
+			}
+			if(p->from.sym == s4) {
+				p->reg = 1;
+				ps4 = p;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+
+			if(p->reg & NOPROF) {	/* dont profile */
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JMPL	profin
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AJMPL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARETURN) {
+
+			/*
+			 * RETURN
+			 */
+			q = prg();
+			q->as = ARETURN;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JMPL profout
+			 */
+			p->as = AJMPL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x01020304L, i+1);
+		if(i >= 2)
+			inuxi2[i-2] = c;
+		if(i >= 3)
+			inuxi1[i-3] = c;
+		inuxi4[i] = c;
+
+		fnuxi8[i] = c+4;
+		fnuxi8[i+4] = c;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
--- /dev/null
+++ b/utils/kl/optab.c
@@ -1,0 +1,199 @@
+#include	"l.h"
+
+#define	X	99
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+	{ ANOP,		C_NONE,	C_NONE,	C_NONE, 	 0, 0, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+
+	{ AMOVW,	C_SCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMOVW,	C_SACON,C_NONE,	C_REG,		 2, 4, REGSP },
+	{ AMOVW,	C_SECON,C_NONE,	C_REG,		 2, 4, REGSB },
+
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		 3, 4, 0 },
+	{ AMOVW,	C_ZOREG,C_REG,	C_REG,		 3, 4, 0 },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		 3, 4, 0 },
+	{ AMOVB,	C_ZOREG,C_REG,	C_REG,		 3, 4, 0 },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVD,	C_SOREG,C_NONE,	C_REG,		 3, 4, 0 },
+	{ AMOVD,	C_ZOREG,C_REG,	C_REG,		 3, 4, 0 },
+	{ AMOVD,	C_SAUTO,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVD,	C_SEXT,	C_NONE,	C_REG,		 3, 4, REGSB },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	 4, 4, 0 },
+	{ AMOVW,	C_REG,	C_REG,	C_ZOREG,	 4, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	 4, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		 4, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	 4, 4, 0 },
+	{ AMOVB,	C_REG,	C_REG,	C_ZOREG,	 4, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	 4, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		 4, 4, REGSB },
+	{ AMOVD,	C_REG,	C_NONE,	C_SOREG,	 4, 4, 0 },
+	{ AMOVD,	C_REG,	C_REG,	C_ZOREG,	 4, 4, 0 },
+	{ AMOVD,	C_REG,	C_NONE,	C_SAUTO,	 4, 4, REGSP },
+	{ AMOVD,	C_REG,	C_NONE,	C_SEXT,		 4, 4, REGSB },
+
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		 5, 8, 0 },
+
+	{ AMOVW,	C_ASI,	C_NONE,	C_REG,		 6, 4, 0 },
+	{ AMOVW,	C_ASI,	C_REG,	C_REG,		 6, 4, 0 },
+	{ AMOVB,	C_ASI,	C_NONE,	C_REG,		 6, 4, 0 },
+	{ AMOVB,	C_ASI,	C_REG,	C_REG,		 6, 4, 0 },
+	{ AMOVD,	C_ASI,	C_NONE,	C_REG,		 6, 4, 0 },
+	{ AMOVD,	C_ASI,	C_REG,	C_REG,		 6, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_ASI,		 7, 4, 0 },
+	{ AMOVW,	C_REG,	C_REG,	C_ASI,		 7, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_ASI,		 7, 4, 0 },
+	{ AMOVB,	C_REG,	C_REG,	C_ASI,		 7, 4, 0 },
+	{ AMOVD,	C_REG,	C_NONE,	C_ASI,		 7, 4, 0 },
+	{ AMOVD,	C_REG,	C_REG,	C_ASI,		 7, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_PREG,		 8, 4, 0 },
+	{ AMOVW,	C_PREG,	C_NONE,	C_REG,		 8, 4, 0 },
+
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		 9, 8, 0 },
+
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		10,12, REGSP },
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		10,12, REGSB },
+
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		11,12, 0 },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		11,12, REGSP },
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		11,12, REGSB },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		11,12, 0 },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		11,12, REGSP },
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		11,12, REGSB },
+	{ AMOVD,	C_LOREG,C_NONE,	C_REG,		11,12, 0 },
+	{ AMOVD,	C_LAUTO,C_NONE,	C_REG,		11,12, REGSP },
+	{ AMOVD,	C_LEXT,	C_NONE,	C_REG,		11,12, REGSB },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	12,12, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	12,12, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		12,12, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	12,12, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	12,12, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		12,12, REGSB },
+	{ AMOVD,	C_REG,	C_NONE,	C_LOREG,	12,12, 0 },
+	{ AMOVD,	C_REG,	C_NONE,	C_LAUTO,	12,12, REGSP },
+	{ AMOVD,	C_REG,	C_NONE,	C_LEXT,		12,12, REGSB },
+
+	{ AMOVW,	C_UCON,	C_NONE,	C_REG,		13, 4, 0 },
+
+	{ AADD,		C_SCON,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AADD,		C_SCON,	C_REG,	C_REG,		20, 4, 0 },
+
+	{ AADD,		C_REG,	C_NONE,	C_REG,		21, 4, 0 },
+	{ AADD,		C_REG,	C_REG,	C_REG,		21, 4, 0 },
+
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		22,12, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		22,12, 0 },
+
+	{ ACMP,		C_REG,	C_NONE,	C_REG,		23, 4, 0 },
+	{ ACMP,		C_REG,	C_NONE,	C_SCON,		24, 4, 0 },
+	{ ACMP,		C_SCON,	C_NONE,	C_REG,		25, 8, 0 },
+
+	{ AADD,		C_UCON,	C_NONE,	C_REG,		26, 8, 0 },
+	{ AADD,		C_UCON,	C_REG,	C_REG,		26, 8, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_SOREG,	30, 4, 0 },
+	{ AJMPL,	C_NONE,	C_NONE,	C_SOREG,	30, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_SBRA,		31, 4, 0 },
+	{ ABA,		C_NONE,	C_NONE,	C_SBRA,		31, 4, 0 },
+
+	{ AJMPL,	C_NONE,	C_NONE,	C_LBRA,		32, 4, 0 },
+
+	{ ATA,		C_REG,	C_NONE,	C_NONE,		33, 4, 0 },
+	{ ARETT,	C_REG,	C_NONE,	C_REG,		34, 8, 0 },
+
+	{ AMOVW,	C_SOREG,C_NONE,	C_FSR,		40, 4, 0 },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_FSR,		40, 4, REGSP },
+	{ AMOVW,	C_SEXT,	C_NONE,	C_FSR,		40, 4, REGSB },
+	{ AMOVW,	C_FSR,	C_NONE,	C_SOREG,	40, 4, 0 },
+	{ AMOVW,	C_FSR,	C_NONE,	C_SAUTO,	40, 4, REGSP },
+	{ AMOVW,	C_FSR,	C_NONE,	C_SEXT,		40, 4, REGSB },
+	{ AMOVD,	C_FQ,	C_NONE,	C_SOREG,	40, 4, 0 },
+	{ AMOVD,	C_FQ,	C_NONE,	C_SAUTO,	40, 4, REGSP },
+	{ AMOVD,	C_FQ,	C_NONE,	C_SEXT,		40, 4, REGSB },
+
+	{ AFMOVF,	C_SOREG,C_NONE,	C_FREG,		41, 4, 0 },
+	{ AFMOVF,	C_SAUTO,C_NONE,	C_FREG,		41, 4, REGSP },
+	{ AFMOVF,	C_SEXT,	C_NONE,	C_FREG,		41, 4, REGSB },
+	{ AMOVW,	C_SOREG,C_NONE,	C_FREG,		41, 4, 0 },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_FREG,		41, 4, REGSP },
+	{ AMOVW,	C_SEXT,	C_NONE,	C_FREG,		41, 4, REGSB },
+	{ AMOVD,	C_SOREG,C_NONE,	C_FREG,		41, 4, 0 },
+	{ AMOVD,	C_SAUTO,C_NONE,	C_FREG,		41, 4, REGSP },
+	{ AFMOVD,	C_ESAUTO,C_NONE,C_FREG,		41, 4, REGSP },
+	{ AMOVD,	C_SEXT,	C_NONE,	C_FREG,		41, 4, REGSB },
+	{ AFMOVD,	C_ESEXT,C_NONE,	C_FREG,		41, 4, REGSB },
+
+	{ AFMOVD,	C_SOREG,C_NONE,	C_FREG,		42, 8, 0 },
+	{ AFMOVD,	C_SAUTO,C_NONE,	C_FREG,		42, 8, REGSP },
+	{ AFMOVD,	C_SEXT,	C_NONE,	C_FREG,		42, 8, REGSB },
+
+	{ AFMOVF,	C_FREG,	C_NONE,	C_SOREG,	43, 4, 0 },
+	{ AFMOVF,	C_FREG,	C_NONE,	C_SAUTO,	43, 4, REGSP },
+	{ AFMOVF,	C_FREG,	C_NONE,	C_SEXT,		43, 4, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SOREG,	43, 4, 0 },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SAUTO,	43, 4, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SEXT,		43, 4, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SOREG,	43, 4, 0 },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SAUTO,	43, 4, REGSP },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_ESAUTO,	43, 4, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SEXT,		43, 4, REGSB },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_ESEXT,	43, 4, REGSB },
+
+	{ AFMOVD,	C_FREG,	C_NONE,	C_SOREG,	44, 8, 0 },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_SAUTO,	44, 8, REGSP },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_SEXT,		44, 8, REGSB },
+
+	{ AFMOVF,	C_LOREG,C_NONE,	C_FREG,		45,12, 0 },
+	{ AFMOVF,	C_LAUTO,C_NONE,	C_FREG,		45,12, REGSP },
+	{ AFMOVF,	C_LEXT,	C_NONE,	C_FREG,		45,12, REGSB },
+
+	{ AFMOVD,	C_LOREG,C_NONE,	C_FREG,		46,16, 0 },
+	{ AFMOVD,	C_LAUTO,C_NONE,	C_FREG,		46,16, REGSP },
+	{ AFMOVD,	C_LEXT,	C_NONE,	C_FREG,		46,16, REGSB },
+
+	{ AFMOVF,	C_FREG,	C_NONE,	C_LOREG,	47,12, 0 },
+	{ AFMOVF,	C_FREG,	C_NONE,	C_LAUTO,	47,12, REGSP },
+	{ AFMOVF,	C_FREG,	C_NONE,	C_LEXT,		47,12, REGSB },
+
+	{ AFMOVD,	C_FREG,	C_NONE,	C_LOREG,	48,16, 0 },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_LAUTO,	48,16, REGSP },
+	{ AFMOVD,	C_FREG,	C_NONE,	C_LEXT,		48,16, REGSB },
+
+	{ AFMOVD,	C_FREG,	C_NONE,	C_FREG,		49, 8, 0 },
+	{ AFCMPD,	C_FREG,	C_NONE,	C_FREG,		50, 4, 0 },
+
+	{ AFABSF,	C_FREG,	C_NONE,	C_FREG,		57, 4, 0 },
+	{ AFMOVF,	C_FREG,	C_NONE,	C_FREG,		57, 4, 0 },
+	{ AFADDD,	C_FREG,	C_NONE,	C_FREG,		21, 4, 0 },
+	{ AFADDD,	C_FREG,	C_REG,	C_FREG,		21, 4, 0 },
+
+	{ AWORD,	C_LCON,	C_NONE,	C_NONE,		51, 4, 0 },
+
+	{ ADIV,		C_REG,	C_NONE,	C_REG,		52,12, 0 },
+	{ ADIV,		C_REG,	C_REG,	C_REG,		52,12, 0 },
+
+	{ ADIVL,	C_REG,	C_NONE,	C_REG,		53, 8, 0 },
+	{ ADIVL,	C_REG,	C_REG,	C_REG,		53, 8, 0 },
+
+	{ AMOD,		C_REG,	C_NONE,	C_REG,		54,20, 0 },
+	{ AMOD,		C_REG,	C_REG,	C_REG,		54,20, 0 },
+
+	{ AMODL,	C_REG,	C_NONE,	C_REG,		55,16, 0 },
+	{ AMODL,	C_REG,	C_REG,	C_REG,		55,16, 0 },
+
+	{ ABE,		C_NONE,	C_NONE,	C_SBRA,		56, 4, 0 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/kl/pass.c
@@ -1,0 +1,551 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p, *p1;
+	long orig, orig1, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on REGSB)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 3)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+	orig1 = orig;
+
+	/*
+	 * pass 2
+	 *	assign 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	/*
+	 * pass 4
+	 *	add literals to all large values.
+	 *	at this time:
+	 *		small data is allocated DATA
+	 *		large data is allocated DATA1
+	 *		large bss is allocated BSS
+	 *	the new literals are loaded between
+	 *	small data and large data.
+	 */
+	orig = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as != AMOVW)
+			continue;
+		if(p->from.type != D_CONST)
+			continue;
+		if(s = p->from.sym) {
+			t = s->type;
+			if(t != SDATA && t != SDATA1 && t != SBSS)
+				continue;
+			t = p->from.name;
+			if(t != D_EXTERN && t != D_STATIC)
+				continue;
+			v = s->value + p->from.offset;
+			if(v >= 0 && v <= 0xffff)
+				continue;
+			if(!strcmp(s->name, "setSB"))
+				continue;
+			/* size should be 19 max */
+			if(strlen(s->name) >= 10)	/* has loader address */ 
+				sprint(literal, "$%p.%lux", s, p->from.offset);
+			else
+				sprint(literal, "$%s.%d.%lux", s->name, s->version, p->from.offset);
+		} else {
+			if(p->from.name != D_NONE)
+				continue;
+			if(p->from.reg != NREG)
+				continue;
+			v = p->from.offset;
+			if(v >= -0x7fff && v <= 0xffff)
+				continue;
+			if(!(v & 0xffff))
+				continue;
+			/* size should be 9 max */
+			sprint(literal, "$%lux", v);
+		}
+		s = lookup(literal, 0);
+		if(s->type == 0) {
+			s->type = SDATA;
+			s->value = orig1+orig;
+			orig += 4;
+			p1 = prg();
+			p1->as = ADATA;
+			p1->line = p->line;
+			p1->from.type = D_OREG;
+			p1->from.sym = s;
+			p1->from.name = D_EXTERN;
+			p1->reg = 4;
+			p1->to = p->from;
+			p1->link = datap;
+			datap = p1;
+		}
+		if(s->type != SDATA)
+			diag("literal not data: %s", s->name);
+		p->from.type = D_OREG;
+		p->from.sym = s;
+		p->from.name = D_EXTERN;
+		p->from.offset = 0;
+		continue;
+	}
+	while(orig & 7)
+		orig++;
+	/*
+	 * pass 5
+	 *	re-adjust offsets
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t == SBSS) {
+			s->value += orig;
+			continue;
+		}
+		if(t == SDATA1) {
+			s->type = SDATA;
+			s->value += orig;
+			continue;
+		}
+	}
+	datsize += orig;
+	xdefine("setSB", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+int
+relinv(int a)
+{
+
+	switch(a) {
+	case ABA:	return ABN;
+	case ABN:	return ABA;
+
+	case ABE:	return ABNE;
+	case ABNE:	return ABE;
+
+	case ABLE:	return ABG;
+	case ABG:	return ABLE;
+
+	case ABL:	return ABGE;
+	case ABGE:	return ABL;
+
+	case ABLEU:	return ABGU;
+	case ABGU:	return ABLEU;
+
+	case ABCS:	return ABCC;
+	case ABCC:	return ABCS;
+
+	case ABNEG:	return ABPOS;
+	case ABPOS:	return ABNEG;
+
+	case ABVC:	return ABVS;
+	case ABVS:	return ABVC;
+
+	case AFBN:	return AFBA;
+	case AFBA:	return AFBN;
+
+	case AFBE:	return AFBLG;
+	case AFBLG:	return AFBE;
+
+	case AFBG:	return AFBLE;
+	case AFBLE:	return AFBG;
+
+	case AFBGE:	return AFBL;
+	case AFBL:	return AFBGE;
+
+	/* unordered fp compares have no inverse
+		that traps in the same way */
+	}
+	return 0;
+}
+
+void
+follow(void)
+{
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, b, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AJMP) {
+		q = p->cond;
+		if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
+			p->mark |= FOLL;
+			lastp->link = p;
+			lastp = p;
+			p = p->link;
+			xfol(p);
+			p = q;
+			if(p && !(p->mark & FOLL))
+				goto loop;
+			return;
+		}
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp || (q->mark&NOSCHED))
+				break;
+			b = 0;		/* set */
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AJMP || a == ARETURN || a == ARETT)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			b = relinv(a);
+			if(!b)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AJMP || a == ARETURN || a == ARETT)
+					return;
+				r->as = b;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+
+		a = AJMP;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AJMP || a == ARETURN || a == ARETT){
+		if(p->mark & NOSCHED){
+			p = p->link;
+			goto loop;
+		}
+		return;
+	}
+	if(p->cond != P)
+	if(a != AJMPL && p->link != P) {
+		xfol(p->link);
+		p = p->cond;
+		if(p == P || (p->mark&FOLL))
+			return;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == AJMPL || a == ARETURN) && p->to.sym != S) {
+			s = p->to.sym;
+			if(s->type != STEXT) {
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+			}
+			p->to.offset = s->value;
+			p->to.type = D_BRANCH;
+		}
+		if(p->to.type != D_BRANCH)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->cond != P) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AJMP || (p->mark&NOSCHED))
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/utils/kl/sched.c
@@ -1,0 +1,672 @@
+#include	"l.h"
+
+enum
+{
+	E_ICC	= 1<<0,
+	E_FCC	= 1<<1,
+	E_MEM	= 1<<2,
+	E_MEMSP	= 1<<3,	/* uses offset and size */
+	E_MEMSB	= 1<<4,	/* uses offset and size */
+	ANYMEM	= E_MEM|E_MEMSP|E_MEMSB,
+	ALL	= ~0
+};
+
+typedef	struct	Sch	Sch;
+typedef	struct	Dep	Dep;
+
+struct	Dep
+{
+	ulong	ireg;
+	ulong	freg;
+	ulong	cc;
+};
+struct	Sch
+{
+	Prog	p;
+	Dep	set;
+	Dep	used;
+	long	soffset;
+	char	size;
+	char	nop;
+	char	comp;
+};
+
+void	regsused(Sch*, Prog*);
+int	depend(Sch*, Sch*);
+int	conflict(Sch*, Sch*);
+int	offoverlap(Sch*, Sch*);
+void	dumpbits(Sch*, Dep*);
+
+void
+sched(Prog *p0, Prog *pe)
+{
+	Prog *p, *q;
+	Sch sch[NSCHED], *s, *t, *u, *se, stmp;
+
+	/*
+	 * build side structure
+	 */
+	s = sch;
+	for(p=p0;; p=p->link) {
+		memset(s, 0, sizeof(*s));
+		s->p = *p;
+		regsused(s, p);
+		if(debug['X']) {
+			Bprint(&bso, "%P\tset", &s->p);
+			dumpbits(s, &s->set);
+			Bprint(&bso, "; used");
+			dumpbits(s, &s->used);
+			if(s->comp)
+				Bprint(&bso, "; compound");
+			if(s->p.mark & LOAD)
+				Bprint(&bso, "; load");
+			if(s->p.mark & BRANCH)
+				Bprint(&bso, "; branch");
+			if(s->p.mark & FCMP)
+				Bprint(&bso, "; fcmp");
+			Bprint(&bso, "\n");
+		}
+		s++;
+		if(p == pe)
+			break;
+	}
+	se = s;
+
+	for(s=se-1; s>=sch; s--) {
+		/*
+		 * branch delay slot
+		 */
+		if(s->p.mark & BRANCH) {
+			/* t is the trial instruction to use */
+			for(t=s-1; t>=sch; t--) {
+				if(t->comp || (t->p.mark & FCMP))
+					goto no1;
+				for(u=t+1; u<=s; u++)
+					if(depend(u, t))
+						goto no1;
+				goto out1;
+			no1:;
+			}
+			if(debug['X'])
+				Bprint(&bso, "?b%P\n", &s->p);
+			s->nop = 1;
+			continue;
+
+		out1:
+			if(debug['X']) {
+				Bprint(&bso, "!b%P\n", &t->p);
+				Bprint(&bso, "%P\n", &s->p);
+			}
+			stmp = *t;
+			memmove(t, t+1, (uchar*)s - (uchar*)t);
+			*s = stmp;
+			s--;
+			continue;
+		}
+
+		/*
+		 * load delay. interlocked.
+		 */
+		if(s->p.mark & LOAD) {
+			if(s >= se-1)
+				continue;
+			if(!conflict(s, (s+1)))
+				continue;
+			/*
+			 * s is load, s+1 is immediate use of result
+			 * t is the trial instruction to insert between s and s+1
+			 */
+			for(t=s-1; t>=sch; t--) {
+				if(t->p.mark & BRANCH)
+					goto no2;
+				if(t->p.mark & FCMP)
+					if((s+1)->p.mark & BRANCH)
+						goto no2;
+				if(t->p.mark & LOAD)
+					if(conflict(t, (s+1)))
+						goto no2;
+				for(u=t+1; u<=s; u++)
+					if(depend(u, t))
+						goto no2;
+				goto out2;
+			no2:;
+			}
+			if(debug['X'])
+				Bprint(&bso, "?l%P\n", &s->p);
+			continue;
+		out2:
+			if(debug['X']) {
+				Bprint(&bso, "!l%P\n", &t->p);
+				Bprint(&bso, "%P\n", &s->p);
+			}
+			stmp = *t;
+			memmove(t, t+1, (uchar*)s - (uchar*)t);
+			*s = stmp;
+			s--;
+			continue;
+		}
+
+		/*
+		 * fop2 delay.
+		 */
+		if(s->p.mark & FCMP) {
+			if(s >= se-1) {
+				s->nop = 1;
+				continue;
+			}
+			if(!((s+1)->p.mark & BRANCH))
+				continue;
+			/* t is the trial instruction to use */
+			for(t=s-1; t>=sch; t--) {
+				for(u=t+1; u<=s; u++)
+					if(depend(u, t))
+						goto no3;
+				goto out3;
+			no3:;
+			}
+			if(debug['X'])
+				Bprint(&bso, "?f%P\n", &s->p);
+			s->nop = 1;
+			continue;
+		out3:
+			if(debug['X']) {
+				Bprint(&bso, "!f%P\n", &t->p);
+				Bprint(&bso, "%P\n", &s->p);
+			}
+			stmp = *t;
+			memmove(t, t+1, (uchar*)s - (uchar*)t);
+			*s = stmp;
+			s--;
+			continue;
+		}
+	}
+
+	/*
+	 * put it all back
+	 */
+	for(s=sch, p=p0; s<se; s++, p=q) {
+		q = p->link;
+		if(q != s->p.link) {
+			*p = s->p;
+			p->link = q;
+		}
+		if(s->nop)
+			addnop(p);
+	}
+	if(debug['X'])
+		Bprint(&bso, "\n");
+}
+
+void
+regsused(Sch *s, Prog *realp)
+{
+	int c, ar, ad, ld, sz;
+	ulong m;
+	Prog *p;
+
+	p = &s->p;
+	s->comp = compound(p);
+	s->nop = 0;
+	if(s->comp) {
+		s->set.ireg |= 1<<REGTMP;
+		s->used.ireg |= 1<<REGTMP;
+	}
+	ar = 0;		/* dest is really reference */
+	ad = 0;		/* source/dest is really address */
+	ld = 0;		/* opcode is load instruction */
+	sz = 20;		/* size of load/store for overlap computation */
+
+/*
+ * flags based on opcode
+ */
+	switch(p->as) {
+	case ATEXT:
+		curtext = realp;
+		autosize = p->to.offset + 4;
+		ad = 1;
+		break;
+	case AJMPL:
+		c = p->reg;
+		if(c == NREG)
+			c = REGLINK;
+		s->set.ireg |= 1<<c;
+		ar = 1;
+		ad = 1;
+		break;
+	case AJMP:
+		ar = 1;
+		ad = 1;
+		break;
+	case ACMP:
+		s->set.cc |= E_ICC;
+		ar = 1;
+		break;
+	case AFCMPD:
+	case AFCMPED:
+	case AFCMPEF:
+	case AFCMPEX:
+	case AFCMPF:
+	case AFCMPX:
+		s->set.cc |= E_FCC;
+		ar = 1;
+		break;
+	case ABE:
+	case ABNE:
+	case ABLE:
+	case ABG:
+	case ABL:
+	case ABGE:
+	case ABLEU:
+	case ABGU:
+	case ABCS:
+	case ABCC:
+	case ABNEG:
+	case ABPOS:
+	case ABVC:
+	case ABVS:
+		s->used.cc |= E_ICC;
+		ar = 1;
+		break;
+	case AFBE:
+	case AFBLG:
+	case AFBG:
+	case AFBLE:
+	case AFBGE:
+	case AFBL:
+		s->used.cc |= E_FCC;
+		ar = 1;
+		break;
+	case AMOVB:
+	case AMOVBU:
+		sz = 1;
+		ld = 1;
+		break;
+	case AMOVH:
+	case AMOVHU:
+		sz = 2;
+		ld = 1;
+		break;
+	case AFMOVF:
+	case AMOVW:
+		sz = 4;
+		ld = 1;
+		break;
+	case AMOVD:
+	case AFMOVD:
+		sz = 8;
+		ld = 1;
+		break;
+	case AFMOVX:	/* gok */
+		sz = 16;
+		ld = 1;
+		break;
+	case AADDCC:
+	case AADDXCC:
+	case AANDCC:
+	case AANDNCC:
+	case AORCC:
+	case AORNCC:
+	case ASUBCC:
+	case ASUBXCC:
+	case ATADDCC:
+	case ATADDCCTV:
+	case ATSUBCC:
+	case ATSUBCCTV:
+	case AXNORCC:
+	case AXORCC:
+		s->set.cc |= E_ICC;
+		break;
+	case ADIV:
+	case ADIVL:
+	case AMOD:
+	case AMODL:
+	case AMUL:
+	case AMULSCC:
+	case ATAS:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		break;
+	}
+
+/*
+ * flags based on 'to' field
+ */
+	c = p->to.class;
+	if(c == 0) {
+		c = aclass(&p->to) + 1;
+		p->to.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->to);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+	case C_CREG:
+	case C_FSR:
+	case C_FQ:
+	case C_PREG:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+	case C_ASI:
+		c = p->to.reg;
+		s->used.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		if(ar)
+			s->used.cc |= m;
+		else
+			s->set.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		if(ar)
+			s->used.ireg |= 1<<p->to.reg;
+		else
+			s->set.ireg |= 1<<p->to.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		if(ar) {
+			s->used.freg |= 1<<p->to.reg;
+			s->used.freg |= 1<<(p->to.reg|1);
+		} else {
+			s->set.freg |= 1<<p->to.reg;
+			s->set.freg |= 1<<(p->to.reg|1);
+		}
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+	case C_ESAUTO:
+	case C_OSAUTO:
+	case C_ELAUTO:
+	case C_OLAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSP;
+		else
+			s->set.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+	case C_ESEXT:
+	case C_OSEXT:
+	case C_ELEXT:
+	case C_OLEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSB;
+		else
+			s->set.cc |= E_MEMSB;
+		break;
+	}
+
+/*
+ * flags based on 'from' field
+ */
+	c = p->from.class;
+	if(c == 0) {
+		c = aclass(&p->from) + 1;
+		p->from.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->from);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		c = p->from.reg;
+		if(c != NREG)
+			s->used.ireg |= 1<<c;
+		break;
+	case C_CREG:
+	case C_FSR:
+	case C_FQ:
+	case C_PREG:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+	case C_ASI:
+		c = p->from.reg;
+		s->used.ireg |= 1<<c;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		s->used.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		s->used.ireg |= 1<<p->from.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		s->used.freg |= 1<<p->from.reg;
+		s->used.freg |= 1<<(p->from.reg|1);
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+	case C_ESAUTO:
+	case C_ELAUTO:
+	case C_OSAUTO:
+	case C_OLAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+	case C_ESEXT:
+	case C_ELEXT:
+	case C_OSEXT:
+	case C_OLEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSB;
+		break;
+	}
+	
+	c = p->reg;
+	if(c != NREG) {
+		if(p->from.type == D_FREG || p->to.type == D_FREG) {
+			s->used.freg |= 1<<c;
+			s->used.freg |= 1<<(c|1);
+		} else
+			s->used.ireg |= 1<<c;
+	}
+	s->set.ireg &= ~(1<<0);		/* R0 cant be set */
+}
+
+/*
+ * test to see if 2 instrictions can be
+ * interchanged without changing semantics
+ */
+int
+depend(Sch *sa, Sch *sb)
+{
+	ulong x;
+
+	if(sa->set.ireg & (sb->set.ireg|sb->used.ireg))
+		return 1;
+	if(sb->set.ireg & sa->used.ireg)
+		return 1;
+
+	if(sa->set.freg & (sb->set.freg|sb->used.freg))
+		return 1;
+	if(sb->set.freg & sa->used.freg)
+		return 1;
+
+	x = (sa->set.cc & (sb->set.cc|sb->used.cc)) |
+		(sb->set.cc & sa->used.cc);
+	if(x) {
+		/*
+		 * allow SB and SP to pass each other.
+		 * allow SB to pass SB iff doffsets are ok
+		 * anything else conflicts
+		 */
+		if(x != E_MEMSP && x != E_MEMSB)
+			return 1;
+		x = sa->set.cc | sb->set.cc |
+			sa->used.cc | sb->used.cc;
+		if(x & E_MEM)
+			return 1;
+		if(offoverlap(sa, sb))
+			return 1;
+	}
+
+	return 0; 
+}
+
+int
+offoverlap(Sch *sa, Sch *sb)
+{
+
+	if(sa->soffset < sb->soffset) {
+		if(sa->soffset+sa->size > sb->soffset)
+			return 1;
+		return 0;
+	}
+	if(sb->soffset+sb->size > sa->soffset)
+		return 1;
+	return 0;
+}
+
+/*
+ * test 2 adjacent instructions
+ * and find out if inserted instructions
+ * are desired to prevent stalls.
+ * first instruction is a load instruction.
+ */
+int
+conflict(Sch *sa, Sch *sb)
+{
+
+	if(sa->set.ireg & sb->used.ireg)
+		return 1;
+	if(sa->set.freg & sb->used.freg)
+		return 1;
+	return 0;
+}
+
+int
+compound(Prog *p)
+{
+	Optab *o;
+
+	o = oplook(p);
+	if(o->size != 4)
+		return 1;
+	if(p->to.type == D_REG && p->to.reg == REGSB)
+		return 1;
+	return 0;
+}
+
+void
+dumpbits(Sch *s, Dep *d)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(d->ireg & (1<<i))
+			Bprint(&bso, " R%d", i);
+	for(i=0; i<32; i++)
+		if(d->freg & (1<<i))
+			Bprint(&bso, " F%d", i);
+	for(i=0; i<32; i++)
+		switch(d->cc & (1<<i)) {
+		default:
+			break;
+		case E_ICC:
+			Bprint(&bso, " ICC");
+			break;
+		case E_FCC:
+			Bprint(&bso, " FCC");
+			break;
+		case E_MEM:
+			Bprint(&bso, " MEM%d", s->size);
+			break;
+		case E_MEMSB:
+			Bprint(&bso, " SB%d", s->size);
+			break;
+		case E_MEMSP:
+			Bprint(&bso, " SP%d", s->size);
+			break;
+		}
+}
--- /dev/null
+++ b/utils/kl/span.c
@@ -1,0 +1,524 @@
+#include	"l.h"
+
+void
+span(void)
+{
+	Prog *p;
+	Sym *setext;
+	Optab *o;
+	int m;
+	long c;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+	c = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 4;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				continue;
+			}
+			if(p->as != ANOP)
+				diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		c += m;
+	}
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+		
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_CREG:
+		return C_CREG;
+
+	case D_PREG:
+		if(a->reg == D_FSR)
+			return C_FSR;
+		if(a->reg == D_FPQ)
+			return C_FQ;
+		return C_PREG;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == S)
+				break;
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			instoffset = a->sym->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG) {
+				if(instoffset & 7)
+					return C_OSEXT;
+				return C_ESEXT;
+			}
+			if(instoffset & 7)
+				return C_OLEXT;
+			return C_ELEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			goto dauto;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+		dauto:
+			if(instoffset >= -BIG && instoffset < BIG) {
+				if(instoffset & 7)
+					return C_OSAUTO;
+				return C_ESAUTO;
+			}
+			if(instoffset & 7)
+				return C_OLAUTO;
+			return C_ELAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			if(instoffset == 0)
+				return C_ZOREG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SOREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_ASI:
+		if(a->name == D_NONE)
+			return C_ASI;
+		return C_GOK;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+		consize:
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x1000 && instoffset <= 0xfff)
+				return C_SCON;
+			if((instoffset & 0x3ff) == 0)
+				return C_UCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			if(s->type == STEXT || s->type == SLEAF) {
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			if(s->type == SCONST) {
+				instoffset = s->value + a->offset;
+				goto consize;
+			}
+			instoffset = s->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG && instoffset != 0)
+				return C_SECON;
+			instoffset = s->value + a->offset + INITDAT;
+/* not sure why this barfs */
+return C_LCON;
+/*
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x1000 && instoffset <= 0xfff)
+				return C_SCON;
+			if((instoffset & 0x3ff) == 0)
+				return C_UCON;
+			return C_LCON;
+*/
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r;
+	char *c1, *c3;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0)
+		o = oprange[r].stop; /* just generate an error */
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, a1, a2, a3);
+	if(1||!debug['a'])
+		prasm(p);
+	if(o == 0)
+		errorexit();
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_ZCON || b == C_SCON || b == C_UCON)
+			return 1;
+		break;
+	case C_UCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_SCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_SACON)
+			return 1;
+		break;
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_ELEXT:
+		if(b == C_ESEXT)
+			return 1;
+		break;
+	case C_LEXT:
+		if(b == C_SEXT ||
+		   b == C_ESEXT || b == C_OSEXT ||
+		   b == C_ELEXT || b == C_OLEXT)
+			return 1;
+		break;
+	case C_SEXT:
+		if(b == C_ESEXT || b == C_OSEXT)
+			return 1;
+		break;
+	case C_ELAUTO:
+		if(b == C_ESAUTO)
+			return 1;
+		break;
+	case C_LAUTO:
+		if(b == C_SAUTO ||
+		   b == C_ESAUTO || b == C_OSAUTO ||
+		   b == C_ELAUTO || b == C_OLAUTO)
+			return 1;
+		break;
+	case C_SAUTO:
+		if(b == C_ESAUTO || b == C_OSAUTO)
+			return 1;
+		break;
+	case C_REG:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LOREG:
+		if(b == C_ZOREG || b == C_SOREG)
+			return 1;
+		break;
+	case C_SOREG:
+		if(b == C_ZOREG)
+			return 1;
+		break;
+
+	case C_ANY:
+		return 1;
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	for(i=0; i<C_NCLASS; i++)
+		for(n=0; n<C_NCLASS; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+		
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case AADD:
+			oprange[AADDX] = oprange[r];
+			oprange[ASUB] = oprange[r];
+			oprange[ASUBX] = oprange[r];
+			oprange[AMUL] = oprange[r];
+			oprange[AXOR] = oprange[r];
+			oprange[AXNOR] = oprange[r];
+			oprange[AAND] = oprange[r];
+			oprange[AANDN] = oprange[r];
+			oprange[AOR] = oprange[r];
+			oprange[AORN] = oprange[r];
+			oprange[ASLL] = oprange[r];
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			oprange[AADDCC] = oprange[r];
+			oprange[AADDXCC] = oprange[r];
+			oprange[ATADDCC] = oprange[r];
+			oprange[ATADDCCTV] = oprange[r];
+			oprange[ASUBCC] = oprange[r];
+			oprange[ASUBXCC] = oprange[r];
+			oprange[ATSUBCC] = oprange[r];
+			oprange[ATSUBCCTV] = oprange[r];
+			oprange[AXORCC] = oprange[r];
+			oprange[AXNORCC] = oprange[r];
+			oprange[AANDCC] = oprange[r];
+			oprange[AANDNCC] = oprange[r];
+			oprange[AORCC] = oprange[r];
+			oprange[AORNCC] = oprange[r];
+			oprange[AMULSCC] = oprange[r];
+			oprange[ASAVE] = oprange[r];
+			oprange[ARESTORE] = oprange[r];
+			break;
+		case AMOVB:
+			oprange[AMOVH] = oprange[r];
+			oprange[AMOVHU] = oprange[r];
+			oprange[AMOVBU] = oprange[r];
+			oprange[ASWAP] = oprange[r];
+			oprange[ATAS] = oprange[r];
+			break;
+		case ABA:
+			oprange[ABN] = oprange[r];
+			oprange[AFBA] = oprange[r];
+			oprange[AFBN] = oprange[r];
+			break;
+		case ABE:
+			oprange[ABCC] = oprange[r];
+			oprange[ABCS] = oprange[r];
+			oprange[ABGE] = oprange[r];
+			oprange[ABGU] = oprange[r];
+			oprange[ABG] = oprange[r];
+			oprange[ABLEU] = oprange[r];
+			oprange[ABLE] = oprange[r];
+			oprange[ABL] = oprange[r];
+			oprange[ABNEG] = oprange[r];
+			oprange[ABNE] = oprange[r];
+			oprange[ABPOS] = oprange[r];
+			oprange[ABVC] = oprange[r];
+			oprange[ABVS] = oprange[r];
+
+			oprange[AFBE] = oprange[r];
+			oprange[AFBG] = oprange[r];
+			oprange[AFBGE] = oprange[r];
+			oprange[AFBL] = oprange[r];
+			oprange[AFBLE] = oprange[r];
+			oprange[AFBLG] = oprange[r];
+			oprange[AFBNE] = oprange[r];
+			oprange[AFBO] = oprange[r];
+			oprange[AFBU] = oprange[r];
+			oprange[AFBUE] = oprange[r];
+			oprange[AFBUG] = oprange[r];
+			oprange[AFBUGE] = oprange[r];
+			oprange[AFBUL] = oprange[r];
+			oprange[AFBULE] = oprange[r];
+			break;
+		case ATA:
+			oprange[ATCC] = oprange[r];
+			oprange[ATCS] = oprange[r];
+			oprange[ATE] = oprange[r];
+			oprange[ATGE] = oprange[r];
+			oprange[ATGU] = oprange[r];
+			oprange[ATG] = oprange[r];
+			oprange[ATLEU] = oprange[r];
+			oprange[ATLE] = oprange[r];
+			oprange[ATL] = oprange[r];
+			oprange[ATNEG] = oprange[r];
+			oprange[ATNE] = oprange[r];
+			oprange[ATN] = oprange[r];
+			oprange[ATPOS] = oprange[r];
+			oprange[ATVC] = oprange[r];
+			oprange[ATVS] = oprange[r];
+			break;
+		case AFADDD:
+			oprange[AFADDF] = oprange[r];
+			oprange[AFADDX] = oprange[r];
+			oprange[AFDIVD] = oprange[r];
+			oprange[AFDIVF] = oprange[r];
+			oprange[AFDIVX] = oprange[r];
+			oprange[AFMULD] = oprange[r];
+			oprange[AFMULF] = oprange[r];
+			oprange[AFMULX] = oprange[r];
+			oprange[AFSUBD] = oprange[r];
+			oprange[AFSUBF] = oprange[r];
+			oprange[AFSUBX] = oprange[r];
+			break;
+		case AFCMPD:
+			oprange[AFCMPF] = oprange[r];
+			oprange[AFCMPX] = oprange[r];
+			oprange[AFCMPED] = oprange[r];
+			oprange[AFCMPEF] = oprange[r];
+			oprange[AFCMPEX] = oprange[r];
+			break;
+		case AFABSF:
+			oprange[AFMOVDF] = oprange[r];
+			oprange[AFMOVDW] = oprange[r];
+			oprange[AFMOVFD] = oprange[r];
+			oprange[AFMOVFW] = oprange[r];
+			oprange[AFMOVWD] = oprange[r];
+			oprange[AFMOVWF] = oprange[r];
+			oprange[AFNEGF] = oprange[r];
+			oprange[AFSQRTD] = oprange[r];
+			oprange[AFSQRTF] = oprange[r];
+			break;
+		case AFMOVF:
+		case AFMOVD:
+		case AMOVW:
+		case AMOVD:
+		case AWORD:
+		case ARETT:
+		case AJMPL:
+		case AJMP:
+		case ACMP:
+		case ANOP:
+		case ATEXT:
+		case ADIV:
+		case ADIVL:
+		case AMOD:
+		case AMODL:
+			break;
+		}
+	}
+}
--- /dev/null
+++ b/utils/kprof/kprof.c
@@ -1,0 +1,155 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+enum {
+	SpecialTotalTicks,
+	SpecialOutsideTicks,
+	SpecialMicroSecondsPerTick,
+	SpecialSamples,
+	SpecialSampleSize,
+	SpecialSampleLogBucketSize,
+	SpecialMax
+};
+
+int pcres = 8;
+ulong uspertick;
+
+struct COUNTER
+{
+	char 	*name;		/* function name */
+	ulong	time;		/* ticks spent there */
+};
+
+void
+error(int perr, char *s)
+{
+	fprint(2, "kprof: %s", s);
+	if(perr)
+		fprint(2, ": %r\n");
+	else
+		fprint(2, "\n");
+	exits(s);
+}
+
+int
+compar(void *va, void *vb)
+{
+	struct COUNTER *a, *b;
+
+	a = (struct COUNTER *)va;
+	b = (struct COUNTER *)vb;
+	if(a->time < b->time)
+		return -1;
+	if(a->time == b->time)
+		return 0;
+	return 1;
+}
+
+ulong
+tickstoms(ulong ticks)
+{
+	return ((vlong)ticks * uspertick) / 1000;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int fd;
+	long i, j, k, n;
+	Dir *d;
+	char *name;
+	ulong *data;
+	ulong tbase, sum;
+	long delta;
+	Symbol s;
+	Biobuf outbuf;
+	Fhdr f;
+	struct COUNTER *cp;
+
+	if(argc != 3)
+		error(0, "usage: kprof text data");
+	/*
+	 * Read symbol table
+	 */
+	fd = open(argv[1], OREAD);
+	if(fd < 0)
+		error(1, argv[1]);
+	if (!crackhdr(fd, &f))
+		error(1, "read text header");
+	if (f.type == FNONE)
+		error(0, "text file not an a.out");
+	if (syminit(fd, &f) < 0)
+		error(1, "syminit");
+	close(fd);
+	/*
+	 * Read timing data
+	 */
+	fd = open(argv[2], OREAD);
+	if(fd < 0)
+		error(1, argv[2]);
+	if((d = dirfstat(fd)) == nil)
+		error(1, "stat");
+	n = d->length/sizeof(data[0]);
+	if(n < 2)
+		error(0, "data file too short");
+	data = malloc(d->length);
+	if(data == 0)
+		error(1, "malloc");
+	if(read(fd, data, d->length) < 0)
+		error(1, "text read");
+	close(fd);
+	free(d);
+	for(i=0; i<n; i++)
+		data[i] = beswal(data[i]);
+	pcres = 1 << data[SpecialSampleLogBucketSize];
+	uspertick = data[SpecialMicroSecondsPerTick];
+	if (data[SpecialSampleSize] != sizeof(data[0]))
+		error(0, "only sample size 4 supported\n");
+	delta = data[SpecialTotalTicks] - data[SpecialOutsideTicks];
+	print("total: %lud	in kernel text: %lud	outside kernel text: %lud\n",
+		data[0], delta, data[1]);
+	if(data[0] == 0)
+		exits(0);
+	if (!textsym(&s, 0))
+		error(0, "no text symbols");
+	tbase = s.value & ~(mach->pgsize-1);	/* align down to page */
+	print("KTZERO %.8lux\n", tbase);
+	/*
+	 * Accumulate counts for each function
+	 */
+	cp = 0;
+	k = 0;
+	for (i = 0, j = (s.value-tbase)/pcres+SpecialMax; j < n; i++) {
+		name = s.name;		/* save name */
+		if (!textsym(&s, i))	/* get next symbol */
+			break;
+		sum = 0;
+		while (j < n && j*pcres < s.value-tbase)
+			sum += data[j++];
+		if (sum) {
+			cp = realloc(cp, (k+1)*sizeof(struct COUNTER));
+			if (cp == 0)
+				error(1, "realloc");
+			cp[k].name = name;
+			cp[k].time = sum;
+			k++;
+		}
+	}
+	if (!k)
+		error(0, "no counts");
+	cp[k].time = 0;			/* "etext" can take no time */
+	/*
+	 * Sort by time and print
+	 */
+	qsort(cp, k, sizeof(struct COUNTER), compar);
+	Binit(&outbuf, 1, OWRITE);
+	Bprint(&outbuf, "ms	  %%	sym\n");
+	while(--k>=0)
+		Bprint(&outbuf, "%lud\t%3lud.%ld\t%s\n",
+				tickstoms(cp[k].time),
+				100*cp[k].time/delta,
+				(1000*cp[k].time/delta)%10,
+				cp[k].name);
+	exits(0);
+}
--- /dev/null
+++ b/utils/kprof/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=kprof
+
+OFILES=	kprof.$O\
+
+LIBS=	mach bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
--- /dev/null
+++ b/utils/ksize/ksize.c
@@ -1,0 +1,49 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	<mach.h>
+
+int
+size(char *file)
+{
+	int fd;
+	Fhdr f;
+
+	if((fd = open(file, OREAD)) < 0){
+		fprint(2, "size: ");
+		perror(file);
+		return 1;
+	}
+	if(crackhdr(fd, &f)) {
+		print("%ldt + %ldd + %ldb = %ld\t%s\n", f.txtsz, f.datsz,
+			f.bsssz, f.txtsz+f.datsz+f.bsssz, file);
+		close(fd);
+		return 0;
+	}
+
+	/* get error string from libmach and display */
+	fprint(2, "ksize: %s %r\n", file);
+	close(fd);
+	return 1;
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *err;
+	int i;
+
+	ARGBEGIN {
+	default:
+		fprint(2, "usage: ksize [a.out ...]\n");
+		exits("usage");
+	} ARGEND;
+
+	err = 0;
+	if(argc == 0)
+		if(size("8.out"))
+			err = "error";
+	for(i=0; i<argc; i++)
+		if(size(argv[i]))
+			err = "error";
+	exits(err);
+}
--- /dev/null
+++ b/utils/ksize/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=ksize
+
+OFILES=	ksize.$O\
+
+LIBS=	mach bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
--- /dev/null
+++ b/utils/kstrip/kstrip.c
@@ -1,0 +1,166 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+static void
+error(char* fmt, ...)
+{
+	va_list arg;
+
+	fprint(2, "kstrip: ");
+	va_start(arg, fmt);
+	vfprint(2, fmt, arg);
+	va_end(arg);
+	fprint(2, "\n");
+}
+
+static void
+usage(void)
+{
+	error("usage: %s -o ofile file\n\t%s file ...\n", argv0, argv0);
+	exits("usage");
+}
+
+static int
+strip(char* file, char* out)
+{
+	Dir *dir;
+	int fd, i;
+	Fhdr fhdr;
+	Exec *exec;
+	ulong mode;
+	void *data;
+	vlong length;
+
+	if((fd = open(file, OREAD)) < 0){
+		error("%s: open: %r", file);
+		return 1;
+	}
+
+	if(!crackhdr(fd, &fhdr)){
+		error("%s: %r", file);
+		close(fd);
+		return 1;
+	}
+	for(i = MIN_MAGIC; i <= MAX_MAGIC; i++){
+		if(fhdr.magic == _MAGIC(0, i) || fhdr.magic == _MAGIC(HDR_MAGIC, i))
+			break;
+	}
+	if(i > MAX_MAGIC){
+		error("%s: not a recognizable binary", file);
+		close(fd);
+		return 1;
+	}
+
+	if((dir = dirfstat(fd)) == nil){
+		error("%s: stat: %r", file);
+		close(fd);
+		return 1;
+	}
+
+	length = fhdr.datoff+fhdr.datsz;
+	if(length == dir->length){
+		if(out == nil){	/* nothing to do */
+			error("%s: already stripped", file);
+			free(dir);
+			close(fd);
+			return 0;
+		}
+	}
+	if(length > dir->length){
+		error("%s: strange length", file);
+		close(fd);
+		free(dir);
+		return 1;
+	}
+
+	mode = dir->mode;
+	free(dir);
+
+	if((data = malloc(length)) == nil){
+		error("%s: out of memory", file);
+		close(fd);
+		return 1;
+	}
+	seek(fd, 0, 0);
+	if(read(fd, data, length) != length){
+		error("%s: read error: %r", file);
+		close(fd);
+		free(data);
+		return 1;
+	}
+	close(fd);
+
+	exec = data;
+	exec->syms = 0;
+	exec->spsz = 0;
+	exec->pcsz = 0;
+
+	if(out == nil){
+		if(remove(file) < 0) {
+			error("%s: can't remove: %r", file);
+			free(data);
+			return 1;
+		}
+		out = file;
+	}
+	if((fd = create(out, OWRITE, mode)) < 0){
+		error("%s: can't create: %r", out);
+		free(data);
+		return 1;
+	}
+	if(write(fd, data, length) != length){
+		error("%s: write error: %r", out);
+		close(fd);
+		free(data);
+		return 1;
+	}
+	close(fd);
+	free(data);
+
+	return 0;
+}
+
+void
+main(int argc, char* argv[])
+{
+	int r;
+	char *p;
+
+	p = nil;
+
+	ARGBEGIN{
+	default:
+		usage();
+		break;
+	case 'o':
+		p = ARGF();
+		if(p == nil)
+			usage();
+		break;
+	}ARGEND;
+
+	switch(argc){
+	case 0:
+		usage();
+		return;
+	case 1:
+		if(p != nil){
+			r = strip(*argv, p);
+			break;
+		}
+		/*FALLTHROUGH*/
+	default:
+		r = 0;
+		while(argc > 0){
+			r |= strip(*argv, nil);
+			argc--;
+			argv++;
+		}
+		break;
+	}
+
+	if(r)
+		exits("error");
+	exits(0);
+}
--- /dev/null
+++ b/utils/kstrip/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=kstrip
+
+OFILES=	kstrip.$O\
+
+LIBS=	mach bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
--- /dev/null
+++ b/utils/ld/Nt.c
@@ -1,0 +1,101 @@
+#include <windows.h>
+#include <lib9.h>
+
+/*
+ *	We can't include l.h, because Windoze wants to use some names
+ *	like FLOAT and ABC which we declare.  Define what we need here.
+ */
+typedef	unsigned char	uchar;
+typedef	unsigned int	uint;
+typedef	unsigned long	ulong;
+
+extern char	*hunk;
+extern long	nhunk;
+
+void	gethunk(void);
+
+/*
+ * fake malloc
+ */
+void*
+malloc(uint n)
+{
+	void *p;
+
+	while(n & 7)
+		n++;
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void
+free(void *p)
+{
+}
+
+void*
+calloc(uint m, uint n)
+{
+	void *p;
+
+	n *= m;
+	p = malloc(n);
+	memset(p, 0, n);
+	return p;
+}
+
+void*
+realloc(void *p, uint n)
+{
+	void *new;
+
+	new = malloc(n);
+	if(new && p)
+		memmove(new, p, n);
+	return new;
+}
+
+#define	Chunk	(1*1024*1024)
+
+void*
+mysbrk(ulong size)
+{
+	void *v;
+	static int chunk;
+	static uchar *brk;
+
+	if(chunk < size) {
+		chunk = Chunk;
+		if(chunk < size)
+			chunk = Chunk + size;
+		brk = VirtualAlloc(NULL, chunk, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 	
+		if(brk == 0)
+			return (void*)-1;
+	}
+	v = brk;
+	chunk -= size;
+	brk += size;
+	return v;
+}
+
+double
+cputime(void)
+{
+	return ((double)0);
+}
+
+int
+fileexists(char *name)
+{
+	int fd;
+
+	fd = open(name, OREAD);
+	if(fd < 0)
+		return 0;
+	close(fd);
+	return 1;
+}
--- /dev/null
+++ b/utils/ld/Plan9.c
@@ -1,0 +1,66 @@
+#include	"l.h"
+
+/*
+ * fake malloc
+ */
+void*
+malloc(ulong n)
+{
+	void *p;
+
+	while(n & 7)
+		n++;
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void
+free(void *p)
+{
+	USED(p);
+}
+
+void*
+calloc(ulong m, ulong n)
+{
+	void *p;
+
+	n *= m;
+	p = malloc(n);
+	memset(p, 0, n);
+	return p;
+}
+
+void*
+realloc(void *p, ulong n)
+{
+	USED(p);
+	USED(n);
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void*
+mysbrk(ulong size)
+{
+	return sbrk(size);
+}
+
+void
+setmalloctag(void*, ulong)
+{
+}
+
+int
+fileexists(char *s)
+{
+	uchar dirbuf[400];
+
+	/* it's fine if stat result doesn't fit in dirbuf, since even then the file exists */
+	return stat(s, dirbuf, sizeof(dirbuf)) >= 0;
+}
--- /dev/null
+++ b/utils/ld/Posix.c
@@ -1,0 +1,89 @@
+#include	"l.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#undef getwd
+#include <unistd.h>	/* For sysconf() and _SC_CLK_TCK */
+
+/*
+ * fake malloc
+ */
+void*
+malloc(size_t n)
+{
+	void *p;
+
+	while(n & 7)
+		n++;
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void
+free(void *p)
+{
+	USED(p);
+}
+
+void*
+calloc(size_t m, size_t n)
+{
+	void *p;
+
+	n *= m;
+	p = malloc(n);
+	memset(p, 0, n);
+	return p;
+}
+
+void*
+realloc(void *p, size_t n)
+{
+	fprint(2, "realloc called\n", p, n);
+	abort();
+	return 0;
+}
+
+void*
+mysbrk(ulong size)
+{
+	return (void*)sbrk(size);
+}
+
+double
+cputime(void)
+{
+
+	struct tms tmbuf;
+	double	ret_val;
+
+	/*
+	 * times() only fails if &tmbuf is invalid.
+	 */
+	(void)times(&tmbuf);
+	/*
+	 * Return the total time (in system clock ticks)
+	 * spent in user code and system
+	 * calls by both the calling process and its children.
+	 */
+	ret_val = (double)(tmbuf.tms_utime + tmbuf.tms_stime +
+			tmbuf.tms_cutime + tmbuf.tms_cstime);
+	/*
+	 * Convert to seconds.
+	 */
+	ret_val *= sysconf(_SC_CLK_TCK);
+	return ret_val;
+	
+}
+
+int
+fileexists(char *name)
+{
+	struct stat sb;
+
+	return stat(name, &sb) >= 0;
+}
--- /dev/null
+++ b/utils/ld/elf.c
@@ -1,0 +1,266 @@
+/*
+ * emit 32- or 64-bit elf headers for any architecture.
+ * this is a component of ?l.
+ */
+#include "l.h"
+
+enum {
+	/* offsets into string table */
+	Stitext		= 1,
+	Stidata		= 7,
+	Stistrtab	= 13,
+};
+
+void
+elfident(int bo, int class)
+{
+	strnput("\177ELF", 4);		/* e_ident */
+	cput(class);
+	cput(bo);			/* byte order */
+	cput(1);			/* version = CURRENT */
+	if(debug['k']){			/* boot/embedded/standalone */
+		cput(255);
+		cput(0);
+	}
+	else{
+		cput(0);		/* osabi = SYSV */
+		cput(0);		/* abiversion = 3 */
+	}
+	strnput("", 7);
+}
+
+void
+elfstrtab(void)
+{
+	/* string table */
+	cput(0);
+	strnput(".text", 5);		/* +1 */
+	cput(0);
+	strnput(".data", 5);		/* +7 */
+	cput(0);
+	strnput(".strtab", 7);		/* +13 */
+	cput(0);
+	cput(0);
+}
+
+void
+elf32phdr(void (*putl)(long), ulong type, ulong off, ulong vaddr, ulong paddr,
+	ulong filesz, ulong memsz, ulong prots, ulong align)
+{
+	putl(type);
+	putl(off);
+	putl(vaddr);
+	putl(paddr);
+	putl(filesz);
+	putl(memsz);
+	putl(prots);
+	putl(align);
+}
+
+void
+elf32shdr(void (*putl)(long), ulong name, ulong type, ulong flags, ulong vaddr,
+	ulong off, ulong sectsz, ulong link, ulong addnl, ulong align,
+	ulong entsz)
+{
+	putl(name);
+	putl(type);
+	putl(flags);
+	putl(vaddr);
+	putl(off);
+	putl(sectsz);
+	putl(link);
+	putl(addnl);
+	putl(align);
+	putl(entsz);
+}
+
+static void
+elf32sectab(void (*putl)(long))
+{
+	seek(cout, HEADR+textsize+datsize+symsize, 0);
+	elf32shdr(putl, Stitext, Progbits, Salloc|Sexec, INITTEXT,
+		HEADR, textsize, 0, 0, 0x10000, 0);
+	elf32shdr(putl, Stidata, Progbits, Salloc|Swrite, INITDAT,
+		HEADR+textsize, datsize, 0, 0, 0x10000, 0);
+	elf32shdr(putl, Stistrtab, Strtab, 1 << 5, 0,
+		HEADR+textsize+datsize+symsize+3*Shdr32sz, 14, 0, 0, 1, 0);
+	elfstrtab();
+}
+
+/* if addpsects > 0, putpsects must emit exactly that many psects. */
+void
+elf32(int mach, int bo, int addpsects, void (*putpsects)(Putl))
+{
+	ulong phydata;
+	void (*putw)(long), (*putl)(long);
+
+	if(bo == ELFDATA2MSB){
+		putw = wput;
+		putl = lput;
+	}else if(bo == ELFDATA2LSB){
+		putw = wputl;
+		putl = lputl;
+	}else{
+		print("elf32 byte order is mixed-endian\n");
+		errorexit();
+		return;
+	}
+
+	elfident(bo, ELFCLASS32);
+	putw(EXEC);
+	putw(mach);
+	putl(1L);			/* version = CURRENT */
+	putl(entryvalue());		/* entry vaddr */
+	putl(Ehdr32sz);			/* offset to first phdr */
+	if(debug['S'])
+		putl(HEADR+textsize+datsize+symsize); /* offset to first shdr */
+	else
+		putl(0);
+	putl(0L);			/* flags */
+	putw(Ehdr32sz);
+	putw(Phdr32sz);
+	putw(3 + addpsects);		/* # of Phdrs */
+	putw(Shdr32sz);
+	if(debug['S']){
+		putw(3);		/* # of Shdrs */
+		putw(2);		/* Shdr table index */
+	}else{
+		putw(0);
+		putw(0);
+	}
+
+	/*
+	 * could include ELF headers in text -- 8l doesn't,
+	 * but in theory it aids demand loading.
+	 */
+	elf32phdr(putl, PT_LOAD, HEADR, INITTEXT, INITTEXTP,
+		textsize, textsize, R|X, INITRND);	/* text */
+	/*
+	 * we need INITDATP, but it has to be computed.
+	 * assume distance between INITTEXT & INITTEXTP is also
+	 * correct for INITDAT and INITDATP.
+	 */
+	phydata = INITDAT - (INITTEXT - INITTEXTP);
+	elf32phdr(putl, PT_LOAD, HEADR+textsize, INITDAT, phydata,
+		datsize, datsize+bsssize, R|W|X, INITRND); /* data */
+	elf32phdr(putl, NOPTYPE, HEADR+textsize+datsize, 0, 0,
+		symsize, lcsize, R, 4);			/* symbol table */
+	if (addpsects > 0)
+		putpsects(putl);
+	cflush();
+
+	if(debug['S'])
+		elf32sectab(putl);
+}
+
+/*
+ * elf64
+ */
+
+void
+elf64phdr(void (*putl)(long), void (*putll)(vlong), ulong type, uvlong off,
+	uvlong vaddr, uvlong paddr, uvlong filesz, uvlong memsz, ulong prots,
+	uvlong align)
+{
+	putl(type);		
+	putl(prots);		
+	putll(off);		
+	putll(vaddr);	
+	putll(paddr);	
+	putll(filesz);	
+	putll(memsz);	
+	putll(align);		
+}
+
+void
+elf64shdr(void (*putl)(long), void (*putll)(vlong), ulong name, ulong type,
+	uvlong flags, uvlong vaddr, uvlong off, uvlong sectsz, ulong link,
+	ulong addnl, uvlong align, uvlong entsz)
+{
+	putl(name);
+	putl(type);
+	putll(flags);
+	putll(vaddr);
+	putll(off);
+	putll(sectsz);
+	putl(link);
+	putl(addnl);
+	putll(align);
+	putll(entsz);
+}
+
+static void
+elf64sectab(void (*putl)(long), void (*putll)(vlong))
+{
+	seek(cout, HEADR+textsize+datsize+symsize, 0);
+	elf64shdr(putl, putll, Stitext, Progbits, Salloc|Sexec, INITTEXT,
+		HEADR, textsize, 0, 0, 0x10000, 0);
+	elf64shdr(putl, putll, Stidata, Progbits, Salloc|Swrite, INITDAT,
+		HEADR+textsize, datsize, 0, 0, 0x10000, 0);
+	elf64shdr(putl, putll, Stistrtab, Strtab, 1 << 5, 0,
+		HEADR+textsize+datsize+symsize+3*Shdr64sz, 14, 0, 0, 1, 0);
+	elfstrtab();
+}
+
+/* if addpsects > 0, putpsects must emit exactly that many psects. */
+void
+elf64(int mach, int bo, int addpsects, void (*putpsects)(Putl))
+{
+	uvlong phydata;
+	void (*putw)(long), (*putl)(long);
+	void (*putll)(vlong);
+
+	if(bo == ELFDATA2MSB){
+		putw = wput;
+		putl = lput;
+		putll = llput;
+	}else if(bo == ELFDATA2LSB){
+		putw = wputl;
+		putl = lputl;
+		putll = llputl;
+	}else{
+		print("elf64 byte order is mixed-endian\n");
+		errorexit();
+		return;
+	}
+
+	elfident(bo, ELFCLASS64);
+	putw(EXEC);
+	putw(mach);
+	putl(1L);			/* version = CURRENT */
+	putll(entryvalue());		/* entry vaddr */
+	putll(Ehdr64sz);		/* offset to first phdr */
+	if(debug['S'])
+		putll(HEADR+textsize+datsize+symsize); /* offset to 1st shdr */
+	else
+		putll(0);
+	putl(0L);			/* flags */
+	putw(Ehdr64sz);
+	putw(Phdr64sz);
+	putw(3 + addpsects);		/* # of Phdrs */
+	putw(Shdr64sz);
+	if(debug['S']){
+		putw(3);		/* # of Shdrs */
+		putw(2);		/* Shdr table index */
+	}else{
+		putw(0);
+		putw(0);
+	}
+
+	elf64phdr(putl, putll, PT_LOAD, HEADR, INITTEXT, INITTEXTP,
+		textsize, textsize, R|X, INITRND);	/* text */
+	/*
+	 * see 32-bit ELF case for physical data address computation.
+	 */
+	phydata = INITDAT - (INITTEXT - INITTEXTP);
+	elf64phdr(putl, putll, PT_LOAD, HEADR+textsize, INITDAT, phydata,
+		datsize, datsize+bsssize, R|W, INITRND); /* data */
+	elf64phdr(putl, putll, NOPTYPE, HEADR+textsize+datsize, 0, 0,
+		symsize, lcsize, R, 4);			/* symbol table */
+	if (addpsects > 0)
+		putpsects(putl);
+	cflush();
+
+	if(debug['S'])
+		elf64sectab(putl, putll);
+}
--- /dev/null
+++ b/utils/ld/elf.h
@@ -1,0 +1,96 @@
+enum {
+	Ehdr32sz	= 52,
+	Phdr32sz	= 32,
+	Shdr32sz	= 40,
+
+	Ehdr64sz	= 64,
+	Phdr64sz	= 56,
+	Shdr64sz	= 64,
+};
+
+/* from /sys/src/libmach/elf.h */
+enum {
+	/* Ehdr codes */
+	MAG0 = 0,		/* ident[] indexes */
+	MAG1 = 1,
+	MAG2 = 2,
+	MAG3 = 3,
+	CLASS = 4,
+	DATA = 5,
+	VERSION = 6,
+
+	ELFCLASSNONE = 0,	/* ident[CLASS] */
+	ELFCLASS32 = 1,
+	ELFCLASS64 = 2,
+	ELFCLASSNUM = 3,
+
+	ELFDATANONE = 0,	/* ident[DATA] */
+	ELFDATA2LSB = 1,
+	ELFDATA2MSB = 2,
+	ELFDATANUM = 3,
+
+	NOETYPE = 0,		/* type */
+	REL = 1,
+	EXEC = 2,
+	DYN = 3,
+	CORE = 4,
+
+	NONE = 0,		/* machine */
+	M32 = 1,		/* AT&T WE 32100 */
+	SPARC = 2,		/* Sun SPARC */
+	I386 = 3,		/* Intel 80386 */
+	M68K = 4,		/* Motorola 68000 */
+	M88K = 5,		/* Motorola 88000 */
+	I486 = 6,		/* Intel 80486 */
+	I860 = 7,		/* Intel i860 */
+	MIPS = 8,		/* Mips R2000 */
+	S370 = 9,		/* Amdhal	*/
+	SPARC64 = 18,		/* Sun SPARC v9 */
+	POWER = 20,		/* PowerPC */
+	POWER64 = 21,		/* PowerPC64 */
+	ARM = 40,		/* ARM */
+	AMD64 = 62,		/* Amd64 */
+	ARM64 = 183,		/* ARM64 */
+
+	NO_VERSION = 0,		/* version, ident[VERSION] */
+	CURRENT = 1,
+
+	/* Phdr Codes */
+	NOPTYPE = 0,		/* type */
+	PT_LOAD = 1,
+	DYNAMIC = 2,
+	INTERP = 3,
+	NOTE = 4,
+	SHLIB = 5,
+	PHDR = 6,
+
+	R = 0x4,		/* flags */
+	W = 0x2,
+	X = 0x1,
+
+	/* Shdr Codes */
+	Progbits = 1,	/* section types */
+	Strtab = 3,
+	Nobits = 8,
+
+	Swrite = 1,	/* section attributes (flags) */
+	Salloc = 2,
+	Sexec = 4,
+};
+
+typedef void (*Putl)(long);
+
+void	elf32(int mach, int bo, int addpsects, void (*putpsects)(Putl));
+void	elf32phdr(void (*putl)(long), ulong type, ulong off, ulong vaddr,
+	ulong paddr, ulong filesz, ulong memsz, ulong prots, ulong align);
+void	elf32shdr(void (*putl)(long), ulong name, ulong type, ulong flags,
+	ulong vaddr, ulong off, ulong sectsz, ulong link, ulong addnl,
+	ulong align, ulong entsz);
+
+void	elf64(int mach, int bo, int addpsects, void (*putpsects)(Putl));
+void	elf64phdr(void (*putl)(long), void (*putll)(vlong), ulong type,
+	uvlong off, uvlong vaddr, uvlong paddr, uvlong filesz, uvlong memsz,
+	ulong prots, uvlong align);
+void	elf64shdr(void (*putl)(long), void (*putll)(vlong), ulong name,
+	ulong type, uvlong flags, uvlong vaddr, uvlong off, uvlong sectsz,
+	ulong link, ulong addnl, uvlong align, uvlong entsz);
--- /dev/null
+++ b/utils/lib/rcmain
@@ -1,0 +1,29 @@
+# rcmain: 9pm version
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs=' 	
+'
+switch($#prompt){
+case 0
+case 1
+	prompt=('% ' '	')
+}
+if(~ $rcname v.out) prompt=('broken! ' '	')
+if(! ~ $#cflag 0){
+	if(flag l && test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l && test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	if(! ~ $#* 0) . $*
+	if not . -i 'stdin$'
+}
+if not {
+	if(~ $#* 0) . 'stdin$'
+	if not{
+		status=''
+		. $*
+	}
+}
+exit $status
--- /dev/null
+++ b/utils/lib/yaccpar
@@ -1,0 +1,241 @@
+#define YYFLAG 		-1000
+#define	yyclearin	yychar = -1
+#define	yyerrok		yyerrflag = 0
+
+#ifdef	yydebug
+#include	"y.debug"
+#else
+#define	yydebug		0
+char*	yytoknames[1];		/* for debugging */
+char*	yystates[1];		/* for debugging */
+#endif
+
+/*	parser for yacc output	*/
+
+int	yynerrs = 0;		/* number of errors */
+int	yyerrflag = 0;		/* error recovery flag */
+
+extern	int	fprint(int, char*, ...);
+extern	int	sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+	static char x[16];
+
+	if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+	if(yytoknames[yyc-1])
+		return yytoknames[yyc-1];
+	sprint(x, "<%d>", yyc);
+	return x;
+}
+
+char*
+yystatname(int yys)
+{
+	static char x[16];
+
+	if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+	if(yystates[yys])
+		return yystates[yys];
+	sprint(x, "<%d>\n", yys);
+	return x;
+}
+
+long
+yylex1(void)
+{
+	long yychar;
+	long *t3p;
+	int c;
+
+	yychar = yylex();
+	if(yychar <= 0) {
+		c = yytok1[0];
+		goto out;
+	}
+	if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+		c = yytok1[yychar];
+		goto out;
+	}
+	if(yychar >= YYPRIVATE)
+		if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+			c = yytok2[yychar-YYPRIVATE];
+			goto out;
+		}
+	for(t3p=yytok3;; t3p+=2) {
+		c = t3p[0];
+		if(c == yychar) {
+			c = t3p[1];
+			goto out;
+		}
+		if(c == 0)
+			break;
+	}
+	c = 0;
+
+out:
+	if(c == 0)
+		c = yytok2[1];	/* unknown char */
+	if(yydebug >= 3)
+		fprint(2, "lex %.4lux %s\n", yychar, yytokname(c));
+	return c;
+}
+
+int
+yyparse(void)
+{
+	struct
+	{
+		YYSTYPE	yyv;
+		int	yys;
+	} yys[YYMAXDEPTH], *yyp, *yypt;
+	short *yyxi;
+	int yyj, yym, yystate, yyn, yyg;
+	long yychar;
+	YYSTYPE save1, save2;
+	int save3, save4;
+
+	save1 = yylval;
+	save2 = yyval;
+	save3 = yynerrs;
+	save4 = yyerrflag;
+
+	yystate = 0;
+	yychar = -1;
+	yynerrs = 0;
+	yyerrflag = 0;
+	yyp = &yys[-1];
+	goto yystack;
+
+ret0:
+	yyn = 0;
+	goto ret;
+
+ret1:
+	yyn = 1;
+	goto ret;
+
+ret:
+	yylval = save1;
+	yyval = save2;
+	yynerrs = save3;
+	yyerrflag = save4;
+	return yyn;
+
+yystack:
+	/* put a state and value onto the stack */
+	if(yydebug >= 4)
+		fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+	yyp++;
+	if(yyp >= &yys[YYMAXDEPTH]) {
+		yyerror("yacc stack overflow");
+		goto ret1;
+	}
+	yyp->yys = yystate;
+	yyp->yyv = yyval;
+
+yynewstate:
+	yyn = yypact[yystate];
+	if(yyn <= YYFLAG)
+		goto yydefault; /* simple state */
+	if(yychar < 0)
+		yychar = yylex1();
+	yyn += yychar;
+	if(yyn < 0 || yyn >= YYLAST)
+		goto yydefault;
+	yyn = yyact[yyn];
+	if(yychk[yyn] == yychar) { /* valid shift */
+		yychar = -1;
+		yyval = yylval;
+		yystate = yyn;
+		if(yyerrflag > 0)
+			yyerrflag--;
+		goto yystack;
+	}
+
+yydefault:
+	/* default state action */
+	yyn = yydef[yystate];
+	if(yyn == -2) {
+		if(yychar < 0)
+			yychar = yylex1();
+
+		/* look through exception table */
+		for(yyxi=yyexca;; yyxi+=2)
+			if(yyxi[0] == -1 && yyxi[1] == yystate)
+				break;
+		for(yyxi += 2;; yyxi += 2) {
+			yyn = yyxi[0];
+			if(yyn < 0 || yyn == yychar)
+				break;
+		}
+		yyn = yyxi[1];
+		if(yyn < 0)
+			goto ret0;
+	}
+	if(yyn == 0) {
+		/* error ... attempt to resume parsing */
+		switch(yyerrflag) {
+		case 0:   /* brand new error */
+			yyerror("syntax error");
+			yynerrs++;
+			if(yydebug >= 1) {
+				fprint(2, "%s", yystatname(yystate));
+				fprint(2, "saw %s\n", yytokname(yychar));
+			}
+
+		case 1:
+		case 2: /* incompletely recovered error ... try again */
+			yyerrflag = 3;
+
+			/* find a state where "error" is a legal shift action */
+			while(yyp >= yys) {
+				yyn = yypact[yyp->yys] + YYERRCODE;
+				if(yyn >= 0 && yyn < YYLAST) {
+					yystate = yyact[yyn];  /* simulate a shift of "error" */
+					if(yychk[yystate] == YYERRCODE)
+						goto yystack;
+				}
+
+				/* the current yyp has no shift onn "error", pop stack */
+				if(yydebug >= 2)
+					fprint(2, "error recovery pops state %d, uncovers %d\n",
+						yyp->yys, (yyp-1)->yys );
+				yyp--;
+			}
+			/* there is no state on the stack with an error shift ... abort */
+			goto ret1;
+
+		case 3:  /* no shift yet; clobber input char */
+			if(yydebug >= 2)
+				fprint(2, "error recovery discards %s\n", yytokname(yychar));
+			if(yychar == YYEOFCODE)
+				goto ret1;
+			yychar = -1;
+			goto yynewstate;   /* try again in the same state */
+		}
+	}
+
+	/* reduction by production yyn */
+	if(yydebug >= 2)
+		fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+	yypt = yyp;
+	yyp -= yyr2[yyn];
+	yyval = (yyp+1)->yyv;
+	yym = yyn;
+
+	/* consult goto table to find next state */
+	yyn = yyr1[yyn];
+	yyg = yypgo[yyn];
+	yyj = yyg + yyp->yys + 1;
+
+	if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+		yystate = yyact[yyg];
+	switch(yym) {
+		$A
+	}
+	goto yystack;  /* stack new state and value */
+}
--- /dev/null
+++ b/utils/libmach/4.c
@@ -1,0 +1,139 @@
+/*
+ * mips definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "ureg4.h"
+#include "mach.h"
+
+#define	FPREGBYTES	4
+#define	REGOFF(x)	(ulong)(&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(u0.sp)
+#define PC		REGOFF(pc)
+#define	R1		REGOFF(hr1)
+#define	R31		REGOFF(hr31)
+#define	FP_REG(x)	(R1+8+FPREGBYTES*(x))
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	(FPREGBYTES*33)
+
+Reglist mips2reglist[] = {
+	{"STATUS",	REGOFF(status),		RINT|RRDONLY, 'X'},
+	{"CAUSE",	REGOFF(cause),		RINT|RRDONLY, 'X'},
+	{"BADVADDR",	REGOFF(badvaddr),	RINT|RRDONLY, 'X'},
+	{"TLBVIRT",	REGOFF(tlbvirt),	RINT|RRDONLY, 'X'},
+	{"HI",		REGOFF(hhi),		RINT|RRDONLY, 'Y'},
+	{"LO",		REGOFF(hlo),		RINT|RRDONLY, 'Y'},
+	{"PC",		PC,		RINT, 'X'},
+	{"SP",		SP,		RINT, 'X'},
+	{"R31",		R31,		RINT, 'Y'},
+	{"R30",		REGOFF(hr30),	RINT, 'Y'},
+	{"R28",		REGOFF(hr28),	RINT, 'Y'},
+	{"R27",		REGOFF(hr27),	RINT, 'Y'},
+	{"R26",		REGOFF(hr26),	RINT, 'Y'},
+	{"R25",		REGOFF(hr25),	RINT, 'Y'},
+	{"R24",		REGOFF(hr24),	RINT, 'Y'},
+	{"R23",		REGOFF(hr23),	RINT, 'Y'},
+	{"R22",		REGOFF(hr22),	RINT, 'Y'},
+	{"R21",		REGOFF(hr21),	RINT, 'Y'},
+	{"R20",		REGOFF(hr20),	RINT, 'Y'},
+	{"R19",		REGOFF(hr19),	RINT, 'Y'},
+	{"R18",		REGOFF(hr18),	RINT, 'Y'},
+	{"R17",		REGOFF(hr17),	RINT, 'Y'},
+	{"R16",		REGOFF(hr16),	RINT, 'Y'},
+	{"R15",		REGOFF(hr15),	RINT, 'Y'},
+	{"R14",		REGOFF(hr14),	RINT, 'Y'},
+	{"R13",		REGOFF(hr13),	RINT, 'Y'},
+	{"R12",		REGOFF(hr12),	RINT, 'Y'},
+	{"R11",		REGOFF(hr11),	RINT, 'Y'},
+	{"R10",		REGOFF(hr10),	RINT, 'Y'},
+	{"R9",		REGOFF(hr9),	RINT, 'Y'},
+	{"R8",		REGOFF(hr8),	RINT, 'Y'},
+	{"R7",		REGOFF(hr7),	RINT, 'Y'},
+	{"R6",		REGOFF(hr6),	RINT, 'Y'},
+	{"R5",		REGOFF(hr5),	RINT, 'Y'},
+	{"R4",		REGOFF(hr4),	RINT, 'Y'},
+	{"R3",		REGOFF(hr3),	RINT, 'Y'},
+	{"R2",		REGOFF(hr2),	RINT, 'Y'},
+	{"R1",		REGOFF(hr1),	RINT, 'Y'},
+	{"F0",		FP_REG(0),	RFLT, 'F'},
+	{"F1",		FP_REG(1),	RFLT, 'f'},
+	{"F2",		FP_REG(2),	RFLT, 'F'},
+	{"F3",		FP_REG(3),	RFLT, 'f'},
+	{"F4",		FP_REG(4),	RFLT, 'F'},
+	{"F5",		FP_REG(5),	RFLT, 'f'},
+	{"F6",		FP_REG(6),	RFLT, 'F'},
+	{"F7",		FP_REG(7),	RFLT, 'f'},
+	{"F8",		FP_REG(8),	RFLT, 'F'},
+	{"F9",		FP_REG(9),	RFLT, 'f'},
+	{"F10",		FP_REG(10),	RFLT, 'F'},
+	{"F11",		FP_REG(11),	RFLT, 'f'},
+	{"F12",		FP_REG(12),	RFLT, 'F'},
+	{"F13",		FP_REG(13),	RFLT, 'f'},
+	{"F14",		FP_REG(14),	RFLT, 'F'},
+	{"F15",		FP_REG(15),	RFLT, 'f'},
+	{"F16",		FP_REG(16),	RFLT, 'F'},
+	{"F17",		FP_REG(17),	RFLT, 'f'},
+	{"F18",		FP_REG(18),	RFLT, 'F'},
+	{"F19",		FP_REG(19),	RFLT, 'f'},
+	{"F20",		FP_REG(20),	RFLT, 'F'},
+	{"F21",		FP_REG(21),	RFLT, 'f'},
+	{"F22",		FP_REG(22),	RFLT, 'F'},
+	{"F23",		FP_REG(23),	RFLT, 'f'},
+	{"F24",		FP_REG(24),	RFLT, 'F'},
+	{"F25",		FP_REG(25),	RFLT, 'f'},
+	{"F26",		FP_REG(26),	RFLT, 'F'},
+	{"F27",		FP_REG(27),	RFLT, 'f'},
+	{"F28",		FP_REG(28),	RFLT, 'F'},
+	{"F29",		FP_REG(29),	RFLT, 'f'},
+	{"F30",		FP_REG(30),	RFLT, 'F'},
+	{"F31",		FP_REG(31),	RFLT, 'f'},
+	{"FPCR",	FP_REG(32),	RFLT, 'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mmips2be =
+{
+	"mips2",
+	MMIPS2,		/* machine type */
+	mips2reglist,	/* register set */
+	REGSIZE,	/* number of bytes in reg set */
+	FPREGSIZE,	/* number of bytes in fp reg set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R31",		/* name of link register */
+	"setR30",	/* static base register name */
+	0,		/* SB value */
+	0x1000,		/* page size */
+	0xC0000000,	/* kernel base */
+	0x40000000,	/* kernel text mask */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	8,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
+
+Mach mmips2le =
+{
+	"mips2",
+	NMIPS2,		/* machine type */
+	mips2reglist,	/* register set */
+	REGSIZE,	/* number of bytes in reg set */
+	FPREGSIZE,	/* number of bytes in fp reg set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R31",		/* name of link register */
+	"setR30",	/* static base register name */
+	0,		/* SB value */
+	0x1000,		/* page size */
+	0xC0000000,	/* kernel base */
+	0x40000000,	/* kernel text mask */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	8,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/4db.c
@@ -1,0 +1,57 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+/*
+ * Mips-specific debugger interface
+ */
+
+char	*mipsexcep(Map*, Rgetter);
+int	mipsfoll(Map*, ulong, Rgetter, ulong*);
+int	mipsinst(Map*, ulong, char, char*, int);
+int	mipsdas(Map*, ulong, char*, int);
+int	mipsinstlen(Map*, ulong);
+/*
+ *	Debugger interface
+ */
+Machdata mipsmach2be =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	beswab,			/* short to local byte order */
+	beswal,			/* long to local byte order */
+	beswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	beieeesftos,		/* single precision float printer */
+	beieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
+
+/*
+ *	Debugger interface
+ */
+Machdata mipsmach2le =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	leswab,			/* short to local byte order */
+	leswal,			/* long to local byte order */
+	leswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
--- /dev/null
+++ b/utils/libmach/5.c
@@ -1,0 +1,64 @@
+/*
+ * arm definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "ureg5.h"
+#include "mach.h"
+
+
+#define	REGOFF(x)	(ulong) (&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(r13)
+#define PC		REGOFF(pc)
+
+#define	REGSIZE		sizeof(struct Ureg)
+
+Reglist armreglist[] =
+{
+	{"TYPE",	REGOFF(type),		RINT|RRDONLY, 'X'},
+	{"PSR",		REGOFF(psr),		RINT|RRDONLY, 'X'},
+	{"PC",		PC,			RINT, 'X'},
+	{"SP",		SP,			RINT, 'X'},
+	{"R15",		PC,			RINT, 'X'},
+	{"R14",		REGOFF(r14),		RINT, 'X'},
+	{"R13",		REGOFF(r13),		RINT, 'X'},
+	{"R12",		REGOFF(r12),		RINT, 'X'},
+	{"R11",		REGOFF(r11),		RINT, 'X'},
+	{"R10",		REGOFF(r10),		RINT, 'X'},
+	{"R9",		REGOFF(r9),		RINT, 'X'},
+	{"R8",		REGOFF(r8),		RINT, 'X'},
+	{"R7",		REGOFF(r7),		RINT, 'X'},
+	{"R6",		REGOFF(r6),		RINT, 'X'},
+	{"R5",		REGOFF(r5),		RINT, 'X'},
+	{"R4",		REGOFF(r4),		RINT, 'X'},
+	{"R3",		REGOFF(r3),		RINT, 'X'},
+	{"R2",		REGOFF(r2),		RINT, 'X'},
+	{"R1",		REGOFF(r1),		RINT, 'X'},
+	{"R0",		REGOFF(r0),		RINT, 'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach marm =
+{
+	"arm",
+	MARM,		/* machine type */
+	armreglist,	/* register set */
+	REGSIZE,	/* register set size */
+	0,		/* fp register set size */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R14",		/* name of link register */
+	"setR12",	/* static base register name */
+	0,		/* static base register value */
+	0x1000,		/* page size */
+	0xC0000000,	/* kernel base */
+	0xC0000000,		/* kernel text mask */
+	0x3FFFFFFF,	/* user stack top */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/5db.c
@@ -1,0 +1,1121 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+static int debug = 0;
+
+#define	BITS(a, b)	((1<<(b+1))-(1<<a))
+
+#define LSR(v, s)	((ulong)(v) >> (s))
+#define ASR(v, s)	((long)(v) >> (s))
+#define ROR(v, s)	(LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
+
+
+
+typedef struct	Instr	Instr;
+struct	Instr
+{
+	Map	*map;
+	ulong	w;
+	uvlong	addr;
+	uchar	op;			/* super opcode */
+
+	uchar	cond;			/* bits 28-31 */
+	uchar	store;			/* bit 20 */
+
+	uchar	rd;			/* bits 12-15 */
+	uchar	rn;			/* bits 16-19 */
+	uchar	rs;			/* bits 0-11 (shifter operand) */
+
+	long	imm;			/* rotated imm */
+	char*	curr;			/* fill point in buffer */
+	char*	end;			/* end of buffer */
+	char*	err;			/* error message */
+};
+
+typedef struct Opcode Opcode;
+struct Opcode
+{
+	char*	o;
+	void	(*fmt)(Opcode*, Instr*);
+	uvlong	(*foll)(Map*, Rgetter, Instr*, uvlong);
+	char*	a;
+};
+
+static	void	format(char*, Instr*, char*);
+static	char	FRAMENAME[] = ".frame";
+
+/*
+ * Arm-specific debugger interface
+ */
+
+static	char	*armexcep(Map*, Rgetter);
+static	int	armfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	arminst(Map*, uvlong, char, char*, int);
+static	int	armdas(Map*, uvlong, char*, int);
+static	int	arminstlen(Map*, uvlong);
+
+/*
+ *	Debugger interface
+ */
+Machdata armmach =
+{
+	{0x70, 0x00, 0x20, 0xD1},		/* break point */	/* D1200070 */
+	4,			/* break point size */
+
+	leswab,			/* short to local byte order */
+	leswal,			/* long to local byte order */
+	leswav,			/* long to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	armexcep,			/* print exception */
+	0,			/* breakpoint fixup */
+	0,			/* single precision float printer */
+	0,			/* double precision float printer */
+	armfoll,		/* following addresses */
+	arminst,		/* print instruction */
+	armdas,			/* dissembler */
+	arminstlen,		/* instruction size */
+};
+
+static char*
+armexcep(Map *map, Rgetter rget)
+{
+	uvlong c;
+
+	c = (*rget)(map, "TYPE");
+	switch ((int)c&0x1f) {
+	case 0x11:
+		return "Fiq interrupt";
+	case 0x12:
+		return "Mirq interrupt";
+	case 0x13:
+		return "SVC/SWI Exception";
+	case 0x17:
+		return "Prefetch Abort/Data Abort";
+	case 0x18:
+		return "Data Abort";
+	case 0x1b:
+		return "Undefined instruction/Breakpoint";
+	case 0x1f:
+		return "Sys trap";
+	default:
+		return "Undefined trap";
+	}
+}
+
+static
+char*	cond[16] =
+{
+	"EQ",	"NE",	"CS",	"CC",
+	"MI",	"PL",	"VS",	"VC",
+	"HI",	"LS",	"GE",	"LT",
+	"GT",	"LE",	0,	"NV"
+};
+
+static
+char*	shtype[4] =
+{
+	"<<",	">>",	"->",	"@>"
+};
+
+static
+char *hb[4] =
+{
+	"?",	"HU", "B", "H"
+};
+
+static
+char*	addsub[2] =
+{
+	"-",	"+",
+};
+
+int
+armclass(long w)
+{
+	int op, done;
+
+	op = (w >> 25) & 0x7;
+	switch(op) {
+	case 0:	/* data processing r,r,r */
+		op = ((w >> 4) & 0xf);
+		if(op == 0x9) {
+			op = 48+16;		/* mul, swp or *rex */
+			if((w & 0x0ff00fff) == 0x01900f9f) {
+				op = 93;	/* ldrex */
+				break;
+			}
+			if((w & 0x0ff00ff0) == 0x01800f90) {
+				op = 94;	/* strex */
+				break;
+			}
+			if(w & (1<<24)) {
+				op += 2;
+				if(w & (1<<22))
+					op++;	/* swpb */
+				break;
+			}
+			if(w & (1<<23)) {	/* mullu */
+				op = (48+24+4+4+2+2+4);
+				if(w & (1<<22))	/* mull */
+					op += 2;
+			}
+			if(w & (1<<21))
+				op++;		/* mla */
+			break;
+		}
+		if((op & 0x9) == 0x9)		/* ld/st byte/half s/u */
+		{
+			op = (48+16+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
+			break;
+		}
+		op = (w >> 21) & 0xf;
+		if(w & (1<<4))
+			op += 32;
+		else
+		if((w & (31<<7)) || (w & (1<<5)))
+			op += 16;
+		break;
+	case 1:	/* data processing i,r,r */
+		op = (48) + ((w >> 21) & 0xf);
+		break;
+	case 2:	/* load/store byte/word i(r) */
+		if ((w & 0xffffff8f) == 0xf57ff00f) {	/* barriers, clrex */
+			done = 1;
+			switch ((w >> 4) & 7) {
+			case 1:
+				op = 95;	/* clrex */
+				break;
+			case 4:
+				op = 96;	/* dsb */
+				break;
+			case 5:
+				op = 97;	/* dmb */
+				break;
+			case 6:
+				op = 98;	/* isb */
+				break;
+			default:
+				done = 0;
+				break;
+			}
+			if (done)
+				break;
+		}
+		op = (48+24) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
+		break;
+	case 3:	/* load/store byte/word (r)(r) */
+		op = (48+24+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
+		break;
+	case 4:	/* block data transfer (r)(r) */
+		if ((w & 0xfe50ffff) == 0xf8100a00) {	/* v7 RFE */
+			op = 99;
+			break;
+		}
+		op = (48+24+4+4) + ((w >> 20) & 0x1);
+		break;
+	case 5:	/* branch / branch link */
+		op = (48+24+4+4+2) + ((w >> 24) & 0x1);
+		break;
+	case 7:	/* coprocessor crap */
+		op = (48+24+4+4+2+2) + ((w >> 3) & 0x2) + ((w >> 20) & 0x1);
+		break;
+	default:	  
+		op = (48+24+4+4+2+2+4+4);
+		break;
+	}
+	return op;
+}
+
+static int
+decode(Map *map, uvlong pc, Instr *i)
+{
+	ulong w;
+
+	if(get4(map, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->w = w;
+	i->addr = pc;
+	i->cond = (w >> 28) & 0xF;
+	i->op = armclass(w);
+	i->map = map;
+	return 1;
+}
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static int
+plocal(Instr *i)
+{
+	char *reg;
+	Symbol s;
+	char *fn;
+	int class;
+	int offset;
+
+	if(!findsym(i->addr, CTEXT, &s)) {
+		if(debug)fprint(2,"fn not found @%llux: %r\n", i->addr);
+		return 0;
+	}
+	fn = s.name;
+	if (!findlocal(&s, FRAMENAME, &s)) {
+		if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name);
+			return 0;
+	}
+	if(s.value > i->imm) {
+		class = CAUTO;
+		offset = s.value-i->imm;
+		reg = "(SP)";
+	} else {
+		class = CPARAM;
+		offset = i->imm-s.value-4;
+		reg = "(FP)";
+	}
+	if(!getauto(&s, offset, class, &s)) {
+		if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn,
+			class == CAUTO ? " auto" : "param", offset);
+		return 0;
+	}
+	bprint(i, "%s%c%lld%s", s.name, class == CPARAM ? '+' : '-', s.value, reg);
+	return 1;
+}
+
+/*
+ * Print value v as name[+offset]
+ */
+static int
+gsymoff(char *buf, int n, long v, int space)
+{
+	Symbol s;
+	int r;
+	long delta;
+
+	r = delta = 0;		/* to shut compiler up */
+	if (v) {
+		r = findsym(v, space, &s);
+		if (r)
+			delta = v-s.value;
+		if (delta < 0)
+			delta = -delta;
+	}
+	if (v == 0 || r == 0 || delta >= 4096)
+		return snprint(buf, n, "#%lux", v);
+	if (strcmp(s.name, ".string") == 0)
+		return snprint(buf, n, "#%lux", v);
+	if (!delta)
+		return snprint(buf, n, "%s", s.name);
+	if (s.type != 't' && s.type != 'T')
+		return snprint(buf, n, "%s+%llux", s.name, v-s.value);
+	else
+		return snprint(buf, n, "#%lux", v);
+}
+
+static void
+armdps(Opcode *o, Instr *i)
+{
+	i->store = (i->w >> 20) & 1;
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+	i->rs = (i->w >> 0) & 0xf;
+	if(i->rn == 15 && i->rs == 0) {
+		if(i->op == 8) {
+			format("MOVW", i,"CPSR, R%d");
+			return;
+		} else
+		if(i->op == 10) {
+			format("MOVW", i,"SPSR, R%d");
+			return;
+		}
+	} else
+	if(i->rn == 9 && i->rd == 15) {
+		if(i->op == 9) {
+			format("MOVW", i, "R%s, CPSR");
+			return;
+		} else
+		if(i->op == 11) {
+			format("MOVW", i, "R%s, SPSR");
+			return;
+		}
+	}
+	format(o->o, i, o->a);
+}
+
+static void
+armdpi(Opcode *o, Instr *i)
+{
+	ulong v;
+	int c;
+
+	v = (i->w >> 0) & 0xff;
+	c = (i->w >> 8) & 0xf;
+	while(c) {
+		v = (v<<30) | (v>>2);
+		c--;
+	}
+	i->imm = v;
+	i->store = (i->w >> 20) & 1;
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+	i->rs = i->w&0x0f;
+
+		/* RET is encoded as ADD #0,R14,R15 */
+	if((i->w & 0x0fffffff) == 0x028ef000){
+		format("RET%C", i, "");
+		return;
+	}
+	if((i->w & 0x0ff0ffff) == 0x0280f000){
+		format("B%C", i, "0(R%n)");
+		return;
+	}
+	format(o->o, i, o->a);
+}
+
+static void
+armsdti(Opcode *o, Instr *i)
+{
+	ulong v;
+
+	v = i->w & 0xfff;
+	if(!(i->w & (1<<23)))
+		v = -v;
+	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
+	i->imm = v;
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+		/* RET is encoded as LW.P x,R13,R15 */
+	if ((i->w & 0x0ffff000) == 0x049df000)
+	{
+		format("RET%C%p", i, "%I");
+		return;
+	}
+	format(o->o, i, o->a);
+}
+
+/* arm V4 ld/st halfword, signed byte */
+static void
+armhwby(Opcode *o, Instr *i)
+{
+	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
+	i->imm = (i->w & 0xf) | ((i->w >> 8) & 0xf);
+	if (!(i->w & (1 << 23)))
+		i->imm = - i->imm;
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+	i->rs = (i->w >> 0) & 0xf;
+	format(o->o, i, o->a);
+}
+
+static void
+armsdts(Opcode *o, Instr *i)
+{
+	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
+	i->rs = (i->w >> 0) & 0xf;
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+	format(o->o, i, o->a);
+}
+
+static void
+armbdt(Opcode *o, Instr *i)
+{
+	i->store = (i->w >> 21) & 0x3;		/* S & W bits */
+	i->rn = (i->w >> 16) & 0xf;
+	i->imm = i->w & 0xffff;
+	if(i->w == 0xe8fd8000)
+		format("RFE", i, "");
+	else
+		format(o->o, i, o->a);
+}
+
+static void
+armund(Opcode *o, Instr *i)
+{
+	format(o->o, i, o->a);
+}
+
+static void
+armcdt(Opcode *o, Instr *i)
+{
+	format(o->o, i, o->a);
+}
+
+static void
+armunk(Opcode *o, Instr *i)
+{
+	format(o->o, i, o->a);
+}
+
+static void
+armb(Opcode *o, Instr *i)
+{
+	ulong v;
+
+	v = i->w & 0xffffff;
+	if(v & 0x800000)
+		v |= ~0xffffff;
+	i->imm = (v<<2) + i->addr + 8;
+	format(o->o, i, o->a);
+}
+
+static void
+armco(Opcode *o, Instr *i)		/* coprocessor instructions */
+{
+	int op, p, cp;
+
+	char buf[1024];
+
+	i->rn = (i->w >> 16) & 0xf;
+	i->rd = (i->w >> 12) & 0xf;
+	i->rs = i->w&0xf;
+	cp = (i->w >> 8) & 0xf;
+	p = (i->w >> 5) & 0x7;
+	if(i->w&(1<<4)) {
+		op = (i->w >> 21) & 0x07;
+		snprint(buf, sizeof(buf), "#%x, #%x, R%d, C(%d), C(%d), #%x", cp, op, i->rd, i->rn, i->rs, p);
+	} else {
+		op = (i->w >> 20) & 0x0f;
+		snprint(buf, sizeof(buf), "#%x, #%x, C(%d), C(%d), C(%d), #%x", cp, op, i->rd, i->rn, i->rs, p);
+	}
+	format(o->o, i, buf);
+}
+
+static int
+armcondpass(Map *map, Rgetter rget, uchar cond)
+{
+	uvlong psr;
+	uchar n;
+	uchar z;
+	uchar c;
+	uchar v;
+
+	psr = rget(map, "PSR");
+	n = (psr >> 31) & 1;
+	z = (psr >> 30) & 1;
+	c = (psr >> 29) & 1;
+	v = (psr >> 28) & 1;
+
+	switch(cond) {
+	default:
+	case 0:		return z;
+	case 1:		return !z;
+	case 2:		return c;
+	case 3:		return !c;
+	case 4:		return n;
+	case 5:		return !n;
+	case 6:		return v;
+	case 7:		return !v;
+	case 8:		return c && !z;
+	case 9:		return !c || z;
+	case 10:	return n == v;
+	case 11:	return n != v;
+	case 12:	return !z && (n == v);
+	case 13:	return z && (n != v);
+	case 14:	return 1;
+	case 15:	return 0;
+	}
+}
+
+static ulong
+armshiftval(Map *map, Rgetter rget, Instr *i)
+{
+	if(i->w & (1 << 25)) {				/* immediate */
+		ulong imm = i->w & BITS(0, 7);
+		ulong s = (i->w & BITS(8, 11)) >> 7; /* this contains the *2 */
+		return ROR(imm, s);
+	} else {
+		char buf[8];
+		ulong v;
+		ulong s = (i->w & BITS(7,11)) >> 7;
+
+		sprint(buf, "R%ld", i->w & 0xf);
+		v = rget(map, buf);
+
+		switch((i->w & BITS(4, 6)) >> 4) {
+		default:
+		case 0:					/* LSLIMM */
+			return v << s;
+		case 1:					/* LSLREG */
+			sprint(buf, "R%lud", s >> 1);
+			s = rget(map, buf) & 0xFF;
+			if(s >= 32) return 0;
+			return v << s;
+		case 2:					/* LSRIMM */
+			return LSR(v, s);
+		case 3:					/* LSRREG */
+			sprint(buf, "R%ld", s >> 1);
+			s = rget(map, buf) & 0xFF;
+			if(s >= 32) return 0;
+			return LSR(v, s);
+		case 4:					/* ASRIMM */
+			if(s == 0) {
+				if((v & (1U<<31)) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(v, s);
+		case 5:					/* ASRREG */
+			sprint(buf, "R%ld", s >> 1);
+			s = rget(map, buf) & 0xFF;
+			if(s >= 32) {
+				if((v & (1U<<31)) == 0)
+					return 0;
+				return 0xFFFFFFFF;
+			}
+			return ASR(v, s);
+		case 6:					/* RORIMM */
+			if(s == 0) {
+				ulong c = (rget(map, "PSR") >> 29) & 1;
+
+				return (c << 31) | LSR(v, 1);
+			}
+			return ROR(v, s);
+		case 7:					/* RORREG */
+			sprint(buf, "R%ld", (s>>1)&0xF);
+			s = rget(map, buf);
+			if(s == 0 || (s & 0xF) == 0)
+				return v;
+			return ROR(v, s & 0xF);
+		}
+	}
+}
+
+static int
+nbits(ulong v)
+{
+	int n = 0;
+	int i;
+
+	for(i=0; i < 32 ; i++) {
+		if(v & 1) ++n;
+		v >>= 1;
+	}
+
+	return n;
+}
+
+static ulong
+armmaddr(Map *map, Rgetter rget, Instr *i)
+{
+	ulong v;
+	ulong nb;
+	char buf[8];
+	ulong rn;
+
+	rn = (i->w >> 16) & 0xf;
+	sprint(buf,"R%ld", rn);
+
+	v = rget(map, buf);
+	nb = nbits(i->w & ((1 << 15) - 1));
+
+	switch((i->w >> 23) & 3) {
+	default:
+	case 0: return (v - (nb*4)) + 4;
+	case 1: return v;
+	case 2: return v - (nb*4);
+	case 3: return v + 4;
+	}
+}
+
+static uvlong
+armaddr(Map *map, Rgetter rget, Instr *i)
+{
+	char buf[8];
+	ulong rn;
+
+	snprint(buf, sizeof(buf), "R%ld", (i->w >> 16) & 0xf);
+	rn = rget(map, buf);
+
+	if((i->w & (1<<24)) == 0)			/* POSTIDX */
+		return rn;
+
+	if((i->w & (1<<25)) == 0) {			/* OFFSET */
+		if(i->w & (1U<<23))
+			return rn + (i->w & BITS(0,11));
+		return rn - (i->w & BITS(0,11));
+	} else {					/* REGOFF */
+		ulong index = 0;
+		uchar c;
+		uchar rm;
+
+		sprint(buf, "R%ld", i->w & 0xf);
+		rm = rget(map, buf);
+
+		switch((i->w & BITS(5,6)) >> 5) {
+		case 0: index = rm << ((i->w & BITS(7,11)) >> 7);	break;
+		case 1: index = LSR(rm, ((i->w & BITS(7,11)) >> 7));	break;
+		case 2: index = ASR(rm, ((i->w & BITS(7,11)) >> 7));	break;
+		case 3:
+			if((i->w & BITS(7,11)) == 0) {
+				c = (rget(map, "PSR") >> 29) & 1;
+				index = c << 31 | LSR(rm, 1);
+			} else {
+				index = ROR(rm, ((i->w & BITS(7,11)) >> 7));
+			}
+			break;
+		}
+		if(i->w & (1<<23))
+			return rn + index;
+		return rn - index;
+	}
+}
+
+static uvlong
+armfadd(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	char buf[8];
+	int r;
+
+	r = (i->w >> 12) & 0xf;
+	if(r != 15 || !armcondpass(map, rget, (i->w >> 28) & 0xf))
+		return pc+4;
+
+	r = (i->w >> 16) & 0xf;
+	sprint(buf, "R%d", r);
+
+	return rget(map, buf) + armshiftval(map, rget, i);
+}
+
+static uvlong
+armfmovm(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	ulong v;
+	ulong addr;
+
+	v = i->w & 1<<15;
+	if(!v || !armcondpass(map, rget, (i->w>>28)&0xf))
+		return pc+4;
+
+	addr = armmaddr(map, rget, i) + nbits(i->w & BITS(0,15));
+	if(get4(map, addr, &v) < 0) {
+		werrstr("can't read addr: %r");
+		return -1;
+	}
+	return v;
+}
+
+static uvlong
+armfbranch(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	if(!armcondpass(map, rget, (i->w >> 28) & 0xf))
+		return pc+4;
+
+	return pc + (((signed long)i->w << 8) >> 6) + 8;
+}
+
+static uvlong
+armfmov(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	ulong rd, v;
+
+	rd = (i->w >> 12) & 0xf;
+	if(rd != 15 || !armcondpass(map, rget, (i->w>>28)&0xf))
+		return pc+4;
+
+	 /* LDR */
+	/* BUG: Needs LDH/B, too */
+	if(((i->w>>26)&0x3) == 1) {
+		if(get4(map, armaddr(map, rget, i), &v) < 0) {
+			werrstr("can't read instruction: %r");
+			return pc+4;
+		}
+		return v;
+	}
+
+	 /* MOV */
+	v = armshiftval(map, rget, i);
+
+	return v;
+}
+
+static Opcode opcodes[] =
+{
+	"AND%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"EOR%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"SUB%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"RSB%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"ADD%C%S",	armdps, armfadd,	"R%s,R%n,R%d",
+	"ADC%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"SBC%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"RSC%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"TST%C%S",	armdps, 0,	"R%s,R%n",
+	"TEQ%C%S",	armdps, 0,	"R%s,R%n",
+	"CMP%C%S",	armdps, 0,	"R%s,R%n",
+	"CMN%C%S",	armdps, 0,	"R%s,R%n",
+	"ORR%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"MOVW%C%S",	armdps, armfmov,	"R%s,R%d",
+	"BIC%C%S",	armdps, 0,	"R%s,R%n,R%d",
+	"MVN%C%S",	armdps, 0,	"R%s,R%d",
+
+/* 16 */
+	"AND%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"EOR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"SUB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"RSB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"ADD%C%S",	armdps, armfadd,	"(R%s%h%m),R%n,R%d",
+	"ADC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"SBC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"RSC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"TST%C%S",	armdps, 0,	"(R%s%h%m),R%n",
+	"TEQ%C%S",	armdps, 0,	"(R%s%h%m),R%n",
+	"CMP%C%S",	armdps, 0,	"(R%s%h%m),R%n",
+	"CMN%C%S",	armdps, 0,	"(R%s%h%m),R%n",
+	"ORR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"MOVW%C%S",	armdps, armfmov,	"(R%s%h%m),R%d",
+	"BIC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
+	"MVN%C%S",	armdps, 0,	"(R%s%h%m),R%d",
+
+/* 32 */
+	"AND%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"EOR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"SUB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"RSB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"ADD%C%S",	armdps, armfadd,	"(R%s%hR%M),R%n,R%d",
+	"ADC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"SBC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"RSC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"TST%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
+	"TEQ%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
+	"CMP%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
+	"CMN%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
+	"ORR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"MOVW%C%S",	armdps, armfmov,	"(R%s%hR%M),R%d",
+	"BIC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
+	"MVN%C%S",	armdps, 0,	"(R%s%hR%M),R%d",
+
+/* 48 */
+	"AND%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"EOR%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"SUB%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"RSB%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"ADD%C%S",	armdpi, armfadd,	"$#%i,R%n,R%d",
+	"ADC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"SBC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"RSC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"TST%C%S",	armdpi, 0,	"$#%i,R%n",
+	"TEQ%C%S",	armdpi, 0,	"$#%i,R%n",
+	"CMP%C%S",	armdpi, 0,	"$#%i,R%n",
+	"CMN%C%S",	armdpi, 0,	"$#%i,R%n",
+	"ORR%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"MOVW%C%S",	armdpi, armfmov,	"$#%i,R%d",
+	"BIC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
+	"MVN%C%S",	armdpi, 0,	"$#%i,R%d",
+
+/* 48+16 */
+	"MUL%C%S",	armdpi, 0,	"R%M,R%s,R%n",
+	"MULA%C%S",	armdpi, 0,	"R%M,R%s,R%n,R%d",
+	"SWPW",		armdpi, 0,	"R%s,(R%n),R%d",
+	"SWPB",		armdpi, 0,	"R%s,(R%n),R%d",
+
+/* 48+16+4 */
+	"MOV%u%C%p",	armhwby, 0,	"R%d,(R%n%UR%M)",
+	"MOV%u%C%p",	armhwby, 0,	"R%d,%I",
+	"MOV%u%C%p",	armhwby, armfmov,	"(R%n%UR%M),R%d",
+	"MOV%u%C%p",	armhwby, armfmov,	"%I,R%d",
+
+/* 48+24 */
+	"MOVW%C%p",	armsdti, 0,	"R%d,%I",
+	"MOVB%C%p",	armsdti, 0,	"R%d,%I",
+	"MOVW%C%p",	armsdti, armfmov,	"%I,R%d",
+	"MOVBU%C%p",	armsdti, armfmov,	"%I,R%d",
+
+	"MOVW%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)",
+	"MOVB%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)",
+	"MOVW%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d",
+	"MOVBU%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d",
+
+	"MOVM%C%P%a",	armbdt, armfmovm,		"[%r],(R%n)",
+	"MOVM%C%P%a",	armbdt, armfmovm,		"(R%n),[%r]",
+
+	"B%C",		armb, armfbranch,		"%b",
+	"BL%C",		armb, armfbranch,		"%b",
+
+	"CDP%C",	armco, 0,		"",
+	"CDP%C",	armco, 0,		"",
+	"MCR%C",	armco, 0,		"",
+	"MRC%C",	armco, 0,		"",
+
+/* 48+24+4+4+2+2+4 */
+	"MULLU%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
+	"MULALU%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
+	"MULL%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
+	"MULAL%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
+
+/* 48+24+4+4+2+2+4+4 = 92 */
+	"UNK",		armunk, 0,	"",
+
+	/* new v7 arch instructions */
+/* 93 */
+	"LDREX",	armdpi, 0,	"(R%n),R%d",
+	"STREX",	armdpi, 0,	"R%s,(R%n),R%d",
+	"CLREX",	armunk, 0,	"",
+
+/* 96 */
+	"DSB",		armunk, 0,	"",
+	"DMB",		armunk, 0,	"",
+	"ISB",		armunk, 0,	"",
+
+/* 99 */
+	"RFEV7%P%a",	armbdt, 0,	"(R%n)",
+};
+
+static void
+gaddr(Instr *i)
+{
+	*i->curr++ = '$';
+	i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY);
+}
+
+static	char *mode[] = { 0, "IA", "DB", "IB" };
+static	char *pw[] = { "P", "PW", 0, "W" };
+static	char *sw[] = { 0, "W", "S", "SW" };
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	int j, k, m, n;
+	int g;
+	char *fmt;
+
+	if(mnemonic)
+		format(0, i, mnemonic);
+	if(f == 0)
+		return;
+	if(mnemonic)
+		if(i->curr < i->end)
+			*i->curr++ = '\t';
+	for ( ; *f && i->curr < i->end; f++) {
+		if(*f != '%') {
+			*i->curr++ = *f;
+			continue;
+		}
+		switch (*++f) {
+
+		case 'C':	/* .CONDITION */
+			if(cond[i->cond])
+				bprint(i, ".%s", cond[i->cond]);
+			break;
+
+		case 'S':	/* .STORE */
+			if(i->store)
+				bprint(i, ".S");
+			break;
+
+		case 'P':	/* P & U bits for block move */
+			n = (i->w >>23) & 0x3;
+			if (mode[n])
+				bprint(i, ".%s", mode[n]);
+			break;
+
+		case 'p':	/* P & W bits for single data xfer*/
+			if (pw[i->store])
+				bprint(i, ".%s", pw[i->store]);
+			break;
+
+		case 'a':	/* S & W bits for single data xfer*/
+			if (sw[i->store])
+				bprint(i, ".%s", sw[i->store]);
+			break;
+
+		case 's':
+			bprint(i, "%d", i->rs & 0xf);
+			break;
+				
+		case 'M':
+			bprint(i, "%lud", (i->w>>8) & 0xf);
+			break;
+				
+		case 'm':
+			bprint(i, "%lud", (i->w>>7) & 0x1f);
+			break;
+
+		case 'h':
+			bprint(i, shtype[(i->w>>5) & 0x3]);
+			break;
+
+		case 'u':		/* Signed/unsigned Byte/Halfword */
+			bprint(i, hb[(i->w>>5) & 0x3]);
+			break;
+
+		case 'I':
+			if (i->rn == 13) {
+				if (plocal(i))
+					break;
+			}
+			g = 0;
+			fmt = "#%lx(R%d)";
+			if (i->rn == 15) {
+				/* convert load of offset(PC) to a load immediate */
+				if (get4(i->map, i->addr+i->imm+8, (ulong*)&i->imm) > 0)
+				{
+					g = 1;
+					fmt = "";
+				}
+			}
+			if (mach->sb)
+			{
+				if (i->rd == 11) {
+					ulong nxti;
+
+					if (get4(i->map, i->addr+4, &nxti) > 0) {
+						if ((nxti & 0x0e0f0fff) == 0x060c000b) {
+							i->imm += mach->sb;
+							g = 1;
+							fmt = "-SB";
+						}
+					}
+				}
+				if (i->rn == 12)
+				{
+					i->imm += mach->sb;
+					g = 1;
+					fmt = "-SB(SB)";
+				}
+			}
+			if (g)
+			{
+				gaddr(i);
+				bprint(i, fmt, i->rn);
+			}
+			else
+				bprint(i, fmt, i->imm, i->rn);
+			break;
+		case 'U':		/* Add/subtract from base */
+			bprint(i, addsub[(i->w >> 23) & 1]);
+			break;
+
+		case 'n':
+			bprint(i, "%d", i->rn);
+			break;
+
+		case 'd':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'i':
+			bprint(i, "%lux", i->imm);
+			break;
+
+		case 'b':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				i->imm, CTEXT);
+			break;
+
+		case 'g':
+			i->curr += gsymoff(i->curr, i->end-i->curr,
+				i->imm, CANY);
+			break;
+
+		case 'r':
+			n = i->imm&0xffff;
+			j = 0;
+			k = 0;
+			while(n) {
+				m = j;
+				while(n&0x1) {
+					j++;
+					n >>= 1;
+				}
+				if(j != m) {
+					if(k)
+						bprint(i, ",");
+					if(j == m+1)
+						bprint(i, "R%d", m);
+					else
+						bprint(i, "R%d-R%d", m, j-1);
+					k = 1;
+				}
+				j++;
+				n >>= 1;
+			}
+			break;
+
+		case '\0':
+			*i->curr++ = '%';
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+	*i->curr = 0;
+}
+
+static int
+printins(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+
+	i.curr = buf;
+	i.end = buf+n-1;
+	if(decode(map, pc, &i) < 0)
+		return -1;
+
+	(*opcodes[i.op].fmt)(&opcodes[i.op], &i);
+	return 4;
+}
+
+static int
+arminst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	USED(modifier);
+	return printins(map, pc, buf, n);
+}
+
+static int
+armdas(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+
+	i.curr = buf;
+	i.end = buf+n;
+	if(decode(map, pc, &i) < 0)
+		return -1;
+	if(i.end-i.curr > 8)
+		i.curr = _hexify(buf, i.w, 7);
+	*i.curr = 0;
+	return 4;
+}
+
+static int
+arminstlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	if(decode(map, pc, &i) < 0)
+		return -1;
+	return 4;
+}
+
+static int
+armfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	uvlong d;
+	Instr i;
+
+	if(decode(map, pc, &i) < 0)
+		return -1;
+
+	if(opcodes[i.op].foll) {
+		d = (*opcodes[i.op].foll)(map, rget, &i, pc);
+		if(d == -1)
+			return -1;
+	} else
+		d = pc+4;
+
+	foll[0] = d;
+	return 1;
+}
--- /dev/null
+++ b/utils/libmach/5obj.c
@@ -1,0 +1,137 @@
+/*
+ * 5obj.c - identify and parse a arm object file
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "5c/5.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_is5(char *s)
+{
+	return  s[0] == ANAME				/* ANAME */
+		&& s[1] == D_FILE			/* type */
+		&& s[2] == 1				/* sym */
+		&& s[3] == '<';				/* name of file */
+}
+
+int
+_read5(Biobuf *bp, Prog *p)
+{
+	int as, n;
+	Addr a;
+
+	as = Bgetc(bp);			/* as */
+	if(as < 0)
+		return 0;
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 6);		/* scond(1), reg(1), lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+	case D_FPCR:
+		break;
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/6.c
@@ -1,0 +1,116 @@
+/*
+ * amd64 definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "ureg6.h"
+#include "mach.h"
+
+#define	REGOFF(x)	offsetof(struct Ureg, x)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define FP_CTLS(x)	(REGSIZE+2*(x))
+#define FP_CTL(x)	(REGSIZE+4*(x))
+#define FP_REG(x)	(FP_CTL(8)+16*(x))
+#define XM_REG(x)	(FP_CTL(8)+8*16+16*(x))
+
+#define	FPREGSIZE	512	/* TO DO? currently only 0x1A0 used */
+
+Reglist amd64reglist[] = {
+	{"AX",		REGOFF(ax),	RINT, 'Y'},
+	{"BX",		REGOFF(bx),	RINT, 'Y'},
+	{"CX",		REGOFF(cx),	RINT, 'Y'},
+	{"DX",		REGOFF(dx),	RINT, 'Y'},
+	{"SI",		REGOFF(si),	RINT, 'Y'},
+	{"DI",		REGOFF(di),	RINT, 'Y'},
+	{"BP",		REGOFF(bp),	RINT, 'Y'},
+	{"R8",		REGOFF(r8),	RINT, 'Y'},
+	{"R9",		REGOFF(r9),	RINT, 'Y'},
+	{"R10",		REGOFF(r10),	RINT, 'Y'},
+	{"R11",		REGOFF(r11),	RINT, 'Y'},
+	{"R12",		REGOFF(r12),	RINT, 'Y'},
+	{"R13",		REGOFF(r13),	RINT, 'Y'},
+	{"R14",		REGOFF(r14),	RINT, 'Y'},
+	{"R15",		REGOFF(r15),	RINT, 'Y'},
+	{"DS",		REGOFF(ds),	RINT, 'x'},
+	{"ES",		REGOFF(es),	RINT, 'x'},
+	{"FS",		REGOFF(fs),	RINT, 'x'},
+	{"GS",		REGOFF(gs),	RINT, 'x'},
+	{"TYPE",	REGOFF(type), 	RINT, 'Y'},
+	{"TRAP",	REGOFF(type), 	RINT, 'Y'},	/* alias for acid */
+	{"ERROR",	REGOFF(error),	RINT, 'Y'},
+	{"IP",		REGOFF(ip),	RINT, 'Y'},
+	{"PC",		REGOFF(ip),	RINT, 'Y'},	/* alias for acid */
+	{"CS",		REGOFF(cs),	RINT, 'Y'},
+	{"FLAGS",	REGOFF(flags),	RINT, 'Y'},
+	{"SP",		REGOFF(sp),	RINT, 'Y'},
+	{"SS",		REGOFF(ss),	RINT, 'Y'},
+
+	{"FCW",		FP_CTLS(0),	RFLT, 'x'},
+	{"FSW",		FP_CTLS(1),	RFLT, 'x'},
+	{"FTW",		FP_CTLS(2),	RFLT, 'b'},
+	{"FOP",		FP_CTLS(3),	RFLT, 'x'},
+	{"RIP",		FP_CTL(2),	RFLT, 'Y'},
+	{"RDP",		FP_CTL(4),	RFLT, 'Y'},
+	{"MXCSR",	FP_CTL(6),	RFLT, 'X'},
+	{"MXCSRMASK",	FP_CTL(7),	RFLT, 'X'},
+	{"M0",		FP_REG(0),	RFLT, 'F'},	/* assumes double */
+	{"M1",		FP_REG(1),	RFLT, 'F'},
+	{"M2",		FP_REG(2),	RFLT, 'F'},
+	{"M3",		FP_REG(3),	RFLT, 'F'},
+	{"M4",		FP_REG(4),	RFLT, 'F'},
+	{"M5",		FP_REG(5),	RFLT, 'F'},
+	{"M6",		FP_REG(6),	RFLT, 'F'},
+	{"M7",		FP_REG(7),	RFLT, 'F'},
+	{"X0",		XM_REG(0),	RFLT, 'F'},	/* assumes double */
+	{"X1",		XM_REG(1),	RFLT, 'F'},
+	{"X2",		XM_REG(2),	RFLT, 'F'},
+	{"X3",		XM_REG(3),	RFLT, 'F'},
+	{"X4",		XM_REG(4),	RFLT, 'F'},
+	{"X5",		XM_REG(5),	RFLT, 'F'},
+	{"X6",		XM_REG(6),	RFLT, 'F'},
+	{"X7",		XM_REG(7),	RFLT, 'F'},
+	{"X8",		XM_REG(8),	RFLT, 'F'},
+	{"X9",		XM_REG(9),	RFLT, 'F'},
+	{"X10",		XM_REG(10),	RFLT, 'F'},
+	{"X11",		XM_REG(11),	RFLT, 'F'},
+	{"X12",		XM_REG(12),	RFLT, 'F'},
+	{"X13",		XM_REG(13),	RFLT, 'F'},
+	{"X14",		XM_REG(14),	RFLT, 'F'},
+	{"X15",		XM_REG(15),	RFLT, 'F'},
+	{"X16",		XM_REG(16),	RFLT, 'F'},
+/*
+	{"F0",		FP_REG(7),	RFLT, '3'},
+	{"F1",		FP_REG(6),	RFLT, '3'},
+	{"F2",		FP_REG(5),	RFLT, '3'},
+	{"F3",		FP_REG(4),	RFLT, '3'},
+	{"F4",		FP_REG(3),	RFLT, '3'},
+	{"F5",		FP_REG(2),	RFLT, '3'},
+	{"F6",		FP_REG(1),	RFLT, '3'},
+	{"F7",		FP_REG(0),	RFLT, '3'},
+*/
+	{  0 }
+};
+
+Mach mamd64=
+{
+	"amd64",
+	MAMD64,			/* machine type */
+	amd64reglist,		/* register list */
+	REGSIZE,		/* size of registers in bytes */
+	FPREGSIZE,		/* size of fp registers in bytes */
+	"PC",			/* name of PC */
+	"SP",			/* name of SP */
+	0,			/* link register */
+	"setSB",		/* static base register name (bogus anyways) */
+	0,			/* static base register value */
+	0x200000,			/* page size */
+	0xFFFFFFFFF0110000U,	/* kernel base */
+	0xFFFF800000000000U,	/* kernel text mask */
+	0x00007FFFFFFFF000U,	/* user stack top */
+	1,			/* quantization of pc */
+	8,			/* szaddr */
+	4,			/* szreg */
+	4,			/* szfloat */
+	8,			/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/6obj.c
@@ -1,0 +1,146 @@
+/*
+ * 6obj.c - identify and parse an amd64 object file
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "6c/6.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	sym;
+	char	flags;
+};
+static	Addr	addr(Biobuf*);
+static	char	type2char(int);
+static	void	skip(Biobuf*, int);
+
+int
+_is6(char *t)
+{
+	uchar *s = (uchar*)t;
+
+	return  s[0] == (ANAME&0xff)			/* aslo = ANAME */
+		&& s[1] == ((ANAME>>8)&0xff)
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_read6(Biobuf *bp, Prog* p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);		/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(!(a.flags & T_SYM))
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	int t;
+	long l;
+	vlong off;
+
+	off = 0;
+	a.sym = -1;
+	a.flags = Bgetc(bp);			/* flags */
+	if(a.flags & T_INDEX)
+		skip(bp, 2);
+	if(a.flags & T_OFFSET){
+		l = Bgetc(bp);
+		l |= Bgetc(bp) << 8;
+		l |= Bgetc(bp) << 16;
+		l |= Bgetc(bp) << 24;
+		off = l;
+		if(a.flags & T_64){
+			l = Bgetc(bp);
+			l |= Bgetc(bp) << 8;
+			l |= Bgetc(bp) << 16;
+			l |= Bgetc(bp) << 24;
+			off = ((vlong)l << 32) | (off & 0xFFFFFFFF);
+		}
+		if(off < 0)
+			off = -off;
+	}
+	if(a.flags & T_SYM)
+		a.sym = Bgetc(bp);
+	if(a.flags & T_FCONST)
+		skip(bp, 8);
+	else
+	if(a.flags & T_SCONST)
+		skip(bp, NSNAME);
+	if(a.flags & T_TYPE) {
+		t = Bgetc(bp);
+		if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
+			_offset(a.sym, off);
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/8.c
@@ -1,0 +1,79 @@
+/*
+ * 386 definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "ureg8.h"
+#include "mach.h"
+
+#define	REGOFF(x)	(ulong)(&((struct Ureg *) 0)->x)
+
+#define PC		REGOFF(pc)
+#define SP		REGOFF(u0.sp)
+#define	AX		REGOFF(ax)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define FP_CTL(x)	(REGSIZE+4*(x))
+#define FP_REG(x)	(FP_CTL(7)+10*(x))
+#define	FPREGSIZE	(7*4+8*10)
+
+Reglist i386reglist[] = {
+	{"DI",		REGOFF(di),	RINT, 'X'},
+	{"SI",		REGOFF(si),	RINT, 'X'},
+	{"BP",		REGOFF(bp),	RINT, 'X'},
+	{"BX",		REGOFF(bx),	RINT, 'X'},
+	{"DX",		REGOFF(dx),	RINT, 'X'},
+	{"CX",		REGOFF(cx),	RINT, 'X'},
+	{"AX",		REGOFF(ax),	RINT, 'X'},
+	{"GS",		REGOFF(gs),	RINT, 'X'},
+	{"FS",		REGOFF(fs),	RINT, 'X'},
+	{"ES",		REGOFF(es),	RINT, 'X'},
+	{"DS",		REGOFF(ds),	RINT, 'X'},
+	{"TRAP",	REGOFF(trap), 	RINT, 'X'},
+	{"ECODE",	REGOFF(ecode),	RINT, 'X'},
+	{"PC",		PC,		RINT, 'X'},
+	{"CS",		REGOFF(cs),	RINT, 'X'},
+	{"EFLAGS",	REGOFF(flags),	RINT, 'X'},
+	{"SP",		SP,		RINT, 'X'},
+	{"SS",		REGOFF(ss),	RINT, 'X'},
+
+	{"E0",		FP_CTL(0),	RFLT, 'X'},
+	{"E1",		FP_CTL(1),	RFLT, 'X'},
+	{"E2",		FP_CTL(2),	RFLT, 'X'},
+	{"E3",		FP_CTL(3),	RFLT, 'X'},
+	{"E4",		FP_CTL(4),	RFLT, 'X'},
+	{"E5",		FP_CTL(5),	RFLT, 'X'},
+	{"E6",		FP_CTL(6),	RFLT, 'X'},
+	{"F0",		FP_REG(0),	RFLT, '3'},
+	{"F1",		FP_REG(1),	RFLT, '3'},
+	{"F2",		FP_REG(2),	RFLT, '3'},
+	{"F3",		FP_REG(3),	RFLT, '3'},
+	{"F4",		FP_REG(4),	RFLT, '3'},
+	{"F5",		FP_REG(5),	RFLT, '3'},
+	{"F6",		FP_REG(6),	RFLT, '3'},
+	{"F7",		FP_REG(7),	RFLT, '3'},
+	{  0 }
+};
+
+Mach mi386 =
+{
+	"386",
+	MI386,		/* machine type */
+	i386reglist,	/* register list */
+	REGSIZE,	/* size of registers in bytes */
+	FPREGSIZE,	/* size of fp registers in bytes */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	0,		/* link register */
+	"setSB",	/* static base register name (bogus anyways) */
+	0,		/* static base register value */
+	0x1000,		/* page size */
+	0x80100000,	/* kernel base */
+	0x80000000,		/* kernel text mask */
+	0x7FFFFFFF,		/* user stack top */
+	1,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/8db.c
@@ -1,0 +1,2266 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+/*
+ * i386-specific debugger interface
+ * also amd64 extensions
+ */
+
+static	char	*i386excep(Map*, Rgetter);
+
+static	int	i386trace(Map*, uvlong, uvlong, uvlong, Tracer);
+static	uvlong	i386frame(Map*, uvlong, uvlong, uvlong, uvlong);
+static	int	i386foll(Map*, uvlong, Rgetter, uvlong*);
+static	int	i386inst(Map*, uvlong, char, char*, int);
+static	int	i386das(Map*, uvlong, char*, int);
+static	int	i386instlen(Map*, uvlong);
+
+static	char	STARTSYM[] =	"_main";
+static	char	PROFSYM[] =	"_mainp";
+static	char	FRAMENAME[] =	".frame";
+static char *excname[65] =
+{
+/*[0]*/	"divide error",
+/*[1]*/	"debug exception",
+/*[2]*/	nil,
+/*[3]*/	nil,
+/*[4]*/	"overflow",
+/*[5]*/	"bounds check",
+/*[6]*/	"invalid opcode",
+/*[7]*/	"math coprocessor emulation",
+/*[8]*/	"double fault",
+/*[9]*/	"math coprocessor overrun",
+/*[10]*/	"invalid TSS",
+/*[11]*/	"segment not present",
+/*[12]*/	"stack exception",
+/*[13]*/	"general protection violation",
+/*[14]*/	"page fault",
+/*[15]*/	nil,
+/*[16]*/	"math coprocessor error",
+/*[17]*/	"alignment check",
+/*[18]*/	"machine check",
+/*[19]*/	"floating-point exception",
+/*[20]*/	nil,
+/*[21]*/	nil,
+/*[22]*/	nil,
+/*[23]*/	nil,
+/*[24]*/	"clock",
+/*[25]*/	"keyboard",
+/*[26]*/	nil,
+/*[27]*/	"modem status",
+/*[28]*/	"serial line status",
+/*[29]*/	nil,
+/*[30]*/	"floppy disk",
+/*[31]*/	nil,
+/*[32]*/	nil,
+/*[33]*/	nil,
+/*[34]*/	nil,
+/*[35]*/	nil,
+/*[36]*/	"mouse",
+/*[37]*/	"math coprocessor",
+/*[38]*/	"hard disk",
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,/* 39-54 */
+	0,0,0,0,0,0,0,0,0,		/* 55-63 */
+/*[64]*/	"system call",
+};
+
+Machdata i386mach =
+{
+	{0xCC, 0, 0, 0},	/* break point: INT 3 */
+	1,			/* break point size */
+
+	leswab,			/* convert short to local byte order */
+	leswal,			/* convert long to local byte order */
+	leswav,			/* convert vlong to local byte order */
+	i386trace,		/* C traceback */
+	i386frame,		/* frame finder */
+	i386excep,		/* print exception */
+	0,			/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precision float printer */
+	i386foll,		/* following addresses */
+	i386inst,		/* print instruction */
+	i386das,		/* dissembler */
+	i386instlen,		/* instruction size calculation */
+};
+
+static char*
+i386excep(Map *map, Rgetter rget)
+{
+	ulong c;
+	uvlong pc;
+	static char buf[16];
+
+	c = (*rget)(map, "TRAP");
+	if(c > 64 || excname[c] == 0) {
+		if (c == 3) {
+			pc = (*rget)(map, "PC");
+			if (get1(map, pc, (uchar*)buf, machdata->bpsize) > 0)
+			if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0)
+				return "breakpoint";
+		}
+		snprint(buf, sizeof(buf), "exception %ld", c);
+		return buf;
+	} else
+		return excname[c];
+}
+
+static int
+i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	int i;
+	uvlong osp;
+	Symbol s, f;
+
+	USED(link);
+	i = 0;
+	osp = 0;
+	while(findsym(pc, CTEXT, &s)) {
+		if (osp == sp)
+			break;
+		osp = sp;
+
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc != s.value) {	/* not at first instruction */
+			if(findlocal(&s, FRAMENAME, &f) == 0)
+				break;
+			sp += f.value-mach->szaddr;
+		}
+
+		if (geta(map, sp, &pc) < 0)
+			break;
+
+		if(pc == 0)
+			break;
+
+		(*trace)(map, pc, sp, &s);
+		sp += mach->szaddr;
+
+		if(++i > 1000)
+			break;
+	}
+	return i;
+}
+
+static uvlong
+i386frame(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s, f;
+
+	USED(link);
+	while (findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc != s.value) {	/* not first instruction */
+			if(findlocal(&s, FRAMENAME, &f) == 0)
+				break;
+			sp += f.value-mach->szaddr;
+		}
+
+		if (s.value == addr)
+			return sp;
+
+		if (geta(map, sp, &pc) < 0)
+			break;
+		sp += mach->szaddr;
+	}
+	return 0;
+}
+
+	/* I386/486 - Disassembler and related functions */
+
+/*
+ *  an instruction
+ */
+typedef struct Instr Instr;
+struct	Instr
+{
+	uchar	mem[1+1+1+1+2+1+1+4+4];		/* raw instruction */
+	uvlong	addr;		/* address of start of instruction */
+	int	n;		/* number of bytes in instruction */
+	char	*prefix;	/* instr prefix */
+	char	*segment;	/* segment override */
+	uchar	jumptype;	/* set to the operand type for jump/ret/call */
+	uchar	amd64;
+	uchar	rex;		/* REX prefix (or zero) */
+	char	osize;		/* 'W' or 'L' (or 'Q' on amd64) */
+	char	asize;		/* address size 'W' or 'L' (or 'Q' or amd64) */
+	uchar	mod;		/* bits 6-7 of mod r/m field */
+	uchar	reg;		/* bits 3-5 of mod r/m field */
+	char	ss;		/* bits 6-7 of SIB */
+	char	index;		/* bits 3-5 of SIB */
+	char	base;		/* bits 0-2 of SIB */
+	char	rip;		/* RIP-relative in amd64 mode */
+	uchar	opre;		/* f2/f3 could introduce media */
+	short	seg;		/* segment of far address */
+	ulong	disp;		/* displacement */
+	ulong 	imm;		/* immediate */
+	ulong 	imm2;		/* second immediate operand */
+	uvlong	imm64;		/* big immediate */
+	char	*curr;		/* fill level in output buffer */
+	char	*end;		/* end of output buffer */
+	char	*err;		/* error message */
+};
+
+	/* 386 register (ha!) set */
+enum{
+	AX=0,
+	CX,
+	DX,
+	BX,
+	SP,
+	BP,
+	SI,
+	DI,
+
+	/* amd64 */
+	R8,
+	R9,
+	R10,
+	R11,
+	R12,
+	R13,
+	R14,
+	R15
+};
+
+	/* amd64 rex extension byte */
+enum{
+	REXW		= 1<<3,	/* =1, 64-bit operand size */
+	REXR		= 1<<2,	/* extend modrm reg */
+	REXX		= 1<<1,	/* extend sib index */
+	REXB		= 1<<0	/* extend modrm r/m, sib base, or opcode reg */
+};
+	
+	/* Operand Format codes */
+/*
+%A	-	address size register modifier (!asize -> 'E')
+%C	-	Control register CR0/CR1/CR2
+%D	-	Debug register DR0/DR1/DR2/DR3/DR6/DR7
+%I	-	second immediate operand
+%O	-	Operand size register modifier (!osize -> 'E')
+%T	-	Test register TR6/TR7
+%S	-	size code ('W' or 'L')
+%W	-	Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
+%d	-	displacement 16-32 bits
+%e	-	effective address - Mod R/M value
+%f	-	floating point register F0-F7 - from Mod R/M register
+%g	-	segment register
+%i	-	immediate operand 8-32 bits
+%p	-	PC-relative - signed displacement in immediate field
+%r	-	Reg from Mod R/M
+%w	-	Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
+*/
+
+typedef struct Optable Optable;
+struct Optable
+{
+	int	x;
+	char	operand[2];
+	void	*proto;		/* actually either (char*) or (Optable*) */
+};
+	/* Operand decoding codes */
+enum {
+	Ib = 1,			/* 8-bit immediate - (no sign extension)*/
+	Ibs,			/* 8-bit immediate (sign extended) */
+	Jbs,			/* 8-bit sign-extended immediate in jump or call */
+	Iw,			/* 16-bit immediate -> imm */
+	Iw2,			/* 16-bit immediate -> imm2 */
+	Iwd,			/* Operand-sized immediate (no sign extension)*/
+	Iwdq,			/* Operand-sized immediate, possibly 64 bits */
+	Awd,			/* Address offset */
+	Iwds,			/* Operand-sized immediate (sign extended) */
+	RM,			/* Word or long R/M field with register (/r) */
+	RMB,			/* Byte R/M field with register (/r) */
+	RMOP,			/* Word or long R/M field with op code (/digit) */
+	RMOPB,			/* Byte R/M field with op code (/digit) */
+	RMR,			/* R/M register only (mod = 11) */
+	RMM,			/* R/M memory only (mod = 0/1/2) */
+	R0,			/* Base reg of Mod R/M is literal 0x00 */
+	R1,			/* Base reg of Mod R/M is literal 0x01 */
+	FRMOP,			/* Floating point R/M field with opcode */
+	FRMEX,			/* Extended floating point R/M field with opcode */
+	JUMP,			/* Jump or Call flag - no operand */
+	RET,			/* Return flag - no operand */
+	OA,			/* literal 0x0a byte */
+	PTR,			/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+	AUX,			/* Multi-byte op code - Auxiliary table */
+	AUXMM,			/* multi-byte op code - auxiliary table chosen by prefix */
+	PRE,			/* Instr Prefix */
+	OPRE,			/* Instr Prefix or media op extension */
+	SEG,			/* Segment Prefix */
+	OPOVER,			/* Operand size override */
+	ADDOVER,		/* Address size override */
+};
+	
+static Optable optab0F00[8]=
+{
+0x00,	0,0,		"MOVW	LDT,%e",
+0x01,	0,0,		"MOVW	TR,%e",
+0x02,	0,0,		"MOVW	%e,LDT",
+0x03,	0,0,		"MOVW	%e,TR",
+0x04,	0,0,		"VERR	%e",
+0x05,	0,0,		"VERW	%e",
+};
+
+static Optable optab0F01[8]=
+{
+0x00,	0,0,		"MOVL	GDTR,%e",
+0x01,	0,0,		"MOVL	IDTR,%e",
+0x02,	0,0,		"MOVL	%e,GDTR",
+0x03,	0,0,		"MOVL	%e,IDTR",
+0x04,	0,0,		"MOVW	MSW,%e",	/* word */
+0x06,	0,0,		"MOVW	%e,MSW",	/* word */
+0x07,	0,0,		"INVLPG	%e",		/* or SWAPGS */
+};
+
+static Optable optab0F01F8[1]=
+{
+0x00,	0,0,		"SWAPGS",
+};
+
+/* 0F71 */
+/* 0F72 */
+/* 0F73 */
+
+static Optable optab0FAE[8]=
+{
+0x00,	0,0,		"FXSAVE	%e",
+0x01,	0,0,		"FXRSTOR	%e",
+0x02,	0,0,		"LDMXCSR	%e",
+0x03,	0,0,		"STMXCSR	%e",
+0x05,	0,0,		"LFENCE",
+0x06,	0,0,		"MFENCE",
+0x07,	0,0,		"SFENCE",
+};
+
+/* 0F18 */
+/* 0F0D */
+
+static Optable optab0FBA[8]=
+{
+0x04,	Ib,0,		"BT%S	%i,%e",
+0x05,	Ib,0,		"BTS%S	%i,%e",
+0x06,	Ib,0,		"BTR%S	%i,%e",
+0x07,	Ib,0,		"BTC%S	%i,%e",
+};
+
+static Optable optab0F0F[256]=
+{
+0x0c,	0,0,		"PI2FW	%m,%M",
+0x0d,	0,0,		"PI2L	%m,%M",
+0x1c,	0,0,		"PF2IW	%m,%M",
+0x1d,	0,0,		"PF2IL	%m,%M",
+0x8a,	0,0,		"PFNACC	%m,%M",
+0x8e,	0,0,		"PFPNACC	%m,%M",
+0x90,	0,0,		"PFCMPGE	%m,%M",
+0x94,	0,0,		"PFMIN	%m,%M",
+0x96,	0,0,		"PFRCP	%m,%M",
+0x97,	0,0,		"PFRSQRT	%m,%M",
+0x9a,	0,0,		"PFSUB	%m,%M",
+0x9e,	0,0,		"PFADD	%m,%M",
+0xa0,	0,0,		"PFCMPGT	%m,%M",
+0xa4,	0,0,		"PFMAX	%m,%M",
+0xa6,	0,0,		"PFRCPIT1	%m,%M",
+0xa7,	0,0,		"PFRSQIT1	%m,%M",
+0xaa,	0,0,		"PFSUBR	%m,%M",
+0xae,	0,0,		"PFACC	%m,%M",
+0xb0,	0,0,		"PFCMPEQ	%m,%M",
+0xb4,	0,0,		"PFMUL	%m,%M",
+0xb6,	0,0,		"PFRCPI2T	%m,%M",
+0xb7,	0,0,		"PMULHRW	%m,%M",
+0xbb,	0,0,		"PSWAPL	%m,%M",
+};
+
+static Optable optab0FC7[8]=
+{
+0x01,	0,0,		"CMPXCHG8B	%e",
+};
+
+static Optable optab660F71[8]=
+{
+0x02,	Ib,0,		"PSRLW	%i,%X",
+0x04,	Ib,0,		"PSRAW	%i,%X",
+0x06,	Ib,0,		"PSLLW	%i,%X",
+};
+
+static Optable optab660F72[8]=
+{
+0x02,	Ib,0,		"PSRLL	%i,%X",
+0x04,	Ib,0,		"PSRAL	%i,%X",
+0x06,	Ib,0,		"PSLLL	%i,%X",
+};
+
+static Optable optab660F73[8]=
+{
+0x02,	Ib,0,		"PSRLQ	%i,%X",
+0x03,	Ib,0,		"PSRLO	%i,%X",
+0x06,	Ib,0,		"PSLLQ	%i,%X",
+0x07,	Ib,0,		"PSLLO	%i,%X",
+};
+
+static Optable optab660F[256]=
+{
+0x2B,	RM,0,		"MOVNTPD	%x,%e",
+0x2E,	RM,0,		"UCOMISD	%x,%X",
+0x2F,	RM,0,		"COMISD	%x,%X",
+0x5A,	RM,0,		"CVTPD2PS	%x,%X",
+0x5B,	RM,0,		"CVTPS2PL	%x,%X",
+0x6A,	RM,0,		"PUNPCKHLQ %x,%X",
+0x6B,	RM,0,		"PACKSSLW %x,%X",
+0x6C,	RM,0,		"PUNPCKLQDQ %x,%X",
+0x6D,	RM,0,		"PUNPCKHQDQ %x,%X",
+0x6E,	RM,0,		"MOV%S	%e,%X",
+0x6F,	RM,0,		"MOVO	%x,%X",		/* MOVDQA */
+0x70,	RM,Ib,		"PSHUFL	%i,%x,%X",
+0x71,	RMOP,0,		optab660F71,
+0x72,	RMOP,0,		optab660F72,
+0x73,	RMOP,0,		optab660F73,
+0x7E,	RM,0,		"MOV%S	%X,%e",
+0x7F,	RM,0,		"MOVO	%X,%x",
+0xC4,	RM,Ib,		"PINSRW	%i,%e,%X",
+0xC5,	RMR,Ib,		"PEXTRW	%i,%X,%e",
+0xD4,	RM,0,		"PADDQ	%x,%X",
+0xD5,	RM,0,		"PMULLW	%x,%X",
+0xD6,	RM,0,		"MOVQ	%X,%x",
+0xE6,	RM,0,		"CVTTPD2PL	%x,%X",
+0xE7,	RM,0,		"MOVNTO	%X,%e",
+0xF7,	RM,0,		"MASKMOVOU	%x,%X",
+};
+
+static Optable optabF20F[256]=
+{
+0x10,	RM,0,		"MOVSD	%x,%X",
+0x11,	RM,0,		"MOVSD	%X,%x",
+0x2A,	RM,0,		"CVTS%S2SD	%e,%X",
+0x2C,	RM,0,		"CVTTSD2S%S	%x,%r",
+0x2D,	RM,0,		"CVTSD2S%S	%x,%r",
+0x5A,	RM,0,		"CVTSD2SS	%x,%X",
+0x6F,	RM,0,		"MOVOU	%x,%X",
+0x70,	RM,Ib,		"PSHUFLW	%i,%x,%X",
+0x7F,	RM,0,		"MOVOU	%X,%x",
+0xD6,	RM,0,		"MOVQOZX	%M,%X",
+0xE6,	RM,0,		"CVTPD2PL	%x,%X",
+};
+
+static Optable optabF30F[256]=
+{
+0x10,	RM,0,		"MOVSS	%x,%X",
+0x11,	RM,0,		"MOVSS	%X,%x",
+0x2A,	RM,0,		"CVTS%S2SS	%e,%X",
+0x2C,	RM,0,		"CVTTSS2S%S	%x,%r",
+0x2D,	RM,0,		"CVTSS2S%S	%x,%r",
+0x5A,	RM,0,		"CVTSS2SD	%x,%X",
+0x5B,	RM,0,		"CVTTPS2PL	%x,%X",
+0x6F,	RM,0,		"MOVOU	%x,%X",
+0x70,	RM,Ib,		"PSHUFHW	%i,%x,%X",
+0x7E,	RM,0,		"MOVQOZX	%x,%X",
+0x7F,	RM,0,		"MOVOU	%X,%x",
+0xD6,	RM,0,		"MOVQOZX	%m*,%X",
+0xE6,	RM,0,		"CVTPL2PD	%x,%X",
+};
+
+static Optable optab0F[256]=
+{
+0x00,	RMOP,0,		optab0F00,
+0x01,	RMOP,0,		optab0F01,
+0x02,	RM,0,		"LAR	%e,%r",
+0x03,	RM,0,		"LSL	%e,%r",
+0x05,	0,0,		"SYSCALL",
+0x06,	0,0,		"CLTS",
+0x07,	0,0,		"SYSRET",
+0x08,	0,0,		"INVD",
+0x09,	0,0,		"WBINVD",
+0x0B,	0,0,		"UD2",
+0x0F,	RM,AUX,		optab0F0F,		/* 3DNow! */
+0x10,	RM,0,		"MOVU%s	%x,%X",
+0x11,	RM,0,		"MOVU%s	%X,%x",
+0x12,	RM,0,		"MOV[H]L%s	%x,%X",	/* TO DO: H if source is XMM */
+0x13,	RM,0,		"MOVL%s	%X,%e",
+0x14,	RM,0,		"UNPCKL%s	%x,%X",
+0x15,	RM,0,		"UNPCKH%s	%x,%X",
+0x16,	RM,0,		"MOV[L]H%s	%x,%X",	/* TO DO: L if source is XMM */
+0x17,	RM,0,		"MOVH%s	%X,%x",
+0x20,	RMR,0,		"MOVL	%C,%e",
+0x21,	RMR,0,		"MOVL	%D,%e",
+0x22,	RMR,0,		"MOVL	%e,%C",
+0x23,	RMR,0,		"MOVL	%e,%D",
+0x24,	RMR,0,		"MOVL	%T,%e",
+0x26,	RMR,0,		"MOVL	%e,%T",
+0x28,	RM,0,		"MOVA%s	%x,%X",
+0x29,	RM,0,		"MOVA%s	%X,%x",
+0x2A,	RM,0,		"CVTPL2%s	%m*,%X",
+0x2B,	RM,0,		"MOVNT%s	%X,%e",
+0x2C,	RM,0,		"CVTT%s2PL	%x,%M",
+0x2D,	RM,0,		"CVT%s2PL	%x,%M",
+0x2E,	RM,0,		"UCOMISS	%x,%X",
+0x2F,	RM,0,		"COMISS	%x,%X",
+0x30,	0,0,		"WRMSR",
+0x31,	0,0,		"RDTSC",
+0x32,	0,0,		"RDMSR",
+0x33,	0,0,		"RDPMC",
+0x42,	RM,0,		"CMOVC	%e,%r",		/* CF */
+0x43,	RM,0,		"CMOVNC	%e,%r",		/* ¬ CF */
+0x44,	RM,0,		"CMOVZ	%e,%r",		/* ZF */
+0x45,	RM,0,		"CMOVNZ	%e,%r",		/* ¬ ZF */
+0x46,	RM,0,		"CMOVBE	%e,%r",		/* CF ∨ ZF */
+0x47,	RM,0,		"CMOVA	%e,%r",		/* ¬CF ∧ ¬ZF */
+0x48,	RM,0,		"CMOVS	%e,%r",		/* SF */
+0x49,	RM,0,		"CMOVNS	%e,%r",		/* ¬ SF */
+0x4A,	RM,0,		"CMOVP	%e,%r",		/* PF */
+0x4B,	RM,0,		"CMOVNP	%e,%r",		/* ¬ PF */
+0x4C,	RM,0,		"CMOVLT	%e,%r",		/* LT ≡ OF ≠ SF */
+0x4D,	RM,0,		"CMOVGE	%e,%r",		/* GE ≡ ZF ∨ SF */
+0x4E,	RM,0,		"CMOVLE	%e,%r",		/* LE ≡ ZF ∨ LT */
+0x4F,	RM,0,		"CMOVGT	%e,%r",		/* GT ≡ ¬ZF ∧ GE */
+0x50,	RM,0,		"MOVMSK%s	%X,%r",	/* TO DO: check */
+0x51,	RM,0,		"SQRT%s	%x,%X",
+0x52,	RM,0,		"RSQRT%s	%x,%X",
+0x53,	RM,0,		"RCP%s	%x,%X",
+0x54,	RM,0,		"AND%s	%x,%X",
+0x55,	RM,0,		"ANDN%s	%x,%X",
+0x56,	RM,0,		"OR%s	%x,%X",		/* TO DO: S/D */
+0x57,	RM,0,		"XOR%s	%x,%X",		/* S/D */
+0x58,	RM,0,		"ADD%s	%x,%X",		/* S/P S/D */
+0x59,	RM,0,		"MUL%s	%x,%X",
+0x5A,	RM,0,		"CVTPS2PD	%x,%X",
+0x5B,	RM,0,		"CVTPL2PS	%x,%X",
+0x5C,	RM,0,		"SUB%s	%x,%X",
+0x5D,	RM,0,		"MIN%s	%x,%X",
+0x5E,	RM,0,		"DIV%s	%x,%X",		/* TO DO: S/P S/D */
+0x5F,	RM,0,		"MAX%s	%x,%X",
+0x60,	RM,0,		"PUNPCKLBW %m,%M",
+0x61,	RM,0,		"PUNPCKLWL %m,%M",
+0x62,	RM,0,		"PUNPCKLLQ %m,%M",
+0x63,	RM,0,		"PACKSSWB %m,%M",
+0x64,	RM,0,		"PCMPGTB %m,%M",
+0x65,	RM,0,		"PCMPGTW %m,%M",
+0x66,	RM,0,		"PCMPGTL %m,%M",
+0x67,	RM,0,		"PACKUSWB %m,%M",
+0x68,	RM,0,		"PUNPCKHBW %m,%M",
+0x69,	RM,0,		"PUNPCKHWL %m,%M",
+0x6A,	RM,0,		"PUNPCKHLQ %m,%M",
+0x6B,	RM,0,		"PACKSSLW %m,%M",
+0x6E,	RM,0,		"MOV%S %e,%M",
+0x6F,	RM,0,		"MOVQ %m,%M",
+0x70,	RM,Ib,		"PSHUFW	%i,%m,%M",
+0x74,	RM,0,		"PCMPEQB %m,%M",
+0x75,	RM,0,		"PCMPEQW %m,%M",
+0x76,	RM,0,		"PCMPEQL %m,%M",
+0x7E,	RM,0,		"MOV%S %M,%e",
+0x7F,	RM,0,		"MOVQ %M,%m",
+0xAE,	RMOP,0,		optab0FAE,
+0xAA,	0,0,		"RSM",
+0xB0,	RM,0,		"CMPXCHGB	%r,%e",
+0xB1,	RM,0,		"CMPXCHG%S	%r,%e",
+0xC0,	RMB,0,		"XADDB	%r,%e",
+0xC1,	RM,0,		"XADD%S	%r,%e",
+0xC2,	RM,Ib,		"CMP%s	%i,%x,%X",
+0xC3,	RM,0,		"MOVNTI%S	%r,%e",
+0xC6,	RM,Ib,		"SHUF%s	%i,%x,%X",
+0xC8,	0,0,		"BSWAP	AX",
+0xC9,	0,0,		"BSWAP	CX",
+0xCA,	0,0,		"BSWAP	DX",
+0xCB,	0,0,		"BSWAP	BX",
+0xCC,	0,0,		"BSWAP	SP",
+0xCD,	0,0,		"BSWAP	BP",
+0xCE,	0,0,		"BSWAP	SI",
+0xCF,	0,0,		"BSWAP	DI",
+0xD1,	RM,0,		"PSRLW %m,%M",
+0xD2,	RM,0,		"PSRLL %m,%M",
+0xD3,	RM,0,		"PSRLQ %m,%M",
+0xD5,	RM,0,		"PMULLW %m,%M",
+0xD6,	RM,0,		"MOVQOZX	%m*,%X",
+0xD7,	RM,0,		"PMOVMSKB %m,%r",
+0xD8,	RM,0,		"PSUBUSB %m,%M",
+0xD9,	RM,0,		"PSUBUSW %m,%M",
+0xDA,	RM,0,		"PMINUB %m,%M",
+0xDB,	RM,0,		"PAND %m,%M",
+0xDC,	RM,0,		"PADDUSB %m,%M",
+0xDD,	RM,0,		"PADDUSW %m,%M",
+0xDE,	RM,0,		"PMAXUB %m,%M",
+0xDF,	RM,0,		"PANDN %m,%M",
+0xE0,	RM,0,		"PAVGB %m,%M",
+0xE1,	RM,0,		"PSRAW %m,%M",
+0xE2,	RM,0,		"PSRAL %m,%M",
+0xE3,	RM,0,		"PAVGW %m,%M",
+0xE4,	RM,0,		"PMULHUW %m,%M",
+0xE5,	RM,0,		"PMULHW %m,%M",
+0xE7,	RM,0,		"MOVNTQ	%M,%e",
+0xE8,	RM,0,		"PSUBSB %m,%M",
+0xE9,	RM,0,		"PSUBSW %m,%M",
+0xEA,	RM,0,		"PMINSW %m,%M",
+0xEB,	RM,0,		"POR %m,%M",
+0xEC,	RM,0,		"PADDSB %m,%M",
+0xED,	RM,0,		"PADDSW %m,%M",
+0xEE,	RM,0,		"PMAXSW %m,%M",
+0xEF,	RM,0,		"PXOR %m,%M",
+0xF1,	RM,0,		"PSLLW %m,%M",
+0xF2,	RM,0,		"PSLLL %m,%M",
+0xF3,	RM,0,		"PSLLQ %m,%M",
+0xF4,	RM,0,		"PMULULQ	%m,%M",
+0xF5,	RM,0,		"PMADDWL %m,%M",
+0xF6,	RM,0,		"PSADBW %m,%M",
+0xF7,	RMR,0,		"MASKMOVQ	%m,%M",
+0xF8,	RM,0,		"PSUBB %m,%M",
+0xF9,	RM,0,		"PSUBW %m,%M",
+0xFA,	RM,0,		"PSUBL %m,%M",
+0xFC,	RM,0,		"PADDB %m,%M",
+0xFD,	RM,0,		"PADDW %m,%M",
+0xFE,	RM,0,		"PADDL %m,%M",
+
+0x80,	Iwds,0,		"JOS	%p",
+0x81,	Iwds,0,		"JOC	%p",
+0x82,	Iwds,0,		"JCS	%p",
+0x83,	Iwds,0,		"JCC	%p",
+0x84,	Iwds,0,		"JEQ	%p",
+0x85,	Iwds,0,		"JNE	%p",
+0x86,	Iwds,0,		"JLS	%p",
+0x87,	Iwds,0,		"JHI	%p",
+0x88,	Iwds,0,		"JMI	%p",
+0x89,	Iwds,0,		"JPL	%p",
+0x8a,	Iwds,0,		"JPS	%p",
+0x8b,	Iwds,0,		"JPC	%p",
+0x8c,	Iwds,0,		"JLT	%p",
+0x8d,	Iwds,0,		"JGE	%p",
+0x8e,	Iwds,0,		"JLE	%p",
+0x8f,	Iwds,0,		"JGT	%p",
+0x90,	RMB,0,		"SETOS	%e",
+0x91,	RMB,0,		"SETOC	%e",
+0x92,	RMB,0,		"SETCS	%e",
+0x93,	RMB,0,		"SETCC	%e",
+0x94,	RMB,0,		"SETEQ	%e",
+0x95,	RMB,0,		"SETNE	%e",
+0x96,	RMB,0,		"SETLS	%e",
+0x97,	RMB,0,		"SETHI	%e",
+0x98,	RMB,0,		"SETMI	%e",
+0x99,	RMB,0,		"SETPL	%e",
+0x9a,	RMB,0,		"SETPS	%e",
+0x9b,	RMB,0,		"SETPC	%e",
+0x9c,	RMB,0,		"SETLT	%e",
+0x9d,	RMB,0,		"SETGE	%e",
+0x9e,	RMB,0,		"SETLE	%e",
+0x9f,	RMB,0,		"SETGT	%e",
+0xa0,	0,0,		"PUSHL	FS",
+0xa1,	0,0,		"POPL	FS",
+0xa2,	0,0,		"CPUID",
+0xa3,	RM,0,		"BT%S	%r,%e",
+0xa4,	RM,Ib,		"SHLD%S	%r,%i,%e",
+0xa5,	RM,0,		"SHLD%S	%r,CL,%e",
+0xa8,	0,0,		"PUSHL	GS",
+0xa9,	0,0,		"POPL	GS",
+0xab,	RM,0,		"BTS%S	%r,%e",
+0xac,	RM,Ib,		"SHRD%S	%r,%i,%e",
+0xad,	RM,0,		"SHRD%S	%r,CL,%e",
+0xaf,		RM,0,		"IMUL%S	%e,%r",
+0xb2,	RMM,0,		"LSS	%e,%r",
+0xb3,	RM,0,		"BTR%S	%r,%e",
+0xb4,	RMM,0,		"LFS	%e,%r",
+0xb5,	RMM,0,		"LGS	%e,%r",
+0xb6,	RMB,0,		"MOVBZX	%e,%R",
+0xb7,	RM,0,		"MOVWZX	%e,%R",
+0xba,	RMOP,0,		optab0FBA,
+0xbb,	RM,0,		"BTC%S	%e,%r",
+0xbc,	RM,0,		"BSF%S	%e,%r",
+0xbd,	RM,0,		"BSR%S	%e,%r",
+0xbe,	RMB,0,		"MOVBSX	%e,%R",
+0xbf,	RM,0,		"MOVWSX	%e,%R",
+0xc7,	RMOP,0,		optab0FC7,
+};
+
+static Optable optab80[8]=
+{
+0x00,	Ib,0,		"ADDB	%i,%e",
+0x01,	Ib,0,		"ORB	%i,%e",
+0x02,	Ib,0,		"ADCB	%i,%e",
+0x03,	Ib,0,		"SBBB	%i,%e",
+0x04,	Ib,0,		"ANDB	%i,%e",
+0x05,	Ib,0,		"SUBB	%i,%e",
+0x06,	Ib,0,		"XORB	%i,%e",
+0x07,	Ib,0,		"CMPB	%e,%i",
+};
+
+static Optable optab81[8]=
+{
+0x00,	Iwd,0,		"ADD%S	%i,%e",
+0x01,	Iwd,0,		"OR%S	%i,%e",
+0x02,	Iwd,0,		"ADC%S	%i,%e",
+0x03,	Iwd,0,		"SBB%S	%i,%e",
+0x04,	Iwd,0,		"AND%S	%i,%e",
+0x05,	Iwd,0,		"SUB%S	%i,%e",
+0x06,	Iwd,0,		"XOR%S	%i,%e",
+0x07,	Iwd,0,		"CMP%S	%e,%i",
+};
+
+static Optable optab83[8]=
+{
+0x00,	Ibs,0,		"ADD%S	%i,%e",
+0x01,	Ibs,0,		"OR%S	%i,%e",
+0x02,	Ibs,0,		"ADC%S	%i,%e",
+0x03,	Ibs,0,		"SBB%S	%i,%e",
+0x04,	Ibs,0,		"AND%S	%i,%e",
+0x05,	Ibs,0,		"SUB%S	%i,%e",
+0x06,	Ibs,0,		"XOR%S	%i,%e",
+0x07,	Ibs,0,		"CMP%S	%e,%i",
+};
+
+static Optable optabC0[8] =
+{
+0x00,	Ib,0,		"ROLB	%i,%e",
+0x01,	Ib,0,		"RORB	%i,%e",
+0x02,	Ib,0,		"RCLB	%i,%e",
+0x03,	Ib,0,		"RCRB	%i,%e",
+0x04,	Ib,0,		"SHLB	%i,%e",
+0x05,	Ib,0,		"SHRB	%i,%e",
+0x07,	Ib,0,		"SARB	%i,%e",
+};
+
+static Optable optabC1[8] =
+{
+0x00,	Ib,0,		"ROL%S	%i,%e",
+0x01,	Ib,0,		"ROR%S	%i,%e",
+0x02,	Ib,0,		"RCL%S	%i,%e",
+0x03,	Ib,0,		"RCR%S	%i,%e",
+0x04,	Ib,0,		"SHL%S	%i,%e",
+0x05,	Ib,0,		"SHR%S	%i,%e",
+0x07,	Ib,0,		"SAR%S	%i,%e",
+};
+
+static Optable optabD0[8] =
+{
+0x00,	0,0,		"ROLB	%e",
+0x01,	0,0,		"RORB	%e",
+0x02,	0,0,		"RCLB	%e",
+0x03,	0,0,		"RCRB	%e",
+0x04,	0,0,		"SHLB	%e",
+0x05,	0,0,		"SHRB	%e",
+0x07,	0,0,		"SARB	%e",
+};
+
+static Optable optabD1[8] =
+{
+0x00,	0,0,		"ROL%S	%e",
+0x01,	0,0,		"ROR%S	%e",
+0x02,	0,0,		"RCL%S	%e",
+0x03,	0,0,		"RCR%S	%e",
+0x04,	0,0,		"SHL%S	%e",
+0x05,	0,0,		"SHR%S	%e",
+0x07,	0,0,		"SAR%S	%e",
+};
+
+static Optable optabD2[8] =
+{
+0x00,	0,0,		"ROLB	CL,%e",
+0x01,	0,0,		"RORB	CL,%e",
+0x02,	0,0,		"RCLB	CL,%e",
+0x03,	0,0,		"RCRB	CL,%e",
+0x04,	0,0,		"SHLB	CL,%e",
+0x05,	0,0,		"SHRB	CL,%e",
+0x07,	0,0,		"SARB	CL,%e",
+};
+
+static Optable optabD3[8] =
+{
+0x00,	0,0,		"ROL%S	CL,%e",
+0x01,	0,0,		"ROR%S	CL,%e",
+0x02,	0,0,		"RCL%S	CL,%e",
+0x03,	0,0,		"RCR%S	CL,%e",
+0x04,	0,0,		"SHL%S	CL,%e",
+0x05,	0,0,		"SHR%S	CL,%e",
+0x07,	0,0,		"SAR%S	CL,%e",
+};
+
+static Optable optabD8[8+8] =
+{
+0x00,	0,0,		"FADDF	%e,F0",
+0x01,	0,0,		"FMULF	%e,F0",
+0x02,	0,0,		"FCOMF	%e,F0",
+0x03,	0,0,		"FCOMFP	%e,F0",
+0x04,	0,0,		"FSUBF	%e,F0",
+0x05,	0,0,		"FSUBRF	%e,F0",
+0x06,	0,0,		"FDIVF	%e,F0",
+0x07,	0,0,		"FDIVRF	%e,F0",
+0x08,	0,0,		"FADDD	%f,F0",
+0x09,	0,0,		"FMULD	%f,F0",
+0x0a,	0,0,		"FCOMD	%f,F0",
+0x0b,	0,0,		"FCOMPD	%f,F0",
+0x0c,	0,0,		"FSUBD	%f,F0",
+0x0d,	0,0,		"FSUBRD	%f,F0",
+0x0e,	0,0,		"FDIVD	%f,F0",
+0x0f,	0,0,		"FDIVRD	%f,F0",
+};
+/*
+ *	optabD9 and optabDB use the following encoding: 
+ *	if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
+ *	else instruction = optabDx[(modrm&0x3f)+8];
+ *
+ *	the instructions for MOD == 3, follow the 8 instructions
+ *	for the other MOD values stored at the front of the table.
+ */
+static Optable optabD9[64+8] =
+{
+0x00,	0,0,		"FMOVF	%e,F0",
+0x02,	0,0,		"FMOVF	F0,%e",
+0x03,	0,0,		"FMOVFP	F0,%e",
+0x04,	0,0,		"FLDENV%S %e",
+0x05,	0,0,		"FLDCW	%e",
+0x06,	0,0,		"FSTENV%S %e",
+0x07,	0,0,		"FSTCW	%e",
+0x08,	0,0,		"FMOVD	F0,F0",		/* Mod R/M = 11xx xxxx*/
+0x09,	0,0,		"FMOVD	F1,F0",
+0x0a,	0,0,		"FMOVD	F2,F0",
+0x0b,	0,0,		"FMOVD	F3,F0",
+0x0c,	0,0,		"FMOVD	F4,F0",
+0x0d,	0,0,		"FMOVD	F5,F0",
+0x0e,	0,0,		"FMOVD	F6,F0",
+0x0f,	0,0,		"FMOVD	F7,F0",
+0x10,	0,0,		"FXCHD	F0,F0",
+0x11,	0,0,		"FXCHD	F1,F0",
+0x12,	0,0,		"FXCHD	F2,F0",
+0x13,	0,0,		"FXCHD	F3,F0",
+0x14,	0,0,		"FXCHD	F4,F0",
+0x15,	0,0,		"FXCHD	F5,F0",
+0x16,	0,0,		"FXCHD	F6,F0",
+0x17,	0,0,		"FXCHD	F7,F0",
+0x18,	0,0,		"FNOP",
+0x28,	0,0,		"FCHS",
+0x29,	0,0,		"FABS",
+0x2c,	0,0,		"FTST",
+0x2d,	0,0,		"FXAM",
+0x30,	0,0,		"FLD1",
+0x31,	0,0,		"FLDL2T",
+0x32,	0,0,		"FLDL2E",
+0x33,	0,0,		"FLDPI",
+0x34,	0,0,		"FLDLG2",
+0x35,	0,0,		"FLDLN2",
+0x36,	0,0,		"FLDZ",
+0x38,	0,0,		"F2XM1",
+0x39,	0,0,		"FYL2X",
+0x3a,	0,0,		"FPTAN",
+0x3b,	0,0,		"FPATAN",
+0x3c,	0,0,		"FXTRACT",
+0x3d,	0,0,		"FPREM1",
+0x3e,	0,0,		"FDECSTP",
+0x3f,	0,0,		"FNCSTP",
+0x40,	0,0,		"FPREM",
+0x41,	0,0,		"FYL2XP1",
+0x42,	0,0,		"FSQRT",
+0x43,	0,0,		"FSINCOS",
+0x44,	0,0,		"FRNDINT",
+0x45,	0,0,		"FSCALE",
+0x46,	0,0,		"FSIN",
+0x47,	0,0,		"FCOS",
+};
+
+static Optable optabDA[8+8] =
+{
+0x00,	0,0,		"FADDL	%e,F0",
+0x01,	0,0,		"FMULL	%e,F0",
+0x02,	0,0,		"FCOML	%e,F0",
+0x03,	0,0,		"FCOMLP	%e,F0",
+0x04,	0,0,		"FSUBL	%e,F0",
+0x05,	0,0,		"FSUBRL	%e,F0",
+0x06,	0,0,		"FDIVL	%e,F0",
+0x07,	0,0,		"FDIVRL	%e,F0",
+0x0d,	R1,0,		"FUCOMPP",
+};
+
+static Optable optabDB[8+64] =
+{
+0x00,	0,0,		"FMOVL	%e,F0",
+0x02,	0,0,		"FMOVL	F0,%e",
+0x03,	0,0,		"FMOVLP	F0,%e",
+0x05,	0,0,		"FMOVX	%e,F0",
+0x07,	0,0,		"FMOVXP	F0,%e",
+0x2a,	0,0,		"FCLEX",
+0x2b,	0,0,		"FINIT",
+};
+
+static Optable optabDC[8+8] =
+{
+0x00,	0,0,		"FADDD	%e,F0",
+0x01,	0,0,		"FMULD	%e,F0",
+0x02,	0,0,		"FCOMD	%e,F0",
+0x03,	0,0,		"FCOMDP	%e,F0",
+0x04,	0,0,		"FSUBD	%e,F0",
+0x05,	0,0,		"FSUBRD	%e,F0",
+0x06,	0,0,		"FDIVD	%e,F0",
+0x07,	0,0,		"FDIVRD	%e,F0",
+0x08,	0,0,		"FADDD	F0,%f",
+0x09,	0,0,		"FMULD	F0,%f",
+0x0c,	0,0,		"FSUBRD	F0,%f",
+0x0d,	0,0,		"FSUBD	F0,%f",
+0x0e,	0,0,		"FDIVRD	F0,%f",
+0x0f,	0,0,		"FDIVD	F0,%f",
+};
+
+static Optable optabDD[8+8] =
+{
+0x00,	0,0,		"FMOVD	%e,F0",
+0x02,	0,0,		"FMOVD	F0,%e",
+0x03,	0,0,		"FMOVDP	F0,%e",
+0x04,	0,0,		"FRSTOR%S %e",
+0x06,	0,0,		"FSAVE%S %e",
+0x07,	0,0,		"FSTSW	%e",
+0x08,	0,0,		"FFREED	%f",
+0x0a,	0,0,		"FMOVD	%f,F0",
+0x0b,	0,0,		"FMOVDP	%f,F0",
+0x0c,	0,0,		"FUCOMD	%f,F0",
+0x0d,	0,0,		"FUCOMDP %f,F0",
+};
+
+static Optable optabDE[8+8] =
+{
+0x00,	0,0,		"FADDW	%e,F0",
+0x01,	0,0,		"FMULW	%e,F0",
+0x02,	0,0,		"FCOMW	%e,F0",
+0x03,	0,0,		"FCOMWP	%e,F0",
+0x04,	0,0,		"FSUBW	%e,F0",
+0x05,	0,0,		"FSUBRW	%e,F0",
+0x06,	0,0,		"FDIVW	%e,F0",
+0x07,	0,0,		"FDIVRW	%e,F0",
+0x08,	0,0,		"FADDDP	F0,%f",
+0x09,	0,0,		"FMULDP	F0,%f",
+0x0b,	R1,0,		"FCOMPDP",
+0x0c,	0,0,		"FSUBRDP F0,%f",
+0x0d,	0,0,		"FSUBDP	F0,%f",
+0x0e,	0,0,		"FDIVRDP F0,%f",
+0x0f,	0,0,		"FDIVDP	F0,%f",
+};
+
+static Optable optabDF[8+8] =
+{
+0x00,	0,0,		"FMOVW	%e,F0",
+0x02,	0,0,		"FMOVW	F0,%e",
+0x03,	0,0,		"FMOVWP	F0,%e",
+0x04,	0,0,		"FBLD	%e",
+0x05,	0,0,		"FMOVL	%e,F0",
+0x06,	0,0,		"FBSTP	%e",
+0x07,	0,0,		"FMOVLP	F0,%e",
+0x0c,	R0,0,		"FSTSW	%OAX",
+};
+
+static Optable optabF6[8] =
+{
+0x00,	Ib,0,		"TESTB	%i,%e",
+0x02,	0,0,		"NOTB	%e",
+0x03,	0,0,		"NEGB	%e",
+0x04,	0,0,		"MULB	AL,%e",
+0x05,	0,0,		"IMULB	AL,%e",
+0x06,	0,0,		"DIVB	AL,%e",
+0x07,	0,0,		"IDIVB	AL,%e",
+};
+
+static Optable optabF7[8] =
+{
+0x00,	Iwd,0,		"TEST%S	%i,%e",
+0x02,	0,0,		"NOT%S	%e",
+0x03,	0,0,		"NEG%S	%e",
+0x04,	0,0,		"MUL%S	%OAX,%e",
+0x05,	0,0,		"IMUL%S	%OAX,%e",
+0x06,	0,0,		"DIV%S	%OAX,%e",
+0x07,	0,0,		"IDIV%S	%OAX,%e",
+};
+
+static Optable optabFE[8] =
+{
+0x00,	0,0,		"INCB	%e",
+0x01,	0,0,		"DECB	%e",
+};
+
+static Optable optabFF[8] =
+{
+0x00,	0,0,		"INC%S	%e",
+0x01,	0,0,		"DEC%S	%e",
+0x02,	JUMP,0,		"CALL*	%e",
+0x03,	JUMP,0,		"CALLF*	%e",
+0x04,	JUMP,0,		"JMP*	%e",
+0x05,	JUMP,0,		"JMPF*	%e",
+0x06,	0,0,		"PUSHL	%e",
+};
+
+static Optable optable[256+1] =
+{
+0x00,	RMB,0,		"ADDB	%r,%e",
+0x01,	RM,0,		"ADD%S	%r,%e",
+0x02,	RMB,0,		"ADDB	%e,%r",
+0x03,	RM,0,		"ADD%S	%e,%r",
+0x04,	Ib,0,		"ADDB	%i,AL",
+0x05,	Iwd,0,		"ADD%S	%i,%OAX",
+0x06,	0,0,		"PUSHL	ES",
+0x07,	0,0,		"POPL	ES",
+0x08,	RMB,0,		"ORB	%r,%e",
+0x09,	RM,0,		"OR%S	%r,%e",
+0x0a,	RMB,0,		"ORB	%e,%r",
+0x0b,	RM,0,		"OR%S	%e,%r",
+0x0c,	Ib,0,		"ORB	%i,AL",
+0x0d,	Iwd,0,		"OR%S	%i,%OAX",
+0x0e,	0,0,		"PUSHL	CS",
+0x0f,	AUXMM,0,	optab0F,
+0x10,	RMB,0,		"ADCB	%r,%e",
+0x11,	RM,0,		"ADC%S	%r,%e",
+0x12,	RMB,0,		"ADCB	%e,%r",
+0x13,	RM,0,		"ADC%S	%e,%r",
+0x14,	Ib,0,		"ADCB	%i,AL",
+0x15,	Iwd,0,		"ADC%S	%i,%OAX",
+0x16,	0,0,		"PUSHL	SS",
+0x17,	0,0,		"POPL	SS",
+0x18,	RMB,0,		"SBBB	%r,%e",
+0x19,	RM,0,		"SBB%S	%r,%e",
+0x1a,	RMB,0,		"SBBB	%e,%r",
+0x1b,	RM,0,		"SBB%S	%e,%r",
+0x1c,	Ib,0,		"SBBB	%i,AL",
+0x1d,	Iwd,0,		"SBB%S	%i,%OAX",
+0x1e,	0,0,		"PUSHL	DS",
+0x1f,	0,0,		"POPL	DS",
+0x20,	RMB,0,		"ANDB	%r,%e",
+0x21,	RM,0,		"AND%S	%r,%e",
+0x22,	RMB,0,		"ANDB	%e,%r",
+0x23,	RM,0,		"AND%S	%e,%r",
+0x24,	Ib,0,		"ANDB	%i,AL",
+0x25,	Iwd,0,		"AND%S	%i,%OAX",
+0x26,	SEG,0,		"ES:",
+0x27,	0,0,		"DAA",
+0x28,	RMB,0,		"SUBB	%r,%e",
+0x29,	RM,0,		"SUB%S	%r,%e",
+0x2a,	RMB,0,		"SUBB	%e,%r",
+0x2b,	RM,0,		"SUB%S	%e,%r",
+0x2c,	Ib,0,		"SUBB	%i,AL",
+0x2d,	Iwd,0,		"SUB%S	%i,%OAX",
+0x2e,	SEG,0,		"CS:",
+0x2f,	0,0,		"DAS",
+0x30,	RMB,0,		"XORB	%r,%e",
+0x31,	RM,0,		"XOR%S	%r,%e",
+0x32,	RMB,0,		"XORB	%e,%r",
+0x33,	RM,0,		"XOR%S	%e,%r",
+0x34,	Ib,0,		"XORB	%i,AL",
+0x35,	Iwd,0,		"XOR%S	%i,%OAX",
+0x36,	SEG,0,		"SS:",
+0x37,	0,0,		"AAA",
+0x38,	RMB,0,		"CMPB	%r,%e",
+0x39,	RM,0,		"CMP%S	%r,%e",
+0x3a,	RMB,0,		"CMPB	%e,%r",
+0x3b,	RM,0,		"CMP%S	%e,%r",
+0x3c,	Ib,0,		"CMPB	%i,AL",
+0x3d,	Iwd,0,		"CMP%S	%i,%OAX",
+0x3e,	SEG,0,		"DS:",
+0x3f,	0,0,		"AAS",
+0x40,	0,0,		"INC%S	%OAX",
+0x41,	0,0,		"INC%S	%OCX",
+0x42,	0,0,		"INC%S	%ODX",
+0x43,	0,0,		"INC%S	%OBX",
+0x44,	0,0,		"INC%S	%OSP",
+0x45,	0,0,		"INC%S	%OBP",
+0x46,	0,0,		"INC%S	%OSI",
+0x47,	0,0,		"INC%S	%ODI",
+0x48,	0,0,		"DEC%S	%OAX",
+0x49,	0,0,		"DEC%S	%OCX",
+0x4a,	0,0,		"DEC%S	%ODX",
+0x4b,	0,0,		"DEC%S	%OBX",
+0x4c,	0,0,		"DEC%S	%OSP",
+0x4d,	0,0,		"DEC%S	%OBP",
+0x4e,	0,0,		"DEC%S	%OSI",
+0x4f,	0,0,		"DEC%S	%ODI",
+0x50,	0,0,		"PUSH%S	%OAX",
+0x51,	0,0,		"PUSH%S	%OCX",
+0x52,	0,0,		"PUSH%S	%ODX",
+0x53,	0,0,		"PUSH%S	%OBX",
+0x54,	0,0,		"PUSH%S	%OSP",
+0x55,	0,0,		"PUSH%S	%OBP",
+0x56,	0,0,		"PUSH%S	%OSI",
+0x57,	0,0,		"PUSH%S	%ODI",
+0x58,	0,0,		"POP%S	%OAX",
+0x59,	0,0,		"POP%S	%OCX",
+0x5a,	0,0,		"POP%S	%ODX",
+0x5b,	0,0,		"POP%S	%OBX",
+0x5c,	0,0,		"POP%S	%OSP",
+0x5d,	0,0,		"POP%S	%OBP",
+0x5e,	0,0,		"POP%S	%OSI",
+0x5f,	0,0,		"POP%S	%ODI",
+0x60,	0,0,		"PUSHA%S",
+0x61,	0,0,		"POPA%S",
+0x62,	RMM,0,		"BOUND	%e,%r",
+0x63,	RM,0,		"ARPL	%r,%e",
+0x64,	SEG,0,		"FS:",
+0x65,	SEG,0,		"GS:",
+0x66,	OPOVER,0,	"",
+0x67,	ADDOVER,0,	"",
+0x68,	Iwd,0,		"PUSH%S	%i",
+0x69,	RM,Iwd,		"IMUL%S	%e,%i,%r",
+0x6a,	Ib,0,		"PUSH%S	%i",
+0x6b,	RM,Ibs,		"IMUL%S	%e,%i,%r",
+0x6c,	0,0,		"INSB	DX,(%ODI)",
+0x6d,	0,0,		"INS%S	DX,(%ODI)",
+0x6e,	0,0,		"OUTSB	(%ASI),DX",
+0x6f,	0,0,		"OUTS%S	(%ASI),DX",
+0x70,	Jbs,0,		"JOS	%p",
+0x71,	Jbs,0,		"JOC	%p",
+0x72,	Jbs,0,		"JCS	%p",
+0x73,	Jbs,0,		"JCC	%p",
+0x74,	Jbs,0,		"JEQ	%p",
+0x75,	Jbs,0,		"JNE	%p",
+0x76,	Jbs,0,		"JLS	%p",
+0x77,	Jbs,0,		"JHI	%p",
+0x78,	Jbs,0,		"JMI	%p",
+0x79,	Jbs,0,		"JPL	%p",
+0x7a,	Jbs,0,		"JPS	%p",
+0x7b,	Jbs,0,		"JPC	%p",
+0x7c,	Jbs,0,		"JLT	%p",
+0x7d,	Jbs,0,		"JGE	%p",
+0x7e,	Jbs,0,		"JLE	%p",
+0x7f,	Jbs,0,		"JGT	%p",
+0x80,	RMOPB,0,	optab80,
+0x81,	RMOP,0,		optab81,
+0x83,	RMOP,0,		optab83,
+0x84,	RMB,0,		"TESTB	%r,%e",
+0x85,	RM,0,		"TEST%S	%r,%e",
+0x86,	RMB,0,		"XCHGB	%r,%e",
+0x87,	RM,0,		"XCHG%S	%r,%e",
+0x88,	RMB,0,		"MOVB	%r,%e",
+0x89,	RM,0,		"MOV%S	%r,%e",
+0x8a,	RMB,0,		"MOVB	%e,%r",
+0x8b,	RM,0,		"MOV%S	%e,%r",
+0x8c,	RM,0,		"MOVW	%g,%e",
+0x8d,	RM,0,		"LEA%S	%e,%r",
+0x8e,	RM,0,		"MOVW	%e,%g",
+0x8f,	RM,0,		"POP%S	%e",
+0x90,	0,0,		"NOP",
+0x91,	0,0,		"XCHG	%OCX,%OAX",
+0x92,	0,0,		"XCHG	%ODX,%OAX",
+0x93,	0,0,		"XCHG	%OBX,%OAX",
+0x94,	0,0,		"XCHG	%OSP,%OAX",
+0x95,	0,0,		"XCHG	%OBP,%OAX",
+0x96,	0,0,		"XCHG	%OSI,%OAX",
+0x97,	0,0,		"XCHG	%ODI,%OAX",
+0x98,	0,0,		"%W",			/* miserable CBW or CWDE */
+0x99,	0,0,		"%w",			/* idiotic CWD or CDQ */
+0x9a,	PTR,0,		"CALL%S	%d",
+0x9b,	0,0,		"WAIT",
+0x9c,	0,0,		"PUSHF",
+0x9d,	0,0,		"POPF",
+0x9e,	0,0,		"SAHF",
+0x9f,	0,0,		"LAHF",
+0xa0,	Awd,0,		"MOVB	%i,AL",
+0xa1,	Awd,0,		"MOV%S	%i,%OAX",
+0xa2,	Awd,0,		"MOVB	AL,%i",
+0xa3,	Awd,0,		"MOV%S	%OAX,%i",
+0xa4,	0,0,		"MOVSB	(%ASI),(%ADI)",
+0xa5,	0,0,		"MOVS%S	(%ASI),(%ADI)",
+0xa6,	0,0,		"CMPSB	(%ASI),(%ADI)",
+0xa7,	0,0,		"CMPS%S	(%ASI),(%ADI)",
+0xa8,	Ib,0,		"TESTB	%i,AL",
+0xa9,	Iwd,0,		"TEST%S	%i,%OAX",
+0xaa,	0,0,		"STOSB	AL,(%ADI)",
+0xab,	0,0,		"STOS%S	%OAX,(%ADI)",
+0xac,	0,0,		"LODSB	(%ASI),AL",
+0xad,	0,0,		"LODS%S	(%ASI),%OAX",
+0xae,	0,0,		"SCASB	(%ADI),AL",
+0xaf,	0,0,		"SCAS%S	(%ADI),%OAX",
+0xb0,	Ib,0,		"MOVB	%i,AL",
+0xb1,	Ib,0,		"MOVB	%i,CL",
+0xb2,	Ib,0,		"MOVB	%i,DL",
+0xb3,	Ib,0,		"MOVB	%i,BL",
+0xb4,	Ib,0,		"MOVB	%i,AH",
+0xb5,	Ib,0,		"MOVB	%i,CH",
+0xb6,	Ib,0,		"MOVB	%i,DH",
+0xb7,	Ib,0,		"MOVB	%i,BH",
+0xb8,	Iwdq,0,		"MOV%S	%i,%OAX",
+0xb9,	Iwdq,0,		"MOV%S	%i,%OCX",
+0xba,	Iwdq,0,		"MOV%S	%i,%ODX",
+0xbb,	Iwdq,0,		"MOV%S	%i,%OBX",
+0xbc,	Iwdq,0,		"MOV%S	%i,%OSP",
+0xbd,	Iwdq,0,		"MOV%S	%i,%OBP",
+0xbe,	Iwdq,0,		"MOV%S	%i,%OSI",
+0xbf,	Iwdq,0,		"MOV%S	%i,%ODI",
+0xc0,	RMOPB,0,	optabC0,
+0xc1,	RMOP,0,		optabC1,
+0xc2,	Iw,0,		"RET	%i",
+0xc3,	RET,0,		"RET",
+0xc4,	RM,0,		"LES	%e,%r",
+0xc5,	RM,0,		"LDS	%e,%r",
+0xc6,	RMB,Ib,		"MOVB	%i,%e",
+0xc7,	RM,Iwd,		"MOV%S	%i,%e",
+0xc8,	Iw2,Ib,		"ENTER	%i,%I",		/* loony ENTER */
+0xc9,	RET,0,		"LEAVE",		/* bizarre LEAVE */
+0xca,	Iw,0,		"RETF	%i",
+0xcb,	RET,0,		"RETF",
+0xcc,	0,0,		"INT	3",
+0xcd,	Ib,0,		"INTB	%i",
+0xce,	0,0,		"INTO",
+0xcf,	0,0,		"IRET",
+0xd0,	RMOPB,0,	optabD0,
+0xd1,	RMOP,0,		optabD1,
+0xd2,	RMOPB,0,	optabD2,
+0xd3,	RMOP,0,		optabD3,
+0xd4,	OA,0,		"AAM",
+0xd5,	OA,0,		"AAD",
+0xd7,	0,0,		"XLAT",
+0xd8,	FRMOP,0,	optabD8,
+0xd9,	FRMEX,0,	optabD9,
+0xda,	FRMOP,0,	optabDA,
+0xdb,	FRMEX,0,	optabDB,
+0xdc,	FRMOP,0,	optabDC,
+0xdd,	FRMOP,0,	optabDD,
+0xde,	FRMOP,0,	optabDE,
+0xdf,	FRMOP,0,	optabDF,
+0xe0,	Jbs,0,		"LOOPNE	%p",
+0xe1,	Jbs,0,		"LOOPE	%p",
+0xe2,	Jbs,0,		"LOOP	%p",
+0xe3,	Jbs,0,		"JCXZ	%p",
+0xe4,	Ib,0,		"INB	%i,AL",
+0xe5,	Ib,0,		"IN%S	%i,%OAX",
+0xe6,	Ib,0,		"OUTB	AL,%i",
+0xe7,	Ib,0,		"OUT%S	%OAX,%i",
+0xe8,	Iwds,0,		"CALL	%p",
+0xe9,	Iwds,0,		"JMP	%p",
+0xea,	PTR,0,		"JMP	%d",
+0xeb,	Jbs,0,		"JMP	%p",
+0xec,	0,0,		"INB	DX,AL",
+0xed,	0,0,		"IN%S	DX,%OAX",
+0xee,	0,0,		"OUTB	AL,DX",
+0xef,	0,0,		"OUT%S	%OAX,DX",
+0xf0,	PRE,0,		"LOCK",
+0xf2,	OPRE,0,		"REPNE",
+0xf3,	OPRE,0,		"REP",
+0xf4,	0,0,		"HLT",
+0xf5,	0,0,		"CMC",
+0xf6,	RMOPB,0,	optabF6,
+0xf7,	RMOP,0,		optabF7,
+0xf8,	0,0,		"CLC",
+0xf9,	0,0,		"STC",
+0xfa,	0,0,		"CLI",
+0xfb,	0,0,		"STI",
+0xfc,	0,0,		"CLD",
+0xfd,	0,0,		"STD",
+0xfe,	RMOPB,0,	optabFE,
+0xff,	RMOP,0,		optabFF,
+0x100,	RM,0,		"MOVLQSX	%r,%e",
+};
+
+static struct {
+	Optable	*tab;
+	uint	nel;
+} optabtab[] = {
+	optab0F00, nelem(optab0F00),
+	optab0F01, nelem(optab0F01),
+	optab0F01F8, nelem(optab0F01F8),
+	optab0FAE, nelem(optab0FAE),
+	optab0FBA, nelem(optab0FBA),
+	optab0F0F, nelem(optab0F0F),
+	optab0FC7, nelem(optab0FC7),
+	optab660F71, nelem(optab660F71),
+	optab660F72, nelem(optab660F72),
+	optab660F73, nelem(optab660F73),
+	optab660F, nelem(optab660F),
+	optabF20F, nelem(optabF20F),
+	optabF30F, nelem(optabF30F),
+	optab0F, nelem(optab0F),
+	optab80, nelem(optab80),
+	optab81, nelem(optab81),
+	optab83, nelem(optab83),
+	optabC0, nelem(optabC0),
+	optabC1, nelem(optabC1),
+	optabD0, nelem(optabD0),
+	optabD1, nelem(optabD1),
+	optabD2, nelem(optabD2),
+	optabD3, nelem(optabD3),
+	optabD8, nelem(optabD8),
+	optabD9, nelem(optabD9),
+	optabDA, nelem(optabDA),
+	optabDB, nelem(optabDB),
+	optabDC, nelem(optabDC),
+	optabDD, nelem(optabDD),
+	optabDE, nelem(optabDE),
+	optabDF, nelem(optabDF),
+	optabF6, nelem(optabF6),
+	optabF7, nelem(optabF7),
+	optabFE, nelem(optabFE),
+	optabFF, nelem(optabFF),
+	optable, nelem(optable),
+};
+
+/*
+ * compensate for Microsoft's ageing compilers
+ */
+static void
+ordertab(Optable *tab, int nel)
+{
+	int i, x;
+	static Optable empty;
+
+	for(i = nel; --i >= 0;){
+		x = tab[i].x;
+		if(x != i){
+			tab[x] = tab[i];
+			tab[i] = empty;
+		}
+	}
+}
+
+static void
+soptoms(void)
+{
+	int i;
+	static int reordered;	/* assumes non-concurrent use */
+
+	if(reordered)
+		return;
+	reordered = 1;
+	for(i = 0; i < nelem(optabtab); i++)
+		ordertab(optabtab[i].tab, optabtab[i].nel);
+}
+
+/*
+ *  get a byte of the instruction
+ */
+static int
+igetc(Map *map, Instr *ip, uchar *c)
+{
+	if(ip->n+1 > sizeof(ip->mem)){
+		werrstr("instruction too long");
+		return -1;
+	}
+	if (get1(map, ip->addr+ip->n, c, 1) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	ip->mem[ip->n++] = *c;
+	return 1;
+}
+
+/*
+ *  get two bytes of the instruction
+ */
+static int
+igets(Map *map, Instr *ip, ushort *sp)
+{
+	uchar c;
+	ushort s;
+
+	if (igetc(map, ip, &c) < 0)
+		return -1;
+	s = c;
+	if (igetc(map, ip, &c) < 0)
+		return -1;
+	s |= (c<<8);
+	*sp = s;
+	return 1;
+}
+
+/*
+ *  get 4 bytes of the instruction
+ */
+static int
+igetl(Map *map, Instr *ip, ulong *lp)
+{
+	ushort s;
+	long	l;
+
+	if (igets(map, ip, &s) < 0)
+		return -1;
+	l = s;
+	if (igets(map, ip, &s) < 0)
+		return -1;
+	l |= (s<<16);
+	*lp = l;
+	return 1;
+}
+
+/*
+ *  get 8 bytes of the instruction
+ */
+static int
+igetq(Map *map, Instr *ip, vlong *qp)
+{
+	ulong	l;
+	uvlong q;
+
+	if (igetl(map, ip, &l) < 0)
+		return -1;
+	q = l;
+	if (igetl(map, ip, &l) < 0)
+		return -1;
+	q |= ((uvlong)l<<32);
+	*qp = q;
+	return 1;
+}
+
+static int
+getdisp(Map *map, Instr *ip, int mod, int rm, int code, int pcrel)
+{
+	uchar c;
+	ushort s;
+
+	if (mod > 2)
+		return 1;
+	if (mod == 1) {
+		if (igetc(map, ip, &c) < 0)
+			return -1;
+		if (c&0x80)
+			ip->disp = c|0xffffff00;
+		else
+			ip->disp = c&0xff;
+	} else if (mod == 2 || rm == code) {
+		if (ip->asize == 'E') {
+			if (igetl(map, ip, &ip->disp) < 0)
+				return -1;
+			if (mod == 0)
+				ip->rip = pcrel;
+		} else {
+			if (igets(map, ip, &s) < 0)
+				return -1;
+			if (s&0x8000)
+				ip->disp = s|0xffff0000;
+			else
+				ip->disp = s;
+		}
+		if (mod == 0)
+			ip->base = -1;
+	}
+	return 1;
+}
+
+static int
+modrm(Map *map, Instr *ip, uchar c)
+{
+	uchar rm, mod;
+
+	mod = (c>>6)&3;
+	rm = c&7;
+	ip->mod = mod;
+	ip->base = rm;
+	ip->reg = (c>>3)&7;
+	ip->rip = 0;
+	if (mod == 3)			/* register */
+		return 1;
+	if (ip->asize == 0) {		/* 16-bit mode */
+		switch(rm) {
+		case 0:
+			ip->base = BX; ip->index = SI;
+			break;
+		case 1:
+			ip->base = BX; ip->index = DI;
+			break;
+		case 2:
+			ip->base = BP; ip->index = SI;
+			break;
+		case 3:
+			ip->base = BP; ip->index = DI;
+			break;
+		case 4:
+			ip->base = SI;
+			break;
+		case 5:
+			ip->base = DI;
+			break;
+		case 6:
+			ip->base = BP;
+			break;
+		case 7:
+			ip->base = BX;
+			break;
+		default:
+			break;
+		}
+		return getdisp(map, ip, mod, rm, 6, 0);
+	}
+	if (rm == 4) {	/* scummy sib byte */
+		if (igetc(map, ip, &c) < 0)
+			return -1;
+		ip->ss = (c>>6)&0x03;
+		ip->index = (c>>3)&0x07;
+		if (ip->index == 4)
+			ip->index = -1;
+		ip->base = c&0x07;
+		return getdisp(map, ip, mod, ip->base, 5, 0);
+	}
+	return getdisp(map, ip, mod, rm, 5, ip->amd64);
+}
+
+static Optable *
+mkinstr(Map *map, Instr *ip, uvlong pc)
+{
+	int i, n, norex;
+	uchar c;
+	ushort s;
+	Optable *op, *obase;
+	char buf[128];
+
+	soptoms();
+	memset(ip, 0, sizeof(*ip));
+	norex = 1;
+	ip->base = -1;
+	ip->index = -1;
+	if(asstype == AI8086)
+		ip->osize = 'W';
+	else {
+		ip->osize = 'L';
+		ip->asize = 'E';
+		ip->amd64 = asstype != AI386;
+		norex = 0;
+	}
+	ip->addr = pc;
+	if (igetc(map, ip, &c) < 0)
+		return 0;
+	obase = optable;
+newop:
+	if(ip->amd64 && !norex){
+		if(c >= 0x40 && c <= 0x4f) {
+			ip->rex = c;
+			if(igetc(map, ip, &c) < 0)
+				return 0;
+		}
+		if(c == 0x63){
+			op = &obase[0x100];	/* MOVLQSX */
+			goto hack;
+		}
+	}
+	op = &obase[c];
+hack:
+	if (op->proto == 0) {
+badop:
+		n = snprint(buf, sizeof(buf), "opcode: ??");
+		for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
+			_hexify(buf+n, ip->mem[i], 1);
+		strcpy(buf+n, "??");
+		werrstr(buf);
+		return 0;
+	}
+	for(i = 0; i < 2 && op->operand[i]; i++) {
+		switch(op->operand[i]) {
+		case Ib:	/* 8-bit immediate - (no sign extension)*/
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			ip->imm = c&0xff;
+			ip->imm64 = ip->imm;
+			break;
+		case Jbs:	/* 8-bit jump immediate (sign extended) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c&0x80)
+				ip->imm = c|0xffffff00;
+			else
+				ip->imm = c&0xff;
+			ip->imm64 = (long)ip->imm;
+			ip->jumptype = Jbs;
+			break;
+		case Ibs:	/* 8-bit immediate (sign extended) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c&0x80)
+				if (ip->osize == 'L')
+					ip->imm = c|0xffffff00;
+				else
+					ip->imm = c|0xff00;
+			else
+				ip->imm = c&0xff;
+			ip->imm64 = (long)ip->imm;
+			break;
+		case Iw:	/* 16-bit immediate -> imm */
+			if (igets(map, ip, &s) < 0)
+				return 0;
+			ip->imm = s&0xffff;
+			ip->imm64 = ip->imm;
+			ip->jumptype = Iw;
+			break;
+		case Iw2:	/* 16-bit immediate -> in imm2*/
+			if (igets(map, ip, &s) < 0)
+				return 0;
+			ip->imm2 = s&0xffff;
+			break;
+		case Iwd:	/* Operand-sized immediate (no sign extension unless 64 bits)*/
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				ip->imm64 = ip->imm;
+				if(ip->rex&REXW && (ip->imm & (1<<31)) != 0)
+					ip->imm64 |= (vlong)~0 << 32;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+				ip->imm64 = ip->imm;
+			}
+			break;
+		case Iwdq:	/* Operand-sized immediate, possibly big */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				ip->imm64 = ip->imm;
+				if (ip->rex & REXW) {
+					ulong l;
+					if (igetl(map, ip, &l) < 0)
+						return 0;
+					ip->imm64 |= (uvlong)l << 32;
+				}
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+			}
+			break;
+		case Awd:	/* Address-sized immediate (no sign extension)*/
+			if (ip->asize == 'E') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				/* TO DO: REX */
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+			}
+			break;
+		case Iwds:	/* Operand-sized immediate (sign extended) */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				if (s&0x8000)
+					ip->imm = s|0xffff0000;
+				else
+					ip->imm = s&0xffff;
+			}
+			ip->jumptype = Iwds;
+			break;
+		case OA:	/* literal 0x0a byte */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c != 0x0a)
+				goto badop;
+			break;
+		case R0:	/* base register must be R0 */
+			if (ip->base != 0)
+				goto badop;
+			break;
+		case R1:	/* base register must be R1 */
+			if (ip->base != 1)
+				goto badop;
+			break;
+		case RMB:	/* R/M field with byte register (/r)*/
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			ip->osize = 'B';
+			break;
+		case RM:	/* R/M field with register (/r) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case RMOPB:	/* R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			c = ip->reg;		/* secondary op code */
+			obase = (Optable*)op->proto;
+			ip->osize = 'B';
+			goto newop;
+		case RMOP:	/* R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			obase = (Optable*)op->proto;
+			if(ip->amd64 && obase == optab0F01 && c == 0xF8)
+				return optab0F01F8;
+			c = ip->reg;
+			goto newop;
+		case FRMOP:	/* FP R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0)
+				c = ip->reg+8;		/* 16 entry table */
+			else
+				c = ip->reg;
+			obase = (Optable*)op->proto;
+			goto newop;
+		case FRMEX:	/* Extended FP R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0)
+				c = (c&0x3f)+8;		/* 64-entry table */
+			else
+				c = ip->reg;
+			obase = (Optable*)op->proto;
+			goto newop;
+		case RMR:	/* R/M register only (mod = 11) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if ((c&0xc0) != 0xc0) {
+				werrstr("invalid R/M register: %x", c);
+				return 0;
+			}
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case RMM:	/* R/M register only (mod = 11) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0) {
+				werrstr("invalid R/M memory mode: %x", c);
+				return 0;
+			}
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case PTR:	/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->disp) < 0)
+					return 0;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->disp = s&0xffff;
+			}
+			if (igets(map, ip, (ushort*)&ip->seg) < 0)
+				return 0;
+			ip->jumptype = PTR;
+			break;
+		case AUXMM:	/* Multi-byte op code; prefix determines table selection */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			obase = (Optable*)op->proto;
+			switch (ip->opre) {
+			case 0x66:	op = optab660F; break;
+			case 0xF2:	op = optabF20F; break;
+			case 0xF3:	op = optabF30F; break;
+			default:	op = nil; break;
+			}
+			if(op != nil && op[c].proto != nil)
+				obase = op;
+			norex = 1;	/* no more rex prefixes */
+			/* otherwise the optab entry captures it */
+			goto newop;
+		case AUX:	/* Multi-byte op code - Auxiliary table */
+			obase = (Optable*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case OPRE:	/* Instr Prefix or media op */
+			ip->opre = c;
+			/* fall through */
+		case PRE:	/* Instr Prefix */
+			ip->prefix = (char*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (ip->opre && c == 0x0F)
+				ip->prefix = 0;
+			goto newop;
+		case SEG:	/* Segment Prefix */
+			ip->segment = (char*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case OPOVER:	/* Operand size override */
+			ip->opre = c;
+			ip->osize = 'W';
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c == 0x0F)
+				ip->osize = 'L';
+			else if (ip->amd64 && (c&0xF0) == 0x40)
+				ip->osize = 'Q';
+			goto newop;
+		case ADDOVER:	/* Address size override */
+			ip->asize = 0;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case JUMP:	/* mark instruction as JUMP or RET */
+		case RET:
+			ip->jumptype = op->operand[i];
+			break;
+		default:
+			werrstr("bad operand type %d", op->operand[i]);
+			return 0;
+		}
+	}
+	return op;
+}
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *ip, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
+	va_end(arg);
+}
+
+/*
+ *  if we want to call 16 bit regs AX,BX,CX,...
+ *  and 32 bit regs EAX,EBX,ECX,... then
+ *  change the defs of ANAME and ONAME to:
+ *  #define	ANAME(ip)	((ip->asize == 'E' ? "E" : "")
+ *  #define	ONAME(ip)	((ip)->osize == 'L' ? "E" : "")
+ */
+#define	ANAME(ip)	""
+#define	ONAME(ip)	""
+
+static char *reg[] =  {
+	"AX",
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+
+	/* amd64 */
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+};
+
+static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
+static char *breg64[] = { "AL", "CL", "DL", "BL", "SPB", "BPB", "SIB", "DIB",
+	"R8B", "R9B", "R10B", "R11B", "R12B", "R13B", "R14B", "R15B" };
+static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+static void
+plocal(Instr *ip)
+{
+	int ret;
+	long offset;
+	Symbol s;
+	char *reg;
+
+	offset = ip->disp;
+	if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
+		bprint(ip, "%lux(SP)", offset);
+		return;
+	}
+
+	if (s.value > ip->disp) {
+		ret = getauto(&s, s.value-ip->disp-mach->szaddr, CAUTO, &s);
+		reg = "(SP)";
+	} else {
+		offset -= s.value;
+		ret = getauto(&s, offset, CPARAM, &s);
+		reg = "(FP)";
+	}
+	if (ret)
+		bprint(ip, "%s+", s.name);
+	else
+		offset = ip->disp;
+	bprint(ip, "%lux%s", offset, reg);
+}
+
+static int
+isjmp(Instr *ip)
+{
+	switch(ip->jumptype){
+	case Iwds:
+	case Jbs:
+	case JUMP:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * This is too smart for its own good, but it really is nice
+ * to have accurate translations when debugging, and it
+ * helps us identify which code is different in binaries that
+ * are changed on sources.
+ */
+static int
+issymref(Instr *ip, Symbol *s, long w, long val)
+{
+	Symbol next, tmp;
+	long isstring, size;
+
+	if (isjmp(ip))
+		return 1;
+	if (s->class==CTEXT && w==0)
+		return 1;
+	if (s->class==CDATA) {
+		/* use first bss symbol (or "end") rather than edata */
+		if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
+			if((s ->index >= 0 && globalsym(&tmp, s->index+1) && tmp.value==s->value)
+			|| (s->index > 0 && globalsym(&tmp, s->index-1) && tmp.value==s->value))
+				*s = tmp;
+		}
+		if (w == 0)
+			return 1;
+		for (next=*s; next.value==s->value; next=tmp)
+			if (!globalsym(&tmp, next.index+1))
+				break;
+		size = next.value - s->value;
+		if (w >= size)
+			return 0;
+		if (w > size-w)
+			w = size-w;
+		/* huge distances are usually wrong except in .string */
+		isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
+		if (w > 8192 && !isstring)
+			return 0;
+		/* medium distances are tricky - look for constants */
+		/* near powers of two */
+		if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
+			return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+immediate(Instr *ip, vlong val)
+{
+	Symbol s;
+	long w;
+
+	if (findsym(val, CANY, &s)) {		/* TO DO */
+		w = val - s.value;
+		if (w < 0)
+			w = -w;
+		if (issymref(ip, &s, w, val)) {
+			if (w)
+				bprint(ip, "%s+%lux(SB)", s.name, w);
+			else
+				bprint(ip, "%s(SB)", s.name);
+			return;
+		}
+/*
+		if (s.class==CDATA && globalsym(&s, s.index+1)) {
+			w = s.value - val;
+			if (w < 0)
+				w = -w;
+			if (w < 4096) {
+				bprint(ip, "%s-%lux(SB)", s.name, w);
+				return;
+			}
+		}
+*/
+	}
+	if((ip->rex & REXW) == 0)
+		bprint(ip, "%lux", (long)val);
+	else
+		bprint(ip, "%llux", val);
+}
+
+static void
+pea(Instr *ip)
+{
+	if (ip->mod == 3) {
+		if (ip->osize == 'B')
+			bprint(ip, (ip->rex & REXB? breg64: breg)[ip->base]);
+		else if(ip->rex & REXB)
+			bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
+		else
+			bprint(ip, "%s%s", ANAME(ip), reg[ip->base]);
+		return;
+	}
+	if (ip->segment)
+		bprint(ip, ip->segment);
+	if (ip->asize == 'E' && ip->base == SP)
+		plocal(ip);
+	else {
+		if (ip->base < 0)
+			immediate(ip, ip->disp);
+		else {
+			bprint(ip, "%lux", ip->disp);
+			if(ip->rip)
+				bprint(ip, "(RIP)");
+			bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]);
+		}
+	}
+	if (ip->index >= 0)
+		bprint(ip,"(%s%s*%d)", ANAME(ip), reg[ip->rex&REXX? ip->index+8: ip->index], 1<<ip->ss);
+}
+
+static void
+prinstr(Instr *ip, char *fmt)
+{
+	vlong v;
+
+	if (ip->prefix)
+		bprint(ip, "%s ", ip->prefix);
+	for (; *fmt && ip->curr < ip->end; fmt++) {
+		if (*fmt != '%'){
+			*ip->curr++ = *fmt;
+			continue;
+		}
+		switch(*++fmt){
+		case '%':
+			*ip->curr++ = '%';
+			break;
+		case 'A':
+			bprint(ip, "%s", ANAME(ip));
+			break;
+		case 'C':
+			bprint(ip, "CR%d", ip->reg);
+			break;
+		case 'D':
+			if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
+				bprint(ip, "DR%d",ip->reg);
+			else
+				bprint(ip, "?");
+			break;
+		case 'I':
+			bprint(ip, "$");
+			immediate(ip, ip->imm2);
+			break;
+		case 'O':
+			bprint(ip,"%s", ONAME(ip));
+			break;
+		case 'i':
+			bprint(ip, "$");
+			v = ip->imm;
+			if(ip->rex & REXW)
+				v = ip->imm64;
+			immediate(ip, v);
+			break;
+		case 'R':
+			bprint(ip, "%s%s", ONAME(ip), reg[ip->rex&REXR? ip->reg+8: ip->reg]);
+			break;
+		case 'S':
+			if(ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
+				bprint(ip, "Q");
+			else
+				bprint(ip, "%c", ip->osize);
+			break;
+		case 's':
+			if(ip->opre == 0 || ip->opre == 0x66)
+				bprint(ip, "P");
+			else
+				bprint(ip, "S");
+			if(ip->opre == 0xf2 || ip->opre == 0x66)
+				bprint(ip, "D");
+			else
+				bprint(ip, "S");
+			break;
+		case 'T':
+			if (ip->reg == 6 || ip->reg == 7)
+				bprint(ip, "TR%d",ip->reg);
+			else
+				bprint(ip, "?");
+			break;
+		case 'W':
+			if (ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
+				bprint(ip, "CDQE");
+			else if (ip->osize == 'L')
+				bprint(ip,"CWDE");
+			else
+				bprint(ip, "CBW");
+			break;
+		case 'd':
+			bprint(ip,"%ux:%lux",ip->seg,ip->disp);
+			break;
+		case 'm':
+			if (ip->mod == 3 && ip->osize != 'B') {
+				if(fmt[1] != '*'){
+					if(ip->opre != 0) {
+						bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
+						break;
+					}
+				} else
+					fmt++;
+				bprint(ip, "M%d", ip->base);
+				break;
+			}
+			pea(ip);
+			break;
+		case 'e':
+			pea(ip);
+			break;
+		case 'f':
+			bprint(ip, "F%d", ip->base);
+			break;
+		case 'g':
+			if (ip->reg < 6)
+				bprint(ip,"%s",sreg[ip->reg]);
+			else
+				bprint(ip,"?");
+			break;
+		case 'p':
+			/*
+			 * signed immediate in the ulong ip->imm.
+			 */
+			v = (long)ip->imm;
+			immediate(ip, v+ip->addr+ip->n);
+			break;
+		case 'r':
+			if (ip->osize == 'B')
+				bprint(ip,"%s", (ip->rex? breg64: breg)[ip->rex&REXR? ip->reg+8: ip->reg]);
+			else
+				bprint(ip, reg[ip->rex&REXR? ip->reg+8: ip->reg]);
+			break;
+		case 'w':
+			if (ip->osize == 'Q' || ip->rex & REXW)
+				bprint(ip, "CQO");
+			else if (ip->osize == 'L')
+				bprint(ip,"CDQ");
+			else
+				bprint(ip, "CWD");
+			break;
+		case 'M':
+			if(ip->opre != 0)
+				bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
+			else
+				bprint(ip, "M%d", ip->reg);
+			break;
+		case 'x':
+			if (ip->mod == 3 && ip->osize != 'B') {
+				bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
+				break;
+			}
+			pea(ip);
+			break;
+		case 'X':
+			bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
+			break;
+		default:
+			bprint(ip, "%%%c", *fmt);
+			break;
+		}
+	}
+	*ip->curr = 0;		/* there's always room for 1 byte */
+}
+
+static int
+i386inst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	Instr instr;
+	Optable *op;
+
+	USED(modifier);
+	op = mkinstr(map, &instr, pc);
+	if (op == 0) {
+		errstr(buf, n);
+		return -1;
+	}
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	prinstr(&instr, op->proto);
+	return instr.n;
+}
+
+static int
+i386das(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr instr;
+	int i;
+
+	if (mkinstr(map, &instr, pc) == 0) {
+		errstr(buf, n);
+		return -1;
+	}
+	for(i = 0; i < instr.n && n > 2; i++) {
+		_hexify(buf, instr.mem[i], 1);
+		buf += 2;
+		n -= 2;
+	}
+	*buf = 0;
+	return instr.n;
+}
+
+static int
+i386instlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	if (mkinstr(map, &i, pc))
+		return i.n;
+	return -1;
+}
+
+static int
+i386foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	Instr i;
+	Optable *op;
+	ushort s;
+	uvlong l, addr;
+	vlong v;
+	int n;
+
+	op = mkinstr(map, &i, pc);
+	if (!op)
+		return -1;
+
+	n = 0;
+
+	switch(i.jumptype) {
+	case RET:		/* RETURN or LEAVE */
+	case Iw:		/* RETURN */
+		if (strcmp(op->proto, "LEAVE") == 0) {
+			if (geta(map, (*rget)(map, "BP"), &l) < 0)
+				return -1;
+		} else if (geta(map, (*rget)(map, mach->sp), &l) < 0)
+			return -1;
+		foll[0] = l;
+		return 1;
+	case Iwds:		/* pc relative JUMP or CALL*/
+	case Jbs:		/* pc relative JUMP or CALL */
+		v = (long)i.imm;
+		foll[0] = pc+v+i.n;
+		n = 1;
+		break;
+	case PTR:		/* seg:displacement JUMP or CALL */
+		foll[0] = (i.seg<<4)+i.disp;
+		return 1;
+	case JUMP:		/* JUMP or CALL EA */
+
+		if(i.mod == 3) {
+			foll[0] = (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]);
+			return 1;
+		}
+			/* calculate the effective address */
+		addr = i.disp;
+		if (i.base >= 0) {
+			if (geta(map, (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]), &l) < 0)
+				return -1;
+			addr += l;
+		}
+		if (i.index >= 0) {
+			if (geta(map, (*rget)(map, reg[i.rex&REXX? i.index+8: i.index]), &l) < 0)
+				return -1;
+			addr += l*(1<<i.ss);
+		}
+			/* now retrieve a seg:disp value at that address */
+		if (get2(map, addr, &s) < 0)			/* seg */
+			return -1;
+		foll[0] = s<<4;
+		addr += 2;
+		if (i.asize == 'L') {
+			if (geta(map, addr, &l) < 0)		/* disp32 */
+				return -1;
+			foll[0] += l;
+		} else {					/* disp16 */
+			if (get2(map, addr, &s) < 0)
+				return -1;
+			foll[0] += s;
+		}
+		return 1;
+	default:
+		break;
+	}		
+	if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
+		return 1;
+	foll[n++] = pc+i.n;
+	return n;
+}
--- /dev/null
+++ b/utils/libmach/8obj.c
@@ -1,0 +1,137 @@
+/*
+ * 8obj.c - identify and parse a 386 object file
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "8c/8.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	sym;
+	char	flags;
+};
+static	Addr	addr(Biobuf*);
+static	char	type2char(int);
+static	void	skip(Biobuf*, int);
+
+int
+_is8(char *t)
+{
+	uchar *s = (uchar*)t;
+
+	return  s[0] == (ANAME&0xff)			/* aslo = ANAME */
+		&& s[1] == ((ANAME>>8)&0xff)
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_read8(Biobuf *bp, Prog* p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);		/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(!(a.flags & T_SYM))
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	int t;
+	long off;
+
+	off = 0;
+	a.sym = -1;
+	a.flags = Bgetc(bp);			/* flags */
+	if(a.flags & T_INDEX)
+		skip(bp, 2);
+	if(a.flags & T_OFFSET){
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+	}
+	if(a.flags & T_SYM)
+		a.sym = Bgetc(bp);
+	if(a.flags & T_FCONST)
+		skip(bp, 8);
+	else
+	if(a.flags & T_SCONST)
+		skip(bp, NSNAME);
+	if(a.flags & T_TYPE) {
+		t = Bgetc(bp);
+		if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
+			_offset(a.sym, off);
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/9.c
@@ -1,0 +1,120 @@
+/*
+ * PowerPC 64 definition
+ *	forsyth@vitanuova.com
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "ureg9.h"
+#include "mach.h"
+
+
+#define	REGOFF(x)	offsetof(struct Ureg, x)
+
+#define R31		REGOFF(r31)
+#define FP_REG(x)	(R31+4+8*(x))
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	(8*33)	
+
+Reglist power64reglist[] = {
+	{"CAUSE",	REGOFF(cause),	RINT|RRDONLY,	'Y'},
+	{"TRAP",	REGOFF(cause),	RINT|RRDONLY,	'Y'},	/* alias for acid */
+	{"MSR",	REGOFF(msr),	RINT|RRDONLY,	'Y'},
+	{"PC",		REGOFF(pc),	RINT,		'Y'},
+	{"LR",		REGOFF(lr),	RINT,		'Y'},
+	{"CR",		REGOFF(cr),	RINT,		'X'},
+	{"XER",		REGOFF(xer),	RINT,		'Y'},
+	{"CTR",		REGOFF(ctr),	RINT,		'Y'},
+	{"PC",		REGOFF(pc),		RINT,		'Y'},
+	{"SP",		REGOFF(sp),		RINT,		'Y'},
+	{"R0",		REGOFF(r0),	RINT,		'Y'},
+	/* R1 is SP */
+	{"R2",		REGOFF(r2),	RINT,		'Y'},
+	{"R3",		REGOFF(r3),	RINT,		'Y'},
+	{"R4",		REGOFF(r4),	RINT,		'Y'},
+	{"R5",		REGOFF(r5),	RINT,		'Y'},
+	{"R6",		REGOFF(r6),	RINT,		'Y'},
+	{"R7",		REGOFF(r7),	RINT,		'Y'},
+	{"R8",		REGOFF(r8),	RINT,		'Y'},
+	{"R9",		REGOFF(r9),	RINT,		'Y'},
+	{"R10",		REGOFF(r10),	RINT,		'Y'},
+	{"R11",		REGOFF(r11),	RINT,		'Y'},
+	{"R12",		REGOFF(r12),	RINT,		'Y'},
+	{"R13",		REGOFF(r13),	RINT,		'Y'},
+	{"R14",		REGOFF(r14),	RINT,		'Y'},
+	{"R15",		REGOFF(r15),	RINT,		'Y'},
+	{"R16",		REGOFF(r16),	RINT,		'Y'},
+	{"R17",		REGOFF(r17),	RINT,		'Y'},
+	{"R18",		REGOFF(r18),	RINT,		'Y'},
+	{"R19",		REGOFF(r19),	RINT,		'Y'},
+	{"R20",		REGOFF(r20),	RINT,		'Y'},
+	{"R21",		REGOFF(r21),	RINT,		'Y'},
+	{"R22",		REGOFF(r22),	RINT,		'Y'},
+	{"R23",		REGOFF(r23),	RINT,		'Y'},
+	{"R24",		REGOFF(r24),	RINT,		'Y'},
+	{"R25",		REGOFF(r25),	RINT,		'Y'},
+	{"R26",		REGOFF(r26),	RINT,		'Y'},
+	{"R27",		REGOFF(r27),	RINT,		'Y'},
+	{"R28",		REGOFF(r28),	RINT,		'Y'},
+	{"R29",		REGOFF(r29),	RINT,		'Y'},
+	{"R30",		REGOFF(r30),	RINT,		'Y'},
+	{"R31",		REGOFF(r31),	RINT,		'Y'},
+	{"F0",		FP_REG(0),	RFLT,		'F'},
+	{"F1",		FP_REG(1),	RFLT,		'F'},
+	{"F2",		FP_REG(2),	RFLT,		'F'},
+	{"F3",		FP_REG(3),	RFLT,		'F'},
+	{"F4",		FP_REG(4),	RFLT,		'F'},
+	{"F5",		FP_REG(5),	RFLT,		'F'},
+	{"F6",		FP_REG(6),	RFLT,		'F'},
+	{"F7",		FP_REG(7),	RFLT,		'F'},
+	{"F8",		FP_REG(8),	RFLT,		'F'},
+	{"F9",		FP_REG(9),	RFLT,		'F'},
+	{"F10",		FP_REG(10),	RFLT,		'F'},
+	{"F11",		FP_REG(11),	RFLT,		'F'},
+	{"F12",		FP_REG(12),	RFLT,		'F'},
+	{"F13",		FP_REG(13),	RFLT,		'F'},
+	{"F14",		FP_REG(14),	RFLT,		'F'},
+	{"F15",		FP_REG(15),	RFLT,		'F'},
+	{"F16",		FP_REG(16),	RFLT,		'F'},
+	{"F17",		FP_REG(17),	RFLT,		'F'},
+	{"F18",		FP_REG(18),	RFLT,		'F'},
+	{"F19",		FP_REG(19),	RFLT,		'F'},
+	{"F20",		FP_REG(20),	RFLT,		'F'},
+	{"F21",		FP_REG(21),	RFLT,		'F'},
+	{"F22",		FP_REG(22),	RFLT,		'F'},
+	{"F23",		FP_REG(23),	RFLT,		'F'},
+	{"F24",		FP_REG(24),	RFLT,		'F'},
+	{"F25",		FP_REG(25),	RFLT,		'F'},
+	{"F26",		FP_REG(26),	RFLT,		'F'},
+	{"F27",		FP_REG(27),	RFLT,		'F'},
+	{"F28",		FP_REG(28),	RFLT,		'F'},
+	{"F29",		FP_REG(29),	RFLT,		'F'},
+	{"F30",		FP_REG(30),	RFLT,		'F'},
+	{"F31",		FP_REG(31),	RFLT,		'F'},
+	{"FPSCR",	FP_REG(32)+4,	RFLT,		'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mpower64 =
+{
+	"power64",
+	MPOWER64,		/* machine type */
+	power64reglist,	/* register set */
+	REGSIZE,	/* number of bytes in register set */
+	FPREGSIZE,	/* number of bytes in FP register set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"LR",		/* name of link register */
+	"setSB",	/* static base register name */
+	0,		/* value */
+	0x1000,		/* page size */
+	0x80000000U,	/* kernel base */
+	0,		/* kernel text mask */
+	0x7FFFFFFFU,	/* user stack top */
+	4,		/* quantization of pc */
+	8,		/* szaddr */
+	8,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/9obj.c
@@ -1,0 +1,155 @@
+/*
+ * 9obj.c - identify and parse a PowerPC-64 object file
+ *	forsyth@terzarima.net
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "9c/9.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_is9(char *s)
+{
+	return  (s[0]&0377) == ANAME				/* ANAME */
+		&& (s[1]&0377) == ANAME>>8
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_read9(Biobuf *bp, Prog *p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);			/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = beswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	n = Bgetc(bp);	/* reg and flag */
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	if(n & 0x40)
+		addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	vlong off;
+	long l;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE: case D_REG: case D_FREG: case D_CREG:
+	case D_FPSCR: case D_MSR:
+		break;
+	case D_SPR:
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+	case D_DCONST:
+	case D_DCR:
+		l = Bgetc(bp);
+		l |= Bgetc(bp) << 8;
+		l |= Bgetc(bp) << 16;
+		l |= Bgetc(bp) << 24;
+		off = l;
+		if(a.type == D_DCONST){
+			l = Bgetc(bp);
+			l |= Bgetc(bp) << 8;
+			l |= Bgetc(bp) << 16;
+			l |= Bgetc(bp) << 24;
+			off = ((vlong)l << 32) | (off & 0xFFFFFFFF);
+			a.type = D_CONST;	/* perhaps */
+		}
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/NOTICE
@@ -1,0 +1,31 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.
+	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+	Portions Copyright © 1997-1999 Vita Nuova Limited.
+	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/libmach/access.c
@@ -1,0 +1,275 @@
+/*
+ * functions to read and write an executable or file image
+ */
+
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+static	int	mget(Map*, uvlong, void*, int);
+static	int	mput(Map*, uvlong, void*, int);
+static	struct	segment*	reloc(Map*, uvlong, vlong*);
+
+/*
+ * routines to get/put various types
+ */
+int
+geta(Map *map, uvlong addr, uvlong *x)
+{
+	ulong l;
+	uvlong vl;
+
+	if (mach->szaddr == 8){
+		if (get8(map, addr, &vl) < 0)
+			return -1;
+		*x = vl;
+		return 1;
+	}
+
+	if (get4(map, addr, &l) < 0)
+		return -1;
+	*x = l;
+
+	return 1;
+}
+
+int
+get8(Map *map, uvlong addr, uvlong *x)
+{
+	if (!map) {
+		werrstr("get8: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 8) < 0)
+		return -1;
+	*x = machdata->swav(*x);
+	return 1;
+}
+
+int
+get4(Map *map, uvlong addr, ulong *x)
+{
+	if (!map) {
+		werrstr("get4: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 4) < 0)
+		return -1;
+	*x = machdata->swal(*x);
+	return 1;
+}
+
+int
+get2(Map *map, uvlong addr, ushort *x)
+{
+	if (!map) {
+		werrstr("get2: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 2) < 0)
+		return -1;
+	*x = machdata->swab(*x);
+	return 1;
+}
+
+int
+get1(Map *map, uvlong addr, uchar *x, int size)
+{
+	uchar *cp;
+
+	if (!map) {
+		werrstr("get1: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		cp = (uchar*)&addr;
+		while (cp < (uchar*)(&addr+1) && size-- > 0)
+			*x++ = *cp++;
+		while (size-- > 0)
+			*x++ = 0;
+	} else
+		return mget(map, addr, x, size);
+	return 1;
+}
+
+int
+puta(Map *map, uvlong addr, uvlong v)
+{
+	if (mach->szaddr == 8)
+		return put8(map, addr, v);
+
+	return put4(map, addr, v);
+}
+
+int
+put8(Map *map, uvlong addr, uvlong v)
+{
+	if (!map) {
+		werrstr("put8: invalid map");
+		return -1;
+	}
+	v = machdata->swav(v);
+	return mput(map, addr, &v, 8);
+}
+
+int
+put4(Map *map, uvlong addr, ulong v)
+{
+	if (!map) {
+		werrstr("put4: invalid map");
+		return -1;
+	}
+	v = machdata->swal(v);
+	return mput(map, addr, &v, 4);
+}
+
+int
+put2(Map *map, uvlong addr, ushort v)
+{
+	if (!map) {
+		werrstr("put2: invalid map");
+		return -1;
+	}
+	v = machdata->swab(v);
+	return mput(map, addr, &v, 2);
+}
+
+int
+put1(Map *map, uvlong addr, uchar *v, int size)
+{
+	if (!map) {
+		werrstr("put1: invalid map");
+		return -1;
+	}
+	return mput(map, addr, v, size);
+}
+
+static int
+spread(struct segment *s, void *buf, int n, uvlong off)
+{
+	uvlong base;
+
+	static struct {
+		struct segment *s;
+		char a[8192];
+		uvlong off;
+	} cache;
+
+	if(s->cache){
+		base = off&~(sizeof cache.a-1);
+		if(cache.s != s || cache.off != base){
+			cache.off = ~0;
+			if(seek(s->fd, base, 0) >= 0
+			&& readn(s->fd, cache.a, sizeof cache.a) == sizeof cache.a){
+				cache.s = s;
+				cache.off = base;
+			}
+		}
+		if(cache.s == s && cache.off == base){
+			off &= sizeof cache.a-1;
+			if(off+n > sizeof cache.a)
+				n = sizeof cache.a - off;
+			memmove(buf, cache.a+off, n);
+			return n;
+		}
+	}
+
+	return pread(s->fd, buf, n, off);
+}
+
+static int
+mget(Map *map, uvlong addr, void *buf, int size)
+{
+	uvlong off;
+	int i, j, k;
+	struct segment *s;
+
+	s = reloc(map, addr, (vlong*)&off);
+	if (!s)
+		return -1;
+	if (s->fd < 0) {
+		werrstr("unreadable map");
+		return -1;
+	}
+	if (s->mget)
+		return s->mget(s, addr, off, buf, size);
+	for (i = j = 0; i < 2; i++) {	/* in case read crosses page */
+		k = spread(s, (void*)((uchar *)buf+j), size-j, off+j);
+		if (k < 0) {
+			werrstr("can't read address %llux: %r", addr);
+			return -1;
+		}
+		j += k;
+		if (j == size)
+			return j;
+	}
+	werrstr("partial read at address %llux (size %d j %d)", addr, size, j);
+	return -1;
+}
+
+static int
+mput(Map *map, uvlong addr, void *buf, int size)
+{
+	vlong off;
+	int i, j, k;
+	struct segment *s;
+
+	s = reloc(map, addr, &off);
+	if (!s)
+		return -1;
+	if (s->fd < 0) {
+		werrstr("unwritable map");
+		return -1;
+	}
+	if (s->mput)
+		return s->mput(s, addr, off, buf, size);
+
+	seek(s->fd, off, 0);
+	for (i = j = 0; i < 2; i++) {	/* in case read crosses page */
+		k = write(s->fd, buf, size-j);
+		if (k < 0) {
+			werrstr("can't write address %llux: %r", addr);
+			return -1;
+		}
+		j += k;
+		if (j == size)
+			return j;
+	}
+	werrstr("partial write at address %llux", addr);
+	return -1;
+}
+
+/*
+ *	convert address to file offset; returns nonzero if ok
+ */
+static struct segment*
+reloc(Map *map, uvlong addr, vlong *offp)
+{
+	int i;
+
+	for (i = 0; i < map->nsegs; i++) {
+		if (map->seg[i].inuse)
+		if (map->seg[i].b <= addr && addr < map->seg[i].e) {
+			*offp = addr + map->seg[i].f - map->seg[i].b;
+			return &map->seg[i];
+		}
+	}
+	werrstr("can't translate address %llux", addr);
+	return 0;
+}
--- /dev/null
+++ b/utils/libmach/bootexec.h
@@ -1,0 +1,136 @@
+struct coffsect
+{
+	char	name[8];
+	ulong	phys;
+	ulong	virt;
+	ulong	size;
+	ulong	fptr;
+	ulong	fptrreloc;
+	ulong	fptrlineno;
+	ulong	nrelocnlineno;
+	ulong	flags;
+};
+
+/*
+ * proprietary exec headers, needed to bootstrap various machines
+ */
+struct mipsexec
+{
+	short	mmagic;		/* (0x160) mips magic number */
+	short	nscns;		/* (unused) number of sections */
+	long	timdat;		/* (unused) time & date stamp */
+	long	symptr;		/* offset to symbol table */
+	long	nsyms;		/* size of symbol table */
+	short	opthdr;		/* (0x38) sizeof(optional hdr) */
+	short	pcszs;		/* flags */
+	short	amagic;		/* see above */
+	short	vstamp;		/* version stamp */
+	long	tsize;		/* text size in bytes */
+	long	dsize;		/* initialized data */
+	long	bsize;		/* uninitialized data */
+	long	mentry;		/* entry pt.				*/
+	long	text_start;	/* base of text used for this file	*/
+	long	data_start;	/* base of data used for this file	*/
+	long	bss_start;	/* base of bss used for this file	*/
+	long	gprmask;	/* general purpose register mask	*/
+	long	cprmask[4];	/* co-processor register masks		*/
+	long	gp_value;	/* the gp value used for this object    */
+};
+#define	pcsize	cprmask[0]
+
+struct mips4kexec
+{
+	struct mipsexec	h;
+	struct coffsect	itexts;
+	struct coffsect idatas;
+	struct coffsect ibsss;
+};
+
+struct sparcexec
+{
+	short	sjunk;		/* dynamic bit and version number */
+	short	smagic;		/* 0407 */
+	ulong	stext;
+	ulong	sdata;
+	ulong	sbss;
+	ulong	ssyms;
+	ulong	sentry;
+	ulong	strsize;
+	ulong	sdrsize;
+};
+
+struct nextexec
+{
+	struct	nexthdr{
+		ulong	nmagic;
+		ulong	ncputype;
+		ulong	ncpusubtype;
+		ulong	nfiletype;
+		ulong	ncmds;
+		ulong	nsizeofcmds;
+		ulong	nflags;
+	}texth;
+
+	struct nextcmd{
+		ulong	cmd;
+		ulong	cmdsize;
+		uchar	segname[16];
+		ulong	vmaddr;
+		ulong	vmsize;
+		ulong	fileoff;
+		ulong	filesize;
+		ulong	maxprot;
+		ulong	initprot;
+		ulong	nsects;
+		ulong	flags;
+	}textc;
+	struct nextsect{
+		char	sectname[16];
+		char	segname[16];
+		ulong	addr;
+		ulong	size;
+		ulong	offset;
+		ulong	align;
+		ulong	reloff;
+		ulong	nreloc;
+		ulong	flags;
+		ulong	reserved1;
+		ulong	reserved2;
+	}texts;
+	struct nextcmd	datac;
+	struct nextsect	datas;
+	struct nextsect	bsss;
+	struct nextsym{
+		ulong	cmd;
+		ulong	cmdsize;
+		ulong	symoff;
+		ulong	nsyms;
+		ulong	spoff;
+		ulong	pcoff;
+	}symc;
+};
+
+struct i386exec
+{
+	struct	i386coff{
+		ulong	isectmagic;
+		ulong	itime;
+		ulong	isyms;
+		ulong	insyms;
+		ulong	iflags;
+	} icoff;
+	struct	i386hdr{
+		ulong	imagic;
+		ulong	itextsize;
+		ulong	idatasize;
+		ulong	ibsssize;
+		ulong	ientry;
+		ulong	itextstart;
+		ulong	idatastart;
+	} ihdr;
+	struct coffsect	itexts;
+	struct coffsect idatas;
+	struct coffsect ibsss;
+	struct coffsect icomments;
+};
+
--- /dev/null
+++ b/utils/libmach/elf.h
@@ -1,0 +1,114 @@
+/*
+ *	Definitions needed for  accessing Irix ELF headers
+ */
+typedef struct {
+	uchar	ident[16];	/* ident bytes */
+	ushort	type;		/* file type */
+	ushort	machine;	/* target machine */
+	int	version;	/* file version */
+	ulong	elfentry;	/* start address */
+	ulong	phoff;		/* phdr file offset */
+	ulong	shoff;		/* shdr file offset */
+	int	flags;		/* file flags */
+	ushort	ehsize;		/* sizeof ehdr */
+	ushort	phentsize;	/* sizeof phdr */
+	ushort	phnum;		/* number phdrs */
+	ushort	shentsize;	/* sizeof shdr */
+	ushort	shnum;		/* number shdrs */
+	ushort	shstrndx;	/* shdr string index */
+} Ehdr;
+
+typedef struct {
+	int	type;		/* entry type */
+	ulong	offset;		/* file offset */
+	ulong	vaddr;		/* virtual address */
+	ulong	paddr;		/* physical address */
+	int	filesz;		/* file size */
+	ulong	memsz;		/* memory size */
+	int	flags;		/* entry flags */
+	int	align;		/* memory/file alignment */
+} Phdr;
+
+typedef struct {
+	ulong	name;		/* section name */
+	ulong	type;		/* SHT_... */
+	ulong	flags;		/* SHF_... */
+	ulong	addr;		/* virtual address */
+	ulong	offset;		/* file offset */
+	ulong	size;		/* section size */
+	ulong	link;		/* misc info */
+	ulong	info;		/* misc info */
+	ulong	addralign;	/* memory alignment */
+	ulong	entsize;	/* entry size if table */
+} Shdr;
+
+enum {
+	/* Ehdr codes */
+	MAG0 = 0,		/* ident[] indexes */
+	MAG1 = 1,
+	MAG2 = 2,
+	MAG3 = 3,
+	CLASS = 4,
+	DATA = 5,
+	VERSION = 6,
+
+	ELFCLASSNONE = 0,	/* ident[CLASS] */
+	ELFCLASS32 = 1,
+	ELFCLASS64 = 2,
+	ELFCLASSNUM = 3,
+
+	ELFDATANONE = 0,	/* ident[DATA] */
+	ELFDATA2LSB = 1,
+	ELFDATA2MSB = 2,
+	ELFDATANUM = 3,
+
+	NOETYPE = 0,		/* type */
+	REL = 1,
+	EXEC = 2,
+	DYN = 3,
+	CORE = 4,
+
+	NONE = 0,		/* machine */
+	M32 = 1,		/* AT&T WE 32100 */
+	SPARC = 2,		/* Sun SPARC */
+	I386 = 3,		/* Intel 80386 */
+	M68K = 4,		/* Motorola 68000 */
+	M88K = 5,		/* Motorola 88000 */
+	I486 = 6,		/* Intel 80486 */
+	I860 = 7,		/* Intel i860 */
+	MIPS = 8,		/* Mips R2000 */
+	S370 = 9,		/* Amdhal	*/
+	SPARC64 = 18,		/* Sun SPARC v9 */
+	POWER = 20,		/* PowerPC */
+	POWER64 = 21,		/* PowerPC64 */
+	ARM = 40,		/* ARM */
+	AMD64 = 62,		/* Amd64 */
+	ARM64 = 183,		/* ARM64 */
+
+	NO_VERSION = 0,		/* version, ident[VERSION] */
+	CURRENT = 1,
+
+	/* Phdr Codes */
+	NOPTYPE = 0,		/* type */
+	LOAD = 1,
+	DYNAMIC = 2,
+	INTERP = 3,
+	NOTE = 4,
+	SHLIB = 5,
+	PHDR = 6,
+
+	R = 0x4,		/* flags */
+	W = 0x2,
+	X = 0x1,
+
+	/* Shdr Codes */
+	Progbits = 1,	/* section types */
+	Strtab = 3,
+	Nobits = 8,
+
+	Swrite = 1,	/* section attributes */
+	Salloc = 2,
+	Sexec = 4,
+};
+
+#define	ELF_MAG		((0x7f<<24) | ('E'<<16) | ('L'<<8) | 'F')
--- /dev/null
+++ b/utils/libmach/executable.c
@@ -1,0 +1,713 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"bootexec.h"
+#include	"mach.h"
+#include	"elf.h"
+
+/*
+ *	All a.out header types.  The dummy entry allows canonical
+ *	processing of the union as a sequence of longs
+ */
+
+typedef struct {
+	union{
+		Exec	exec;
+		struct {
+			u32int	ohdr[8];	/* Exec */
+			uvlong hdr[1];
+		} exechdr64;
+		Ehdr elfhdr32;			/* elf.h */
+		struct mipsexec mips;	/* bootexec.h */
+		struct mips4kexec mips4k;	/* bootexec.h */
+		struct sparcexec sparc;	/* bootexec.h */
+		struct nextexec next;	/* bootexec.h */
+	} e;
+	u32int dummy;			/* padding to ensure extra u32int */
+} ExecHdr;
+
+static	int	nextboot(int, Fhdr*, ExecHdr*);
+static	int	sparcboot(int, Fhdr*, ExecHdr*);
+static	int	mipsboot(int, Fhdr*, ExecHdr*);
+static	int	mips4kboot(int, Fhdr*, ExecHdr*);
+static	int	common(int, Fhdr*, ExecHdr*);
+static	int	commonllp64(int, Fhdr*, ExecHdr*);
+static	int	adotout(int, Fhdr*, ExecHdr*);
+static	int	elfdotout(int, Fhdr*, ExecHdr*);
+static	int	armdotout(int, Fhdr*, ExecHdr*);
+static	void	setsym(Fhdr*, long, long, long, vlong);
+static	void	setdata(Fhdr*, uvlong, long, vlong, long);
+static	void	settext(Fhdr*, uvlong, uvlong, long, vlong);
+static	void	hswal(void*, int, ulong(*)(ulong));
+static	uvlong	_round(uvlong, ulong);
+
+/*
+ *	definition of per-executable file type structures
+ */
+
+typedef struct Exectable{
+	long	magic;			/* big-endian magic number of file */
+	char	*name;			/* executable identifier */
+	char	*dlmname;		/* dynamically loadable module identifier */
+	uchar	type;			/* Internal code */
+	uchar	_magic;			/* _MAGIC() magic */
+	Mach	*mach;			/* Per-machine data */
+	long	hsize;			/* header size */
+	ulong	(*swal)(ulong);		/* beswal or leswal */
+	int	(*hparse)(int, Fhdr*, ExecHdr*);
+} ExecTable;
+
+extern	Mach	mmips;
+extern	Mach	mmips2le;
+extern	Mach	mmips2be;
+extern	Mach	msparc;
+extern	Mach	mi386;
+extern	Mach	mamd64;
+extern	Mach	marm;
+extern	Mach	mpower;
+extern	Mach	mpower64;
+
+ExecTable exectab[] =
+{
+	{ V_MAGIC,			/* Mips v.out */
+		"mips plan 9 executable BE",
+		"mips plan 9 dlm BE",
+		FMIPS,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ P_MAGIC,			/* Mips 0.out (r3k le) */
+		"mips plan 9 executable LE",
+		"mips plan 9 dlm LE",
+		FMIPSLE,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ M_MAGIC,			/* Mips 4.out */
+		"mips 4k plan 9 executable BE",
+		"mips 4k plan 9 dlm BE",
+		FMIPS2BE,
+		1,
+		&mmips2be,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ N_MAGIC,			/* Mips 0.out */
+		"mips 4k plan 9 executable LE",
+		"mips 4k plan 9 dlm LE",
+		FMIPS2LE,
+		1,
+		&mmips2le,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x160<<16,			/* Mips boot image */
+		"mips plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips,
+		sizeof(struct mipsexec),
+		beswal,
+		mipsboot },
+	{ (0x160<<16)|3,		/* Mips boot image */
+		"mips 4k plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips2be,
+		sizeof(struct mips4kexec),
+		beswal,
+		mips4kboot },
+	{ K_MAGIC,			/* Sparc k.out */
+		"sparc plan 9 executable",
+		"sparc plan 9 dlm",
+		FSPARC,
+		1,
+		&msparc,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x01030107, 			/* Sparc boot image */
+		"sparc plan 9 boot image",
+		nil,
+		FSPARCB,
+		0,
+		&msparc,
+		sizeof(struct sparcexec),
+		beswal,
+		sparcboot },
+	{ I_MAGIC,			/* I386 8.out & boot image */
+		"386 plan 9 executable",
+		"386 plan 9 dlm",
+		FI386,
+		1,
+		&mi386,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ S_MAGIC,			/* amd64 6.out & boot image */
+		"amd64 plan 9 executable",
+		"amd64 plan 9 dlm",
+		FAMD64,
+		1,
+		&mamd64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ Q_MAGIC,			/* PowerPC q.out & boot image */
+		"power plan 9 executable",
+		"power plan 9 dlm",
+		FPOWER,
+		1,
+		&mpower,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ T_MAGIC,			/* power64 9.out & boot image */
+		"power64 plan 9 executable",
+		"power64 plan 9 dlm",
+		FPOWER64,
+		1,
+		&mpower64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ ELF_MAG,			/* any elf32 */
+		"elf executable",
+		nil,
+		FNONE,
+		0,
+		&mi386,
+		sizeof(Ehdr),
+		nil,
+		elfdotout },
+	{ E_MAGIC,			/* Arm 5.out and boot image */
+		"arm plan 9 executable",
+		"arm plan 9 dlm",
+		FARM,
+		1,
+		&marm,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ (143<<16)|0413,		/* (Free|Net)BSD Arm */
+		"arm *bsd executable",
+		nil,
+		FARM,
+		0,
+		&marm,
+		sizeof(Exec),
+		leswal,
+		armdotout },
+	{ 0 },
+};
+
+Mach	*mach = &mi386;			/* Global current machine table */
+
+static ExecTable*
+couldbe4k(ExecTable *mp)
+{
+	Dir *d;
+	ExecTable *f;
+
+	if((d=dirstat("/proc/1/regs")) == nil)
+		return mp;
+	if(d->length < 32*8){		/* R3000 */
+		free(d);
+		return mp;
+	}
+	free(d);
+	for (f = exectab; f->magic; f++)
+		if(f->magic == M_MAGIC) {
+			f->name = "mips plan 9 executable on mips2 kernel";
+			return f;
+		}
+	return mp;
+}
+
+int
+crackhdr(int fd, Fhdr *fp)
+{
+	ExecTable *mp;
+	ExecHdr d;
+	int nb, ret;
+	ulong magic;
+
+	fp->type = FNONE;
+	nb = read(fd, (char *)&d.e, sizeof(d.e));
+	if (nb <= 0)
+		return 0;
+
+	ret = 0;
+	magic = beswal(d.e.exec.magic);		/* big-endian */
+	for (mp = exectab; mp->magic; mp++) {
+		if (nb < mp->hsize)
+			continue;
+
+		/*
+		 * The.exec.magic number has morphed into something
+		 * with fields (the straw was DYN_MAGIC) so now
+		 * a flag is needed in Fhdr to distinguish _MAGIC()
+		 * magic numbers from foreign magic numbers.
+		 *
+		 * This code is creaking a bit and if it has to
+		 * be modified/extended much more it's probably
+		 * time to step back and redo it all.
+		 */
+		if(mp->_magic){
+			if(mp->magic != (magic & ~DYN_MAGIC))
+				continue;
+
+			if(mp->magic == V_MAGIC)
+				mp = couldbe4k(mp);
+
+			if ((magic & DYN_MAGIC) && mp->dlmname != nil)
+				fp->name = mp->dlmname;
+			else
+				fp->name = mp->name;
+		}
+		else{
+			if(mp->magic != magic)
+				continue;
+			fp->name = mp->name;
+		}
+		fp->type = mp->type;
+		fp->hdrsz = mp->hsize;		/* will be zero on bootables */
+		fp->_magic = mp->_magic;
+		fp->magic = magic;
+
+		mach = mp->mach;
+		if(mp->swal != nil)
+			hswal(&d, sizeof(d.e)/sizeof(ulong), mp->swal);
+		ret = mp->hparse(fd, fp, &d);
+		seek(fd, mp->hsize, 0);		/* seek to end of header */
+		break;
+	}
+	if(mp->magic == 0)
+		werrstr("unknown header type");
+	return ret;
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(void *v, int n, ulong (*swap)(ulong))
+{
+	ulong *ulp;
+
+	for(ulp = v; n--; ulp++)
+		*ulp = (*swap)(*ulp);
+}
+
+/*
+ *	Crack a normal a.out-type header
+ */
+static int
+adotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	long pgsize;
+
+	USED(fd);
+	pgsize = mach->pgsize;
+	settext(fp, hp->e.exec.entry, pgsize+sizeof(Exec),
+			hp->e.exec.text, sizeof(Exec));
+	setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
+		hp->e.exec.data, fp->txtsz+sizeof(Exec), hp->e.exec.bss);
+	setsym(fp, hp->e.exec.syms, hp->e.exec.spsz, hp->e.exec.pcsz, fp->datoff+fp->datsz);
+	return 1;
+}
+
+static void
+commonboot(Fhdr *fp)
+{
+	if (!(fp->entry & mach->ktmask))
+		return;
+
+	switch(fp->type) {				/* boot image */
+	case F68020:
+		fp->type = F68020B;
+		fp->name = "68020 plan 9 boot image";
+		break;
+	case FI386:
+		fp->type = FI386B;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "386 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		break;
+	case FARM:
+		fp->type = FARMB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "ARM plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		return;
+	case FPOWER:
+		fp->type = FPOWERB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "power plan 9 boot image";
+		fp->dataddr = fp->txtaddr+fp->txtsz;
+		break;
+	case FAMD64:
+		fp->type = FAMD64B;
+		fp->txtaddr = fp->entry;
+		fp->name = "amd64 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		break;
+	default:
+		return;
+	}
+	fp->hdrsz = 0;			/* header stripped */
+}
+
+/*
+ *	_MAGIC() style headers and
+ *	alpha plan9-style bootable images for axp "headerless" boot
+ *
+ */
+static int
+common(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	adotout(fd, fp, hp);
+	if(hp->e.exec.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+static int
+commonllp64(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	long pgsize;
+	uvlong entry;
+
+	USED(fd);
+	hswal(&hp->e, sizeof(Exec)/sizeof(long), beswal);
+	if(!(hp->e.exec.magic & HDR_MAGIC))
+		return 0;
+
+	/*
+	 * There can be more.exec.magic here if the
+	 * header ever needs more expansion.
+	 * For now just catch use of any of the
+	 * unused bits.
+	 */
+	if((hp->e.exec.magic & ~DYN_MAGIC)>>16)
+		return 0;
+	entry = beswav(hp->e.exechdr64.hdr[0]);
+
+	pgsize = mach->pgsize;
+	settext(fp, entry, pgsize+fp->hdrsz, hp->e.exec.text, fp->hdrsz);
+	setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
+		hp->e.exec.data, fp->txtsz+fp->hdrsz, hp->e.exec.bss);
+	setsym(fp, hp->e.exec.syms, hp->e.exec.spsz, hp->e.exec.pcsz, fp->datoff+fp->datsz);
+
+	if(hp->e.exec.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+/*
+ *	mips bootable image.
+ */
+static int
+mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.mips.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mips.mentry, (u32int)hp->e.mips.text_start,
+			hp->e.mips.tsize, sizeof(struct mipsexec)+4);
+		setdata(fp, (u32int)hp->e.mips.data_start, hp->e.mips.dsize,
+			fp->txtoff+hp->e.mips.tsize, hp->e.mips.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mips.mentry, (u32int)hp->e.mips.text_start,
+			hp->e.mips.tsize, 0);
+		setdata(fp, (u32int)hp->e.mips.data_start, hp->e.mips.dsize,
+			hp->e.mips.tsize, hp->e.mips.bsize);
+		break;
+	}
+	setsym(fp, hp->e.mips.nsyms, 0, hp->e.mips.pcsize, hp->e.mips.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	mips4k bootable image.
+ */
+static int
+mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.mips4k.h.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mips4k.h.mentry, (u32int)hp->e.mips4k.h.text_start,
+			hp->e.mips4k.h.tsize, sizeof(struct mips4kexec));
+		setdata(fp, (u32int)hp->e.mips4k.h.data_start, hp->e.mips4k.h.dsize,
+			fp->txtoff+hp->e.mips4k.h.tsize, hp->e.mips4k.h.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mips4k.h.mentry, (u32int)hp->e.mips4k.h.text_start,
+			hp->e.mips4k.h.tsize, 0);
+		setdata(fp, (u32int)hp->e.mips4k.h.data_start, hp->e.mips4k.h.dsize,
+			hp->e.mips4k.h.tsize, hp->e.mips4k.h.bsize);
+		break;
+	}
+	setsym(fp, hp->e.mips4k.h.nsyms, 0, hp->e.mips4k.h.pcsize, hp->e.mips4k.h.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	sparc bootable image
+ */
+static int
+sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FSPARCB;
+	settext(fp, hp->e.sparc.sentry, hp->e.sparc.sentry, hp->e.sparc.stext,
+		sizeof(struct sparcexec));
+	setdata(fp, hp->e.sparc.sentry+hp->e.sparc.stext, hp->e.sparc.sdata,
+		fp->txtoff+hp->e.sparc.stext, hp->e.sparc.sbss);
+	setsym(fp, hp->e.sparc.ssyms, 0, hp->e.sparc.sdrsize, fp->datoff+hp->e.sparc.sdata);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	next bootable image
+ */
+static int
+nextboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FNEXTB;
+	settext(fp, hp->e.next.textc.vmaddr, hp->e.next.textc.vmaddr,
+		hp->e.next.texts.size, hp->e.next.texts.offset);
+	setdata(fp, hp->e.next.datac.vmaddr, hp->e.next.datas.size,
+		hp->e.next.datas.offset, hp->e.next.bsss.size);
+	setsym(fp, hp->e.next.symc.nsyms, hp->e.next.symc.spoff, hp->e.next.symc.pcoff,
+		hp->e.next.symc.symoff);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ * Elf32 binaries.
+ */
+static int
+elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+
+	ulong (*swal)(ulong);
+	ushort (*swab)(ushort);
+	Ehdr *ep;
+	Phdr *ph;
+	int i, it, id, is, phsz;
+
+	/* bitswap the header according to the DATA format */
+	ep = &hp->e.elfhdr32;
+	if(ep->ident[CLASS] != ELFCLASS32) {
+		werrstr("bad ELF class - not 32 bit");
+		return 0;
+	}
+	if(ep->ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+	} else if(ep->ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+	} else {
+		werrstr("bad ELF encoding - not big or little endian");
+		return 0;
+	}
+
+	ep->type = swab(ep->type);
+	ep->machine = swab(ep->machine);
+	ep->version = swal(ep->version);
+	ep->elfentry = swal(ep->elfentry);
+	ep->phoff = swal(ep->phoff);
+	ep->shoff = swal(ep->shoff);
+	ep->flags = swal(ep->flags);
+	ep->ehsize = swab(ep->ehsize);
+	ep->phentsize = swab(ep->phentsize);
+	ep->phnum = swab(ep->phnum);
+	ep->shentsize = swab(ep->shentsize);
+	ep->shnum = swab(ep->shnum);
+	ep->shstrndx = swab(ep->shstrndx);
+	if(ep->type != EXEC || ep->version != CURRENT)
+		return 0;
+
+	/* we could definitely support a lot more machines here */
+	fp->magic = ELF_MAG;
+	fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
+	switch(ep->machine) {
+	case I386:
+		mach = &mi386;
+		fp->type = FI386;
+		break;
+	case MIPS:
+		mach = &mmips;
+		fp->type = FMIPS;
+		break;
+	case SPARC64:
+		return 0;
+	case POWER:
+		mach = &mpower;
+		fp->type = FPOWER;
+		break;
+	case AMD64:
+		mach = &mamd64;
+		fp->type = FAMD64;
+		break;
+	case ARM:
+		mach = &marm;
+		fp->type = FARM;
+		break;
+	default:
+		return 0;
+	}
+
+	if(ep->phentsize != sizeof(Phdr)) {
+		werrstr("bad ELF header size");
+		return 0;
+	}
+	phsz = sizeof(Phdr)*ep->phnum;
+	ph = malloc(phsz);
+	if(!ph)
+		return 0;
+	seek(fd, ep->phoff, 0);
+	if(read(fd, ph, phsz) < 0) {
+		free(ph);
+		return 0;
+	}
+	hswal(ph, phsz/sizeof(ulong), swal);
+
+	/* find text, data and symbols and install them */
+	it = id = is = -1;
+	for(i = 0; i < ep->phnum; i++) {
+		if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
+			it = i;
+		else if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
+			id = i;
+		else if(ph[i].type == NOPTYPE && is == -1)
+			is = i;
+	}
+	if(it == -1 || id == -1) {
+		/*
+		 * The SPARC64 boot image is something of an ELF hack.
+		 * Text+Data+BSS are represented by ph[0].  Symbols
+		 * are represented by ph[1]:
+		 *
+		 *		filesz, memsz, vaddr, paddr, off
+		 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
+		 * ph[1] : symsz, lcsz, 0, 0, symoff
+		 */
+		if(ep->machine == SPARC64 && ep->phnum == 2) {
+			ulong txtaddr, txtsz, dataddr, bsssz;
+
+			txtaddr = ph[0].vaddr | 0x80000000;
+			txtsz = ph[0].filesz - ph[0].paddr;
+			dataddr = txtaddr + txtsz;
+			bsssz = ph[0].memsz - ph[0].filesz;
+			settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
+			setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
+			setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
+			free(ph);
+			return 1;
+		}
+
+		werrstr("No TEXT or DATA sections");
+		free(ph);
+		return 0;
+	}
+
+	settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
+	setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
+	if(is != -1)
+		setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
+	free(ph);
+	return 1;
+}
+
+/*
+ * (Free|Net)BSD ARM header.
+ */
+static int
+armdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	uvlong kbase;
+
+	USED(fd);
+	settext(fp, hp->e.exec.entry, sizeof(Exec), hp->e.exec.text, sizeof(Exec));
+	setdata(fp, fp->txtsz, hp->e.exec.data, fp->txtsz, hp->e.exec.bss);
+	setsym(fp, hp->e.exec.syms, hp->e.exec.spsz, hp->e.exec.pcsz, fp->datoff+fp->datsz);
+
+	kbase = 0xF0000000;
+	if ((fp->entry & kbase) == kbase) {		/* Boot image */
+		fp->txtaddr = kbase+sizeof(Exec);
+		fp->name = "ARM *BSD boot image";
+		fp->hdrsz = 0;		/* header stripped */
+		fp->dataddr = kbase+fp->txtsz;
+	}
+	return 1;
+}
+
+static void
+settext(Fhdr *fp, uvlong e, uvlong a, long s, vlong off)
+{
+	fp->txtaddr = a;
+	fp->entry = e;
+	fp->txtsz = s;
+	fp->txtoff = off;
+}
+
+static void
+setdata(Fhdr *fp, uvlong a, long s, vlong off, long bss)
+{
+	fp->dataddr = a;
+	fp->datsz = s;
+	fp->datoff = off;
+	fp->bsssz = bss;
+}
+
+static void
+setsym(Fhdr *fp, long symsz, long sppcsz, long lnpcsz, vlong symoff)
+{
+	fp->symsz = symsz;
+	fp->symoff = symoff;
+	fp->sppcsz = sppcsz;
+	fp->sppcoff = fp->symoff+fp->symsz;
+	fp->lnpcsz = lnpcsz;
+	fp->lnpcoff = fp->sppcoff+fp->sppcsz;
+}
+
+
+static uvlong
+_round(uvlong a, ulong b)
+{
+	uvlong w;
+
+	w = (a/b)*b;
+	if (a!=w)
+		w += b;
+	return(w);
+}
--- /dev/null
+++ b/utils/libmach/k.c
@@ -1,0 +1,118 @@
+/*
+ * sparc definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "uregk.h"
+#include "mach.h"
+
+#define	REGOFF(x)	(ulong)(&((struct Ureg *) 0)->x)
+
+#define R1		REGOFF(u0.r1)
+#define R7		REGOFF(r7)
+#define PC		REGOFF(pc)
+#define	R15		REGOFF(r15)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FP_REG(x)	(REGSIZE+4*(x))
+#define	FPREGSIZE	(33*4)
+
+Reglist sparcreglist[] = {
+	{"Y",		REGOFF(y),	RINT|RRDONLY, 'X'},
+	{"TBR",		REGOFF(tbr),	RINT|RRDONLY, 'X'},
+	{"PSR",		REGOFF(psr),	RINT|RRDONLY, 'X'},
+	{"PC",		REGOFF(pc),	RINT, 'X'},
+	{"R1",		REGOFF(u0.r1),	RINT, 'X'},
+	{"R2",		REGOFF(r2),	RINT, 'X'},
+	{"R3",		REGOFF(r3),	RINT, 'X'},
+	{"R4",		REGOFF(r4),	RINT, 'X'},
+	{"R5",		REGOFF(r5),	RINT, 'X'},
+	{"R6",		REGOFF(r6),	RINT, 'X'},
+	{"R7",		REGOFF(r7),	RINT, 'X'},
+	{"R8",		REGOFF(r8),	RINT, 'X'},
+	{"R9",		REGOFF(r9),	RINT, 'X'},
+	{"R10",		REGOFF(r10),	RINT, 'X'},
+	{"R11",		REGOFF(r11),	RINT, 'X'},
+	{"R12",		REGOFF(r12),	RINT, 'X'},
+	{"R13",		REGOFF(r13),	RINT, 'X'},
+	{"R14",		REGOFF(r14),	RINT, 'X'},
+	{"R15",		REGOFF(r15),	RINT, 'X'},
+	{"R16",		REGOFF(r16),	RINT, 'X'},
+	{"R17",		REGOFF(r17),	RINT, 'X'},
+	{"R18",		REGOFF(r18),	RINT, 'X'},
+	{"R19",		REGOFF(r19),	RINT, 'X'},
+	{"R20",		REGOFF(r20),	RINT, 'X'},
+	{"R21",		REGOFF(r21),	RINT, 'X'},
+	{"R22",		REGOFF(r22),	RINT, 'X'},
+	{"R23",		REGOFF(r23),	RINT, 'X'},
+	{"R24",		REGOFF(r24),	RINT, 'X'},
+	{"R25",		REGOFF(r25),	RINT, 'X'},
+	{"R26",		REGOFF(r26),	RINT, 'X'},
+	{"R27",		REGOFF(r27),	RINT, 'X'},
+	{"R28",		REGOFF(r28),	RINT, 'X'},
+	{"R29",		REGOFF(r29),	RINT, 'X'},
+	{"R30",		REGOFF(r30),	RINT, 'X'},
+	{"R31",		REGOFF(r31),	RINT, 'X'},
+	{"NPC",		REGOFF(npc),	RINT, 'X'},
+
+	{"FSR",		FP_REG(0),	RINT, 'X'},
+	{"F0",		FP_REG(1),	RFLT, 'F'},
+	{"F1",		FP_REG(2),	RFLT, 'f'},
+	{"F2",		FP_REG(3),	RFLT, 'F'},
+	{"F3",		FP_REG(4),	RFLT, 'f'},
+	{"F4",		FP_REG(5),	RFLT, 'F'},
+	{"F5",		FP_REG(6),	RFLT, 'f'},
+	{"F6",		FP_REG(7),	RFLT, 'F'},
+	{"F7",		FP_REG(8),	RFLT, 'f'},
+	{"F8",		FP_REG(9),	RFLT, 'F'},
+	{"F9",		FP_REG(10),	RFLT, 'f'},
+	{"F10",		FP_REG(11),	RFLT, 'F'},
+	{"F11",		FP_REG(12),	RFLT, 'f'},
+	{"F12",		FP_REG(13),	RFLT, 'F'},
+	{"F13",		FP_REG(14),	RFLT, 'f'},
+	{"F14",		FP_REG(15),	RFLT, 'F'},
+	{"F15",		FP_REG(16),	RFLT, 'f'},
+	{"F16",		FP_REG(17),	RFLT, 'F'},
+	{"F17",		FP_REG(18),	RFLT, 'f'},
+	{"F18",		FP_REG(19),	RFLT, 'F'},
+	{"F19",		FP_REG(20),	RFLT, 'f'},
+	{"F20",		FP_REG(21),	RFLT, 'F'},
+	{"F21",		FP_REG(22),	RFLT, 'f'},
+	{"F22",		FP_REG(23),	RFLT, 'F'},
+	{"F23",		FP_REG(24),	RFLT, 'f'},
+	{"F24",		FP_REG(25),	RFLT, 'F'},
+	{"F25",		FP_REG(26),	RFLT, 'f'},
+	{"F26",		FP_REG(27),	RFLT, 'F'},
+	{"F27",		FP_REG(28),	RFLT, 'f'},
+	{"F28",		FP_REG(29),	RFLT, 'F'},
+	{"F29",		FP_REG(30),	RFLT, 'f'},
+	{"F30",		FP_REG(31),	RFLT, 'F'},
+	{"F31",		FP_REG(32),	RFLT, 'f'},
+	{  0 }
+};
+
+/*
+ * sparc has same stack format as mips
+ */
+Mach msparc =
+{
+	"sparc",
+	MSPARC,		/* machine type */
+	sparcreglist,	/* register list */
+	REGSIZE,	/* register set size in bytes */
+	FPREGSIZE,	/* floating point register size in bytes */
+	"PC",		/* name of PC */
+	"R1",		/* name of SP */
+	"R15",		/* name of link register */
+	"setSB",	/* static base register name */
+	0,		/* value */
+	0x1000,		/* page size */
+	0xE0000000,	/* kernel base */
+	0xE0000000,		/* kernel text mask */
+	0x7FFFFFFF,		/* user stack top */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/kdb.c
@@ -1,0 +1,1058 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+/*
+ * Sparc-specific debugger interface
+ */
+
+static	char	*sparcexcep(Map*, Rgetter);
+static	int	sparcfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	sparcinst(Map*, uvlong, char, char*, int);
+static	int	sparcdas(Map*, uvlong, char*, int);
+static	int	sparcinstlen(Map*, uvlong);
+
+Machdata sparcmach =
+{
+	{0x91, 0xd0, 0x20, 0x01},	/* breakpoint: TA $1 */
+	4,			/* break point size */
+
+	beswab,			/* convert short to local byte order */
+	beswal,			/* convert long to local byte order */
+	beswav,			/* convert vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* frame finder */
+	sparcexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	beieeesftos,		/* single precision float printer */
+	beieeedftos,		/* double precision float printer */
+	sparcfoll,		/* following addresses */
+	sparcinst,		/* print instruction */
+	sparcdas,		/* dissembler */
+	sparcinstlen,		/* instruction size */
+};
+
+static char *trapname[] =
+{
+	"reset",
+	"instruction access exception",
+	"illegal instruction",
+	"privileged instruction",
+	"fp disabled",
+	"window overflow",
+	"window underflow",
+	"unaligned address",
+	"fp exception",
+	"data access exception",
+	"tag overflow",
+};
+
+static char*
+excname(ulong tbr)
+{
+	static char buf[32];
+
+	if(tbr < sizeof trapname/sizeof(char*))
+		return trapname[tbr];
+	if(tbr >= 130)
+		sprint(buf, "trap instruction %ld", tbr-128);
+	else if(17<=tbr && tbr<=31)
+		sprint(buf, "interrupt level %ld", tbr-16);
+	else switch(tbr){
+	case 36:
+		return "cp disabled";
+	case 40:
+		return "cp exception";
+	case 128:
+		return "syscall";
+	case 129:
+		return "breakpoint";
+	default:
+		sprint(buf, "unknown trap %ld", tbr);
+	}
+	return buf;
+}
+
+static char*
+sparcexcep(Map *map, Rgetter rget)
+{
+	long tbr;
+
+	tbr = (*rget)(map, "TBR");
+	tbr = (tbr&0xFFF)>>4;
+	return excname(tbr);
+}
+
+	/* Sparc disassembler and related functions */
+typedef struct instr Instr;
+
+struct opcode {
+	char	*mnemonic;
+	void	(*f)(Instr*, char*);
+	int	flag;
+};
+
+static	char FRAMENAME[] = ".frame";
+
+
+struct instr {
+	uchar	op;		/* bits 31-30 */
+	uchar	rd;		/* bits 29-25 */
+	uchar	op2;		/* bits 24-22 */
+	uchar	a;		/* bit  29    */
+	uchar	cond;		/* bits 28-25 */
+	uchar	op3;		/* bits 24-19 */
+	uchar	rs1;		/* bits 18-14 */
+	uchar	i;		/* bit  13    */
+	uchar	asi;		/* bits 12-05 */
+	uchar	rs2;		/* bits 04-00 */
+	short	simm13;		/* bits 12-00, signed */
+	ushort	opf;		/* bits 13-05 */
+	ulong	immdisp22;	/* bits 21-00 */
+	ulong	simmdisp22;	/* bits 21-00, signed */
+	ulong	disp30;		/* bits 30-00 */
+	ulong	imm32;		/* SETHI+ADD constant */
+	int	target;		/* SETHI+ADD dest reg */
+	long	w0;
+	long	w1;
+	uvlong	addr;		/* pc of instruction */
+	char	*curr;		/* current fill level in output buffer */
+	char	*end;		/* end of buffer */
+	int 	size;		/* number of longs in instr */
+	char	*err;		/* errmsg */
+};
+
+static	Map	*mymap;		/* disassembler context */
+static	int	dascase;
+
+static int	mkinstr(uvlong, Instr*);
+static void	bra1(Instr*, char*, char*[]);
+static void	bra(Instr*, char*);
+static void	fbra(Instr*, char*);
+static void	cbra(Instr*, char*);
+static void	unimp(Instr*, char*);
+static void	fpop(Instr*, char*);
+static void	shift(Instr*, char*);
+static void	sethi(Instr*, char*);
+static void	load(Instr*, char*);
+static void	loada(Instr*, char*);
+static void	store(Instr*, char*);
+static void	storea(Instr*, char*);
+static void	add(Instr*, char*);
+static void	cmp(Instr*, char*);
+static void	wr(Instr*, char*);
+static void	jmpl(Instr*, char*);
+static void	rd(Instr*, char*);
+static void	loadf(Instr*, char*);
+static void	storef(Instr*, char*);
+static void	loadc(Instr*, char*);
+static void	loadcsr(Instr*, char*);
+static void	trap(Instr*, char*);
+
+static struct opcode sparcop0[8] = {
+	"UNIMP",	unimp,	0,	/* page 137 */	/* 0 */
+		"",		0,	0,		/* 1 */
+	"B",		bra,	0,	/* page 119 */	/* 2 */
+		"",		0,	0,		/* 3 */
+	"SETHI",	sethi,	0,	/* page 104 */	/* 4 */
+		"",		0,	0,		/* 5 */
+	"FB",		fbra,	0,	/* page 121 */	/* 6 */
+	"CB",		cbra,	0,	/* page 123 */	/* 7 */
+};
+
+static struct opcode sparcop2[64] = {
+	"ADD",		add,	0,	/* page 108 */	/* 0x00 */
+	"AND",		add,	0,	/* page 106 */	/* 0x01 */
+	"OR",		add,	0,			/* 0x02 */
+	"XOR",		add,	0,			/* 0x03 */
+	"SUB",		add,	0,	/* page 110 */	/* 0x04 */
+	"ANDN",		add,	0,			/* 0x05 */
+	"ORN",		add,	0,			/* 0x06 */
+	"XORN",		add,	0,			/* 0x07 */
+	"ADDX",		add,	0,			/* 0x08 */
+	"",		0,	0,			/* 0x09 */
+	"UMUL",		add,	0,	/* page 113 */	/* 0x0a */
+	"SMUL",		add,	0,			/* 0x0b */
+	"SUBX",		add,	0,			/* 0x0c */
+	"",		0,	0,			/* 0x0d */
+	"UDIV",		add,	0,	/* page 115 */	/* 0x0e */
+	"SDIV",		add,	0,			/* 0x0f */
+	"ADDCC",	add,	0,			/* 0x10 */
+	"ANDCC",	add,	0,			/* 0x11 */
+	"ORCC",		add,	0,			/* 0x12 */
+	"XORCC",	add,	0,			/* 0x13 */
+	"SUBCC",	cmp,	0,			/* 0x14 */
+	"ANDNCC",	add,	0,			/* 0x15 */
+	"ORNCC",	add,	0,			/* 0x16 */
+	"XORNCC",	add,	0,			/* 0x17 */
+	"ADDXCC",	add,	0,			/* 0x18 */
+	"",		0,	0,			/* 0x19 */
+	"UMULCC",	add,	0,			/* 0x1a */
+	"SMULCC",	add,	0,			/* 0x1b */
+	"SUBXCC",	add,	0,			/* 0x1c */
+	"",		0,	0,			/* 0x1d */
+	"UDIVCC",	add,	0,			/* 0x1e */
+	"SDIVCC",	add,	0,			/* 0x1f */
+	"TADD",		add,	0,	/* page 109 */	/* 0x20 */
+	"TSUB",		add,	0,	/* page 111 */	/* 0x21 */
+	"TADDCCTV",	add,	0,			/* 0x22 */
+	"TSUBCCTV",	add,	0,			/* 0x23 */
+	"MULSCC",	add,	0,	/* page 112 */	/* 0x24 */
+	"SLL",		shift,	0,	/* page 107 */	/* 0x25 */
+	"SRL",		shift,	0,			/* 0x26 */
+	"SRA",		shift,	0,			/* 0x27 */
+	"rdy",		rd,	0,	/* page 131 */	/* 0x28 */
+	"rdpsr",	rd,	0,			/* 0x29 */
+	"rdwim",	rd,	0,			/* 0x2a */
+	"rdtbr",	rd,	0,			/* 0x2b */
+	"",		0,	0,			/* 0x2c */
+	"",		0,	0,			/* 0x2d */
+	"",		0,	0,			/* 0x2e */
+	"",		0,	0,			/* 0x2f */
+	"wry",		wr,	0,	/* page 133 */	/* 0x30 */
+	"wrpsr",	wr,	0,			/* 0x31 */
+	"wrwim",	wr,	0,			/* 0x32 */
+	"wrtbr",	wr,	0,			/* 0x33 */
+	"FPOP",		fpop,	0,	/* page 140 */	/* 0x34 */
+	"FPOP",		fpop,	0,			/* 0x35 */
+	"",		0,	0,			/* 0x36 */
+	"",		0,	0,			/* 0x37 */
+	"JMPL",		jmpl,	0,	/* page 126 */	/* 0x38 */
+	"RETT",		add,	0,	/* page 127 */	/* 0x39 */
+	"T",		trap,	0,	/* page 129 */	/* 0x3a */
+	"flush",	add,	0,	/* page 138 */	/* 0x3b */
+	"SAVE",		add,	0,	/* page 117 */	/* 0x3c */
+	"RESTORE",	add,	0,			/* 0x3d */
+};
+
+static struct opcode sparcop3[64]={
+	"ld",		load,	0,			/* 0x00 */
+	"ldub",		load,	0,			/* 0x01 */
+	"lduh",		load,	0,			/* 0x02 */
+	"ldd",		load,	0,			/* 0x03 */
+	"st",		store,	0,			/* 0x04 */
+	"stb",		store,	0,	/* page 95 */	/* 0x05 */
+	"sth",		store,	0,			/* 0x06 */
+	"std",		store,	0,			/* 0x07 */
+	"",		0,	0,			/* 0x08 */
+	"ldsb",		load,	0,	/* page 90 */	/* 0x09 */
+	"ldsh",		load,	0,			/* 0x0a */
+	"",		0,	0,			/* 0x0b */
+	"",		0,	0,			/* 0x0c */
+	"ldstub",	store,	0,	/* page 101 */	/* 0x0d */
+	"",		0,	0,			/* 0x0e */
+	"swap",		load,	0,	/* page 102 */	/* 0x0f */
+	"lda",		loada,	0,			/* 0x10 */
+	"lduba",	loada,	0,			/* 0x11 */
+	"lduha",	loada,	0,			/* 0x12 */
+	"ldda",		loada,	0,			/* 0x13 */
+	"sta",		storea,	0,			/* 0x14 */
+	"stba",		storea,	0,			/* 0x15 */
+	"stha",		storea,	0,			/* 0x16 */
+	"stda",		storea,	0,			/* 0x17 */
+	"",		0,	0,			/* 0x18 */
+	"ldsba",	loada,	0,			/* 0x19 */
+	"ldsha",	loada,	0,			/* 0x1a */
+	"",		0,	0,			/* 0x1b */
+	"",		0,	0,			/* 0x1c */
+	"ldstuba",	storea,	0,			/* 0x1d */
+	"",		0,	0,			/* 0x1e */
+	"swapa",	loada,	0,			/* 0x1f */
+	"ldf",		loadf,	0,	/* page 92 */	/* 0x20 */
+	"ldfsr",	loadf,0,			/* 0x21 */
+	"",		0,	0,			/* 0x22 */
+	"lddf",		loadf,	0,			/* 0x23 */
+	"stf",		storef,	0,	/* page 97 */	/* 0x24 */
+	"stfsr",	storef,0,			/* 0x25 */
+	"stdfq",	storef,0,			/* 0x26 */
+	"stdf",		storef,	0,			/* 0x27 */
+	"",		0,	0,			/* 0x28 */
+	"",		0,	0,			/* 0x29 */
+	"",		0,	0,			/* 0x2a */
+	"",		0,	0,			/* 0x2b */
+	"",		0,	0,			/* 0x2c */
+	"",		0,	0,			/* 0x2d */
+	"",		0,	0,			/* 0x2e */
+	"",		0,	0,			/* 0x2f */
+	"ldc",		loadc,	0,	/* page 94 */	/* 0x30 */
+	"ldcsr",	loadcsr,0,			/* 0x31 */
+	"",		0,	0,			/* 0x32 */
+	"lddc",		loadc,	0,			/* 0x33 */
+	"stc",		loadc,	0,	/* page 99 */	/* 0x34 */
+	"stcsr",	loadcsr,0,			/* 0x35 */
+	"stdcq",	loadcsr,0,			/* 0x36 */
+	"stdc",		loadc,	0,			/* 0x37 */
+};
+
+#pragma	varargck	argpos	bprint	2
+#pragma	varargck	type	"T"	char*
+
+/* convert to lower case from upper, according to dascase */
+static int
+Tfmt(Fmt *f)
+{
+	char buf[128];
+	char *s, *t, *oa;
+
+	oa = va_arg(f->args, char*);
+	if(dascase){
+		for(s=oa,t=buf; *t = *s; s++,t++)
+			if('A'<=*t && *t<='Z')
+				*t += 'a'-'A';
+		return fmtstrcpy(f, buf);
+	}
+	return fmtstrcpy(f, oa);
+}
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static int
+decode(uvlong pc, Instr *i)
+{
+	ulong w;
+
+	if (get4(mymap, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->op = (w >> 30) & 0x03;
+	i->rd = (w >> 25) & 0x1F;
+	i->op2 = (w >> 22) & 0x07;
+	i->a = (w >> 29) & 0x01;
+	i->cond = (w >> 25) & 0x0F;
+	i->op3 = (w >> 19) & 0x3F;
+	i->rs1 = (w >> 14) & 0x1F;
+	i->i = (w >> 13) & 0x01;
+	i->asi = (w >> 5) & 0xFF;
+	i->rs2 = (w >> 0) & 0x1F;
+	i->simm13 = (w >> 0) & 0x1FFF;
+	if(i->simm13 & (1<<12))
+		i->simm13 |= ~((1<<13)-1);
+	i->opf = (w >> 5) & 0x1FF;
+	i->immdisp22 = (w >> 0) & 0x3FFFFF;
+	i->simmdisp22 = i->immdisp22;
+	if(i->simmdisp22 & (1<<21))
+		i->simmdisp22 |= ~((1<<22)-1);
+	i->disp30 = (w >> 0) & 0x3FFFFFFF;
+	i->w0 = w;
+	i->target = -1;
+	i->addr = pc;
+	i->size = 1;
+	return 1;
+}
+
+static int
+mkinstr(uvlong pc, Instr *i)
+{
+	Instr xi;
+
+	if (decode(pc, i) < 0)
+		return -1;
+	if(i->op==0 && i->op2==4 && !dascase){	/* SETHI */
+		if (decode(pc+4, &xi) < 0)
+			return -1;
+		if(xi.op==2 && xi.op3==0)		/* ADD */
+		if(xi.i == 1 && xi.rs1 == i->rd){	/* immediate to same reg */
+			i->imm32 = xi.simm13 + (i->immdisp22<<10);
+			i->target = xi.rd;
+			i->w1 = xi.w0;
+			i->size++;
+			return 1;
+		}
+	}
+	if(i->op==2 && i->opf==1 && !dascase){	/* FMOVS */
+		if (decode(pc+4, &xi) < 0)
+			return -1;
+		if(i->op==2 && i->opf==1)		/* FMOVS */
+		if(xi.rd==i->rd+1 && xi.rs2==i->rs2+1){	/* next pair */
+			i->w1 = xi.w0;
+			i->size++;
+		}
+	}
+	return 1;
+}
+
+static int
+printins(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr instr;
+	void (*f)(Instr*, char*);
+
+	mymap = map;
+	memset(&instr, 0, sizeof(instr));
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	if (mkinstr(pc, &instr) < 0)
+		return -1;
+	switch(instr.op){
+	case 0:
+		f = sparcop0[instr.op2].f;
+		if(f)
+			(*f)(&instr, sparcop0[instr.op2].mnemonic);
+		else
+			bprint(&instr, "unknown %lux", instr.w0);
+		break;
+
+	case 1:
+		bprint(&instr, "%T", "CALL\t");
+		instr.curr += symoff(instr.curr, instr.end-instr.curr,
+					pc+instr.disp30*4, CTEXT);
+		if (!dascase)
+			bprint(&instr, "(SB)");
+		break;
+
+	case 2:
+		f = sparcop2[instr.op3].f;
+		if(f)
+			(*f)(&instr, sparcop2[instr.op3].mnemonic);
+		else
+			bprint(&instr, "unknown %lux", instr.w0);
+		break;
+
+	case 3:
+		f = sparcop3[instr.op3].f;
+		if(f)
+			(*f)(&instr, sparcop3[instr.op3].mnemonic);
+		else
+			bprint(&instr, "unknown %lux", instr.w0);
+		break;
+	}
+	if (instr.err) {
+		if (instr.curr != buf)
+			bprint(&instr, "\t\t;");
+		bprint(&instr, instr.err);
+	}
+	return instr.size*4;
+}
+
+static int
+sparcinst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	static int fmtinstalled = 0;
+
+		/* a modifier of 'I' toggles the dissassembler type */
+	if (!fmtinstalled) {
+		fmtinstalled = 1;
+		fmtinstall('T', Tfmt);
+	}
+	if ((asstype == ASUNSPARC && modifier == 'i')
+		|| (asstype == ASPARC && modifier == 'I'))
+		dascase = 'a'-'A';
+	else
+		dascase = 0;
+	return printins(map, pc, buf, n);
+}
+
+static int
+sparcdas(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr instr;
+
+	mymap = map;
+	memset(&instr, 0, sizeof(instr));
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	if (mkinstr(pc, &instr) < 0)
+		return -1;
+	if (instr.end-instr.curr > 8)
+		instr.curr = _hexify(instr.curr, instr.w0, 7);
+	if (instr.end-instr.curr > 9 && instr.size == 2) {
+		*instr.curr++ = ' ';
+		instr.curr = _hexify(instr.curr, instr.w1, 7);
+	}
+	*instr.curr = 0;
+	return instr.size*4;
+}
+
+static int
+sparcinstlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	return i.size*4;
+}
+
+static int
+plocal(Instr *i)
+{
+	int offset;
+	Symbol s;
+
+	if (!findsym(i->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s))
+		return -1;
+	if (s.value > i->simm13) {
+		if(getauto(&s, s.value-i->simm13, CAUTO, &s)) {
+			bprint(i, "%s+%lld(SP)", s.name, s.value);
+			return 1;
+		}
+	} else {
+		offset = i->simm13-s.value;
+		if (getauto(&s, offset-4, CPARAM, &s)) {
+			bprint(i, "%s+%d(FP)", s.name, offset);
+			return 1;
+		}
+	}
+	return -1;
+}
+
+static void
+address(Instr *i)
+{
+	Symbol s, s2;
+	uvlong off, off1;
+
+	if (i->rs1 == 1 && plocal(i) >= 0)
+		return;
+	off = mach->sb+i->simm13;
+	if(i->rs1 == 2	&& findsym(off, CANY, &s)
+			&& s.value-off < 4096
+			&& (s.class == CDATA || s.class == CTEXT)) {
+		if(off==s.value && s.name[0]=='$'){
+			off1 = 0;
+			geta(mymap, s.value, &off1);
+			if(off1 && findsym(off1, CANY, &s2) && s2.value == off1){
+				bprint(i, "$%s(SB)", s2.name);
+				return;
+			}
+		}
+		bprint(i, "%s", s.name);
+		if (s.value != off)
+			bprint(i, "+%llux", s.value-off);
+		bprint(i, "(SB)");
+		return;
+	}
+	bprint(i, "%ux(R%d)", i->simm13, i->rs1);
+}
+
+static void
+unimp(Instr *i, char *m)
+{
+	bprint(i, "%T", m);
+}
+
+static char	*bratab[16] = {	/* page 91 */
+	"N",		/* 0x0 */
+	"E",		/* 0x1 */
+	"LE",		/* 0x2 */
+	"L",		/* 0x3 */
+	"LEU",		/* 0x4 */
+	"CS",		/* 0x5 */
+	"NEG",		/* 0x6 */
+	"VS",		/* 0x7 */
+	"A",		/* 0x8 */
+	"NE",		/* 0x9 */
+	"G",		/* 0xa */
+	"GE",		/* 0xb */
+	"GU",		/* 0xc */
+	"CC",		/* 0xd */
+	"POS",		/* 0xe */
+	"VC",		/* 0xf */
+};
+
+static char	*fbratab[16] = {	/* page 91 */
+	"N",		/* 0x0 */
+	"NE",		/* 0x1 */
+	"LG",		/* 0x2 */
+	"UL",		/* 0x3 */
+	"L",		/* 0x4 */
+	"UG",		/* 0x5 */
+	"G",		/* 0x6 */
+	"U",		/* 0x7 */
+	"A",		/* 0x8 */
+	"E",		/* 0x9 */
+	"UE",		/* 0xa */
+	"GE",		/* 0xb */
+	"UGE",		/* 0xc */
+	"LE",		/* 0xd */
+	"ULE",		/* 0xe */
+	"O",		/* 0xf */
+};
+
+static char	*cbratab[16] = {	/* page 91 */
+	"N",		/* 0x0 */
+	"123",		/* 0x1 */
+	"12",		/* 0x2 */
+	"13",		/* 0x3 */
+	"1",		/* 0x4 */
+	"23",		/* 0x5 */
+	"2",		/* 0x6 */
+	"3",		/* 0x7 */
+	"A",		/* 0x8 */
+	"0",		/* 0x9 */
+	"03",		/* 0xa */
+	"02",		/* 0xb */
+	"023",		/* 0xc */
+	"01",		/* 0xd */
+	"013",		/* 0xe */
+	"012",		/* 0xf */
+};
+
+static void
+bra1(Instr *i, char *m, char *tab[])
+{
+	long imm;
+
+	imm = i->simmdisp22;
+	if(i->a)
+		bprint(i, "%T%T.%c\t", m, tab[i->cond], 'A'+dascase);
+	else
+		bprint(i, "%T%T\t", m, tab[i->cond]);
+	i->curr += symoff(i->curr, i->end-i->curr, i->addr+4*imm, CTEXT);
+	if (!dascase)
+		bprint(i, "(SB)");
+}
+
+static void
+bra(Instr *i, char *m)			/* page 91 */
+{
+	bra1(i, m, bratab);
+}
+
+static void
+fbra(Instr *i, char *m)			/* page 93 */
+{
+	bra1(i, m, fbratab);
+}
+
+static void
+cbra(Instr *i, char *m)			/* page 95 */
+{
+	bra1(i, m, cbratab);
+}
+
+static void
+trap(Instr *i, char *m)			/* page 101 */
+{
+	if(i->i == 0)
+		bprint(i, "%T%T\tR%d+R%d", m, bratab[i->cond], i->rs2, i->rs1);
+	else
+		bprint(i, "%T%T\t$%ux+R%d", m, bratab[i->cond], i->simm13, i->rs1);
+}
+
+static void
+sethi(Instr *i, char *m)		/* page 89 */
+{
+	ulong imm;
+
+	imm = i->immdisp22<<10;
+	if(dascase){
+		bprint(i, "%T\t%lux, R%d", m, imm, i->rd);
+		return;
+	}
+	if(imm==0 && i->rd==0){
+		bprint(i, "NOP");
+		return;
+	}
+	if(i->target < 0){
+		bprint(i, "MOVW\t$%lux, R%d", imm, i->rd);
+		return;
+	}
+	bprint(i, "MOVW\t$%lux, R%d", i->imm32, i->target);
+}
+
+static char ldtab[] = {
+	'W',
+	'B',
+	'H',
+	'D',
+};
+
+static char*
+moveinstr(int op3, char *m)
+{
+	char *s;
+	int c;
+	static char buf[8];
+
+	if(!dascase){
+		/* batshit cases */
+		if(op3 == 0xF || op3 == 0x1F)
+			return "SWAP";
+		if(op3 == 0xD || op3 == 0x1D)
+			return "TAS";	/* really LDSTUB */
+		c = ldtab[op3&3];
+		s = "";
+		if((op3&11)==1 || (op3&11)==2)
+			s="U";
+		sprint(buf, "MOV%c%s", c, s);
+		return buf;
+	}
+	return m;
+}
+
+static void
+load(Instr *i, char *m)			/* page 68 */
+{
+	m = moveinstr(i->op3, m);
+	if(i->i == 0)
+		bprint(i, "%s\t(R%d+R%d), R%d", m, i->rs1, i->rs2, i->rd);
+	else{
+		bprint(i, "%s\t", m);
+		address(i);
+		bprint(i, ", R%d", i->rd);
+	}
+}
+
+static void
+loada(Instr *i, char *m)		/* page 68 */
+{
+	m = moveinstr(i->op3, m);
+	if(i->i == 0)
+		bprint(i, "%s\t(R%d+R%d, %d), R%d", m, i->rs1, i->rs2, i->asi, i->rd);
+	else
+		bprint(i, "unknown ld asi %lux", i->w0);
+}
+
+static void
+store(Instr *i, char *m)		/* page 74 */
+{
+	m = moveinstr(i->op3, m);
+	if(i->i == 0)
+		bprint(i, "%s\tR%d, (R%d+R%d)",
+				m, i->rd, i->rs1, i->rs2);
+	else{
+		bprint(i, "%s\tR%d, ", m, i->rd);
+		address(i);
+	}
+}
+
+static void
+storea(Instr *i, char *m)		/* page 74 */
+{
+	m = moveinstr(i->op3, m);
+	if(i->i == 0)
+		bprint(i, "%s\tR%d, (R%d+R%d, %d)", m, i->rd, i->rs1, i->rs2, i->asi);
+	else
+		bprint(i, "%s\tR%d, %d(R%d, %d), ?", m, i->rd, i->simm13, i->rs1, i->asi);
+}
+
+static void
+shift(Instr *i, char *m)	/* page 88 */
+{
+	if(i->i == 0){
+		if(i->rs1 == i->rd)
+			if(dascase)
+				bprint(i, "%T\tR%d, R%d", m, i->rs1, i->rs2);
+			else
+				bprint(i, "%T\tR%d, R%d", m, i->rs2, i->rs1);
+		else
+			if(dascase)
+				bprint(i, "%T\tR%d, R%d, R%d", m, i->rs1, i->rs2, i->rd);
+			else
+				bprint(i, "%T\tR%d, R%d, R%d", m, i->rs2, i->rs1, i->rd);
+	}else{
+		if(i->rs1 == i->rd)
+			if(dascase)
+				bprint(i, "%T\t$%d,R%d", m, i->simm13&0x1F, i->rs1);
+			else
+				bprint(i, "%T\tR%d, $%d", m,  i->rs1, i->simm13&0x1F);
+		else
+			if(dascase)
+				bprint(i, "%T\tR%d, $%d, R%d",m,i->rs1,i->simm13&0x1F,i->rd);
+			else
+				bprint(i, "%T\t$%d, R%d, R%d",m,i->simm13&0x1F,i->rs1,i->rd);
+	}
+}
+
+static void
+add(Instr *i, char *m)	/* page 82 */
+{
+	if(i->i == 0){
+		if(dascase)
+			bprint(i, "%T\tR%d, R%d", m, i->rs1, i->rs2);
+		else
+			if(i->op3==2 && i->rs1==0 && i->rd)  /* OR R2, R0, R1 */
+				bprint(i, "MOVW\tR%d", i->rs2);
+			else
+				bprint(i, "%T\tR%d, R%d", m, i->rs2, i->rs1);
+	}else{
+		if(dascase)
+			bprint(i, "%T\tR%d, $%ux", m, i->rs1, i->simm13);
+		else
+			if(i->op3==0 && i->rd && i->rs1==0)	/* ADD $x, R0, R1 */
+				bprint(i, "MOVW\t$%ux", i->simm13);
+			else if(i->op3==0 && i->rd && i->rs1==2){
+				/* ADD $x, R2, R1 -> MOVW $x(SB), R1 */
+				bprint(i, "MOVW\t$");
+				address(i);
+			} else
+				bprint(i, "%T\t$%ux, R%d", m, i->simm13, i->rs1);
+	}
+	if(i->rs1 != i->rd)
+		bprint(i, ", R%d", i->rd);
+}
+
+static void
+cmp(Instr *i, char *m)
+{
+	if(dascase || i->rd){
+		add(i, m);
+		return;
+	}
+	if(i->i == 0)
+		bprint(i, "CMP\tR%d, R%d", i->rs1, i->rs2);
+	else
+		bprint(i, "CMP\tR%d, $%ux", i->rs1, i->simm13);
+}
+
+static char *regtab[4] = {
+	"Y",
+	"PSR",
+	"WIM",
+	"TBR",
+};
+
+static void
+wr(Instr *i, char *m)		/* page 82 */
+{
+	if(dascase){
+		if(i->i == 0)
+			bprint(i, "%s\tR%d, R%d", m, i->rs1, i->rs2);
+		else
+			bprint(i, "%s\tR%d, $%ux", m, i->rs1, i->simm13);
+	}else{
+		if(i->i && i->simm13==0)
+			bprint(i, "MOVW\tR%d", i->rs1);
+		else if(i->i == 0)
+			bprint(i, "wr\tR%d, R%d", i->rs2, i->rs1);
+		else
+			bprint(i, "wr\t$%ux, R%d", i->simm13, i->rs1);
+	}
+	bprint(i, ", %s", regtab[i->op3&3]);
+}
+
+static void
+rd(Instr *i, char *m)		/* page 103 */
+{
+	if(i->rs1==15 && i->rd==0){
+		m = "stbar";
+		if(!dascase)
+			m = "STBAR";
+		bprint(i, "%s", m);
+	}else{
+		if(!dascase)
+			m = "MOVW";
+		bprint(i, "%s\t%s, R%d", m, regtab[i->op3&3], i->rd);
+	}
+}
+
+static void
+jmpl(Instr *i, char *m)		/* page 82 */
+{
+	if(i->i == 0){
+		if(i->rd == 15)
+			bprint(i, "%T\t(R%d+R%d)", "CALL", i->rs2, i->rs1);
+		else
+			bprint(i, "%T\t(R%d+R%d), R%d", m, i->rs2, i->rs1, i->rd);
+	}else{
+		if(!dascase && i->simm13==8 && i->rs1==15 && i->rd==0)
+			bprint(i, "RETURN");
+		else{
+			bprint(i, "%T\t", m);
+			address(i);
+			bprint(i, ", R%d", i->rd);
+		}
+	}
+}
+
+static void
+loadf(Instr *i, char *m)		/* page 70 */
+{
+	if(!dascase){
+		m = "FMOVD";
+		if(i->op3 == 0x20)
+			m = "FMOVF";
+		else if(i->op3 == 0x21)
+			m = "MOVW";
+	}
+	if(i->i == 0)
+		bprint(i, "%s\t(R%d+R%d)", m, i->rs1, i->rs2);
+	else{
+		bprint(i, "%s\t", m);
+		address(i);
+	}
+	if(i->op3 == 0x21)
+		bprint(i, ", FSR");
+	else
+		bprint(i, ", R%d", i->rd);
+}
+
+static void
+storef(Instr *i, char *m)		/* page 70 */
+{
+	if(!dascase){
+		m = "FMOVD";
+		if(i->op3 == 0x25 || i->op3 == 0x26)
+			m = "MOVW";
+		else if(i->op3 == 0x20)
+			m = "FMOVF";
+	}
+	bprint(i, "%s\t", m);
+	if(i->op3 == 0x25)
+		bprint(i, "FSR, ");
+	else if(i->op3 == 0x26)
+		bprint(i, "FQ, ");
+	else
+		bprint(i, "R%d, ", i->rd);
+	if(i->i == 0)
+		bprint(i, "(R%d+R%d)", i->rs1, i->rs2);
+	else
+		address(i);
+}
+
+static void
+loadc(Instr *i, char *m)			/* page 72 */
+{
+	if(i->i == 0)
+		bprint(i, "%s\t(R%d+R%d), C%d", m, i->rs1, i->rs2, i->rd);
+	else{
+		bprint(i, "%s\t", m);
+		address(i);
+		bprint(i, ", C%d", i->rd);
+	}
+}
+
+static void
+loadcsr(Instr *i, char *m)			/* page 72 */
+{
+	if(i->i == 0)
+		bprint(i, "%s\t(R%d+R%d), CSR", m, i->rs1, i->rs2);
+	else{
+		bprint(i, "%s\t", m);
+		address(i);
+		bprint(i, ", CSR");
+	}
+}
+
+static struct{
+	int	opf;
+	char	*name;
+} fptab1[] = {			/* ignores rs1 */
+	0xC4,	"FITOS",	/* page 109 */
+	0xC8,	"FITOD",
+	0xCC,	"FITOX",
+
+	0xD1,	"FSTOI",	/* page 110 */
+	0xD2,	"FDTOI",
+	0xD3,	"FXTOI",
+
+	0xC9,	"FSTOD",	/* page 111 */
+	0xCD,	"FSTOX",
+	0xC6,	"FDTOS",
+	0xCE,	"FDTOX",
+	0xC7,	"FXTOS",
+	0xCB,	"FXTOD",
+
+	0x01,	"FMOVS",	/* page 112 */
+	0x05,	"FNEGS",
+	0x09,	"FABSS",
+
+	0x29,	"FSQRTS", 	/* page 113 */
+	0x2A,	"FSQRTD",
+	0x2B,	"FSQRTX",
+
+	0,	0,
+};
+
+static struct{
+	int	opf;
+	char	*name;
+} fptab2[] = {			/* uses rs1 */
+
+	0x41,	"FADDS",	/* page 114 */
+	0x42,	"FADDD",
+	0x43,	"FADDX",
+	0x45,	"FSUBS",
+	0x46,	"FSUBD",
+	0x47,	"FSUBX",
+
+	0x49,	"FMULS",	/* page 115 */
+	0x4A,	"FMULD",
+	0x4B,	"FMULX",
+	0x4D,	"FDIVS",
+	0x4E,	"FDIVD",
+	0x4F,	"FDIVX",
+
+	0x51,	"FCMPS",	/* page 116 */
+	0x52,	"FCMPD",
+	0x53,	"FCMPX",
+	0x55,	"FCMPES",
+	0x56,	"FCMPED",
+	0x57,	"FCMPEX",
+
+	0, 0
+};
+
+static void
+fpop(Instr *i, char *m)	/* page 108-116 */
+{
+	int j;
+
+	if(dascase==0 && i->size==2){
+		bprint(i, "FMOVD\tF%d, F%d", i->rs2, i->rd);
+		return;
+	}
+	for(j=0; fptab1[j].name; j++)
+		if(fptab1[j].opf == i->opf){
+			bprint(i, "%T\tF%d, F%d", fptab1[j].name, i->rs2, i->rd);
+			return;
+		}
+	for(j=0; fptab2[j].name; j++)
+		if(fptab2[j].opf == i->opf){
+			bprint(i, "%T\tF%d, F%d, F%d", fptab2[j].name, i->rs1, i->rs2, i->rd);
+			return;
+		}
+	bprint(i, "%T%ux\tF%d, F%d, F%d", m, i->opf, i->rs1, i->rs2, i->rd);
+}
+
+static int
+sparcfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	ulong w, r1, r2;
+	char buf[8];
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	w = i.w0;
+	switch(w & 0xC1C00000){
+	case 0x00800000:		/* branch on int cond */
+	case 0x01800000:		/* branch on fp cond */
+	case 0x01C00000:		/* branch on copr cond */
+		foll[0] = pc+8;
+		foll[1] = pc + (i.simmdisp22<<2);
+		return 2;
+	}
+
+	if((w&0xC0000000) == 0x40000000){	/* CALL */
+		foll[0] = pc + (i.disp30<<2);
+		return 1;
+	}
+
+	if((w&0xC1F80000) == 0x81C00000){	/* JMPL */
+		sprint(buf, "R%ld", (w>>14)&0xF);
+		r1 = (*rget)(map, buf);
+		if(w & 0x2000)			/* JMPL R1+simm13 */
+			r2 = i.simm13;
+		else{				/* JMPL R1+R2 */
+			sprint(buf, "R%ld", w&0xF);
+			r2 = (*rget)(map, buf);
+		}
+		foll[0] = r1 + r2;
+		return 1;
+	}
+	foll[0] = pc+i.size*4;
+	return 1;
+}
--- /dev/null
+++ b/utils/libmach/kobj.c
@@ -1,0 +1,136 @@
+/*
+ * kobj.c - identify and parse a sparc object file
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "kc/k.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static	void	skip(Biobuf*, int);
+
+
+int
+_isk(char *s)
+{
+	return  s[0] == ANAME				/* ANAME */
+		&& s[1] == D_FILE			/* type */
+		&& s[2] == 1				/* sym */
+		&& s[3] == '<';				/* name of file */
+}
+
+
+int
+_readk(Biobuf *bp, Prog *p)
+{
+	int as, n;
+	Addr a;
+
+	as = Bgetc(bp);			/* as */
+	if(as < 0)
+		return 0;
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = beswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 5);		/* reg (1 byte); lineno (4 bytes) */
+	a = addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp, 1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type) {
+	default:
+	case D_NONE: case D_REG: case D_FREG: case D_CREG: case D_PREG:
+		break;
+	case D_BRANCH:
+	case D_OREG:
+	case D_ASI:
+	case D_CONST:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym!=0 && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/machdata.c
@@ -1,0 +1,450 @@
+/*
+ * Debugger utilities shared by at least two architectures
+ */
+
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+#define STARTSYM	"_main"
+#define PROFSYM		"_mainp"
+#define	FRAMENAME	".frame"
+
+extern	Machdata	mipsmach;
+
+int	asstype = AMIPS;		/* disassembler type */
+Machdata *machdata;		/* machine-dependent functions */
+
+int
+localaddr(Map *map, char *fn, char *var, uvlong *r, Rgetter rget)
+{
+	Symbol s;
+	uvlong fp, pc, sp, link;
+
+	if (!lookup(fn, 0, &s)) {
+		werrstr("function not found");
+		return -1;
+	}
+	pc = rget(map, mach->pc);
+	sp = rget(map, mach->sp);
+	if(mach->link)
+		link = rget(map, mach->link);
+	else
+		link = 0;
+	fp = machdata->findframe(map, s.value, pc, sp, link);
+	if (fp == 0) {
+		werrstr("stack frame not found");
+		return -1;
+	}
+
+	if (!var || !var[0]) {
+		*r = fp;
+		return 1;
+	}
+
+	if (findlocal(&s, var, &s) == 0) {
+		werrstr("local variable not found");
+		return -1;
+	}
+
+	switch (s.class) {
+	case CAUTO:
+		*r = fp - s.value;
+		break;
+	case CPARAM:		/* assume address size is stack width */
+		*r = fp + s.value + mach->szaddr;
+		break;
+	default:
+		werrstr("local variable not found: %d", s.class);
+		return -1;
+	}
+	return 1;
+}
+
+/*
+ * Print value v as s.name[+offset] if possible, or just v.
+ */
+int
+symoff(char *buf, int n, uvlong v, int space)
+{
+	Symbol s;
+	int r;
+	long delta;
+
+	r = delta = 0;		/* to shut compiler up */
+	if (v) {
+		r = findsym(v, space, &s);
+		if (r)
+			delta = v-s.value;
+		if (delta < 0)
+			delta = -delta;
+	}
+	if (v == 0 || r == 0)
+		return snprint(buf, n, "%llux", v);
+	if (s.type != 't' && s.type != 'T' && delta >= 4096)
+		return snprint(buf, n, "%llux", v);
+	else if (strcmp(s.name, ".string") == 0)
+		return snprint(buf, n, "%llux", v);
+	else if (delta)
+		return snprint(buf, n, "%s+%lux", s.name, delta);
+	else
+		return snprint(buf, n, "%s", s.name);
+}
+
+/*
+ *	Format floating point registers
+ *
+ *	Register codes in format field:
+ *	'X' - print as 32-bit hexadecimal value
+ *	'F' - 64-bit double register when modif == 'F'; else 32-bit single reg
+ *	'f' - 32-bit ieee float
+ *	'8' - big endian 80-bit ieee extended float
+ *	'3' - little endian 80-bit ieee extended float with hole in bytes 8&9
+ */
+int
+fpformat(Map *map, Reglist *rp, char *buf, int n, int modif)
+{
+	char reg[12];
+	ulong r;
+
+	switch(rp->rformat)
+	{
+	case 'X':
+		if (get4(map, rp->roffs, &r) < 0)
+			return -1;
+		snprint(buf, n, "%lux", r);
+		break;
+	case 'F':	/* first reg of double reg pair */
+		if (modif == 'F')
+		if ((rp->rformat=='F') || (((rp+1)->rflags&RFLT) && (rp+1)->rformat == 'f')) {
+			if (get1(map, rp->roffs, (uchar *)reg, 8) < 0)
+				return -1;
+			machdata->dftos(buf, n, reg);
+			if (rp->rformat == 'F')
+				return 1;
+			return 2;
+		}	
+			/* treat it like 'f' */
+		if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
+			return -1;
+		machdata->sftos(buf, n, reg);
+		break;
+	case 'f':	/* 32 bit float */
+		if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
+			return -1;
+		machdata->sftos(buf, n, reg);
+		break;
+	case '3':	/* little endian ieee 80 with hole in bytes 8&9 */
+		if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
+			return -1;
+		memmove(reg+10, reg+8, 2);	/* open hole */
+		memset(reg+8, 0, 2);		/* fill it */
+		leieee80ftos(buf, n, reg);
+		break;
+	case '8':	/* big-endian ieee 80 */
+		if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
+			return -1;
+		beieee80ftos(buf, n, reg);
+		break;
+	default:	/* unknown */
+		break;
+	}
+	return 1;
+}
+
+char *
+_hexify(char *buf, ulong p, int zeros)
+{
+	ulong d;
+
+	d = p/16;
+	if(d)
+		buf = _hexify(buf, d, zeros-1);
+	else
+		while(zeros--)
+			*buf++ = '0';
+	*buf++ = "0123456789abcdef"[p&0x0f];
+	return buf;
+}
+
+/*
+ * These routines assume that if the number is representable
+ * in IEEE floating point, it will be representable in the native
+ * double format.  Naive but workable, probably.
+ */
+int
+ieeedftos(char *buf, int n, ulong h, ulong l)
+{
+	double fr;
+	int exp;
+
+	if (n <= 0)
+		return 0;
+
+
+	if(h & (1L<<31)){
+		*buf++ = '-';
+		h &= ~(1L<<31);
+	}else
+		*buf++ = ' ';
+	n--;
+	if(l == 0 && h == 0)
+		return snprint(buf, n, "0.");
+	exp = (h>>20) & ((1L<<11)-1L);
+	if(exp == 0)
+		return snprint(buf, n, "DeN(%.8lux%.8lux)", h, l);
+	if(exp == ((1L<<11)-1L)){
+		if(l==0 && (h&((1L<<20)-1L)) == 0)
+			return snprint(buf, n, "Inf");
+		else
+			return snprint(buf, n, "NaN(%.8lux%.8lux)", h&((1L<<20)-1L), l);
+	}
+	exp -= (1L<<10) - 2L;
+	fr = l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	fr = ldexp(fr, exp);
+	return snprint(buf, n, "%.18g", fr);
+}
+
+int
+ieeesftos(char *buf, int n, ulong h)
+{
+	double fr;
+	int exp;
+
+	if (n <= 0)
+		return 0;
+
+	if(h & (1L<<31)){
+		*buf++ = '-';
+		h &= ~(1L<<31);
+	}else
+		*buf++ = ' ';
+	n--;
+	if(h == 0)
+		return snprint(buf, n, "0.");
+	exp = (h>>23) & ((1L<<8)-1L);
+	if(exp == 0)
+		return snprint(buf, n, "DeN(%.8lux)", h);
+	if(exp == ((1L<<8)-1L)){
+		if((h&((1L<<23)-1L)) == 0)
+			return snprint(buf, n, "Inf");
+		else
+			return snprint(buf, n, "NaN(%.8lux)", h&((1L<<23)-1L));
+	}
+	exp -= (1L<<7) - 2L;
+	fr = (h & ((1L<<23)-1L)) | (1L<<23);
+	fr /= 1L<<24;
+	fr = ldexp(fr, exp);
+	return snprint(buf, n, "%.9g", fr);
+}
+
+int
+beieeesftos(char *buf, int n, void *s)
+{
+	return ieeesftos(buf, n, beswal(*(ulong*)s));
+}
+
+int
+beieeedftos(char *buf, int n, void *s)
+{
+	return ieeedftos(buf, n, beswal(*(ulong*)s), beswal(((ulong*)(s))[1]));
+}
+
+int
+leieeesftos(char *buf, int n, void *s)
+{
+	return ieeesftos(buf, n, leswal(*(ulong*)s));
+}
+
+int
+leieeedftos(char *buf, int n, void *s)
+{
+	return ieeedftos(buf, n, leswal(((ulong*)(s))[1]), leswal(*(ulong*)s));
+}
+
+/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
+int
+beieee80ftos(char *buf, int n, void *s)
+{
+	uchar *reg = (uchar*)s;
+	int i;
+	ulong x;
+	uchar ieee[8+8];	/* room for slop */
+	uchar *p, *q;
+
+	memset(ieee, 0, sizeof(ieee));
+	/* sign */
+	if(reg[0] & 0x80)
+		ieee[0] |= 0x80;
+
+	/* exponent */
+	x = ((reg[0]&0x7F)<<8) | reg[1];
+	if(x == 0)		/* number is ±0 */
+		goto done;
+	if(x == 0x7FFF){
+		if(memcmp(reg+4, ieee+1, 8) == 0){ /* infinity */
+			x = 2047;
+		}else{				/* NaN */
+			x = 2047;
+			ieee[7] = 0x1;		/* make sure */
+		}
+		ieee[0] |= x>>4;
+		ieee[1] |= (x&0xF)<<4;
+		goto done;
+	}
+	x -= 0x3FFF;		/* exponent bias */
+	x += 1023;
+	if(x >= (1<<11) || ((reg[4]&0x80)==0 && x!=0))
+		return snprint(buf, n, "not in range");
+	ieee[0] |= x>>4;
+	ieee[1] |= (x&0xF)<<4;
+
+	/* mantissa */
+	p = reg+4;
+	q = ieee+1;
+	for(i=0; i<56; i+=8, p++, q++){	/* move one byte */
+		x = (p[0]&0x7F) << 1;
+		if(p[1] & 0x80)
+			x |= 1;
+		q[0] |= x>>4;
+		q[1] |= (x&0xF)<<4;
+	}
+    done:
+	return beieeedftos(buf, n, (void*)ieee);
+}
+
+int
+leieee80ftos(char *buf, int n, void *s)
+{
+	int i;
+	char *cp;
+	char b[12];
+
+	cp = (char*) s;
+	for(i=0; i<12; i++)
+		b[11-i] = *cp++;
+	return beieee80ftos(buf, n, b);
+}
+
+int
+cisctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	Symbol s;
+	int found, i;
+	uvlong opc, moved;
+
+	USED(link);
+	i = 0;
+	opc = 0;
+	while(pc && opc != pc) {
+		moved = pc2sp(pc);
+		if (moved == ~0)
+			break;
+		found = findsym(pc, CTEXT, &s);
+		if (!found)
+			break;
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		sp += moved;
+		opc = pc;
+		if (geta(map, sp, &pc) < 0)
+			break;
+		(*trace)(map, pc, sp, &s);
+		sp += mach->szaddr;	/*assumes address size = stack width*/
+		if(++i > 40)
+			break;
+	}
+	return i;
+}
+
+int
+risctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	int i;
+	Symbol s, f;
+	uvlong oldpc;
+
+	i = 0;
+	while(findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc == s.value)	/* at first instruction */
+			f.value = 0;
+		else if(findlocal(&s, FRAMENAME, &f) == 0)
+			break;
+
+		oldpc = pc;
+		if(s.type == 'L' || s.type == 'l' || pc <= s.value+mach->pcquant)
+			pc = link;
+		else
+			if (geta(map, sp, &pc) < 0)
+				break;
+
+		if(pc == 0 || (pc == oldpc && f.value == 0))
+			break;
+
+		sp += f.value;
+		(*trace)(map, pc-8, sp, &s);
+
+		if(++i > 40)
+			break;
+	}
+	return i;
+}
+
+uvlong
+ciscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s;
+	uvlong moved;
+
+	USED(link);
+	for(;;) {
+		moved = pc2sp(pc);
+		if (moved  == ~0)
+			break;
+		sp += moved;
+		findsym(pc, CTEXT, &s);
+		if (addr == s.value)
+			return sp;
+		if (geta(map, sp, &pc) < 0)
+			break;
+		sp += mach->szaddr;	/*assumes sizeof(addr) = stack width*/
+	}
+	return 0;
+}
+
+uvlong
+riscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s, f;
+
+	while (findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc == s.value)	/* at first instruction */
+			f.value = 0;
+		else
+		if(findlocal(&s, FRAMENAME, &f) == 0)
+			break;
+
+		sp += f.value;
+		if (s.value == addr)
+			return sp;
+
+		if (s.type == 'L' || s.type == 'l' || pc-s.value <= mach->szaddr*2)
+			pc = link;
+		else
+		if (geta(map, sp-f.value, &pc) < 0)
+			break;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/libmach/map.c
@@ -1,0 +1,203 @@
+/*
+ * file map routines
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+Map *
+newmap(Map *map, int n)
+{
+	int size;
+
+	size = sizeof(Map)+(n-1)*sizeof(struct segment);
+	if (map == 0)
+		map = malloc(size);
+	else
+		map = realloc(map, size);
+	if (map == 0) {
+		werrstr("out of memory: %r");
+		return 0;
+	}
+	memset(map, 0, size);
+	map->nsegs = n;
+	return map;
+}
+
+int
+setmap(Map *map, int fd, uvlong b, uvlong e, vlong f, char *name)
+{
+	int i;
+
+	if (map == 0)
+		return 0;
+	for (i = 0; i < map->nsegs; i++)
+		if (!map->seg[i].inuse)
+			break;
+	if (i >= map->nsegs)
+		return 0;
+	map->seg[i].b = b;
+	map->seg[i].e = e;
+	map->seg[i].f = f;
+	map->seg[i].inuse = 1;
+	map->seg[i].name = name;
+	map->seg[i].fd = fd;
+	return 1;
+}
+
+static uvlong
+stacktop(int pid)
+{
+	char buf[64];
+	int fd;
+	int n;
+	char *cp;
+
+	snprint(buf, sizeof(buf), "/proc/%d/segment", pid);
+	fd = open(buf, 0);
+	if (fd < 0)
+		return 0;
+	n = read(fd, buf, sizeof(buf)-1);
+	close(fd);
+	buf[n] = 0;
+	if (strncmp(buf, "Stack", 5))
+		return 0;
+	for (cp = buf+5; *cp && *cp == ' '; cp++)
+		;
+	if (!*cp)
+		return 0;
+	cp = strchr(cp, ' ');
+	if (!cp)
+		return 0;
+	while (*cp && *cp == ' ')
+		cp++;
+	if (!*cp)
+		return 0;
+	return strtoull(cp, 0, 16);
+}
+
+Map*
+attachproc(int pid, int kflag, int corefd, Fhdr *fp)
+{
+	char buf[64], *regs;
+	int fd;
+	Map *map;
+	uvlong n;
+
+	map = newmap(0, 4);
+	if (!map)
+		return 0;
+	if(kflag)
+		regs = "kregs";
+	else
+		regs = "regs";
+	if (mach->regsize) {
+		sprint(buf, "/proc/%d/%s", pid, regs);
+		fd = open(buf, ORDWR);
+		if(fd < 0)
+			fd = open(buf, OREAD);
+		if(fd < 0) {
+			free(map);
+			return 0;
+		}
+		setmap(map, fd, 0, mach->regsize, 0, "regs");
+	}
+	if (mach->fpregsize) {
+		sprint(buf, "/proc/%d/fpregs", pid);
+		fd = open(buf, ORDWR);
+		if(fd < 0)
+			fd = open(buf, OREAD);
+		if(fd < 0) {
+			close(map->seg[0].fd);
+			free(map);
+			return 0;
+		}
+		setmap(map, fd, mach->regsize, mach->regsize+mach->fpregsize, 0, "fpregs");
+	}
+	setmap(map, corefd, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "text");
+	if(kflag || fp->dataddr >= mach->utop) {
+		setmap(map, corefd, fp->dataddr, ~0, fp->dataddr, "data");
+		return map;
+	}
+	n = stacktop(pid);
+	if (n == 0) {
+		setmap(map, corefd, fp->dataddr, mach->utop, fp->dataddr, "data");
+		return map;
+	}
+	setmap(map, corefd, fp->dataddr, n, fp->dataddr, "data");
+	return map;
+}
+	
+int
+findseg(Map *map, char *name)
+{
+	int i;
+
+	if (!map)
+		return -1;
+	for (i = 0; i < map->nsegs; i++)
+		if (map->seg[i].inuse && !strcmp(map->seg[i].name, name))
+			return i;
+	return -1;
+}
+
+void
+unusemap(Map *map, int i)
+{
+	if (map != 0 && 0 <= i && i < map->nsegs)
+		map->seg[i].inuse = 0;
+}
+
+Map*
+loadmap(Map *map, int fd, Fhdr *fp)
+{
+	map = newmap(map, 2);
+	if (map == 0)
+		return 0;
+
+	map->seg[0].b = fp->txtaddr;
+	map->seg[0].e = fp->txtaddr+fp->txtsz;
+	map->seg[0].f = fp->txtoff;
+	map->seg[0].fd = fd;
+	map->seg[0].inuse = 1;
+	map->seg[0].name = "text";
+	map->seg[1].b = fp->dataddr;
+	map->seg[1].e = fp->dataddr+fp->datsz;
+	map->seg[1].f = fp->datoff;
+	map->seg[1].fd = fd;
+	map->seg[1].inuse = 1;
+	map->seg[1].name = "data";
+	return map;
+}
+
+Map*
+attachremt(int fd, Fhdr *f)
+{
+	Map *m;
+	ulong txt;
+
+	m = newmap(0, 3);
+	if (m == 0)
+		return 0;
+
+	/* Space for mach structures */
+	txt = f->txtaddr;
+	if(txt > 8*4096)
+		txt -= 8*4096;
+
+	setmap(m, fd, txt, f->txtaddr+f->txtsz, txt, "*text");
+	/*setmap(m, fd, f->dataddr, 0xffffffff, f->dataddr, "*data");*/ /* pc heap is < KTZERO */
+	setmap(m, fd, 4096, 0xffffffff, 4096, "*data");
+	setmap(m, fd, 0x0, mach->regsize, 0, "kreg");
+
+	return m;
+}
+
+void
+setmapio(Map *map, int i, Rsegio get, Rsegio put)
+{
+	if (map != 0 && 0 <= i && i < map->nsegs) {
+		map->seg[i].mget = get;
+		map->seg[i].mput = put;
+	}
+}
--- /dev/null
+++ b/utils/libmach/mkfile
@@ -1,0 +1,43 @@
+<../../mkconfig
+
+LIB=libmach.a
+OFILES=\
+	4.$O\
+	5.$O\
+	6.$O\
+	8.$O\
+	9.$O\
+	k.$O\
+	q.$O\
+	t.$O\
+	v.$O\
+	5db.$O\
+	8db.$O\
+	kdb.$O\
+	qdb.$O\
+	tdb.$O\
+	vdb.$O\
+	5obj.$O\
+	6obj.$O\
+	8obj.$O\
+	9obj.$O\
+	kobj.$O\
+	qobj.$O\
+	vobj.$O\
+	obj.$O\
+	map.$O\
+	swap.$O\
+	sym.$O\
+	access.$O\
+	machdata.$O\
+	setmach.$O\
+	executable.$O\
+	vcodas.$O\
+
+HFILES=../include/mach.h ../include/a.out.h bootexec.h elf.h ureg4.h ureg6.h ureg8.h uregk.h uregv.h ureg5.h
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+CFLAGS= $CFLAGS -I../include -I..
+
+package:QV:
+	$TRUE
--- /dev/null
+++ b/utils/libmach/obj.c
@@ -1,0 +1,329 @@
+/*
+ * obj.c
+ * routines universal to all object files
+ */
+#include <lib9.h>
+#include <ctype.h>
+#include <bio.h>
+#include <ar.h>
+#include "mach.h"
+#include "obj.h"
+
+#define islocal(t)	((t)=='a' || (t)=='p')
+
+enum
+{
+	NNAMES	= 50,
+	MAXIS	= 8,		/* max length to determine if a file is a .? file */
+	MAXOFF	= 0x7fffffff,	/* larger than any possible local offset */
+	NHASH	= 1024,		/* must be power of two */
+	HASHMUL	= 79L,
+};
+
+/* in [$OS].c */
+int	_is5(char*),
+	_is6(char*),
+	_is8(char*),
+	_is9(char*),
+	_isk(char*),
+	_isq(char*),
+	_isv(char*),
+	_read5(Biobuf*, Prog*),
+	_read6(Biobuf*, Prog*),
+	_read8(Biobuf*, Prog*),
+	_read9(Biobuf*, Prog*),
+	_readk(Biobuf*, Prog*),
+	_readq(Biobuf*, Prog*),
+	_readv(Biobuf*, Prog*);
+
+typedef struct Obj	Obj;
+typedef struct Symtab	Symtab;
+
+struct	Obj		/* functions to handle each intermediate (.$O) file */
+{
+	char	*name;				/* name of each $O file */
+	int	(*is)(char*);			/* test for each type of $O file */
+	int	(*read)(Biobuf*, Prog*);	/* read for each type of $O file*/
+};
+
+static Obj	obj[] =
+{			/* functions to identify and parse each type of obj */
+	/*[Obj68020]*/	{0, 0,},
+	/*[ObjSparc]*/	"sparc .k",	_isk, _readk,
+	/*[ObjMips]*/	"mips .v",	_isv, _readv,
+	/*[Obj386]*/	"386 .8",	_is8, _read8,
+	/*[Obj960]*/	{0, 0,},
+	/*[Obj3210]*/	{0, 0,},
+	/*[ObjMips2]*/	{0, 0,},
+	/*[Obj29000]*/	{0, 0,},
+	/*[ObjArm]*/	"arm .5",	_is5, _read5,
+	/*[ObjPower]*/	"power .q",	_isq, _readq,
+	/*[ObjMips2le]*/	{0, 0,},
+	/*[ObjAlpha]*/	{0, 0,},
+	/*[ObjSparc64]*/	{0, 0,},
+	/*[ObjAmd64]*/	"amd64 .6",	_is6, _read6,
+	/*[ObjSpim]*/	{0, 0,},
+	/*[ObjPower64]*/	"power64 .9",	_is9, _read9,
+	/*[Maxobjtype]*/	0, 0
+};
+
+struct	Symtab
+{
+	struct	Sym 	s;
+	struct	Symtab	*next;
+};
+
+static	Symtab *hash[NHASH];
+static	Sym	*names[NNAMES];	/* working set of active names */
+
+static	int	processprog(Prog*,int);	/* decode each symbol reference */
+static	void	objreset(void);
+static	void	objlookup(int, char *, int, uint);
+static	void 	objupdate(int, int);
+
+int
+objtype(Biobuf *bp, char **name)
+{
+	int i;
+	char buf[MAXIS];
+
+	if(Bread(bp, buf, MAXIS) < MAXIS)
+		return -1;
+	Bseek(bp, -MAXIS, 1);
+	for (i = 0; i < Maxobjtype; i++) {
+		if (obj[i].is && (*obj[i].is)(buf)) {
+			if (name)
+				*name = obj[i].name;
+			return i;
+		}
+	}
+	return -1;
+}
+
+int
+isar(Biobuf *bp)
+{
+	int n;
+	char magbuf[SARMAG];
+
+	n = Bread(bp, magbuf, SARMAG);
+	if(n == SARMAG && strncmp(magbuf, ARMAG, SARMAG) == 0)
+		return 1;
+	return 0;
+}
+
+/*
+ * determine what kind of object file this is and process it.
+ * return whether or not this was a recognized intermediate file.
+ */
+int
+readobj(Biobuf *bp, int objtype)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p))
+		if (!processprog(&p, 1))
+			return 0;
+	return 1;
+}
+
+int
+readar(Biobuf *bp, int objtype, vlong end, int doautos)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p) && Boffset(bp) < end)
+		if (!processprog(&p, doautos))
+			return 0;
+	return 1;
+}
+
+/*
+ *	decode a symbol reference or definition
+ */
+static	int
+processprog(Prog *p, int doautos)
+{
+	if(p->kind == aNone)
+		return 1;
+	if(p->sym < 0 || p->sym >= NNAMES)
+		return 0;
+	switch(p->kind)
+	{
+	case aName:
+		if (!doautos)
+		if(p->type != 'U' && p->type != 'b')
+			break;
+		objlookup(p->sym, p->id, p->type, p->sig);
+		break;
+	case aText:
+		objupdate(p->sym, 'T');
+		break;
+	case aData:
+		objupdate(p->sym, 'D');
+		break;
+	default:
+		break;
+	}
+	return 1;
+}
+
+/*
+ * find the entry for s in the symbol array.
+ * make a new entry if it is not already there.
+ */
+static void
+objlookup(int id, char *name, int type, uint sig)
+{
+	long h;
+	char *cp;
+	Sym *s;
+	Symtab *sp;
+
+	s = names[id];
+	if(s && strcmp(s->name, name) == 0) {
+		s->type = type;
+		s->sig = sig;
+		return;
+	}
+
+	h = *name;
+	for(cp = name+1; *cp; h += *cp++)
+		h *= HASHMUL;
+	if(h < 0)
+		h = ~h;
+	h &= (NHASH-1);
+	if (type == 'U' || type == 'b' || islocal(type)) {
+		for(sp = hash[h]; sp; sp = sp->next)
+			if(strcmp(sp->s.name, name) == 0) {
+				switch(sp->s.type) {
+				case 'T':
+				case 'D':
+				case 'U':
+					if (type == 'U') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 't':
+				case 'd':
+				case 'b':
+					if (type == 'b') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 'a':
+				case 'p':
+					if (islocal(type)) {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+	}
+	sp = malloc(sizeof(Symtab));
+	sp->s.name = name;
+	sp->s.type = type;
+	sp->s.sig = sig;
+	sp->s.value = islocal(type) ? MAXOFF : 0;
+	names[id] = &sp->s;
+	sp->next = hash[h];
+	hash[h] = sp;
+	return;
+}
+/*
+ *	traverse the symbol lists
+ */
+void
+objtraverse(void (*fn)(Sym*, void*), void *pointer)
+{
+	int i;
+	Symtab *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s; s = s->next)
+			(*fn)(&s->s, pointer);
+}
+
+/*
+ * update the offset information for a 'a' or 'p' symbol in an intermediate file
+ */
+void
+_offset(int id, vlong off)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0] && islocal(s->type) && s->value > off)
+		s->value = off;
+}
+
+/*
+ * update the type of a global text or data symbol
+ */
+static void 
+objupdate(int id, int type)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0])
+		if (s->type == 'U')
+			s->type = type;
+		else if (s->type == 'b')
+			s->type = tolower(type);
+}
+
+/*
+ * look for the next file in an archive
+ */
+int
+nextar(Biobuf *bp, int offset, char *buf)
+{
+	struct ar_hdr a;
+	int i, r;
+	long arsize;
+
+	if (offset&01)
+		offset++;
+	Bseek(bp, offset, 0);
+	r = Bread(bp, &a, SAR_HDR);
+	if(r != SAR_HDR)
+		return 0;
+	if(strncmp(a.fmag, ARFMAG, sizeof(a.fmag)))
+		return -1;
+	for(i=0; i<sizeof(a.name) && i<SARNAME && a.name[i] != ' '; i++)
+		buf[i] = a.name[i];
+	buf[i] = 0;
+	arsize = strtol(a.size, 0, 0);
+	if (arsize&1)
+		arsize++;
+	return arsize + SAR_HDR;
+}
+
+static void
+objreset(void)
+{
+	int i;
+	Symtab *s, *n;
+
+	for(i = 0; i < NHASH; i++) {
+		for(s = hash[i]; s; s = n) {
+			n = s->next;
+			free(s->s.name);
+			free(s);
+		}
+		hash[i] = 0;
+	}
+	memset(names, 0, sizeof names);
+}
--- /dev/null
+++ b/utils/libmach/obj.h
@@ -1,0 +1,25 @@
+/*
+ * obj.h -- defs for dealing with object files
+ */
+
+typedef enum Kind		/* variable defs and references in obj */
+{
+	aNone,			/* we don't care about this prog */
+	aName,			/* introduces a name */
+	aText,			/* starts a function */
+	aData,			/* references to a global object */
+} Kind;
+
+typedef struct	Prog	Prog;
+
+struct Prog		/* info from .$O files */
+{
+	Kind	kind;		/* what kind of symbol */
+	char	type;		/* type of the symbol: ie, 'T', 'a', etc. */
+	char	sym;		/* index of symbol's name */
+	char	*id;		/* name for the symbol, if it introduces one */
+	uint	sig;		/* type signature for symbol */
+};
+
+#define UNKNOWN	'?'
+void		_offset(int, vlong);
--- /dev/null
+++ b/utils/libmach/q.c
@@ -1,0 +1,123 @@
+/*
+ * PowerPC definition
+ *	forsyth@terzarima.net
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "uregq.h"
+#include "mach.h"
+
+
+#define	REGOFF(x)	(ulong) (&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(sp)
+#define PC		REGOFF(pc)
+#define	R3		REGOFF(r3)	/* return reg */
+#define	LR		REGOFF(lr)
+#define R31		REGOFF(r31)
+#define FP_REG(x)	(R31+4+8*(x))
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	(8*33)	
+
+Reglist powerreglist[] = {
+	{"CAUSE",	REGOFF(cause),	RINT|RRDONLY,	'X'},
+	{"SRR1",	REGOFF(srr1),	RINT|RRDONLY,	'X'},
+	{"PC",		REGOFF(pc),	RINT,		'X'},
+	{"LR",		REGOFF(lr),	RINT,		'X'},
+	{"CR",		REGOFF(cr),	RINT,		'X'},
+	{"XER",		REGOFF(xer),	RINT,		'X'},
+	{"CTR",		REGOFF(ctr),	RINT,		'X'},
+	{"PC",		PC,		RINT,		'X'},
+	{"SP",		SP,		RINT,		'X'},
+	{"R0",		REGOFF(r0),	RINT,		'X'},
+	/* R1 is SP */
+	{"R2",		REGOFF(r2),	RINT,		'X'},
+	{"R3",		REGOFF(r3),	RINT,		'X'},
+	{"R4",		REGOFF(r4),	RINT,		'X'},
+	{"R5",		REGOFF(r5),	RINT,		'X'},
+	{"R6",		REGOFF(r6),	RINT,		'X'},
+	{"R7",		REGOFF(r7),	RINT,		'X'},
+	{"R8",		REGOFF(r8),	RINT,		'X'},
+	{"R9",		REGOFF(r9),	RINT,		'X'},
+	{"R10",		REGOFF(r10),	RINT,		'X'},
+	{"R11",		REGOFF(r11),	RINT,		'X'},
+	{"R12",		REGOFF(r12),	RINT,		'X'},
+	{"R13",		REGOFF(r13),	RINT,		'X'},
+	{"R14",		REGOFF(r14),	RINT,		'X'},
+	{"R15",		REGOFF(r15),	RINT,		'X'},
+	{"R16",		REGOFF(r16),	RINT,		'X'},
+	{"R17",		REGOFF(r17),	RINT,		'X'},
+	{"R18",		REGOFF(r18),	RINT,		'X'},
+	{"R19",		REGOFF(r19),	RINT,		'X'},
+	{"R20",		REGOFF(r20),	RINT,		'X'},
+	{"R21",		REGOFF(r21),	RINT,		'X'},
+	{"R22",		REGOFF(r22),	RINT,		'X'},
+	{"R23",		REGOFF(r23),	RINT,		'X'},
+	{"R24",		REGOFF(r24),	RINT,		'X'},
+	{"R25",		REGOFF(r25),	RINT,		'X'},
+	{"R26",		REGOFF(r26),	RINT,		'X'},
+	{"R27",		REGOFF(r27),	RINT,		'X'},
+	{"R28",		REGOFF(r28),	RINT,		'X'},
+	{"R29",		REGOFF(r29),	RINT,		'X'},
+	{"R30",		REGOFF(r30),	RINT,		'X'},
+	{"R31",		REGOFF(r31),	RINT,		'X'},
+	{"F0",		FP_REG(0),	RFLT,		'F'},
+	{"F1",		FP_REG(1),	RFLT,		'F'},
+	{"F2",		FP_REG(2),	RFLT,		'F'},
+	{"F3",		FP_REG(3),	RFLT,		'F'},
+	{"F4",		FP_REG(4),	RFLT,		'F'},
+	{"F5",		FP_REG(5),	RFLT,		'F'},
+	{"F6",		FP_REG(6),	RFLT,		'F'},
+	{"F7",		FP_REG(7),	RFLT,		'F'},
+	{"F8",		FP_REG(8),	RFLT,		'F'},
+	{"F9",		FP_REG(9),	RFLT,		'F'},
+	{"F10",		FP_REG(10),	RFLT,		'F'},
+	{"F11",		FP_REG(11),	RFLT,		'F'},
+	{"F12",		FP_REG(12),	RFLT,		'F'},
+	{"F13",		FP_REG(13),	RFLT,		'F'},
+	{"F14",		FP_REG(14),	RFLT,		'F'},
+	{"F15",		FP_REG(15),	RFLT,		'F'},
+	{"F16",		FP_REG(16),	RFLT,		'F'},
+	{"F17",		FP_REG(17),	RFLT,		'F'},
+	{"F18",		FP_REG(18),	RFLT,		'F'},
+	{"F19",		FP_REG(19),	RFLT,		'F'},
+	{"F20",		FP_REG(20),	RFLT,		'F'},
+	{"F21",		FP_REG(21),	RFLT,		'F'},
+	{"F22",		FP_REG(22),	RFLT,		'F'},
+	{"F23",		FP_REG(23),	RFLT,		'F'},
+	{"F24",		FP_REG(24),	RFLT,		'F'},
+	{"F25",		FP_REG(25),	RFLT,		'F'},
+	{"F26",		FP_REG(26),	RFLT,		'F'},
+	{"F27",		FP_REG(27),	RFLT,		'F'},
+	{"F28",		FP_REG(28),	RFLT,		'F'},
+	{"F29",		FP_REG(29),	RFLT,		'F'},
+	{"F30",		FP_REG(30),	RFLT,		'F'},
+	{"F31",		FP_REG(31),	RFLT,		'F'},
+	{"FPSCR",	FP_REG(32)+4,	RFLT,		'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mpower =
+{
+	"power",
+	MPOWER,		/* machine type */
+	powerreglist,	/* register set */
+	REGSIZE,	/* number of bytes in register set */
+	FPREGSIZE,	/* number of bytes in FP register set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"LR",		/* name of link register */
+	"setSB",	/* static base register name */
+	0,		/* value */
+	0x1000,		/* page size */
+	0x80000000U,	/* kernel base */
+	0xF0000000U,	/* kernel text mask */
+	0x7FFFFFFFU,	/* user stack top */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/qdb.c
@@ -1,0 +1,1363 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+/*
+ * PowerPC-specific debugger interface,
+ * including 64-bit modes
+ *	forsyth@terzarima.net
+ */
+
+static	char	*powerexcep(Map*, Rgetter);
+static	int	powerfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	powerinst(Map*, uvlong, char, char*, int);
+static	int	powerinstlen(Map*, uvlong);
+static	int	powerdas(Map*, uvlong, char*, int);
+
+/*
+ *	Machine description
+ */
+Machdata powermach =
+{
+	{0x07f, 0xe0, 0x00, 0x08},		/* breakpoint (tw 31,r0,r0) */
+	4,			/* break point size */
+
+	beswab,			/* short to local byte order */
+	beswal,			/* long to local byte order */
+	beswav,			/* vlong to local byte order */
+	risctrace,		/* print C traceback */
+	riscframe,		/* frame finder */
+	powerexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	beieeesftos,		/* single precision float printer */
+	beieeedftos,		/* double precisioin float printer */
+	powerfoll,		/* following addresses */
+	powerinst,		/* print instruction */
+	powerdas,		/* dissembler */
+	powerinstlen,		/* instruction size */
+};
+
+static char *excname[] =
+{
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"i/o controller interface error",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved",
+	"ITLB miss",
+	"DTLB load miss",
+	"DTLB store miss",
+	"instruction address breakpoint"
+	"SMI interrupt"
+	"reserved 15",
+	"reserved 16",
+	"reserved 17",
+	"reserved 18",
+	"reserved 19",
+	"reserved 1A",
+	/* the following are made up on a program exception */
+	"floating point exception",		/* FPEXC */
+	"illegal instruction",
+	"privileged instruction",
+	"trap",
+	"illegal operation",
+	"breakpoint",	/* 20 */
+};
+
+static char*
+powerexcep(Map *map, Rgetter rget)
+{
+	long c;
+	static char buf[32];
+
+	c = (*rget)(map, "CAUSE") >> 8;
+	if(c < nelem(excname))
+		return excname[c];
+	sprint(buf, "unknown trap #%lx", c);
+	return buf;
+}
+
+/*
+ * disassemble PowerPC opcodes
+ */
+
+#define	REGSP	1	/* should come from q.out.h, but there's a clash */
+#define	REGSB	2
+
+static	char FRAMENAME[] = ".frame";
+
+static Map *mymap;
+
+/*
+ * ibm conventions for these: bit 0 is top bit
+ *	from table 10-1
+ */
+typedef struct {
+	uchar	aa;		/* bit 30 */
+	uchar	crba;		/* bits 11-15 */
+	uchar	crbb;		/* bits 16-20 */
+	long	bd;		/* bits 16-29 */
+	uchar	crfd;		/* bits 6-8 */
+	uchar	crfs;		/* bits 11-13 */
+	uchar	bi;		/* bits 11-15 */
+	uchar	bo;		/* bits 6-10 */
+	uchar	crbd;		/* bits 6-10 */
+	/* union { */
+		short	d;	/* bits 16-31 */
+		short	simm;
+		ushort	uimm;
+	/*}; */
+	uchar	fm;		/* bits 7-14 */
+	uchar	fra;		/* bits 11-15 */
+	uchar	frb;		/* bits 16-20 */
+	uchar	frc;		/* bits 21-25 */
+	uchar	frs;		/* bits 6-10 */
+	uchar	frd;		/* bits 6-10 */
+	uchar	crm;		/* bits 12-19 */
+	long	li;		/* bits 6-29 || b'00' */
+	uchar	lk;		/* bit 31 */
+	uchar	mb;		/* bits 21-25 */
+	uchar	me;		/* bits 26-30 */
+	uchar	xmbe;		/* bits 26,21-25: mb[5] || mb[0:4], also xme */
+	uchar	xsh;		/* bits 30,16-20: sh[5] || sh[0:4] */
+	uchar	nb;		/* bits 16-20 */
+	uchar	op;		/* bits 0-5 */
+	uchar	oe;		/* bit 21 */
+	uchar	ra;		/* bits 11-15 */
+	uchar	rb;		/* bits 16-20 */
+	uchar	rc;		/* bit 31 */
+	/* union { */
+		uchar	rs;	/* bits 6-10 */
+		uchar	rd;
+	/*};*/
+	uchar	sh;		/* bits 16-20 */
+	ushort	spr;		/* bits 11-20 */
+	uchar	to;		/* bits 6-10 */
+	uchar	imm;		/* bits 16-19 */
+	ushort	xo;		/* bits 21-30, 22-30, 26-30, or 30 (beware) */
+	uvlong	imm64;
+	long w0;
+	long w1;
+	uvlong	addr;		/* pc of instruction */
+	short	target;
+	short	m64;		/* 64-bit mode */
+	char	*curr;		/* current fill level in output buffer */
+	char	*end;		/* end of buffer */
+	int 	size;		/* number of longs in instr */
+	char	*err;		/* errmsg */
+} Instr;
+
+#define	IBF(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1))))
+#define	IB(v,b) IBF((v),(b),(b))
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static int
+decode(uvlong pc, Instr *i)
+{
+	ulong w;
+
+	if (get4(mymap, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->m64 = asstype == APOWER64;
+	i->aa = IB(w, 30);
+	i->crba = IBF(w, 11, 15);
+	i->crbb = IBF(w, 16, 20);
+	i->bd = IBF(w, 16, 29)<<2;
+	if(i->bd & 0x8000)
+		i->bd |= ~0L<<16;
+	i->crfd = IBF(w, 6, 8);
+	i->crfs = IBF(w, 11, 13);
+	i->bi = IBF(w, 11, 15);
+	i->bo = IBF(w, 6, 10);
+	i->crbd = IBF(w, 6, 10);
+	i->uimm = IBF(w, 16, 31);	/* also d, simm */
+	i->fm = IBF(w, 7, 14);
+	i->fra = IBF(w, 11, 15);
+	i->frb = IBF(w, 16, 20);
+	i->frc = IBF(w, 21, 25);
+	i->frs = IBF(w, 6, 10);
+	i->frd = IBF(w, 6, 10);
+	i->crm = IBF(w, 12, 19);
+	i->li = IBF(w, 6, 29)<<2;
+	if(IB(w, 6))
+		i->li |= ~0<<25;
+	i->lk = IB(w, 31);
+	i->mb = IBF(w, 21, 25);
+	i->me = IBF(w, 26, 30);
+	i->xmbe = (IB(w,26)<<5) | i->mb;
+	i->nb = IBF(w, 16, 20);
+	i->op = IBF(w, 0, 5);
+	i->oe = IB(w, 21);
+	i->ra = IBF(w, 11, 15);
+	i->rb = IBF(w, 16, 20);
+	i->rc = IB(w, 31);
+	i->rs = IBF(w, 6, 10);	/* also rd */
+	i->sh = IBF(w, 16, 20);
+	i->xsh = (IB(w, 30)<<5) | i->sh;
+	i->spr = IBF(w, 11, 20);
+	i->to = IBF(w, 6, 10);
+	i->imm = IBF(w, 16, 19);
+	i->xo = IBF(w, 21, 30);		/* bits 21-30, 22-30, 26-30, or 30 (beware) */
+	if(i->op == 58){	/* class of 64-bit loads */
+		i->xo = i->simm & 3;
+		i->simm &= ~3;
+	}
+	i->imm64 = i->simm;
+	if(i->op == 15)
+		i->imm64 <<= 16;
+	else if(i->op == 25 || i->op == 27 || i->op == 29)
+		i->imm64 = (uvlong)(i->uimm<<16);
+	i->w0 = w;
+	i->target = -1;
+	i->addr = pc;
+	i->size = 1;
+	return 1;
+}
+
+static int
+mkinstr(uvlong pc, Instr *i)
+{
+	Instr x;
+
+	if(decode(pc, i) < 0)
+		return -1;
+	/*
+	 * combine ADDIS/ORI (CAU/ORIL) into MOVW
+	 * also ORIS/ORIL for unsigned in 64-bit mode
+	 */
+	if ((i->op == 15 || i->op == 25) && i->ra==0) {
+		if(decode(pc+4, &x) < 0)
+			return -1;
+		if (x.op == 24 && x.rs == x.ra && x.ra == i->rd) {
+			i->imm64 |= (x.imm64 & 0xFFFF);
+			if(i->op != 15)
+				i->imm64 &= 0xFFFFFFFFUL;
+			i->w1 = x.w0;
+			i->target = x.rd;
+			i->size++;
+			return 1;
+		}
+	}
+	return 1;
+}
+
+static int
+plocal(Instr *i)
+{
+	long offset;
+	Symbol s;
+
+	if (!findsym(i->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s))
+		return -1;
+	offset = s.value - i->imm64;
+	if (offset > 0) {
+		if(getauto(&s, offset, CAUTO, &s)) {
+			bprint(i, "%s+%lld(SP)", s.name, s.value);
+			return 1;
+		}
+	} else {
+		if (getauto(&s, -offset-4, CPARAM, &s)) {
+			bprint(i, "%s+%ld(FP)", s.name, -offset);
+			return 1;
+		}
+	}
+	return -1;
+}
+
+static int
+pglobal(Instr *i, uvlong off, int anyoff, char *reg)
+{
+	Symbol s, s2;
+	uvlong off1;
+
+	if(findsym(off, CANY, &s) &&
+	   s.value-off < 4096 &&
+	   (s.class == CDATA || s.class == CTEXT)) {
+		if(off==s.value && s.name[0]=='$'){
+			off1 = 0;
+			geta(mymap, s.value, &off1);
+			if(off1 && findsym(off1, CANY, &s2) && s2.value == off1){
+				bprint(i, "$%s%s", s2.name, reg);
+				return 1;
+			}
+		}
+		bprint(i, "%s", s.name);
+		if (s.value != off)
+			bprint(i, "+%llux", off-s.value);
+		bprint(i, reg);
+		return 1;
+	}
+	if(!anyoff)
+		return 0;
+	bprint(i, "%llux%s", off, reg);
+	return 1;
+}
+
+static void
+address(Instr *i)
+{
+	if (i->ra == REGSP && plocal(i) >= 0)
+		return;
+	if (i->ra == REGSB && mach->sb && pglobal(i, mach->sb+i->imm64, 0, "(SB)") >= 0)
+		return;
+	if(i->simm < 0)
+		bprint(i, "-%x(R%d)", -i->simm, i->ra);
+	else
+		bprint(i, "%llux(R%d)", i->imm64, i->ra);
+}
+
+static	char	*tcrbits[] = {"LT", "GT", "EQ", "VS"};
+static	char	*fcrbits[] = {"GE", "LE", "NE", "VC"};
+
+typedef struct Opcode Opcode;
+
+struct Opcode {
+	uchar	op;
+	ushort	xo;
+	ushort	xomask;
+	char	*mnemonic;
+	void	(*f)(Opcode *, Instr *);
+	char	*ken;
+	int	flags;
+};
+
+static void format(char *, Instr *, char *);
+
+static void
+branch(Opcode *o, Instr *i)
+{
+	char buf[8];
+	int bo, bi;
+
+	bo = i->bo & ~1;	/* ignore prediction bit */
+	if(bo==4 || bo==12 || bo==20) {	/* simple forms */
+		if(bo != 20) {
+			bi = i->bi&3;
+			sprint(buf, "B%s%%L", bo==12? tcrbits[bi]: fcrbits[bi]);
+			format(buf, i, nil);
+			bprint(i, "\t");
+			if(i->bi > 4)
+				bprint(i, "CR(%d),", i->bi/4);
+		} else
+			format("BR%L\t", i, nil);
+		if(i->op == 16)
+			format(0, i, "%J");
+		else if(i->op == 19 && i->xo == 528)
+			format(0, i, "(CTR)");
+		else if(i->op == 19 && i->xo == 16)
+			format(0, i, "(LR)");
+	} else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+addi(Opcode *o, Instr *i)
+{
+	if (i->op==14 && i->ra == 0)
+		format("MOVW", i, "%i,R%d");
+	else if (i->ra == REGSB) {
+		bprint(i, "MOVW\t$");
+		address(i);
+		bprint(i, ",R%d", i->rd);
+	} else if(i->op==14 && i->simm < 0) {
+		bprint(i, "SUB\t$%d,R%d", -i->simm, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	} else if(i->ra == i->rd) {
+		format(o->mnemonic, i, "%i");
+		bprint(i, ",R%d", i->rd);
+	} else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+addis(Opcode *o, Instr *i)
+{
+	long v;
+
+	v = i->imm64;
+	if (i->op==15 && i->ra == 0)
+		bprint(i, "MOVW\t$%lux,R%d", v, i->rd);
+	else if (i->op==15 && i->ra == REGSB) {
+		bprint(i, "MOVW\t$");
+		address(i);
+		bprint(i, ",R%d", i->rd);
+	} else if(i->op==15 && v < 0) {
+		bprint(i, "SUB\t$%ld,R%d", -v, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	} else {
+		format(o->mnemonic, i, nil);
+		bprint(i, "\t$%ld,R%d", v, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	}
+}
+
+static void
+andi(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "%I,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+gencc(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, o->ken);
+}
+
+static void
+gen(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, o->ken);
+	if (i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+ldx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b),R%d");
+	else
+		format(o->mnemonic, i, "(R%b+R%a),R%d");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+stx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "R%d,(R%b)");
+	else
+		format(o->mnemonic, i, "R%d,(R%b+R%a)");
+	if(i->rc && i->xo != 150)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+fldx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b),F%d");
+	else
+		format(o->mnemonic, i, "(R%b+R%a),F%d");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+fstx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "F%d,(R%b)");
+	else
+		format(o->mnemonic, i, "F%d,(R%b+R%a)");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+dcb(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b)");
+	else
+		format(o->mnemonic, i, "(R%b+R%a)");
+	if(i->rd)
+		bprint(i, " [illegal Rd]");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+lw(Opcode *o, Instr *i, char r)
+{
+	format(o->mnemonic, i, nil);
+	bprint(i, "\t");
+	address(i);
+	bprint(i, ",%c%d", r, i->rd);
+}
+
+static void
+load(Opcode *o, Instr *i)
+{
+	lw(o, i, 'R');
+}
+
+static void
+fload(Opcode *o, Instr *i)
+{
+	lw(o, i, 'F');
+}
+
+static void
+sw(Opcode *o, Instr *i, char r)
+{
+	int offset;
+	Symbol s;
+
+	if (i->rs == REGSP) {
+		if (findsym(i->addr, CTEXT, &s) && findlocal(&s, FRAMENAME, &s)) {
+			offset = s.value-i->imm64;
+			if (offset > 0 && getauto(&s, offset, CAUTO, &s)) {
+				format(o->mnemonic, i, nil);
+				bprint(i, "\t%c%d,%s-%d(SP)", r, i->rd, s.name, offset);
+				return;
+			}
+		}
+	}
+	if (i->rs == REGSB && mach->sb) {
+		format(o->mnemonic, i, nil);
+		bprint(i, "\t%c%d,", r, i->rd);
+		address(i);
+		return;
+	}
+	if (r == 'F')
+		format(o->mnemonic, i, "F%d,%l");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+store(Opcode *o, Instr *i)
+{
+	sw(o, i, 'R');
+}
+
+static void
+fstore(Opcode *o, Instr *i)
+{
+	sw(o, i, 'F');
+}
+
+static void
+shifti(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "$%k,R%a");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+shift(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "R%b,R%a");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+add(Opcode *o, Instr *i)
+{
+	if (i->rd == i->ra)
+		format(o->mnemonic, i, "R%b,R%d");
+	else if (i->rd == i->rb)
+		format(o->mnemonic, i, "R%a,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+sub(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, nil);
+	bprint(i, "\t");
+	if(i->op == 31) {
+		bprint(i, "\tR%d,R%d", i->ra, i->rb);	/* subtract Ra from Rb */
+		if(i->rd != i->rb)
+			bprint(i, ",R%d", i->rd);
+	} else
+		bprint(i, "\tR%d,$%d,R%d", i->ra, i->simm, i->rd);
+}
+
+static void
+qmuldiv(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, nil);
+	if(i->op == 31)
+		bprint(i, "\tR%d,R%d", i->rb, i->ra);
+	else
+		bprint(i, "\t$%d,R%d", i->simm, i->ra);
+	if(i->ra != i->rd)
+		bprint(i, ",R%d", i->rd);
+}
+
+static void
+and(Opcode *o, Instr *i)
+{
+	if (i->op == 31) {
+		/* Rb,Rs,Ra */
+		if (i->ra == i->rs)
+			format(o->mnemonic, i, "R%b,R%a");
+		else if (i->ra == i->rb)
+			format(o->mnemonic, i, "R%s,R%a");
+		else
+			format(o->mnemonic, i, o->ken);
+	} else {
+		/* imm,Rs,Ra */
+		if (i->ra == i->rs)
+			format(o->mnemonic, i, "%I,R%a");
+		else
+			format(o->mnemonic, i, o->ken);
+	}
+}
+
+static void
+or(Opcode *o, Instr *i)
+{
+	if (i->op == 31) {
+		/* Rb,Rs,Ra */
+		if (i->rs == 0 && i->ra == 0 && i->rb == 0)
+			format("NOP", i, nil);
+		else if (i->rs == i->rb)
+			format("MOVW", i, "R%b,R%a");
+		else
+			and(o, i);
+	} else
+		and(o, i);
+}
+
+static void
+shifted(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, nil);
+	bprint(i, "\t$%lux,", (ulong)i->uimm<<16);
+	if (i->rs == i->ra)
+		bprint(i, "R%d", i->ra);
+	else
+		bprint(i, "R%d,R%d", i->rs, i->ra);
+}
+
+static void
+neg(Opcode *o, Instr *i)
+{
+	if (i->rd == i->ra)
+		format(o->mnemonic, i, "R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static	char	ir2[] = "R%a,R%d";		/* reverse of IBM order */
+static	char	ir3[] = "R%b,R%a,R%d";
+static	char	ir3r[] = "R%a,R%b,R%d";
+static	char	il3[] = "R%b,R%s,R%a";
+static	char	il2u[] = "%I,R%d,R%a";
+static	char	il3s[] = "$%k,R%s,R%a";
+static	char	il2[] = "R%s,R%a";
+static	char	icmp3[] = "R%a,R%b,%D";
+static	char	cr3op[] = "%b,%a,%d";
+static	char	ir2i[] = "%i,R%a,R%d";
+static	char	fp2[] = "F%b,F%d";
+static	char	fp3[] = "F%b,F%a,F%d";
+static	char	fp3c[] = "F%c,F%a,F%d";
+static	char	fp4[] = "F%a,F%c,F%b,F%d";
+static	char	fpcmp[] = "F%a,F%b,%D";
+static	char	ldop[] = "%l,R%d";
+static	char	stop[] = "R%d,%l";
+static	char	fldop[] = "%l,F%d";
+static	char	fstop[] = "F%d,%l";
+static	char	rldc[] = "R%b,R%s,$%E,R%a";
+static	char	rlim[] = "R%b,R%s,$%z,R%a";
+static	char	rlimi[] = "$%k,R%s,$%z,R%a";
+static	char	rldi[] = "$%e,R%s,$%E,R%a";
+
+#define	OEM	IBF(~0,22,30)
+#define	FP4	IBF(~0,26,30)
+#define	ALL	(~0)
+#define	RLDC	0xF
+#define	RLDI	0xE
+/*
+notes:
+	10-26: crfD = rD>>2; rD&3 mbz
+		also, L bit (bit 10) mbz or selects 64-bit operands
+*/
+
+static Opcode opcodes[] = {
+	{31,	266,	OEM,	"ADD%V%C",	add,	ir3},
+	{31,	 10,	OEM,	"ADDC%V%C",	add,	ir3},
+	{31,	138,	OEM,	"ADDE%V%C",	add,	ir3},
+	{14,	0,	0,	"ADD",		addi,	ir2i},
+	{12,	0,	0,	"ADDC",		addi,	ir2i},
+	{13,	0,	0,	"ADDCCC",	addi,	ir2i},
+	{15,	0,	0,	"ADD",		addis,	0},
+	{31,	234,	OEM,	"ADDME%V%C",	gencc,	ir2},
+	{31,	202,	OEM,	"ADDZE%V%C",	gencc,	ir2},
+
+	{31,	28,	ALL,	"AND%C",	and,	il3},
+	{31,	60,	ALL,	"ANDN%C",	and,	il3},
+	{28,	0,	0,	"ANDCC",	andi,	il2u},
+	{29,	0,	0,	"ANDCC",	shifted, 0},
+
+	{18,	0,	0,	"B%L",		gencc,	"%j"},
+	{16,	0,	0,	"BC%L",		branch,	"%d,%a,%J"},
+	{19,	528,	ALL,	"BC%L",		branch,	"%d,%a,(CTR)"},
+	{19,	16,	ALL,	"BC%L",		branch,	"%d,%a,(LR)"},
+
+	{31,	0,	ALL,	"CMP",		0,	icmp3},
+	{11,	0,	0,	"CMP",		0,	"R%a,%i,%D"},
+	{31,	32,	ALL,	"CMPU",		0,	icmp3},
+	{10,	0,	0,	"CMPU",		0,	"R%a,%I,%D"},
+
+	{31,	58,	ALL,	"CNTLZD%C",	gencc,	ir2},	/* 64 */
+	{31,	26,	ALL,	"CNTLZ%W%C",	gencc,	ir2},
+
+	{19,	257,	ALL,	"CRAND",	gen,	cr3op},
+	{19,	129,	ALL,	"CRANDN",	gen,	cr3op},
+	{19,	289,	ALL,	"CREQV",	gen,	cr3op},
+	{19,	225,	ALL,	"CRNAND",	gen,	cr3op},
+	{19,	33,	ALL,	"CRNOR",	gen,	cr3op},
+	{19,	449,	ALL,	"CROR",		gen,	cr3op},
+	{19,	417,	ALL,	"CRORN",	gen,	cr3op},
+	{19,	193,	ALL,	"CRXOR",	gen,	cr3op},
+
+	{31,	86,	ALL,	"DCBF",		dcb,	0},
+	{31,	470,	ALL,	"DCBI",		dcb,	0},
+	{31,	54,	ALL,	"DCBST",	dcb,	0},
+	{31,	278,	ALL,	"DCBT",		dcb,	0},
+	{31,	246,	ALL,	"DCBTST",	dcb,	0},
+	{31,	1014,	ALL,	"DCBZ",		dcb,	0},
+	{31,	454,	ALL,	"DCCCI",	dcb,	0},
+	{31,	966,	ALL,	"ICCCI",	dcb,	0},
+
+	{31,	489,	OEM,	"DIVD%V%C",	qmuldiv,	ir3},	/* 64 */
+	{31,	457,	OEM,	"DIVDU%V%C",	qmuldiv,	ir3},	/* 64 */
+	{31,	491,	OEM,	"DIVW%V%C",	qmuldiv,	ir3},
+	{31,	459,	OEM,	"DIVWU%V%C",	qmuldiv,	ir3},
+
+	{31,	310,	ALL,	"ECIWX",	ldx,	0},
+	{31,	438,	ALL,	"ECOWX",	stx,	0},
+	{31,	854,	ALL,	"EIEIO",	gen,	0},
+
+	{31,	284,	ALL,	"EQV%C",	gencc,	il3},
+
+	{31,	954,	ALL,	"EXTSB%C",	gencc,	il2},
+	{31,	922,	ALL,	"EXTSH%C",	gencc,	il2},
+	{31,	986,	ALL,	"EXTSW%C",	gencc,	il2},	/* 64 */
+
+	{63,	264,	ALL,	"FABS%C",	gencc,	fp2},
+	{63,	21,	ALL,	"FADD%C",	gencc,	fp3},
+	{59,	21,	ALL,	"FADDS%C",	gencc,	fp3},
+	{63,	32,	ALL,	"FCMPO",	gen,	fpcmp},
+	{63,	0,	ALL,	"FCMPU",	gen,	fpcmp},
+	{63,	846,	ALL,	"FCFID%C",	gencc,	fp2},	/* 64 */
+	{63,	814,	ALL,	"FCTID%C",	gencc,	fp2},	/* 64 */
+	{63,	815,	ALL,	"FCTIDZ%C",	gencc,	fp2},	/* 64 */
+	{63,	14,	ALL,	"FCTIW%C",	gencc,	fp2},
+	{63,	15,	ALL,	"FCTIWZ%C",	gencc,	fp2},
+	{63,	18,	ALL,	"FDIV%C",	gencc,	fp3},
+	{59,	18,	ALL,	"FDIVS%C",	gencc,	fp3},
+	{63,	29,	FP4,	"FMADD%C",	gencc,	fp4},
+	{59,	29,	FP4,	"FMADDS%C",	gencc,	fp4},
+	{63,	72,	ALL,	"FMOVD%C",	gencc,	fp2},
+	{63,	28,	FP4,	"FMSUB%C",	gencc,	fp4},
+	{59,	28,	FP4,	"FMSUBS%C",	gencc,	fp4},
+	{63,	25,	FP4,	"FMUL%C",	gencc,	fp3c},
+	{59,	25,	FP4,	"FMULS%C",	gencc,	fp3c},
+	{63,	136,	ALL,	"FNABS%C",	gencc,	fp2},
+	{63,	40,	ALL,	"FNEG%C",	gencc,	fp2},
+	{63,	31,	FP4,	"FNMADD%C",	gencc,	fp4},
+	{59,	31,	FP4,	"FNMADDS%C",	gencc,	fp4},
+	{63,	30,	FP4,	"FNMSUB%C",	gencc,	fp4},
+	{59,	30,	FP4,	"FNMSUBS%C",	gencc,	fp4},
+	{59,	24,	ALL,	"FRES%C",	gencc,	fp2},	/* optional */
+	{63,	12,	ALL,	"FRSP%C",	gencc,	fp2},
+	{63,	26,	ALL,	"FRSQRTE%C",	gencc,	fp2},	/* optional */
+	{63,	23,	FP4,	"FSEL%CC",	gencc,	fp4},	/* optional */
+	{63,	22,	ALL,	"FSQRT%C",	gencc,	fp2},	/* optional */
+	{59,	22,	ALL,	"FSQRTS%C",	gencc,	fp2},	/* optional */
+	{63,	20,	FP4,	"FSUB%C",	gencc,	fp3},
+	{59,	20,	FP4,	"FSUBS%C",	gencc,	fp3},
+
+	{31,	982,	ALL,	"ICBI",		dcb,	0},	/* optional */
+	{19,	150,	ALL,	"ISYNC",	gen,	0},
+
+	{34,	0,	0,	"MOVBZ",	load,	ldop},
+	{35,	0,	0,	"MOVBZU",	load,	ldop},
+	{31,	119,	ALL,	"MOVBZU",	ldx,	0},
+	{31,	87,	ALL,	"MOVBZ",	ldx,	0},
+	{50,	0,	0,	"FMOVD",	fload,	fldop},
+	{51,	0,	0,	"FMOVDU",	fload,	fldop},
+	{31,	631,	ALL,	"FMOVDU",	fldx,	0},
+	{31,	599,	ALL,	"FMOVD",	fldx,	0},
+	{48,	0,	0,	"FMOVS",	load,	fldop},
+	{49,	0,	0,	"FMOVSU",	load,	fldop},
+	{31,	567,	ALL,	"FMOVSU",	fldx,	0},
+	{31,	535,	ALL,	"FMOVS",	fldx,	0},
+	{42,	0,	0,	"MOVH",		load,	ldop},
+	{43,	0,	0,	"MOVHU",	load,	ldop},
+	{31,	375,	ALL,	"MOVHU",	ldx,	0},
+	{31,	343,	ALL,	"MOVH",		ldx,	0},
+	{31,	790,	ALL,	"MOVHBR",	ldx,	0},
+	{40,	0,	0,	"MOVHZ",	load,	ldop},
+	{41,	0,	0,	"MOVHZU",	load,	ldop},
+	{31,	311,	ALL,	"MOVHZU",	ldx,	0},
+	{31,	279,	ALL,	"MOVHZ",	ldx,	0},
+	{46,	0,	0,	"MOVMW",	load,	ldop},
+	{31,	597,	ALL,	"LSW",		gen,	"(R%a),$%n,R%d"},
+	{31,	533,	ALL,	"LSW",		ldx,	0},
+	{31,	20,	ALL,	"LWAR",		ldx,	0},
+	{31,	84,	ALL,	"LWARD",	ldx,	0},	/* 64 */
+
+	{58,	0,	ALL,	"MOVD",		load,	ldop},	/* 64 */
+	{58,	1,	ALL,	"MOVDU",	load,	ldop},	/* 64 */
+	{31,	53,	ALL,	"MOVDU",	ldx,	0},	/* 64 */
+	{31,	21,	ALL,	"MOVD",		ldx,	0},	/* 64 */
+
+	{31,	534,	ALL,	"MOVWBR",	ldx,	0},
+
+	{58,	2,	ALL,	"MOVW",		load,	ldop},	/* 64 (lwa) */
+	{31,	373,	ALL,	"MOVWU",	ldx,	0},	/* 64 */
+	{31,	341,	ALL,	"MOVW",		ldx,	0},	/* 64 */
+
+	{32,	0,	0,	"MOVW%Z",	load,	ldop},
+	{33,	0,	0,	"MOVW%ZU",	load,	ldop},
+	{31,	55,	ALL,	"MOVW%ZU",	ldx,	0},
+	{31,	23,	ALL,	"MOVW%Z",	ldx,	0},
+
+	{19,	0,	ALL,	"MOVFL",	gen,	"%S,%D"},
+	{63,	64,	ALL,	"MOVCRFS",	gen,	"%S,%D"},
+	{31,	512,	ALL,	"MOVW",		gen,	"XER,%D"},
+	{31,	19,	ALL,	"MOVW",		gen,	"CR,R%d"},
+
+	{63,	583,	ALL,	"MOVW%C",	gen,	"FPSCR, F%d"},	/* mffs */
+	{31,	83,	ALL,	"MOVW",		gen,	"MSR,R%d"},
+	{31,	339,	ALL,	"MOVW",		gen,	"%P,R%d"},
+	{31,	595,	ALL,	"MOVW",		gen,	"SEG(%a),R%d"},
+	{31,	659,	ALL,	"MOVW",		gen,	"SEG(R%b),R%d"},
+	{31,	323,	ALL,	"MOVW",		gen,	"DCR(%Q),R%d"},
+	{31,	451,	ALL,	"MOVW",		gen,	"R%s,DCR(%Q)"},
+	{31,	259,	ALL,	"MOVW",		gen,	"DCR(R%a),R%d"},
+	{31,	387,	ALL,	"MOVW",		gen,	"R%s,DCR(R%a)"},
+	{31,	144,	ALL,	"MOVFL",	gen,	"R%s,%m,CR"},
+	{63,	70,	ALL,	"MTFSB0%C",	gencc,	"%D"},
+	{63,	38,	ALL,	"MTFSB1%C",	gencc,	"%D"},
+	{63,	711,	ALL,	"MOVFL%C",	gencc,	"F%b,%M,FPSCR"},	/* mtfsf */
+	{63,	134,	ALL,	"MOVFL%C",	gencc,	"%K,%D"},
+	{31,	146,	ALL,	"MOVW",		gen,	"R%s,MSR"},
+	{31,	178,	ALL,	"MOVD",		gen,	"R%s,MSR"},
+	{31,	467,	ALL,	"MOVW",		gen,	"R%s,%P"},
+	{31,	210,	ALL,	"MOVW",		gen,	"R%s,SEG(%a)"},
+	{31,	242,	ALL,	"MOVW",		gen,	"R%s,SEG(R%b)"},
+
+	{31,	73,	ALL,	"MULHD%C",	gencc,	ir3},
+	{31,	9,	ALL,	"MULHDU%C",	gencc,	ir3},
+	{31,	233,	OEM,	"MULLD%V%C",	gencc,	ir3},
+
+	{31,	75,	ALL,	"MULHW%C",	gencc,	ir3},
+	{31,	11,	ALL,	"MULHWU%C",	gencc,	ir3},
+	{31,	235,	OEM,	"MULLW%V%C",	gencc,	ir3},
+
+	{7,	0,	0,	"MULLW",	qmuldiv,	"%i,R%a,R%d"},
+
+	{31,	476,	ALL,	"NAND%C",	gencc,	il3},
+	{31,	104,	OEM,	"NEG%V%C",	neg,	ir2},
+	{31,	124,	ALL,	"NOR%C",	gencc,	il3},
+	{31,	444,	ALL,	"OR%C",		or,	il3},
+	{31,	412,	ALL,	"ORN%C",	or,	il3},
+	{24,	0,	0,	"OR",		and,	"%I,R%d,R%a"},
+	{25,	0,	0,	"OR",		shifted, 0},
+
+	{19,	50,	ALL,	"RFI",		gen,	0},
+	{19,	51,	ALL,	"RFCI",		gen,	0},
+
+	{30,	8,	RLDC,	"RLDCL%C",	gencc,	rldc},	/* 64 */
+	{30,	9,	RLDC,	"RLDCR%C",	gencc,	rldc},	/* 64 */
+	{30,	0,	RLDI,	"RLDCL%C",	gencc,	rldi},	/* 64 */
+	{30,	1<<1, RLDI,	"RLDCR%C",	gencc,	rldi},	/* 64 */
+	{30,	2<<1, RLDI,	"RLDC%C",	gencc,	rldi},	/* 64 */
+	{30,	3<<1, RLDI,	"RLDMI%C",	gencc,	rldi},	/* 64 */
+
+	{20,	0,	0,	"RLWMI%C",	gencc,	rlimi},
+	{21,	0,	0,	"RLWNM%C",	gencc,	rlimi},
+	{23,	0,	0,	"RLWNM%C",	gencc,	rlim},
+
+	{17,	1,	ALL,	"SYSCALL",	gen,	0},
+
+	{31,	27,	ALL,	"SLD%C",	shift,	il3},	/* 64 */
+	{31,	24,	ALL,	"SLW%C",	shift,	il3},
+
+	{31,	794,	ALL,	"SRAD%C",	shift,	il3},	/* 64 */
+	{31,	(413<<1)|0,	ALL,	"SRAD%C",	shifti,	il3s},	/* 64 */
+	{31,	(413<<1)|1,	ALL,	"SRAD%C",	shifti,	il3s},	/* 64 */
+	{31,	792,	ALL,	"SRAW%C",	shift,	il3},
+	{31,	824,	ALL,	"SRAW%C",	shifti,	il3s},
+
+	{31,	539,	ALL,	"SRD%C",	shift,	il3},	/* 64 */
+	{31,	536,	ALL,	"SRW%C",	shift,	il3},
+
+	{38,	0,	0,	"MOVB",		store,	stop},
+	{39,	0,	0,	"MOVBU",	store,	stop},
+	{31,	247,	ALL,	"MOVBU",	stx,	0},
+	{31,	215,	ALL,	"MOVB",		stx,	0},
+	{54,	0,	0,	"FMOVD",	fstore,	fstop},
+	{55,	0,	0,	"FMOVDU",	fstore,	fstop},
+	{31,	759,	ALL,	"FMOVDU",	fstx,	0},
+	{31,	727,	ALL,	"FMOVD",	fstx,	0},
+	{52,	0,	0,	"FMOVS",	fstore,	fstop},
+	{53,	0,	0,	"FMOVSU",	fstore,	fstop},
+	{31,	695,	ALL,	"FMOVSU",	fstx,	0},
+	{31,	663,	ALL,	"FMOVS",	fstx,	0},
+	{44,	0,	0,	"MOVH",		store,	stop},
+	{31,	918,	ALL,	"MOVHBR",	stx,	0},
+	{45,	0,	0,	"MOVHU",	store,	stop},
+	{31,	439,	ALL,	"MOVHU",	stx,	0},
+	{31,	407,	ALL,	"MOVH",		stx,	0},
+	{47,	0,	0,	"MOVMW",	store,	stop},
+	{31,	725,	ALL,	"STSW",		gen,	"R%d,$%n,(R%a)"},
+	{31,	661,	ALL,	"STSW",		stx,	0},
+	{36,	0,	0,	"MOVW",		store,	stop},
+	{31,	662,	ALL,	"MOVWBR",	stx,	0},
+	{31,	150,	ALL,	"STWCCC",	stx,	0},
+	{31,	214,	ALL,	"STDCCC",	stx,	0},	/* 64 */
+	{37,	0,	0,	"MOVWU",	store,	stop},
+	{31,	183,	ALL,	"MOVWU",	stx,	0},
+	{31,	151,	ALL,	"MOVW",		stx,	0},
+
+	{62,	0,	0,	"MOVD%U",	store,	stop},	/* 64 */
+	{31,	149,	ALL,	"MOVD",		stx,	0,},	/* 64 */
+	{31,	181,	ALL,	"MOVDU",	stx,	0},	/* 64 */
+
+	{31,	498,	ALL,	"SLBIA",	gen,	0},	/* 64 */
+	{31,	434,	ALL,	"SLBIE",	gen,	"R%b"},	/* 64 */
+	{31,	466,	ALL,	"SLBIEX",	gen,	"R%b"},	/* 64 */
+	{31,	915,	ALL,	"SLBMFEE",	gen,	"R%b,R%d"},	/* 64 */
+	{31,	851,	ALL,	"SLBMFEV",	gen,	"R%b,R%d"},	/* 64 */
+	{31,	402,	ALL,	"SLBMTE",	gen,	"R%s,R%b"},	/* 64 */
+
+	{31,	40,	OEM,	"SUB%V%C",	sub,	ir3},
+	{31,	8,	OEM,	"SUBC%V%C",	sub,	ir3},
+	{31,	136,	OEM,	"SUBE%V%C",	sub,	ir3},
+	{8,	0,	0,	"SUBC",		gen,	"R%a,%i,R%d"},
+	{31,	232,	OEM,	"SUBME%V%C",	sub,	ir2},
+	{31,	200,	OEM,	"SUBZE%V%C",	sub,	ir2},
+
+	{31,	598,	ALL,	"SYNC",		gen,	0},	/* TO DO: there's a parameter buried in there */
+	{2,	0,	0,	"TD",		gen,	"%d,R%a,%i"},	/* 64 */
+	{31,	370,	ALL,	"TLBIA",	gen,	0},	/* optional */
+	{31,	306,	ALL,	"TLBIE",	gen,	"R%b"},	/* optional */
+	{31,	274,	ALL,	"TLBIEL",	gen,	"R%b"},	/* optional */
+	{31,	1010,	ALL,	"TLBLI",	gen,	"R%b"},	/* optional */
+	{31,	978,	ALL,	"TLBLD",	gen,	"R%b"},	/* optional */
+	{31,	566,	ALL,	"TLBSYNC",	gen,	0},	/* optional */
+	{31,	68,	ALL,	"TD",		gen,	"%d,R%a,R%b"},	/* 64 */
+	{31,	4,	ALL,	"TW",		gen,	"%d,R%a,R%b"},
+	{3,	0,	0,	"TW",		gen,	"%d,R%a,%i"},
+
+	{31,	316,	ALL,	"XOR",		and,	il3},
+	{26,	0,	0,	"XOR",		and,	il2u},
+	{27,	0,	0,	"XOR",		shifted, 0},
+
+	{0},
+};
+
+typedef struct Spr Spr;
+struct Spr {
+	int	n;
+	char	*name;
+};
+
+static	Spr	sprname[] = {
+	{0, "MQ"},
+	{1, "XER"},
+	{268, "TBL"},
+	{269, "TBU"},
+	{8, "LR"},
+	{9, "CTR"},
+	{528, "IBAT0U"},
+	{529, "IBAT0L"},
+	{530, "IBAT1U"},
+	{531, "IBAT1L"},
+	{532, "IBAT2U"},
+	{533, "IBAT2L"},
+	{534, "IBAT3U"},
+	{535, "IBAT3L"},
+	{536, "DBAT0U"},
+	{537, "DBAT0L"},
+	{538, "DBAT1U"},
+	{539, "DBAT1L"},
+	{540, "DBAT2U"},
+	{541, "DBAT2L"},
+	{542, "DBAT3U"},
+	{543, "DBAT3L"},
+	{25, "SDR1"},
+	{19, "DAR"},
+	{272, "SPRG0"},
+	{273, "SPRG1"},
+	{274, "SPRG2"},
+	{275, "SPRG3"},
+	{18, "DSISR"},
+	{26, "SRR0"},
+	{27, "SRR1"},
+	{284, "TBLW"},
+	{285, "TBUW"},	
+	{22, "DEC"},
+	{282, "EAR"},
+	{1008, "HID0"},
+	{1009, "HID1"},
+	{976, "DMISS"},
+	{977, "DCMP"},
+	{978, "HASH1"},
+	{979, "HASH2"},
+	{980, "IMISS"},
+	{981, "ICMP"},
+	{982, "RPA"},
+	{1010, "IABR"},
+	{1013, "DABR"},
+	{0,0},
+};
+
+static int
+shmask(uvlong *m)
+{
+	int i;
+
+	for(i=0; i<63; i++)
+		if(*m & ((uvlong)1<<i))
+			break;
+	if(i > 63)
+		return 0;
+	if(*m & ~((uvlong)1<<i)){	/* more than one bit: do multiples of bytes */
+		i = (i/8)*8;
+		if(i == 0)
+			return 0;
+	}
+	*m >>= i;
+	return i;
+}
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	int n, s;
+	ulong mask;
+	uvlong vmask;
+
+	if (mnemonic)
+		format(0, i, mnemonic);
+	if (f == 0)
+		return;
+	if (mnemonic)
+		bprint(i, "\t");
+	for ( ; *f; f++) {
+		if (*f != '%') {
+			bprint(i, "%c", *f);
+			continue;
+		}
+		switch (*++f) {
+
+		case 'a':
+			bprint(i, "%d", i->ra);
+			break;
+
+		case 'b':
+			bprint(i, "%d", i->rb);
+			break;
+
+		case 'c':
+			bprint(i, "%d", i->frc);
+			break;
+
+		case 'd':
+		case 's':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'C':
+			if(i->rc)
+				bprint(i, "CC");
+			break;
+
+		case 'D':
+			if(i->rd & 3)
+				bprint(i, "CR(INVAL:%d)", i->rd);
+			else if(i->op == 63)
+				bprint(i, "FPSCR(%d)", i->crfd);
+			else
+				bprint(i, "CR(%d)", i->crfd);
+			break;
+
+		case 'e':
+			bprint(i, "%d", i->xsh);
+			break;
+
+		case 'E':
+			switch(IBF(i->w0,27,30)){	/* low bit is top bit of shift in rldiX cases */
+			case 8:	i->mb = i->xmbe; i->me = 63; break;	/* rldcl */
+			case 9:	i->mb = 0; i->me = i->xmbe; break;	/* rldcr */
+			case 4: case 5:
+					i->mb = i->xmbe; i->me = 63-i->xsh; break;	/* rldic */
+			case 0: case 1:
+					i->mb = i->xmbe; i->me = 63; break;	/* rldicl */
+			case 2: case 3:
+					i->mb = 0; i->me = i->xmbe; break;	/* rldicr */
+			case 6: case 7:
+					i->mb = i->xmbe; i->me = 63-i->xsh; break;	/* rldimi */
+			}
+			vmask = (~(uvlong)0>>i->mb) & (~(uvlong)0<<(63-i->me));
+			s = shmask(&vmask);
+			if(s)
+				bprint(i, "(%llux<<%d)", vmask, s);
+			else
+				bprint(i, "%llux", vmask);
+			break;
+
+		case 'i':
+			bprint(i, "$%d", i->simm);
+			break;
+
+		case 'I':
+			bprint(i, "$%ux", i->uimm);
+			break;
+
+		case 'j':
+			if(i->aa)
+				pglobal(i, i->li, 1, "(SB)");
+			else
+				pglobal(i, i->addr+i->li, 1, "");
+			break;
+
+		case 'J':
+			if(i->aa)
+				pglobal(i, i->bd, 1, "(SB)");
+			else
+				pglobal(i, i->addr+i->bd, 1, "");
+			break;
+
+		case 'k':
+			bprint(i, "%d", i->sh);
+			break;
+
+		case 'K':
+			bprint(i, "$%x", i->imm);
+			break;
+
+		case 'L':
+			if(i->lk)
+				bprint(i, "L");
+			break;
+
+		case 'l':
+			if(i->simm < 0)
+				bprint(i, "-%x(R%d)", -i->simm, i->ra);
+			else
+				bprint(i, "%x(R%d)", i->simm, i->ra);
+			break;
+
+		case 'm':
+			bprint(i, "%ux", i->crm);
+			break;
+
+		case 'M':
+			bprint(i, "%ux", i->fm);
+			break;
+
+		case 'n':
+			bprint(i, "%d", i->nb==0? 32: i->nb);	/* eg, pg 10-103 */
+			break;
+
+		case 'P':
+			n = ((i->spr&0x1f)<<5)|((i->spr>>5)&0x1f);
+			for(s=0; sprname[s].name; s++)
+				if(sprname[s].n == n)
+					break;
+			if(sprname[s].name) {
+				if(s < 10)
+					bprint(i, sprname[s].name);
+				else
+					bprint(i, "SPR(%s)", sprname[s].name);
+			} else
+				bprint(i, "SPR(%d)", n);
+			break;
+
+		case 'Q':
+			n = ((i->spr&0x1f)<<5)|((i->spr>>5)&0x1f);
+			bprint(i, "%d", n);
+			break;
+
+		case 'S':
+			if(i->ra & 3)
+				bprint(i, "CR(INVAL:%d)", i->ra);
+			else if(i->op == 63)
+				bprint(i, "FPSCR(%d)", i->crfs);
+			else
+				bprint(i, "CR(%d)", i->crfs);
+			break;
+
+		case 'U':
+			if(i->rc)
+				bprint(i, "U");
+			break;
+
+		case 'V':
+			if(i->oe)
+				bprint(i, "V");
+			break;
+
+		case 'w':
+			bprint(i, "[%lux]", i->w0);
+			break;
+
+		case 'W':
+			if(i->m64)
+				bprint(i, "W");
+			break;
+
+		case 'Z':
+			if(i->m64)
+				bprint(i, "Z");
+			break;
+
+		case 'z':
+			if(i->mb <= i->me)
+				mask = ((ulong)~0L>>i->mb) & (~0L<<(31-i->me));
+			else
+				mask = ~(((ulong)~0L>>(i->me+1)) & (~0L<<(31-(i->mb-1))));
+			bprint(i, "%lux", mask);
+			break;
+
+		case '\0':
+			bprint(i, "%%");
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+}
+
+static int
+printins(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+	Opcode *o;
+
+	mymap = map;
+	memset(&i, 0, sizeof(i));
+	i.curr = buf;
+	i.end = buf+n-1;
+	if(mkinstr(pc, &i) < 0)
+		return -1;
+	for(o = opcodes; o->mnemonic != 0; o++)
+		if(i.op == o->op && (i.xo & o->xomask) == o->xo) {
+			if (o->f)
+				(*o->f)(o, &i);
+			else
+				format(o->mnemonic, &i, o->ken);
+			return i.size*4;
+		}
+	bprint(&i, "unknown %lux", i.w0);
+	return i.size*4;
+}
+
+static int
+powerinst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	USED(modifier);
+	return printins(map, pc, buf, n);
+}
+
+static int
+powerdas(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr instr;
+
+	mymap = map;
+	memset(&instr, 0, sizeof(instr));
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	if (mkinstr(pc, &instr) < 0)
+		return -1;
+	if (instr.end-instr.curr > 8)
+		instr.curr = _hexify(instr.curr, instr.w0, 7);
+	if (instr.end-instr.curr > 9 && instr.size == 2) {
+		*instr.curr++ = ' ';
+		instr.curr = _hexify(instr.curr, instr.w1, 7);
+	}
+	*instr.curr = 0;
+	return instr.size*4;
+}
+
+static int
+powerinstlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	return i.size*4;
+}
+
+static int
+powerfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	char *reg;
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	foll[0] = pc+4;
+	foll[1] = pc+4;
+	switch(i.op) {
+	default:
+		return 1;
+
+	case 18:	/* branch */
+		foll[0] = i.li;
+		if(!i.aa)
+			foll[0] += pc;
+		break;
+			
+	case 16:	/* conditional branch */
+		foll[0] = i.bd;
+		if(!i.aa)
+			foll[0] += pc;
+		break;
+
+	case 19:	/* conditional branch to register */
+		if(i.xo == 528)
+			reg = "CTR";
+		else if(i.xo == 16)
+			reg = "LR";
+		else
+			return 1;	/* not a branch */
+		foll[0] = (*rget)(map, reg);
+		break;
+	}
+	if(i.lk)
+		return 2;
+	return 1;
+}
--- /dev/null
+++ b/utils/libmach/qobj.c
@@ -1,0 +1,144 @@
+/*
+ * qobj.c - identify and parse a PowerPC object file
+ *	forsyth@terzarima.net
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "qc/q.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_isq(char *s)
+{
+	return  (s[0]&0377) == ANAME			/* ANAME */
+		&& (s[1]&0377) == ANAME>>8
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_readq(Biobuf *bp, Prog *p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);			/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = beswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	n = Bgetc(bp);	/* reg and flag */
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	if(n & 0x40)
+		addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE: case D_REG: case D_FREG: case D_CREG:
+	case D_FPSCR: case D_MSR: case D_SREG:
+		break;
+	case D_SPR:
+	case D_OREG:
+	case D_DCR:
+	case D_CONST:
+	case D_BRANCH:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libmach/setmach.c
@@ -1,0 +1,144 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"mach.h"
+		/* table for selecting machine-dependent parameters */
+
+typedef	struct machtab Machtab;
+
+struct machtab
+{
+	char		*name;			/* machine name */
+	short		type;			/* executable type */
+	short		boottype;		/* bootable type */
+	int		asstype;		/* disassembler code */
+	Mach		*mach;			/* machine description */
+	Machdata	*machdata;		/* machine functions */
+};
+
+extern	Mach		mmips, msparc, mi386, mamd64,
+			marm, mmips2be, mmips2le, mpower, mpower64;
+extern	Machdata	mipsmach, sparcmach, i386mach,
+			armmach, mipsmach2le, powermach;
+
+/*
+ *	machine selection table.  machines with native disassemblers should
+ *	follow the plan 9 variant in the table; native modes are selectable
+ *	only by name.
+ */
+Machtab	machines[] =
+{
+	{	"mips2LE",			/*plan 9 mips2 little endian*/
+		FMIPS2LE,
+		0,
+		AMIPS,
+		&mmips2le,
+		&mipsmach2le, 	},
+	{	"mips",				/*plan 9 mips*/
+		FMIPS,
+		FMIPSB,
+		AMIPS,
+		&mmips,
+		&mipsmach, 	},
+	{	"mips2",			/*plan 9 mips2*/
+		FMIPS2BE,
+		FMIPSB,
+		AMIPS,
+		&mmips2be,
+		&mipsmach, 	},		/* shares debuggers with native mips */
+	{	"mipsco",			/*native mips - must follow plan 9*/
+		FMIPS,
+		FMIPSB,
+		AMIPSCO,
+		&mmips,
+		&mipsmach,	},
+	{	"sparc",			/*plan 9 sparc */
+		FSPARC,
+		FSPARCB,
+		ASPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"sunsparc",			/*native sparc - must follow plan 9*/
+		FSPARC,
+		FSPARCB,
+		ASUNSPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"386",				/*plan 9 386*/
+		FI386,
+		FI386B,
+		AI386,
+		&mi386,
+		&i386mach,	},
+	{	"86",				/*8086 - a peach of a machine*/
+		FI386,
+		FI386B,
+		AI8086,
+		&mi386,
+		&i386mach,	},
+	{	"amd64",			/*amd64*/
+		FAMD64,
+		FAMD64B,
+		AAMD64,
+		&mamd64,
+		&i386mach,	},
+	{	"arm",				/*ARM*/
+		FARM,
+		FARMB,
+		AARM,
+		&marm,
+		&armmach,	},
+	{	"power",			/*PowerPC*/
+		FPOWER,
+		FPOWERB,
+		APOWER,
+		&mpower,
+		&powermach,	},
+	{	"power64",			/*PowerPC*/
+		FPOWER64,
+		FPOWER64B,
+		APOWER64,
+		&mpower64,
+		&powermach,	},
+	{	0		},		/*the terminator*/
+};
+
+/*
+ *	select a machine by executable file type
+ */
+void
+machbytype(int type)
+{
+	Machtab *mp;
+
+	for (mp = machines; mp->name; mp++){
+		if (mp->type == type || mp->boottype == type) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			break;
+		}
+	}
+}
+/*
+ *	select a machine by name
+ */
+int
+machbyname(char *name)
+{
+	Machtab *mp;
+
+	if (!name) {
+		asstype = AMIPS;
+		machdata = &mipsmach;
+		mach = &mmips;
+		return 1;
+	}
+	for (mp = machines; mp->name; mp++){
+		if (strcmp(mp->name, name) == 0) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			mach = mp->mach;
+			return 1;
+		}
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/libmach/swap.c
@@ -1,0 +1,81 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+/*
+ * big-endian short
+ */
+ushort
+beswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+ulong
+beswal(ulong l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * big-endian vlong
+ */
+uvlong
+beswav(uvlong v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((uvlong)p[0]<<56) | ((uvlong)p[1]<<48) | ((uvlong)p[2]<<40)
+				  | ((uvlong)p[3]<<32) | ((uvlong)p[4]<<24)
+				  | ((uvlong)p[5]<<16) | ((uvlong)p[6]<<8)
+				  | (uvlong)p[7];
+}
+
+/*
+ * little-endian short
+ */
+ushort
+leswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+ulong
+leswal(ulong l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian vlong
+ */
+uvlong
+leswav(uvlong v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((uvlong)p[7]<<56) | ((uvlong)p[6]<<48) | ((uvlong)p[5]<<40)
+				  | ((uvlong)p[4]<<32) | ((uvlong)p[3]<<24)
+				  | ((uvlong)p[2]<<16) | ((uvlong)p[1]<<8)
+				  | (uvlong)p[0];
+}
--- /dev/null
+++ b/utils/libmach/sym.c
@@ -1,0 +1,1373 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+#define	HUGEINT	0x7fffffff
+#define	NNAME	20		/* a relic of the past */
+
+typedef	struct txtsym Txtsym;
+typedef	struct file File;
+typedef	struct hist Hist;
+
+struct txtsym {				/* Text Symbol table */
+	int 	n;			/* number of local vars */
+	Sym	**locals;		/* array of ptrs to autos */
+	Sym	*sym;			/* function symbol entry */
+};
+
+struct hist {				/* Stack of include files & #line directives */
+	char	*name;			/* Assumes names Null terminated in file */
+	long	line;			/* line # where it was included */
+	long	offset;			/* line # of #line directive */
+};
+
+struct file {				/* Per input file header to history stack */
+	uvlong	addr;			/* address of first text sym */
+	/* union { */
+		Txtsym	*txt;		/* first text symbol */
+		Sym	*sym;		/* only during initilization */
+	/* }; */
+	int	n;			/* size of history stack */
+	Hist	*hist;			/* history stack */
+};
+
+static	int	debug = 0;
+
+static	Sym	**autos;		/* Base of auto variables */
+static	File	*files;			/* Base of file arena */
+static	int	fpmax;			/* largest file path index */
+static	Sym	**fnames;		/* file names path component table */
+static	Sym	**globals;		/* globals by addr table */
+static	Hist	*hist;			/* base of history stack */
+static	int	isbuilt;		/* internal table init flag */
+static	long	nauto;			/* number of automatics */
+static	long	nfiles;			/* number of files */
+static	long	nglob;			/* number of globals */
+static	long	nhist;			/* number of history stack entries */
+static	long	nsym;			/* number of symbols */
+static	int	ntxt;			/* number of text symbols */
+static	uchar	*pcline;		/* start of pc-line state table */
+static	uchar 	*pclineend;		/* end of pc-line table */
+static	uchar	*spoff;			/* start of pc-sp state table */
+static	uchar	*spoffend;		/* end of pc-sp offset table */
+static	Sym	*symbols;		/* symbol table */
+static	Txtsym	*txt;			/* Base of text symbol table */
+static	uvlong	txtstart;		/* start of text segment */
+static	uvlong	txtend;			/* end of text segment */
+
+static void	cleansyms(void);
+static long	decodename(Biobuf*, Sym*);
+static short	*encfname(char*);
+static int 	fline(char*, int, long, Hist*, Hist**);
+static void	fillsym(Sym*, Symbol*);
+static int	findglobal(char*, Symbol*);
+static int	findlocvar(Symbol*, char *, Symbol*);
+static int	findtext(char*, Symbol*);
+static int	hcomp(Hist*, short*);
+static int	hline(File*, short*, long*);
+static void	printhist(char*, Hist*, int);
+static int	buildtbls(void);
+static int	symcomp(void*, void*);
+static int	symerrmsg(int, char*);
+static int	txtcomp(void*, void*);
+static int	filecomp(void*, void*);
+
+/*
+ *	initialize the symbol tables
+ */
+int
+syminit(int fd, Fhdr *fp)
+{
+	Sym *p;
+	long i, l, size;
+	vlong vl;
+	Biobuf b;
+	int svalsz;
+
+	if(fp->symsz == 0)
+		return 0;
+	if(fp->type == FNONE)
+		return 0;
+
+	cleansyms();
+	textseg(fp->txtaddr, fp);
+		/* minimum symbol record size = 4+1+2 bytes */
+	symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
+	if(symbols == 0) {
+		werrstr("can't malloc %ld bytes", fp->symsz);
+		return -1;
+	}
+	Binit(&b, fd, OREAD);
+	Bseek(&b, fp->symoff, 0);
+	nsym = 0;
+	size = 0;
+	for(p = symbols; size < fp->symsz; p++, nsym++) {
+		if(fp->_magic && (fp->magic & HDR_MAGIC)){
+			svalsz = 8;
+			if(Bread(&b, &vl, 8) != 8)
+				return symerrmsg(8, "symbol");
+			p->value = beswav(vl);
+		}
+		else{
+			svalsz = 4;
+			if(Bread(&b, &l, 4) != 4)
+				return symerrmsg(4, "symbol");
+			p->value = (u32int)beswal(l);
+		}
+		if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
+			return symerrmsg(sizeof(p->value), "symbol");
+
+		i = decodename(&b, p);
+		if(i < 0)
+			return -1;
+		size += i+svalsz+sizeof(p->type);
+
+		/* count global & auto vars, text symbols, and file names */
+		switch (p->type) {
+		case 'l':
+		case 'L':
+		case 't':
+		case 'T':
+			ntxt++;
+			break;
+		case 'd':
+		case 'D':
+		case 'b':
+		case 'B':
+			nglob++;
+			break;
+		case 'f':
+			if(strcmp(p->name, ".frame") == 0) {
+				p->type = 'm';
+				nauto++;
+			}
+			else if(p->value > fpmax)
+				fpmax = p->value;	/* highest path index */
+			break;
+		case 'a':
+		case 'p':
+		case 'm':
+			nauto++;
+			break;
+		case 'z':
+			if(p->value == 1) {		/* one extra per file */
+				nhist++;
+				nfiles++;
+			}
+			nhist++;
+			break;
+		default:
+			break;
+		}
+	}
+	if (debug)
+		print("NG: %ld NT: %d NF: %d\n", nglob, ntxt, fpmax);
+	if (fp->sppcsz) {			/* pc-sp offset table */
+		spoff = (uchar *)malloc(fp->sppcsz);
+		if(spoff == 0) {
+			werrstr("can't malloc %ld bytes", fp->sppcsz);
+			return -1;
+		}
+		Bseek(&b, fp->sppcoff, 0);
+		if(Bread(&b, spoff, fp->sppcsz) != fp->sppcsz){
+			spoff = 0;
+			return symerrmsg(fp->sppcsz, "sp-pc");
+		}
+		spoffend = spoff+fp->sppcsz;
+	}
+	if (fp->lnpcsz) {			/* pc-line number table */
+		pcline = (uchar *)malloc(fp->lnpcsz);
+		if(pcline == 0) {
+			werrstr("can't malloc %ld bytes", fp->lnpcsz);
+			return -1;
+		}
+		Bseek(&b, fp->lnpcoff, 0);
+		if(Bread(&b, pcline, fp->lnpcsz) != fp->lnpcsz){
+			pcline = 0;
+			return symerrmsg(fp->lnpcsz, "pc-line");
+		}
+		pclineend = pcline+fp->lnpcsz;
+	}
+	return nsym;
+}
+
+static int
+symerrmsg(int n, char *table)
+{
+	werrstr("can't read %d bytes of %s table", n, table);
+	return -1;
+}
+
+static long
+decodename(Biobuf *bp, Sym *p)
+{
+	char *cp;
+	int c1, c2;
+	long n;
+	vlong o;
+
+	if((p->type & 0x80) == 0) {		/* old-style, fixed length names */
+		p->name = malloc(NNAME);
+		if(p->name == 0) {
+			werrstr("can't malloc %d bytes", NNAME);
+			return -1;
+		}
+		if(Bread(bp, p->name, NNAME) != NNAME)
+			return symerrmsg(NNAME, "symbol");
+		Bseek(bp, 3, 1);
+		return NNAME+3;
+	}
+
+	p->type &= ~0x80;
+	if(p->type == 'z' || p->type == 'Z') {
+		o = Bseek(bp, 0, 1);
+		if(Bgetc(bp) < 0) {
+			werrstr("can't read symbol name");
+			return -1;
+		}
+		for(;;) {
+			c1 = Bgetc(bp);
+			c2 = Bgetc(bp);
+			if(c1 < 0 || c2 < 0) {
+				werrstr("can't read symbol name");
+				return -1;
+			}
+			if(c1 == 0 && c2 == 0)
+				break;
+		}
+		n = Bseek(bp, 0, 1)-o;
+		p->name = malloc(n);
+		if(p->name == 0) {
+			werrstr("can't malloc %ld bytes", n);
+			return -1;
+		}
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->name, n) != n) {
+			werrstr("can't read %ld bytes of symbol name", n);
+			return -1;
+		}
+	} else {
+		cp = Brdline(bp, '\0');
+		if(cp == 0) {
+			werrstr("can't read symbol name");
+			return -1;
+		}
+		n = Blinelen(bp);
+		p->name = malloc(n);
+		if(p->name == 0) {
+			werrstr("can't malloc %ld bytes", n);
+			return -1;
+		}
+		strcpy(p->name, cp);
+	}
+	return n;
+}
+
+/*
+ *	free any previously loaded symbol tables
+ */
+static void
+cleansyms(void)
+{
+	if(globals)
+		free(globals);
+	globals = 0;
+	nglob = 0;
+	if(txt)
+		free(txt);
+	txt = 0;
+	ntxt = 0;
+	if(fnames)
+		free(fnames);
+	fnames = 0;
+	fpmax = 0;
+
+	if(files)
+		free(files);
+	files = 0;
+	nfiles = 0;
+	if(hist)
+		free(hist);
+	hist = 0;
+	nhist = 0;
+	if(autos)
+		free(autos);
+	autos = 0;
+	nauto = 0;
+	isbuilt = 0;
+	if(symbols)
+		free(symbols);
+	symbols = 0;
+	nsym = 0;
+	if(spoff)
+		free(spoff);
+	spoff = 0;
+	if(pcline)
+		free(pcline);
+	pcline = 0;
+}
+
+/*
+ *	delimit the text segment
+ */
+void
+textseg(uvlong base, Fhdr *fp)
+{
+	txtstart = base;
+	txtend = base+fp->txtsz;
+}
+
+/*
+ *	symbase: return base and size of raw symbol table
+ *		(special hack for high access rate operations)
+ */
+Sym *
+symbase(long *n)
+{
+	*n = nsym;
+	return symbols;
+}
+
+/*
+ *	Get the ith symbol table entry
+ */
+Sym *
+getsym(int index)
+{
+	if(index >= 0 && index < nsym)
+		return &symbols[index];
+	return 0;
+}
+
+/*
+ *	initialize internal symbol tables
+ */
+static int
+buildtbls(void)
+{
+	long i;
+	int j, nh, ng, nt;
+	File *f;
+	Txtsym *tp;
+	Hist *hp;
+	Sym *p, **ap;
+
+	if(isbuilt)
+		return 1;
+	isbuilt = 1;
+			/* allocate the tables */
+	if(nglob) {
+		globals = malloc(nglob*sizeof(*globals));
+		if(!globals) {
+			werrstr("can't malloc global symbol table");
+			return 0;
+		}
+	}
+	if(ntxt) {
+		txt = malloc(ntxt*sizeof(*txt));
+		if (!txt) {
+			werrstr("can't malloc text symbol table");
+			return 0;
+		}
+	}
+	fnames = malloc((fpmax+1)*sizeof(*fnames));
+	if (!fnames) {
+		werrstr("can't malloc file name table");
+		return 0;
+	}
+	memset(fnames, 0, (fpmax+1)*sizeof(*fnames));
+	files = malloc(nfiles*sizeof(*files));
+	if(!files) {
+		werrstr("can't malloc file table");
+		return 0;
+	}
+	hist = malloc(nhist*sizeof(Hist));
+	if(hist == 0) {
+		werrstr("can't malloc history stack");
+		return 0;
+	}
+	autos = malloc(nauto*sizeof(Sym*));
+	if(autos == 0) {
+		werrstr("can't malloc auto symbol table");
+		return 0;
+	}
+		/* load the tables */
+	ng = nt = nh = 0;
+	f = 0;
+	tp = 0;
+	i = nsym;
+	hp = hist;
+	ap = autos;
+	for(p = symbols; i-- > 0; p++) {
+		switch(p->type) {
+		case 'D':
+		case 'd':
+		case 'B':
+		case 'b':
+			if(debug)
+				print("Global: %s %llux\n", p->name, p->value);
+			globals[ng++] = p;
+			break;
+		case 'z':
+			if(p->value == 1) {		/* New file */
+				if(f) {
+					f->n = nh;
+					f->hist[nh].name = 0;	/* one extra */
+					hp += nh+1;
+					f++;
+				}
+				else
+					f = files;
+				f->hist = hp;
+				f->sym = 0;
+				f->addr = 0;
+				nh = 0;
+			}
+				/* alloc one slot extra as terminator */
+			f->hist[nh].name = p->name;
+			f->hist[nh].line = p->value;
+			f->hist[nh].offset = 0;
+			if(debug)
+				printhist("-> ", &f->hist[nh], 1);
+			nh++;
+			break;
+		case 'Z':
+			if(f && nh > 0)
+				f->hist[nh-1].offset = p->value;
+			break;
+		case 'T':
+		case 't':	/* Text: terminate history if first in file */
+		case 'L':
+		case 'l':
+			tp = &txt[nt++];
+			tp->n = 0;
+			tp->sym = p;
+			tp->locals = ap;
+			if(debug)
+				print("TEXT: %s at %llux\n", p->name, p->value);
+			if(f && !f->sym) {			/* first  */
+				f->sym = p;
+				f->addr = p->value;
+			}
+			break;
+		case 'a':
+		case 'p':
+		case 'm':		/* Local Vars */
+			if(!tp)
+				print("Warning: Free floating local var: %s\n",
+					p->name);
+			else {
+				if(debug)
+					print("Local: %s %llux\n", p->name, p->value);
+				tp->locals[tp->n] = p;
+				tp->n++;
+				ap++;
+			}
+			break;
+		case 'f':		/* File names */
+			if(debug)
+				print("Fname: %s\n", p->name);
+			fnames[p->value] = p;
+			break;
+		default:
+			break;
+		}
+	}
+		/* sort global and text tables into ascending address order */
+	qsort(globals, nglob, sizeof(Sym*), symcomp);
+	qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
+	qsort(files, nfiles, sizeof(File), filecomp);
+	tp = txt;
+	for(i = 0, f = files; i < nfiles; i++, f++) {
+		for(j = 0; j < ntxt; j++) {
+			if(f->sym == tp->sym) {
+				if(debug) {
+					print("LINK: %s to at %llux", f->sym->name, f->addr);
+					printhist("... ", f->hist, 1);
+				}
+				f->txt = tp++;
+				break;
+			}
+			if(++tp >= txt+ntxt)	/* wrap around */
+				tp = txt;
+		}
+	}
+	return 1;
+}
+
+/*
+ * find symbol function.var by name.
+ *	fn != 0 && var != 0	=> look for fn in text, var in data
+ *	fn != 0 && var == 0	=> look for fn in text
+ *	fn == 0 && var != 0	=> look for var first in text then in data space.
+ */
+int
+lookup(char *fn, char *var, Symbol *s)
+{
+	int found;
+
+	if(buildtbls() == 0)
+		return 0;
+	if(fn) {
+		found = findtext(fn, s);
+		if(var == 0)		/* case 2: fn not in text */
+			return found;
+		else if(!found)		/* case 1: fn not found */
+			return 0;
+	} else if(var) {
+		found = findtext(var, s);
+		if(found)
+			return 1;	/* case 3: var found in text */
+	} else return 0;		/* case 4: fn & var == zero */
+
+	if(found)
+		return findlocal(s, var, s);	/* case 1: fn found */
+	return findglobal(var, s);		/* case 3: var not found */
+
+}
+
+/*
+ * find a function by name
+ */
+static int
+findtext(char *name, Symbol *s)
+{
+	int i;
+
+	for(i = 0; i < ntxt; i++) {
+		if(strcmp(txt[i].sym->name, name) == 0) {
+			fillsym(txt[i].sym, s);
+			s->handle = (void *) &txt[i];
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+/*
+ * find global variable by name
+ */
+static int
+findglobal(char *name, Symbol *s)
+{
+	long i;
+
+	for(i = 0; i < nglob; i++) {
+		if(strcmp(globals[i]->name, name) == 0) {
+			fillsym(globals[i], s);
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	find the local variable by name within a given function
+ */
+int
+findlocal(Symbol *s1, char *name, Symbol *s2)
+{
+	if(s1 == 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+	return findlocvar(s1, name, s2);
+}
+
+/*
+ *	find the local variable by name within a given function
+ *		(internal function - does no parameter validation)
+ */
+static int
+findlocvar(Symbol *s1, char *name, Symbol *s2)
+{
+	Txtsym *tp;
+	int i;
+
+	tp = (Txtsym *)s1->handle;
+	if(tp && tp->locals) {
+		for(i = 0; i < tp->n; i++)
+			if (strcmp(tp->locals[i]->name, name) == 0) {
+				fillsym(tp->locals[i], s2);
+				s2->handle = (void *)tp;
+				s2->index = tp->n-1 - i;
+				return 1;
+			}
+	}
+	return 0;
+}
+
+/*
+ *	Get ith text symbol
+ */
+int
+textsym(Symbol *s, int index)
+{
+
+	if(buildtbls() == 0)
+		return 0;
+	if(index < 0 || index >= ntxt)
+		return 0;
+	fillsym(txt[index].sym, s);
+	s->handle = (void *)&txt[index];
+	s->index = index;
+	return 1;
+}
+
+/*	
+ *	Get ith file name
+ */
+int
+filesym(int index, char *buf, int n)
+{
+	Hist *hp;
+
+	if(buildtbls() == 0)
+		return 0;
+	if(index < 0 || index >= nfiles)
+		return 0;
+	hp = files[index].hist;
+	if(!hp || !hp->name)
+		return 0;
+	return fileelem(fnames, (uchar*)hp->name, buf, n);
+}
+
+/*
+ *	Lookup name of local variable located at an offset into the frame.
+ *	The type selects either a parameter or automatic.
+ */
+int
+getauto(Symbol *s1, int off, int type, Symbol *s2)
+{
+	Txtsym *tp;
+	Sym *p;
+	int i, t;
+
+	if(s1 == 0)
+		return 0;
+	if(type == CPARAM)
+		t = 'p';
+	else if(type == CAUTO)
+		t = 'a';
+	else
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+	tp = (Txtsym *)s1->handle;
+	if(tp == 0)
+		return 0;
+	for(i = 0; i < tp->n; i++) {
+		p = tp->locals[i];
+		if(p->type == t && p->value == off) {
+			fillsym(p, s2);
+			s2->handle = s1->handle;
+			s2->index = tp->n-1 - i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Find text symbol containing addr; binary search assumes text array is sorted by addr
+ */
+static int
+srchtext(uvlong addr)
+{
+	uvlong val;
+	int top, bot, mid;
+	Sym *sp;
+
+	val = addr;
+	bot = 0;
+	top = ntxt;
+	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		sp = txt[mid].sym;
+		if(val < sp->value)
+			top = mid;
+		else if(mid != ntxt-1 && val >= txt[mid+1].sym->value)
+			bot = mid;
+		else
+			return mid;
+	}
+	return -1;
+}
+
+/*
+ * Find data symbol containing addr; binary search assumes data array is sorted by addr
+ */
+static int
+srchdata(uvlong addr)
+{
+	uvlong val;
+	int top, bot, mid;
+	Sym *sp;
+
+	bot = 0;
+	top = nglob;
+	val = addr;
+	for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		sp = globals[mid];
+		if(val < sp->value)
+			top = mid;
+		else if(mid < nglob-1 && val >= globals[mid+1]->value)
+			bot = mid;
+		else
+			return mid;
+	}
+	return -1;
+}
+
+/*
+ * Find symbol containing val in specified search space
+ * There is a special case when a value falls beyond the end
+ * of the text segment; if the search space is CTEXT, that value
+ * (usually etext) is returned.  If the search space is CANY, symbols in the
+ * data space are searched for a match.
+ */
+int
+findsym(uvlong val, int type, Symbol *s)
+{
+	int i;
+
+	if(buildtbls() == 0)
+		return 0;
+
+	if(type == CTEXT || type == CANY) {
+		i = srchtext(val);
+		if(i >= 0) {
+			if(type == CTEXT || i != ntxt-1) {
+				fillsym(txt[i].sym, s);
+				s->handle = (void *) &txt[i];
+				s->index = i;
+				return 1;
+			}
+		}
+	}
+	if(type == CDATA || type == CANY) {
+		i = srchdata(val);
+		if(i >= 0) {
+			fillsym(globals[i], s);
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Find the start and end address of the function containing addr
+ */
+int
+fnbound(uvlong addr, uvlong *bounds)
+{
+	int i;
+
+	if(buildtbls() == 0)
+		return 0;
+
+	i = srchtext(addr);
+	if(0 <= i && i < ntxt-1) {
+		bounds[0] = txt[i].sym->value;
+		bounds[1] = txt[i+1].sym->value;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * get the ith local symbol for a function
+ * the input symbol table is reverse ordered, so we reverse
+ * accesses here to maintain approx. parameter ordering in a stack trace.
+ */
+int
+localsym(Symbol *s, int index)
+{
+	Txtsym *tp;
+
+	if(s == 0 || index < 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+
+	tp = (Txtsym *)s->handle;
+	if(tp && tp->locals && index < tp->n) {
+		fillsym(tp->locals[tp->n-index-1], s);	/* reverse */
+		s->handle = (void *)tp;
+		s->index = index;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * get the ith global symbol
+ */
+int
+globalsym(Symbol *s, int index)
+{
+	if(s == 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+
+	if(index >=0 && index < nglob) {
+		fillsym(globals[index], s);
+		s->index = index;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ *	find the pc given a file name and line offset into it.
+ */
+uvlong
+file2pc(char *file, long line)
+{
+	File *fp;
+	long i;
+	uvlong pc, start, end;
+	short *name;
+
+	if(buildtbls() == 0 || files == 0)
+		return ~0;
+	name = encfname(file);
+	if(name == 0) {			/* encode the file name */
+		werrstr("file %s not found", file);
+		return ~0;
+	} 
+		/* find this history stack */
+	for(i = 0, fp = files; i < nfiles; i++, fp++)
+		if (hline(fp, name, &line))
+			break;
+	free(name);
+	if(i >= nfiles) {
+		werrstr("line %ld in file %s not found", line, file);
+		return ~0;
+	}
+	start = fp->addr;		/* first text addr this file */
+	if(i < nfiles-1)
+		end = (fp+1)->addr;	/* first text addr next file */
+	else
+		end = 0;		/* last file in load module */
+	/*
+	 * At this point, line contains the offset into the file.
+	 * run the state machine to locate the pc closest to that value.
+	 */
+	if(debug)
+		print("find pc for %ld - between: %llux and %llux\n", line, start, end);
+	pc = line2addr(line, start, end);
+	if(pc == ~0) {
+		werrstr("line %ld not in file %s", line, file);
+		return ~0;
+	}
+	return pc;
+}
+
+/*
+ *	search for a path component index
+ */
+static int
+pathcomp(char *s, int n)
+{
+	int i;
+
+	for(i = 0; i <= fpmax; i++)
+		if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0)
+			return i;
+	return -1;
+}
+
+/*
+ *	Encode a char file name as a sequence of short indices
+ *	into the file name dictionary.
+ */
+static short*
+encfname(char *file)
+{
+	int i, j;
+	char *cp, *cp2;
+	short *dest;
+
+	if(*file == '/')	/* always check first '/' */
+		cp2 = file+1;
+	else {
+		cp2 = strchr(file, '/');
+		if(!cp2)
+			cp2 = strchr(file, 0);
+	}
+	cp = file;
+	dest = 0;
+	for(i = 0; *cp; i++) {
+		j = pathcomp(cp, cp2-cp);
+		if(j < 0)
+			return 0;	/* not found */
+		dest = realloc(dest, (i+1)*sizeof(short));
+		dest[i] = j;
+		cp = cp2;
+		while(*cp == '/')	/* skip embedded '/'s */
+			cp++;
+		cp2 = strchr(cp, '/');
+		if(!cp2)
+			cp2 = strchr(cp, 0);
+	}
+	dest = realloc(dest, (i+1)*sizeof(short));
+	dest[i] = 0;
+	return dest;
+}
+
+/*
+ *	Search a history stack for a matching file name accumulating
+ *	the size of intervening files in the stack.
+ */
+static int
+hline(File *fp, short *name, long *line)
+{
+	Hist *hp;
+	int offset, depth;
+	long ln;
+
+	for(hp = fp->hist; hp->name; hp++)		/* find name in stack */
+		if(hp->name[1] || hp->name[2]) {
+			if(hcomp(hp, name))
+				break;
+		}
+	if(!hp->name)		/* match not found */
+		return 0;
+	if(debug)
+		printhist("hline found ... ", hp, 1);
+	/*
+	 * unwind the stack until empty or we hit an entry beyond our line
+	 */
+	ln = *line;
+	offset = hp->line-1;
+	depth = 1;
+	for(hp++; depth && hp->name; hp++) {
+		if(debug)
+			printhist("hline inspect ... ", hp, 1);
+		if(hp->name[1] || hp->name[2]) {
+			if(hp->offset){			/* Z record */
+				offset = 0;
+				if(hcomp(hp, name)) {
+					if(*line <= hp->offset)
+						break;
+					ln = *line+hp->line-hp->offset;
+					depth = 1;	/* implicit pop */
+				} else
+					depth = 2;	/* implicit push */
+			} else if(depth == 1 && ln < hp->line-offset)
+					break;		/* Beyond our line */
+			else if(depth++ == 1)		/* push	*/
+				offset -= hp->line;
+		} else if(--depth == 1)		/* pop */
+			offset += hp->line;	
+	}
+	*line = ln+offset;
+	return 1;
+}
+
+/*
+ *	compare two encoded file names
+ */
+static int
+hcomp(Hist *hp, short *sp)
+{
+	uchar *cp;
+	int i, j;
+	short *s;
+
+	cp = (uchar *)hp->name;
+	s = sp;
+	if (*s == 0)
+		return 0;
+	for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
+		if(j == 0)
+			break;
+		if(*s == j)
+			s++;
+		else
+			s = sp;
+	}
+	return *s == 0;
+}
+
+/*
+ *	Convert a pc to a "file:line {file:line}" string.
+ */
+long
+fileline(char *str, int n, uvlong dot)
+{
+	long line, top, bot, mid;
+	File *f;
+
+	*str = 0;
+	if(buildtbls() == 0)
+		return 0;
+		/* binary search assumes file list is sorted by addr */
+	bot = 0;
+	top = nfiles;
+	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		f = &files[mid];
+		if(dot < f->addr)
+			top = mid;
+		else if(mid < nfiles-1 && dot >= (f+1)->addr)
+			bot = mid;
+		else {
+			line = pc2line(dot);
+			if(line > 0 && fline(str, n, line, f->hist, 0) >= 0)
+				return 1;
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Convert a line number within a composite file to relative line
+ *	number in a source file.  A composite file is the source
+ *	file with included files inserted in line.
+ */
+static int
+fline(char *str, int n, long line, Hist *base, Hist **ret)
+{
+	Hist *start;			/* start of current level */
+	Hist *h;			/* current entry */
+	long delta;			/* sum of size of files this level */
+	int k;
+
+	start = base;
+	h = base;
+	delta = h->line;
+	while(h && h->name && line > h->line) {
+		if(h->name[1] || h->name[2]) {
+			if(h->offset != 0) {	/* #line Directive */
+				delta = h->line-h->offset+1;
+				start = h;
+				base = h++;
+			} else {		/* beginning of File */
+				if(start == base)
+					start = h++;
+				else {
+					k = fline(str, n, line, start, &h);
+					if(k <= 0)
+						return k;
+				}
+			}
+		} else {
+			if(start == base && ret) {	/* end of recursion level */
+				*ret = h;
+				return 1;
+			} else {			/* end of included file */
+				delta += h->line-start->line;
+				h++;
+				start = base;
+			}
+		}
+	}
+	if(!h)
+		return -1;
+	if(start != base)
+		line = line-start->line+1;
+	else
+		line = line-delta+1;
+	if(!h->name)
+		strncpy(str, "<eof>", n);
+	else {
+		k = fileelem(fnames, (uchar*)start->name, str, n);
+		if(k+8 < n)
+			sprint(str+k, ":%ld", line);
+	}
+/**********Remove comments for complete back-trace of include sequence
+ *	if(start != base) {
+ *		k = strlen(str);
+ *		if(k+2 < n) {
+ *			str[k++] = ' ';
+ *			str[k++] = '{';
+ *		}
+ *		k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
+ *		if(k+10 < n)
+ *			sprint(str+k, ":%ld}", start->line-delta);
+ *	}
+ ********************/
+	return 0;
+}
+
+/*
+ *	convert an encoded file name to a string.
+ */
+int
+fileelem(Sym **fp, uchar *cp, char *buf, int n)
+{
+	int i, j;
+	char *c, *bp, *end;
+
+	bp = buf;
+	end = buf+n-1;
+	for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
+		c = fp[j]->name;
+		if(bp != buf && bp[-1] != '/' && bp < end)
+			*bp++ = '/';
+		while(bp < end && *c)
+			*bp++ = *c++;
+	}
+	*bp = 0;
+	i =  bp-buf;
+	if(i > 1) {
+		cleanname(buf);
+		i = strlen(buf);
+	}
+	return i;
+}
+
+/*
+ *	compare the values of two symbol table entries.
+ */
+static int
+symcomp(void *a, void *b)
+{
+	int i;
+
+	i = (*(Sym**)a)->value - (*(Sym**)b)->value;
+	if (i)
+		return i;
+	return strcmp((*(Sym**)a)->name, (*(Sym**)b)->name);
+}
+
+/*
+ *	compare the values of the symbols referenced by two text table entries
+ */
+static int
+txtcomp(void *a, void *b)
+{
+	return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
+}
+
+/*
+ *	compare the values of the symbols referenced by two file table entries
+ */
+static int
+filecomp(void *a, void *b)
+{
+	return ((File*)a)->addr - ((File*)b)->addr;
+}
+
+/*
+ *	fill an interface Symbol structure from a symbol table entry
+ */
+static void
+fillsym(Sym *sp, Symbol *s)
+{
+	s->type = sp->type;
+	s->value = sp->value;
+	s->name = sp->name;
+	s->index = 0;
+	switch(sp->type) {
+	case 'b':
+	case 'B':
+	case 'D':
+	case 'd':
+		s->class = CDATA;
+		break;
+	case 't':
+	case 'T':
+	case 'l':
+	case 'L':
+		s->class = CTEXT;
+		break;
+	case 'a':
+		s->class = CAUTO;
+		break;
+	case 'p':
+		s->class = CPARAM;
+		break;
+	case 'm':
+		s->class = CSTAB;
+		break;
+	default:
+		s->class = CNONE;
+		break;
+	}
+	s->handle = 0;
+}
+
+/*
+ *	find the stack frame, given the pc
+ */
+uvlong
+pc2sp(uvlong pc)
+{
+	uchar *c, u;
+	uvlong currpc, currsp;
+
+	if(spoff == 0)
+		return ~0;
+	currsp = 0;
+	currpc = txtstart - mach->pcquant;
+
+	if(pc<currpc || pc>txtend)
+		return ~0;
+	for(c = spoff; c < spoffend; c++) {
+		if (currpc >= pc)
+			return currsp;
+		u = *c;
+		if (u == 0) {
+			currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if (u < 65)
+			currsp += 4*u;
+		else if (u < 129)
+			currsp -= 4*(u-64);
+		else 
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	return ~0;
+}
+
+/*
+ *	find the source file line number for a given value of the pc
+ */
+long
+pc2line(uvlong pc)
+{
+	uchar *c, u;
+	uvlong currpc;
+	long currline;
+
+	if(pcline == 0)
+		return -1;
+	currline = 0;
+	currpc = txtstart-mach->pcquant;
+	if(pc<currpc || pc>txtend)
+		return ~0;
+
+	for(c = pcline; c < pclineend; c++) {
+		if(currpc >= pc)
+			return currline;
+		u = *c;
+		if(u == 0) {
+			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if(u < 65)
+			currline += u;
+		else if(u < 129)
+			currline -= (u-64);
+		else 
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	return ~0;
+}
+
+/*
+ *	find the pc associated with a line number
+ *	basepc and endpc are text addresses bounding the search.
+ *	if endpc == 0, the end of the table is used (i.e., no upper bound).
+ *	usually, basepc and endpc contain the first text address in
+ *	a file and the first text address in the following file, respectively.
+ */
+uvlong
+line2addr(long line, uvlong basepc, uvlong endpc)
+{
+	uchar *c,  u;
+	uvlong currpc, pc;
+	long currline;
+	long delta, d;
+	int found;
+
+	if(pcline == 0 || line == 0)
+		return ~0;
+
+	currline = 0;
+	currpc = txtstart-mach->pcquant;
+	pc = ~0;
+	found = 0;
+	delta = HUGEINT;
+
+	for(c = pcline; c < pclineend; c++) {
+		if(endpc && currpc >= endpc)	/* end of file of interest */
+			break;
+		if(currpc >= basepc) {		/* proper file */
+			if(currline >= line) {
+				d = currline-line;
+				found = 1;
+			} else
+				d = line-currline;
+			if(d < delta) {
+				delta = d;
+				pc = currpc;
+			}
+		}
+		u = *c;
+		if(u == 0) {
+			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if(u < 65)
+			currline += u;
+		else if(u < 129)
+			currline -= (u-64);
+		else 
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	if(found)
+		return pc;
+	return ~0;
+}
+
+/*
+ *	Print a history stack (debug). if count is 0, prints the whole stack
+ */
+static void
+printhist(char *msg, Hist *hp, int count)
+{
+	int i;
+	uchar *cp;
+	char buf[128];
+
+	i = 0;
+	while(hp->name) {
+		if(count && ++i > count)
+			break;
+		print("%s Line: %lx (%ld)  Offset: %lx (%ld)  Name: ", msg,
+			hp->line, hp->line, hp->offset, hp->offset);
+		for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
+			if (cp != (uchar *)hp->name+1)
+				print("/");
+			print("%x", (*cp<<8)|cp[1]);
+		}
+		fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
+		print(" (%s)\n", buf);
+		hp++;
+	}
+}
+
+#ifdef DEBUG
+/*
+ *	print the history stack for a file. (debug only)
+ *	if (name == 0) => print all history stacks.
+ */
+void
+dumphist(char *name)
+{
+	int i;
+	File *f;
+	short *fname;
+
+	if(buildtbls() == 0)
+		return;
+	if(name)
+		fname = encfname(name);
+	for(i = 0, f = files; i < nfiles; i++, f++)
+		if(fname == 0 || hcomp(f->hist, fname))
+			printhist("> ", f->hist, f->n);
+
+	if(fname)
+		free(fname);
+}
+#endif
--- /dev/null
+++ b/utils/libmach/t.c
@@ -1,0 +1,122 @@
+/*
+ * thumb definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "uregt.h"
+#include "mach.h"
+
+
+#define	REGOFF(x)	(ulong) (&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(r13)
+#define PC		REGOFF(pc)
+
+#define	REGSIZE		sizeof(struct Ureg)
+
+Reglist thumbreglist[] =
+{
+	{"LINK",	REGOFF(link),		RINT|RRDONLY, 'X'},
+	{"TYPE",	REGOFF(type),		RINT|RRDONLY, 'X'},
+	{"PSR",		REGOFF(psr),		RINT|RRDONLY, 'X'},
+	{"PC",		PC,			RINT, 'X'},
+	{"SP",		SP,			RINT, 'X'},
+	{"R15",		PC,			RINT, 'X'},
+	{"R14",		REGOFF(r14),		RINT, 'X'},
+	{"R13",		REGOFF(r13),		RINT, 'X'},
+	{"R12",		REGOFF(r12),		RINT, 'X'},
+	{"R11",		REGOFF(r11),		RINT, 'X'},
+	{"R10",		REGOFF(r10),		RINT, 'X'},
+	{"R9",		REGOFF(r9),		RINT, 'X'},
+	{"R8",		REGOFF(r8),		RINT, 'X'},
+	{"R7",		REGOFF(r7),		RINT, 'X'},
+	{"R6",		REGOFF(r6),		RINT, 'X'},
+	{"R5",		REGOFF(r5),		RINT, 'X'},
+	{"R4",		REGOFF(r4),		RINT, 'X'},
+	{"R3",		REGOFF(r3),		RINT, 'X'},
+	{"R2",		REGOFF(r2),		RINT, 'X'},
+	{"R1",		REGOFF(r1),		RINT, 'X'},
+	{"R0",		REGOFF(r0),		RINT, 'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mthumb =
+{
+	"thumb",
+	MARM,	/*MTHUMB,*/		/* machine type */
+	thumbreglist,	/* register set */
+	REGSIZE,	/* register set size */
+	0,		/* fp register set size */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R15",		/* name of link register */
+	"setR12",	/* static base register name */
+	0,		/* static base register value */
+	0x1000,		/* page size */
+	0x80000000,	/* kernel base */
+	0,		/* kernel text mask */
+	0x7FFFFFFF,	/* stack top */
+	2,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
+
+typedef struct pcentry pcentry;
+
+struct pcentry{
+	long start;
+	long stop;
+};
+
+static pcentry *pctab;
+static int npctab;
+
+void
+thumbpctab(Biobuf *b, Fhdr *fp)
+{
+	int n, o, ta;
+	uchar c[8];
+	pcentry *tab;
+
+	Bseek(b, fp->lnpcoff+fp->lnpcsz, 0);
+	o = (int)Boffset(b);
+	Bseek(b, 0, 2);
+	n = (int)Boffset(b)-o;
+	pctab = (pcentry*)malloc(n);
+	if(pctab == 0)
+		return;
+	ta = fp->txtaddr;
+	tab = pctab;
+	Bseek(b, fp->lnpcoff+fp->lnpcsz, 0);
+	while(Bread(b, c, sizeof(c)) == sizeof(c)){
+		tab->start = ta + (c[0]<<24)|(c[1]<<16)|(c[2]<<8)|c[3];
+		tab->stop = ta + (c[4]<<24)|(c[5]<<16)|(c[6]<<8)|c[7];
+		tab++;
+	}
+	npctab = n/sizeof(c);
+}
+
+int
+thumbpclookup(uvlong pc)
+{
+	uvlong l, u, m;
+	pcentry *tab = pctab;
+
+	l = 0;
+	u = npctab-1;
+	while(l < u){
+		m = (l+u)/2;
+		if(pc < tab[m].start)
+			u = m-1;
+		else if(pc > tab[m].stop)
+			l = m+1;
+		else
+			l = u = m;
+	}
+	if(l == u && u < npctab && tab[u].start <= pc && pc <= tab[u].stop)
+		return 1;	// thumb
+	return 0;	// arm
+}
--- /dev/null
+++ b/utils/libmach/tdb.c
@@ -1,0 +1,839 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+static int debug = 0;
+
+typedef struct	Instr	Instr;
+struct	Instr
+{
+	Map	*map;
+	ulong	w;
+	ulong	addr;
+	uchar	op;			/* super opcode */
+
+	uchar	rd;
+	uchar	rn;
+	uchar	rs;
+
+	long	imm;			/* imm */
+
+	char*	curr;			/* fill point in buffer */
+	char*	end;			/* end of buffer */
+	char*	err;			/* error message */
+};
+
+typedef struct Opcode Opcode;
+struct Opcode
+{
+	char*	o;
+	void	(*fmt)(Opcode*, Instr*);
+	uvlong	(*foll)(Map*, Rgetter, Instr*, uvlong);
+	char*	a;
+};
+
+static	void	format(char*, Instr*, char*);
+static	char	FRAMENAME[] = ".frame";
+
+/*
+ * Thumb-specific debugger interface
+ */
+
+static	char	*thumbexcep(Map*, Rgetter);
+static	int	thumbfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	thumbinst(Map*, uvlong, char, char*, int);
+static	int	thumbdas(Map*, uvlong, char*, int);
+static	int	thumbinstlen(Map*, uvlong);
+
+/*
+ *	Debugger interface
+ */
+Machdata thumbmach =
+{
+	{0x0, 0xE8},		/* break point */
+	2,				/* break point size */
+
+	leswab,			/* short to local byte order */
+	leswal,			/* long to local byte order */
+	leswav,			/* long to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	thumbexcep,			/* print exception */
+	0,			/* breakpoint fixup */
+	0,			/* single precision float printer */
+	0,			/* double precision float printer */
+	thumbfoll,		/* following addresses */
+	thumbinst,		/* print instruction */
+	thumbdas,			/* dissembler */
+	thumbinstlen,		/* instruction size */
+};
+
+static void thumbrrh(Opcode *, Instr *);
+static void thumbbcc(Opcode *, Instr *);
+static void thumbb(Opcode *, Instr *);
+static void thumbbl(Opcode *, Instr *);
+
+static char*
+thumbexcep(Map *map, Rgetter rget)
+{
+	long c;
+
+	c = (*rget)(map, "TYPE");
+	switch (c&0x1f) {
+	case 0x11:
+		return "Fiq interrupt";
+	case 0x12:
+		return "Mirq interrupt";
+	case 0x13:
+		return "SVC/SWI Exception";
+	case 0x17:
+		return "Prefetch Abort/Data Abort";
+	case 0x18:
+		return "Data Abort";
+	case 0x1b:
+		return "Undefined instruction/Breakpoint";
+	case 0x1f:
+		return "Sys trap";
+	default:
+		return "Undefined trap";
+	}
+}
+
+static
+char*	cond[16] =
+{
+	"EQ",	"NE",	"CS",	"CC",
+	"MI",	"PL",	"VS",	"VC",
+	"HI",	"LS",	"GE",	"LT",
+	"GT",	"LE",	"\0",	"NV"
+};
+
+#define B(h, l)		bits(ins, h, l)
+
+static int
+bits(int i, int h, int l)
+{
+	if(h < l)
+		print("h < l in bits");
+	return (i&(((1<<(h-l+1))-1)<<l))>>l;
+}
+
+int
+thumbclass(long w)
+{
+	int o;
+	int ins = w;
+
+	if(ins&0xffff0000)
+		return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+2;
+	o = B(15, 13);
+	switch(o){
+	case 0:
+		o = B(12, 11);
+		switch(o){
+			case 0:
+			case 1:
+			case 2:
+				return B(12, 11);
+			case 3:
+				if(B(10, 10) == 0)
+					return 3+B(9, 9);
+				else
+					return 3+2+B(9, 9);
+		}
+	case 1:
+		return 3+2+2+B(12, 11);
+	case 2:
+		o = B(12, 10);
+		if(o == 0)
+			return 3+2+2+4+B(9, 6);
+		if(o == 1){
+			o = B(9, 8);
+			if(o == 3)
+				return 3+2+2+4+16+B(9, 8);
+			return 3+2+2+4+16+B(9, 8);
+		}
+		if(o == 2 || o == 3)
+			return 3+2+2+4+16+4;
+		return 3+2+2+4+16+4+1+B(11, 9);
+	case 3:
+		return 3+2+2+4+16+4+1+8+B(12, 11);
+	case 4:
+		if(B(12, 12) == 0)
+			return 3+2+2+4+16+4+1+8+4+B(11, 11);
+		return 3+2+2+4+16+4+1+8+6+B(11, 11);
+	case 5:
+		if(B(12, 12) == 0)
+			return 3+2+2+4+16+4+1+8+6+2+B(11, 11);
+		if(B(11, 8) == 0)
+			return 3+2+2+4+16+4+1+8+6+2+2+B(7, 7);
+		return 3+2+2+4+16+4+1+8+6+2+2+2+B(11, 11);
+	case 6:
+		if(B(12, 12) == 0)
+			return 3+2+2+4+16+4+1+8+6+2+2+2+2+B(11, 11);
+		if(B(11, 8) == 0xf)
+			return 3+2+2+4+16+4+1+8+6+2+2+2+4;
+		return 3+2+2+4+16+4+1+8+6+2+2+2+4+1;
+	case 7:
+		o = B(12, 11);
+		switch(o){
+			case 0:
+				return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1;
+			case 1:
+				return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+2;
+			case 2:
+				return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1;
+			case 3:
+				return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+1;
+		}
+	}
+	return 0;
+}
+
+static int
+decode(Map *map, uvlong pc, Instr *i)
+{
+	ushort w;
+
+	if(get2(map, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->w = w;
+	i->addr = pc;
+	i->op = thumbclass(w);
+	i->map = map;
+	return 1;
+}
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static int
+plocal(Instr *i)
+{
+	char *reg;
+	Symbol s;
+	char *fn;
+	int class;
+	int offset;
+
+	if(!findsym(i->addr, CTEXT, &s)) {
+		if(debug)fprint(2,"fn not found @%lux: %r\n", i->addr);
+		return 0;
+	}
+	fn = s.name;
+	if (!findlocal(&s, FRAMENAME, &s)) {
+		if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name);
+			return 0;
+	}
+	if(s.value > i->imm) {
+		class = CAUTO;
+		offset = s.value-i->imm;
+		reg = "(SP)";
+	} else {
+		class = CPARAM;
+		offset = i->imm-s.value-4;
+		reg = "(FP)";
+	}
+	if(!getauto(&s, offset, class, &s)) {
+		if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn,
+			class == CAUTO ? " auto" : "param", offset);
+		return 0;
+	}
+	bprint(i, "%s%c%d%s", s.name, class == CPARAM ? '+' : '-', s.value, reg);
+	return 1;
+}
+
+/*
+ * Print value v as name[+offset]
+ */
+static int
+gsymoff(char *buf, int n, long v, int space)
+{
+	Symbol s;
+	int r;
+	long delta;
+
+	r = delta = 0;		/* to shut compiler up */
+	if (v) {
+		r = findsym(v, space, &s);
+		if (r)
+			delta = v-s.value;
+		if (delta < 0)
+			delta = -delta;
+	}
+	if (v == 0 || r == 0 || delta >= 4096)
+		return snprint(buf, n, "#%lux", v);
+	if (strcmp(s.name, ".string") == 0)
+		return snprint(buf, n, "#%lux", v);
+	if (!delta)
+		return snprint(buf, n, "%s", s.name);
+	if (s.type != 't' && s.type != 'T')
+		return snprint(buf, n, "%s+%llux", s.name, v-s.value);
+	else
+		return snprint(buf, n, "#%lux", v);
+}
+
+static int
+thumbcondpass(Map *map, Rgetter rget, uchar cond)
+{
+	ulong psr;
+	uchar n;
+	uchar z;
+	uchar c;
+	uchar v;
+
+	psr = rget(map, "PSR");
+	n = (psr >> 31) & 1;
+	z = (psr >> 30) & 1;
+	c = (psr >> 29) & 1;
+	v = (psr >> 28) & 1;
+
+	switch(cond) {
+		case 0:		return z;
+		case 1:		return !z;
+		case 2:		return c;
+		case 3:		return !c;
+		case 4:		return n;
+		case 5:		return !n;
+		case 6:		return v;
+		case 7:		return !v;
+		case 8:		return c && !z;
+		case 9:		return !c || z;
+		case 10:	return n == v;
+		case 11:	return n != v;
+		case 12:	return !z && (n == v);
+		case 13:	return z && (n != v);
+		case 14:	return 1;
+		case 15:	return 0;
+	}
+	return 0;
+}
+
+static uvlong
+thumbfbranch(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	char buf[8];
+
+	if(i->op == 30){	// BX
+		thumbrrh(nil, i);
+		sprint(buf, "R%ud", i->rn);
+		return rget(map, buf)&~1;		// clear T bit
+	}
+	if(i->op == 57){	// Bcc
+		thumbbcc(nil, i);
+		if(thumbcondpass(map, rget, (i->w >> 8) & 0xf))
+			return i->imm;
+		return pc+2;
+	}
+	if(i->op == 58){	// B
+		thumbb(nil, i);
+		return i->imm;
+	}
+	if(i->op == 60){	// BL
+		thumbbl(nil, i);
+		return i->imm;
+	}
+	print("bad thumbfbranch call");
+	return 0;
+}
+
+static uvlong
+thumbfmov(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	char buf[8];
+	ulong rd;
+
+	thumbrrh(nil, i);
+	rd = i->rd;
+	if(rd != 15)
+		return pc+2;
+	sprint(buf, "R%ud", i->rn);
+	return rget(map, buf);
+}
+
+static uvlong
+thumbfadd(Map *map, Rgetter rget, Instr *i, uvlong pc)
+{
+	char buf[8];
+	ulong rd, v;
+
+	thumbrrh(nil, i);
+	rd = i->rd;
+	if(rd != 15)
+		return pc+2;
+	sprint(buf, "R%ud", i->rn);
+	v = rget(map, buf);
+	sprint(buf, "R15");
+	return rget(map, buf) + v;
+}
+
+static void
+thumbshift(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	i->imm = B(10, 6);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbrrr(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	i->rs = B(8, 6);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbirr(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	i->imm = B(8, 6);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbir(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(10, 8);
+	i->imm = B(7, 0);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbrr(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbrrh(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	if(B(6, 6))
+		i->rn += 8;
+	if(B(7, 7))
+		i->rd += 8;
+	if(o != nil){
+		if(i->w == 0x46b7 || i->w == 0x46f7 || i->w == 0x4730 || i->w == 0x4770)	// mov r6, pc or mov lr, pc or bx r6 or bx lr
+			format("RET", i, "");
+		else
+			format(o->o, i, o->a);
+	}
+}
+
+static void
+thumbpcrel(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rn = 15;
+	i->rd = B(10, 8);
+	i->imm = 4*(B(7, 0)+1);
+	if(i->addr & 3)
+		i->imm -= 2;
+	format(o->o, i, o->a);
+}
+
+static void
+thumbmovirr(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(2, 0);
+	i->rn = B(5, 3);
+	i->imm = B(10, 6);
+	if(strcmp(o->o, "MOVW") == 0)
+		i->imm *= 4;
+	else if(strncmp(o->o, "MOVH", 4) == 0)
+		i->imm *= 2;
+	format(o->o, i, o->a);
+}
+
+static void
+thumbmovsp(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rn = 13;
+	i->rd = B(10, 8);
+	i->imm = 4*B(7, 0);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbaddsppc(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->rd = B(10, 8);
+	i->imm = 4*B(7, 0);
+	if(i->op == 48)
+		i->imm += 4;
+	format(o->o, i, o->a);
+}
+
+static void
+thumbaddsp(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->imm = 4*B(6, 0);
+	format(o->o, i, o->a);
+}	
+
+static void
+thumbswi(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	i->imm = B(7, 0);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbbcc(Opcode *o, Instr *i)
+{
+	int off, ins = i->w;
+
+	off = B(7, 0);
+	if(off & 0x80)
+		off |= 0xffffff00;
+	i->imm = i->addr + 2*off + 4;
+	if(o != nil)
+		format(o->o, i, o->a);
+}
+
+static void
+thumbb(Opcode *o, Instr *i)
+{
+	int off, ins = i->w;
+
+	off = B(10, 0);
+	if(off & 0x400)
+		off |= 0xfffff800;
+	i->imm = i->addr + 2*off + 4;
+	if(o != nil)
+		format(o->o, i, o->a);
+}	
+
+static void
+thumbbl(Opcode *o, Instr *i)
+{
+	int off, h, ins = i->w;
+	static int reglink;
+
+	h = B(11, 11);
+	off = B(10, 0);
+	if(h == 0){
+		if(off & 0x400)
+			off |= 0xfffff800;
+		i->imm = i->addr + (off<<12) + 4;
+		reglink = i->imm;
+	}
+	else{
+		i->imm = reglink + 2*off;
+	}
+	if(o != nil)
+		format(o->o, i, o->a);
+}	
+
+static void
+thumbregs(Opcode *o, Instr *i)
+{
+	int ins = i->w;
+
+	if(i->op == 52 || i->op == 53)
+		i->rd = 13;
+	else
+		i->rd = B(10, 8);
+	i->imm = B(7, 0);
+	format(o->o, i, o->a);
+}
+
+static void
+thumbunk(Opcode *o, Instr *i)
+{
+	format(o->o, i, o->a);
+}
+	
+static Opcode opcodes[] =
+{
+	"LSL",	thumbshift,	0,	"$#%i,R%n,R%d",	// 0
+	"LSR",	thumbshift,	0,	"$#%i,R%n,R%d",	// 1
+	"ASR",	thumbshift,	0,	"$#%i,R%n,R%d",	// 2
+	"ADD",	thumbrrr,		0,	"R%s,R%n,R%d",		// 3
+	"SUB",	thumbrrr,		0,	"R%s,R%n,R%d",		// 4
+	"ADD",	thumbirr,		0,	"$#%i,R%n,R%d",	// 5
+	"SUB",	thumbirr,		0,	"$#%i,R%n,R%d",	// 6
+	"MOVW",	thumbir,		0,	"$#%i,R%d",		// 7
+	"CMP",	thumbir,		0,	"$#%i,R%d",		// 8
+	"ADD",	thumbir,		0,	"$#%i,R%d,R%d",	// 9
+	"SUB",	thumbir,		0,	"$#%i,R%d,R%d",	// 10
+	"AND",	thumbrr,		0,	"R%n,R%d,R%d",	// 11
+	"EOR",	thumbrr,		0,	"R%n,R%d,R%d",	// 12
+	"LSL",	thumbrr,		0,	"R%n,R%d,R%d",	// 13
+	"LSR",	thumbrr,		0,	"R%n,R%d,R%d",	// 14
+	"ASR",	thumbrr,		0,	"R%n,R%d,R%d",	// 15
+	"ADC",	thumbrr,		0,	"R%n,R%d,R%d",	// 16
+	"SBC",	thumbrr,		0,	"R%n,R%d,R%d",	// 17
+	"ROR",	thumbrr,		0,	"R%n,R%d,R%d",	// 18
+	"TST",	thumbrr,		0,	"R%n,R%d",		// 19
+	"NEG",	thumbrr,		0,	"R%n,R%d",		// 20
+	"CMP",	thumbrr,		0,	"R%n,R%d",		// 21
+	"CMPN",	thumbrr,		0,	"R%n,R%d",		// 22
+	"OR",	thumbrr,		0,	"R%n,R%d,R%d",	// 23
+	"MUL",	thumbrr,		0,	"R%n,R%d,R%d",	// 24
+	"BITC",	thumbrr,		0,	"R%n,R%d,R%d",	// 25
+	"MOVN",	thumbrr,		0,	"R%n,R%d",		// 26
+	"ADD",	thumbrrh,		thumbfadd,	"R%n,R%d,R%d",	// 27
+	"CMP",	thumbrrh,		0,	"R%n,R%d",		// 28
+	"MOVW",	thumbrrh,		thumbfmov,	"R%n,R%d",	// 29
+	"BX",		thumbrrh,		thumbfbranch,	"R%n",	// 30
+	"MOVW",	thumbpcrel,	0,	"$%I,R%d",		// 31
+	"MOVW",	thumbrrr,		0,	"R%d, [R%s,R%n]",	// 32
+	"MOVH",	thumbrrr,		0,	"R%d, [R%s,R%n]",	// 33
+	"MOVB",	thumbrrr,		0,	"R%d, [R%s,R%n]",	// 34
+	"MOVB",	thumbrrr,		0,	"[R%s,R%n],R%d",	// 35
+	"MOVW",	thumbrrr,		0,	"[R%s,R%n],R%d",	// 36
+	"MOVHU",	thumbrrr,		0,	"[R%s,R%n],R%d",	// 37
+	"MOVBU",	thumbrrr,		0,	"[R%s,R%n],R%d",	// 38
+	"MOVH",	thumbrrr,		0,	"[R%s,R%n],R%d",	// 39
+	"MOVW",	thumbmovirr,	0,	"R%d,%I",			// 40
+	"MOVW",	thumbmovirr,	0,	"%I,R%d",			// 41
+	"MOVB",	thumbmovirr,	0,	"R%d,%I",			// 42
+	"MOVBU",	thumbmovirr,	0,	"$%I,R%d",		// 43
+	"MOVH",	thumbmovirr,	0,	"R%d,%I",			// 44
+	"MOVHU",	thumbmovirr,	0,	"%I,R%d",			// 45
+	"MOVW",	thumbmovsp,	0,	"R%d,%I",			// 46
+	"MOVW",	thumbmovsp,	0,	"%I,R%d",			// 47
+	"ADD",	thumbaddsppc,0,	"$#%i,PC,R%d",		// 48
+	"ADD",	thumbaddsppc,0,	"$#%i,SP,R%d",		// 49
+	"ADD",	thumbaddsp,	0,	"$#%i,SP,SP",		// 50
+	"SUB",	thumbaddsp,	0,	"$#%i,SP,SP",		// 51
+	"PUSH",	thumbregs,	0,	"R%d, %r",			// 52
+	"POP",	thumbregs,	0,	"R%d, %r",			// 53
+	"STMIA",	thumbregs,	0,	"R%d, %r",			// 54
+	"LDMIA",	thumbregs,	0,	"R%d, %r",			// 55
+	"SWI",	thumbswi,	0,	"$#%i",			// 56
+	"B%c",	thumbbcc,	thumbfbranch,	"%b",		// 57
+	"B",		thumbb,		thumbfbranch,	"%b",		// 58
+	"BL",		thumbbl,		0,	"",				// 59
+	"BL",		thumbbl,		thumbfbranch,	"%b",		// 60
+	"UNK",	thumbunk,	0,	"",				// 61
+};
+
+static void
+gaddr(Instr *i)
+{
+	*i->curr++ = '$';
+	i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY);
+}
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	int j, k, m, n;
+	int g;
+	char *fmt;
+	int ins = i->w;
+
+	if(mnemonic)
+		format(0, i, mnemonic);
+	if(f == 0)
+		return;
+	if(mnemonic)
+		if(i->curr < i->end)
+			*i->curr++ = '\t';
+	for ( ; *f && i->curr < i->end; f++) {
+		if(*f != '%') {
+			*i->curr++ = *f;
+			continue;
+		}
+		switch (*++f) {
+
+		case 'c':	/*Bcc */
+			bprint(i, "%s", cond[B(11, 8)]);
+			break;
+
+		case 's':
+			bprint(i, "%d", i->rs);
+			break;
+				
+		case 'n':
+			bprint(i, "%d", i->rn);
+			break;
+
+		case 'd':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'i':
+			bprint(i, "%lux", i->imm);
+			break;
+
+		case 'b':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				i->imm, CTEXT);
+			break;
+
+		case 'I':
+			if (i->rn == 13) {
+				if (plocal(i))
+					break;
+			}
+			g = 0;
+			fmt = "#%lx(R%d)";
+			if (i->rn == 15) {
+				/* convert load of offset(PC) to a load immediate */
+				if (get4(i->map, i->addr + i->imm, (ulong*)&i->imm) > 0)
+				{
+					g = 1;
+					fmt = "";
+				}
+			}
+			if (mach->sb)
+			{
+				if (i->rn == 12)
+				{
+					i->imm += mach->sb;
+					g = 1;
+					fmt = "-SB(SB)";
+				}
+			}
+			if (g)
+			{
+				gaddr(i);
+				bprint(i, fmt, i->rn);
+			}
+			else
+				bprint(i, fmt, i->imm, i->rn);
+			break;
+
+		case 'r':
+			n = i->imm&0xff;
+			j = 0;
+			k = 0;
+			while(n) {
+				m = j;
+				while(n&0x1) {
+					j++;
+					n >>= 1;
+				}
+				if(j != m) {
+					if(k)
+						bprint(i, ",");
+					if(j == m+1)
+						bprint(i, "R%d", m);
+					else
+						bprint(i, "R%d-R%d", m, j-1);
+					k = 1;
+				}
+				j++;
+				n >>= 1;
+			}
+			break;
+
+		case '\0':
+			*i->curr++ = '%';
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+	*i->curr = 0;
+}
+
+static int
+printins(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+
+	i.curr = buf;
+	i.end = buf+n-1;
+	if(decode(map, pc, &i) < 0)
+		return -1;
+
+	(*opcodes[i.op].fmt)(&opcodes[i.op], &i);
+	return 2;
+}
+
+static int
+thumbinst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	USED(modifier);
+	return printins(map, pc, buf, n);
+}
+
+static int
+thumbdas(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+
+	i.curr = buf;
+	i.end = buf+n;
+	if(decode(map, pc, &i) < 0)
+		return -1;
+	if(i.end-i.curr > 8)
+		i.curr = _hexify(buf, i.w, 7);
+	*i.curr = 0;
+	return 2;
+}
+
+static int
+thumbinstlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	if(decode(map, pc, &i) < 0)
+		return -1;
+	return 2;
+}
+
+static int
+thumbfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	ulong d;
+	Instr i;
+
+	if(decode(map, pc, &i) < 0)
+		return -1;
+
+	if(opcodes[i.op].foll) {
+		d = (*opcodes[i.op].foll)(map, rget, &i, pc);
+		if(d == -1)
+			return -1;
+	} else
+		d = pc+2;
+
+	foll[0] = d;
+	return 1;
+}
--- /dev/null
+++ b/utils/libmach/ureg4.h
@@ -1,0 +1,46 @@
+struct Ureg
+{
+	ulong	status;
+	long	pc;
+	union
+	{
+		long	sp;		/* r29 */
+		long	usp;		/* r29 */
+	} u0;
+	ulong	cause;
+	ulong	badvaddr;
+	ulong	tlbvirt;
+
+	long	hhi;	long	hi;
+	long	hlo;	long	lo;
+	long	hr31;	long	r31;
+	long	hr30;	long	r30;
+	long	hr28;	long	r28;
+	long	hr27;	long	r27;
+	long	hr26;	long	r26;
+	long	hr25;	long	r25;
+	long	hr24;	long	r24;
+	long	hr23;	long	r23;
+	long	hr22;	long	r22;
+	long	hr21;	long	r21;
+	long	hr20;	long	r20;
+	long	hr19;	long	r19;
+	long	hr18;	long	r18;
+	long	hr17;	long	r17;
+	long	hr16;	long	r16;
+	long	hr15;	long	r15;
+	long	hr14;	long	r14;
+	long	hr13;	long	r13;
+	long	hr12;	long	r12;
+	long	hr11;	long	r11;
+	long	hr10;	long	r10;
+	long	hr9;	long	r9;
+	long	hr8;	long	r8;
+	long	hr7;	long	r7;
+	long	hr6;	long	r6;
+	long	hr5;	long	r5;
+	long	hr4;	long	r4;
+	long	hr3;	long	r3;
+	long	hr2;	long	r2;
+	long	hr1;	long	r1;
+};
--- /dev/null
+++ b/utils/libmach/ureg5.h
@@ -1,0 +1,21 @@
+struct Ureg {
+	uint	r0;
+	uint	r1;
+	uint	r2;
+	uint	r3;
+	uint	r4;
+	uint	r5;
+	uint	r6;
+	uint	r7;
+	uint	r8;
+	uint	r9;
+	uint	r10;
+	uint	r11;
+	uint	r12;
+	uint	r13;
+	uint	r14;
+	uint	link;
+	uint	type;
+	uint	psr;
+	uint	pc;
+};
--- /dev/null
+++ b/utils/libmach/ureg6.h
@@ -1,0 +1,30 @@
+struct Ureg {
+	u64int	ax;
+	u64int	bx;
+	u64int	cx;
+	u64int	dx;
+	u64int	si;
+	u64int	di;
+	u64int	bp;
+	u64int	r8;
+	u64int	r9;
+	u64int	r10;
+	u64int	r11;
+	u64int	r12;
+	u64int	r13;
+	u64int	r14;
+	u64int	r15;
+
+	u16int	ds;
+	u16int	es;
+	u16int	fs;
+	u16int	gs;
+
+	u64int	type;
+	u64int	error;		/* error code (or zero) */
+	u64int	ip;		/* pc */
+	u64int	cs;		/* old context */
+	u64int	flags;		/* old flags */
+	u64int	sp;		/* sp */
+	u64int	ss;		/* old stack segment */
+};
--- /dev/null
+++ b/utils/libmach/ureg8.h
@@ -1,0 +1,25 @@
+struct Ureg
+{
+	ulong	di;		/* general registers */
+	ulong	si;		/* ... */
+	ulong	bp;		/* ... */
+	ulong	nsp;
+	ulong	bx;		/* ... */
+	ulong	dx;		/* ... */
+	ulong	cx;		/* ... */
+	ulong	ax;		/* ... */
+	ulong	gs;		/* data segments */
+	ulong	fs;		/* ... */
+	ulong	es;		/* ... */
+	ulong	ds;		/* ... */
+	ulong	trap;		/* trap type */
+	ulong	ecode;		/* error code (or zero) */
+	ulong	pc;		/* pc */
+	ulong	cs;		/* old context */
+	ulong	flags;		/* old flags */
+	union {
+		ulong	usp;
+		ulong	sp;
+	} u0;
+	ulong	ss;		/* old stack segment */
+};
--- /dev/null
+++ b/utils/libmach/ureg9.h
@@ -1,0 +1,44 @@
+struct Ureg
+{
+/*  0*/	u64int	cause;	/* trap or interrupt vector */
+/*  8*/	u64int	msr; /* SRR1 */
+/* 16*/	u64int	pc;	/* SRR0 */
+/* 24*/	u64int	unused;
+/* 32*/	u64int	lr;
+/* 36*/	u32int	pad;
+/* 40*/	u32int	cr;
+/* 48*/	u64int	xer;
+/* 56*/	u64int	ctr;
+/* 64*/	u64int	r0;
+/* 72*/	union{ u64int r1;	u64int	sp;	u64int	usp; };
+/* 80*/	u64int	r2;
+/* 88*/	u64int	r3;
+/* 96*/	u64int	r4;
+/*104*/	u64int	r5;
+/*112*/	u64int	r6;
+/*120*/	u64int	r7;
+/*128*/	u64int	r8;
+/*136*/	u64int	r9;
+/*144*/	u64int	r10;
+/*152*/	u64int	r11;
+/*160*/	u64int	r12;
+/*168*/	u64int	r13;
+/*176*/	u64int	r14;
+/*184*/	u64int	r15;
+/*192*/	u64int	r16;
+/*200*/	u64int	r17;
+/*208*/	u64int	r18;
+/*216*/	u64int	r19;
+/*224*/	u64int	r20;
+/*232*/	u64int	r21;
+/*240*/	u64int	r22;
+/*248*/	u64int	r23;
+/*256*/	u64int	r24;
+/*264*/	u64int	r25;
+/*272*/	u64int	r26;
+/*280*/	u64int	r27;
+/*288*/	u64int	r28;
+/*296*/	u64int	r29;
+/*304*/	u64int	r30;
+/*312*/	u64int	r31;
+};
--- /dev/null
+++ b/utils/libmach/uregk.h
@@ -1,0 +1,45 @@
+struct Ureg
+{
+	ulong	r0;			/* unnecessary; just for symmetry */
+	union{
+		ulong	sp;		/* r1 */
+		ulong	usp;		/* r1 */
+		ulong	r1;
+	} u0;
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;
+	ulong	r13;
+	ulong	r14;
+	ulong	r15;
+	ulong	r16;
+	ulong	r17;
+	ulong	r18;
+	ulong	r19;
+	ulong	r20;
+	ulong	r21;
+	ulong	r22;
+	ulong	r23;
+	ulong	r24;
+	ulong	r25;
+	ulong	r26;
+	ulong	r27;
+	ulong	r28;
+	ulong	r29;
+	ulong	r30;
+	ulong	r31;
+	ulong	y;
+	ulong	tbr;
+	ulong	psr;
+	ulong	npc;
+	ulong	pc;
+	ulong	pad;	/* so structure is double word aligned */
+};
--- /dev/null
+++ b/utils/libmach/uregq.h
@@ -1,0 +1,43 @@
+struct Ureg
+{
+	ulong	cause;
+	ulong	srr1;
+	ulong	pc;	/* SRR0 */
+	ulong	pad;
+	ulong	lr;
+	ulong	cr;
+	ulong	xer;
+	ulong	ctr;
+	ulong	r0;
+	ulong	sp;
+	ulong	r2;
+	ulong	r3;
+	ulong	r4;
+	ulong	r5;
+	ulong	r6;
+	ulong	r7;
+	ulong	r8;
+	ulong	r9;
+	ulong	r10;
+	ulong	r11;
+	ulong	r12;
+	ulong	r13;
+	ulong	r14;
+	ulong	r15;
+	ulong	r16;
+	ulong	r17;
+	ulong	r18;
+	ulong	r19;
+	ulong	r20;
+	ulong	r21;
+	ulong	r22;
+	ulong	r23;
+	ulong	r24;
+	ulong	r25;
+	ulong	r26;
+	ulong	r27;
+	ulong	r28;
+	ulong	r29;
+	ulong	r30;
+	ulong	r31;
+};
--- /dev/null
+++ b/utils/libmach/uregt.h
@@ -1,0 +1,21 @@
+struct Ureg {
+	u32int	r0;
+	u32int	r1;
+	u32int	r2;
+	u32int	r3;
+	u32int	r4;
+	u32int	r5;
+	u32int	r6;
+	u32int	r7;
+	u32int	r8;
+	u32int	r9;
+	u32int	r10;
+	u32int	r11;
+	u32int	r12;
+	u32int	r13;
+	u32int	r14;
+	u32int	link;
+	u32int	type;
+	u32int	psr;
+	u32int	pc;
+};
--- /dev/null
+++ b/utils/libmach/uregv.h
@@ -1,0 +1,44 @@
+struct Ureg
+{
+	ulong	status;
+	ulong	pc;
+	union{
+		ulong	sp;		/* r29 */
+		ulong	usp;		/* r29 */
+	} u0;
+	ulong	cause;
+	ulong	badvaddr;
+	ulong	tlbvirt;
+	ulong	hi;
+	ulong	lo;
+	ulong	r31;
+	ulong	r30;
+	ulong	r28;
+	ulong	r27;		/* unused */
+	ulong	r26;		/* unused */
+	ulong	r25;
+	ulong	r24;
+	ulong	r23;
+	ulong	r22;
+	ulong	r21;
+	ulong	r20;
+	ulong	r19;
+	ulong	r18;
+	ulong	r17;
+	ulong	r16;
+	ulong	r15;
+	ulong	r14;
+	ulong	r13;
+	ulong	r12;
+	ulong	r11;
+	ulong	r10;
+	ulong	r9;
+	ulong	r8;
+	ulong	r7;
+	ulong	r6;
+	ulong	r5;
+	ulong	r4;
+	ulong	r3;
+	ulong	r2;
+	ulong	r1;
+};
--- /dev/null
+++ b/utils/libmach/v.c
@@ -1,0 +1,117 @@
+/*
+ * mips definition
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "uregv.h"
+#include "mach.h"
+
+#define	REGOFF(x)	(ulong)(&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(u0.sp)
+#define PC		REGOFF(pc)
+#define	R1		REGOFF(r1)
+#define	R31		REGOFF(r31)
+#define	FP_REG(x)	(R1+4+4*(x))
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	(4*33)
+
+Reglist mipsreglist[] = {
+	{"STATUS",	REGOFF(status),		RINT|RRDONLY, 'X'},
+	{"CAUSE",	REGOFF(cause),		RINT|RRDONLY, 'X'},
+	{"BADVADDR",	REGOFF(badvaddr),	RINT|RRDONLY, 'X'},
+	{"TLBVIRT",	REGOFF(tlbvirt),	RINT|RRDONLY, 'X'},
+	{"HI",		REGOFF(hi),		RINT|RRDONLY, 'X'},
+	{"LO",		REGOFF(lo),		RINT|RRDONLY, 'X'},
+	{"PC",		PC,		RINT, 'X'},
+	{"SP",		SP,		RINT, 'X'},
+	{"R31",		R31,		RINT, 'X'},
+	{"R30",		REGOFF(r30),	RINT, 'X'},
+	{"R28",		REGOFF(r28),	RINT, 'X'},
+	{"R27",		REGOFF(r27),	RINT, 'X'},
+	{"R26",		REGOFF(r26),	RINT, 'X'},
+	{"R25",		REGOFF(r25),	RINT, 'X'},
+	{"R24",		REGOFF(r24),	RINT, 'X'},
+	{"R23",		REGOFF(r23),	RINT, 'X'},
+	{"R22",		REGOFF(r22),	RINT, 'X'},
+	{"R21",		REGOFF(r21),	RINT, 'X'},
+	{"R20",		REGOFF(r20),	RINT, 'X'},
+	{"R19",		REGOFF(r19),	RINT, 'X'},
+	{"R18",		REGOFF(r18),	RINT, 'X'},
+	{"R17",		REGOFF(r17),	RINT, 'X'},
+	{"R16",		REGOFF(r16),	RINT, 'X'},
+	{"R15",		REGOFF(r15),	RINT, 'X'},
+	{"R14",		REGOFF(r14),	RINT, 'X'},
+	{"R13",		REGOFF(r13),	RINT, 'X'},
+	{"R12",		REGOFF(r12),	RINT, 'X'},
+	{"R11",		REGOFF(r11),	RINT, 'X'},
+	{"R10",		REGOFF(r10),	RINT, 'X'},
+	{"R9",		REGOFF(r9),	RINT, 'X'},
+	{"R8",		REGOFF(r8),	RINT, 'X'},
+	{"R7",		REGOFF(r7),	RINT, 'X'},
+	{"R6",		REGOFF(r6),	RINT, 'X'},
+	{"R5",		REGOFF(r5),	RINT, 'X'},
+	{"R4",		REGOFF(r4),	RINT, 'X'},
+	{"R3",		REGOFF(r3),	RINT, 'X'},
+	{"R2",		REGOFF(r2),	RINT, 'X'},
+	{"R1",		REGOFF(r1),	RINT, 'X'},
+	{"F0",		FP_REG(0),	RFLT, 'F'},
+	{"F1",		FP_REG(1),	RFLT, 'f'},
+	{"F2",		FP_REG(2),	RFLT, 'F'},
+	{"F3",		FP_REG(3),	RFLT, 'f'},
+	{"F4",		FP_REG(4),	RFLT, 'F'},
+	{"F5",		FP_REG(5),	RFLT, 'f'},
+	{"F6",		FP_REG(6),	RFLT, 'F'},
+	{"F7",		FP_REG(7),	RFLT, 'f'},
+	{"F8",		FP_REG(8),	RFLT, 'F'},
+	{"F9",		FP_REG(9),	RFLT, 'f'},
+	{"F10",		FP_REG(10),	RFLT, 'F'},
+	{"F11",		FP_REG(11),	RFLT, 'f'},
+	{"F12",		FP_REG(12),	RFLT, 'F'},
+	{"F13",		FP_REG(13),	RFLT, 'f'},
+	{"F14",		FP_REG(14),	RFLT, 'F'},
+	{"F15",		FP_REG(15),	RFLT, 'f'},
+	{"F16",		FP_REG(16),	RFLT, 'F'},
+	{"F17",		FP_REG(17),	RFLT, 'f'},
+	{"F18",		FP_REG(18),	RFLT, 'F'},
+	{"F19",		FP_REG(19),	RFLT, 'f'},
+	{"F20",		FP_REG(20),	RFLT, 'F'},
+	{"F21",		FP_REG(21),	RFLT, 'f'},
+	{"F22",		FP_REG(22),	RFLT, 'F'},
+	{"F23",		FP_REG(23),	RFLT, 'f'},
+	{"F24",		FP_REG(24),	RFLT, 'F'},
+	{"F25",		FP_REG(25),	RFLT, 'f'},
+	{"F26",		FP_REG(26),	RFLT, 'F'},
+	{"F27",		FP_REG(27),	RFLT, 'f'},
+	{"F28",		FP_REG(28),	RFLT, 'F'},
+	{"F29",		FP_REG(29),	RFLT, 'f'},
+	{"F30",		FP_REG(30),	RFLT, 'F'},
+	{"F31",		FP_REG(31),	RFLT, 'f'},
+	{"FPCR",	FP_REG(32),	RFLT, 'X'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mmips =
+{
+	"mips",
+	MMIPS,		/* machine type */
+	mipsreglist,	/* register set */
+	REGSIZE,	/* number of bytes in reg set */
+	FPREGSIZE,	/* number of bytes in fp reg set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R31",		/* name of link register */
+	"setR30",	/* static base register name */
+	0,		/* value */
+	0x1000,		/* page size */
+	0xC0000000,	/* kernel base */
+	0x40000000,	/* kernel text mask */
+	0x7FFFFFFF,	/* user stack top */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/utils/libmach/vcodas.c
@@ -1,0 +1,555 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+	/* mips native disassembler */
+
+typedef struct {
+	uvlong addr;			/* pc of instr */
+	uchar op;			/* bits 31-26 */
+	uchar rs;			/* bits 25-21 */
+	uchar rt;			/* bits 20-16 */
+	uchar rd;			/* bits 15-11 */
+	uchar sa;			/* bits 10-6 */
+	uchar function;			/* bits 5-0 */
+	long immediate;			/* bits 15-0 */
+	ulong cofun;			/* bits 24-0 */
+	ulong target;			/* bits 25-0 */
+	long w0;
+	char *curr;			/* current fill point */
+	char *end;			/* end of buffer */
+	char *err;
+} Instr;
+
+typedef struct {
+	char *mnemonic;
+	char *mipsco;
+} Opcode;
+
+static char mipscoload[] = "r%t,%l";
+static char mipscoalui[] = "r%t,r%s,%i";
+static char mipscoalu3op[] = "r%d,r%s,r%t";
+static char mipscoboc[] = "r%s,r%t,%b";
+static char mipscoboc0[] = "r%s,%b";
+static char mipscorsrt[] = "r%s,r%t";
+static char mipscorsi[] = "r%s,%i";
+static char mipscoxxx[] = "%w";
+static char mipscofp3[] = "f%a,f%d,f%t";	/* fd,fs,ft */
+static char mipscofp2[] = "f%a,f%d";		/* fd,fs */
+static char mipscofpc[] = "f%d,f%t";		/* fs,ft */
+
+static Opcode opcodes[64] = {
+	0,		0,
+	0,		0,
+	"j",		"%j",
+	"jal",		"%j",
+	"beq",		mipscoboc,
+	"bne",		mipscoboc,
+	"blez",		mipscoboc0,
+	"bgtz",		mipscoboc0,
+	"addi",		mipscoalui,
+	"addiu",	mipscoalui,
+	"slti",		mipscoalui,
+	"sltiu",	mipscoalui,
+	"andi",		mipscoalui,
+	"ori",		mipscoalui,
+	"xori",		mipscoalui,
+	"lui",		"r%t,%u",
+	"cop0",		0,
+	"cop1",		0,
+	"cop2",		0,
+	"cop3",		0,
+	"beql",		mipscoboc,
+	"bnel",		mipscoboc,
+	"blezl",	mipscoboc0,
+	"bgtzl",	mipscoboc0,
+	"instr18",	mipscoxxx,
+	"instr19",	mipscoxxx,
+	"instr1A",	mipscoxxx,
+	"instr1B",	mipscoxxx,
+	"instr1C",	mipscoxxx,
+	"instr1D",	mipscoxxx,
+	"instr1E",	mipscoxxx,
+	"instr1F",	mipscoxxx,
+	"lb",		mipscoload,
+	"lh",		mipscoload,
+	"lwl",		mipscoload,
+	"lw",		mipscoload,
+	"lbu",		mipscoload,
+	"lhu",		mipscoload,
+	"lwr",		mipscoload,
+	"instr27",	mipscoxxx,
+	"sb",		mipscoload,
+	"sh",		mipscoload,
+	"swl",		mipscoload,
+	"sw",		mipscoload,
+	"instr2C",	mipscoxxx,
+	"instr2D",	mipscoxxx,
+	"swr",		mipscoload,
+	"cache",	"",
+	"ll",		mipscoload,
+	"lwc1",		mipscoload,
+	"lwc2",		mipscoload,
+	"lwc3",		mipscoload,
+	"instr34",	mipscoxxx,
+	"ld",		mipscoload,
+	"ld",		mipscoload,
+	"ld",		mipscoload,
+	"sc",		mipscoload,
+	"swc1",		mipscoload,
+	"swc2",		mipscoload,
+	"swc3",		mipscoload,
+	"instr3C",	mipscoxxx,
+	"sd",		mipscoload,
+	"sd",		mipscoload,
+	"sd",		mipscoload,
+};
+
+static Opcode sopcodes[64] = {
+	"sll",		"r%d,r%t,$%a",
+	"special01",	mipscoxxx,
+	"srl",		"r%d,r%t,$%a",
+	"sra",		"r%d,r%t,$%a",
+	"sllv",		"r%d,r%t,R%s",
+	"special05",	mipscoxxx,
+	"srlv",		"r%d,r%t,r%s",
+	"srav",		"r%d,r%t,r%s",
+	"jr",		"r%s",
+	"jalr",		"r%d,r%s",
+	"special0A",	mipscoxxx,
+	"special0B",	mipscoxxx,
+	"syscall",	"",
+	"break",	"",
+	"special0E",	mipscoxxx,
+	"sync",		"",
+	"mfhi",		"r%d",
+	"mthi",		"r%s",
+	"mflo",		"r%d",
+	"mtlo",		"r%s",
+	"special14",	mipscoxxx,
+	"special15",	mipscoxxx,
+	"special16",	mipscoxxx,
+	"special17",	mipscoxxx,
+	"mult",		mipscorsrt,
+	"multu",	mipscorsrt,
+	"div",		mipscorsrt,
+	"divu",		mipscorsrt,
+	"special1C",	mipscoxxx,
+	"special1D",	mipscoxxx,
+	"special1E",	mipscoxxx,
+	"special1F",	mipscoxxx,
+	"add",		mipscoalu3op,
+	"addu",		mipscoalu3op,
+	"sub",		mipscoalu3op,
+	"subu",		mipscoalu3op,
+	"and",		mipscoalu3op,
+	"or",		mipscoalu3op,
+	"xor",		mipscoalu3op,
+	"nor",		mipscoalu3op,
+	"special28",	mipscoxxx,
+	"special29",	mipscoxxx,
+	"slt",		mipscoalu3op,
+	"sltu",		mipscoalu3op,
+	"special2C",	mipscoxxx,
+	"special2D",	mipscoxxx,
+	"special2E",	mipscoxxx,
+	"special2F",	mipscoxxx,
+	"tge",		mipscorsrt,
+	"tgeu",		mipscorsrt,
+	"tlt",		mipscorsrt,
+	"tltu",		mipscorsrt,
+	"teq",		mipscorsrt,
+	"special35",	mipscoxxx,
+	"tne",		mipscorsrt,
+	"special37",	mipscoxxx,
+	"special38",	mipscoxxx,
+	"special39",	mipscoxxx,
+	"special3A",	mipscoxxx,
+	"special3B",	mipscoxxx,
+	"special3C",	mipscoxxx,
+	"special3D",	mipscoxxx,
+	"special3E",	mipscoxxx,
+	"special3F",	mipscoxxx,
+};
+
+static Opcode ropcodes[32] = {
+	"bltz",		mipscoboc0,
+	"bgez",		mipscoboc0,
+	"bltzl",	mipscoboc0,
+	"bgezl",	mipscoboc0,
+	"regimm04",	mipscoxxx,
+	"regimm05",	mipscoxxx,
+	"regimm06",	mipscoxxx,
+	"regimm07",	mipscoxxx,
+	"tgei",		mipscorsi,
+	"tgeiu",	mipscorsi,
+	"tlti",		mipscorsi,
+	"tltiu",	mipscorsi,
+	"teqi",		mipscorsi,
+	"regimm0D",	mipscoxxx,
+	"tnei",		mipscorsi,
+	"regimm0F",	mipscoxxx,
+	"bltzal",	mipscoboc0,
+	"bgezal",	mipscoboc0,
+	"bltzall",	mipscoboc0,
+	"bgezall",	mipscoboc0,
+	"regimm14",	mipscoxxx,
+	"regimm15",	mipscoxxx,
+	"regimm16",	mipscoxxx,
+	"regimm17",	mipscoxxx,
+	"regimm18",	mipscoxxx,
+	"regimm19",	mipscoxxx,
+	"regimm1A",	mipscoxxx,
+	"regimm1B",	mipscoxxx,
+	"regimm1C",	mipscoxxx,
+	"regimm1D",	mipscoxxx,
+	"regimm1E",	mipscoxxx,
+	"regimm1F",	mipscoxxx,
+};
+
+static Opcode fopcodes[64] = {
+	"add.%f",	mipscofp3,
+	"sub.%f",	mipscofp3,
+	"mul.%f",	mipscofp3,
+	"div.%f",	mipscofp3,
+	"sqrt.%f",	mipscofp2,
+	"abs.%f",	mipscofp2,
+	"mov.%f",	mipscofp2,
+	"neg.%f",	mipscofp2,
+	"finstr08",	mipscoxxx,
+	"finstr09",	mipscoxxx,
+	"finstr0A",	mipscoxxx,
+	"finstr0B",	mipscoxxx,
+	"round.w.%f",	mipscofp2,
+	"trunc.w%f",	mipscofp2,
+	"ceil.w%f",	mipscofp2,
+	"floor.w%f",	mipscofp2,
+	"finstr10",	mipscoxxx,
+	"finstr11",	mipscoxxx,
+	"finstr12",	mipscoxxx,
+	"finstr13",	mipscoxxx,
+	"finstr14",	mipscoxxx,
+	"finstr15",	mipscoxxx,
+	"finstr16",	mipscoxxx,
+	"finstr17",	mipscoxxx,
+	"finstr18",	mipscoxxx,
+	"finstr19",	mipscoxxx,
+	"finstr1A",	mipscoxxx,
+	"finstr1B",	mipscoxxx,
+	"finstr1C",	mipscoxxx,
+	"finstr1D",	mipscoxxx,
+	"finstr1E",	mipscoxxx,
+	"finstr1F",	mipscoxxx,
+	"cvt.s.%f",	mipscofp2,
+	"cvt.d.%f",	mipscofp2,
+	"cvt.e.%f",	mipscofp2,
+	"cvt.q.%f",	mipscofp2,
+	"cvt.w.%f",	mipscofp2,
+	"finstr25",	mipscoxxx,
+	"finstr26",	mipscoxxx,
+	"finstr27",	mipscoxxx,
+	"finstr28",	mipscoxxx,
+	"finstr29",	mipscoxxx,
+	"finstr2A",	mipscoxxx,
+	"finstr2B",	mipscoxxx,
+	"finstr2C",	mipscoxxx,
+	"finstr2D",	mipscoxxx,
+	"finstr2E",	mipscoxxx,
+	"finstr2F",	mipscoxxx,
+	"c.f.%f",	mipscofpc,
+	"c.un.%f",	mipscofpc,
+	"c.eq.%f",	mipscofpc,
+	"c.ueq.%f",	mipscofpc,
+	"c.olt.%f",	mipscofpc,
+	"c.ult.%f",	mipscofpc,
+	"c.ole.%f",	mipscofpc,
+	"c.ule.%f",	mipscofpc,
+	"c.sf.%f",	mipscofpc,
+	"c.ngle.%f",	mipscofpc,
+	"c.seq.%f",	mipscofpc,
+	"c.ngl.%f",	mipscofpc,
+	"c.lt.%f",	mipscofpc,
+	"c.nge.%f",	mipscofpc,
+	"c.le.%f",	mipscofpc,
+	"c.ngt.%f",	mipscofpc,
+};
+
+static char fsub[16] = {
+	's', 'd', 'e', 'q', 'w', '?', '?', '?',
+	'?', '?', '?', '?', '?', '?', '?', '?'
+};
+
+
+static int
+mkinstr(Instr *i, Map *map, uvlong pc)
+{
+	ulong w;
+
+	if (get4(map, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->addr = pc;
+	i->op = (w >> 26) & 0x3F;
+	i->rs = (w >> 21) & 0x1F;
+	i->rt = (w >> 16) & 0x1F;
+	i->rd = (w >> 11) & 0x1F;
+	i->sa = (w >> 6) & 0x1F;
+	i->function = w & 0x3F;
+	i->immediate = w & 0x0000FFFF;
+	if (i->immediate & 0x8000)
+		i->immediate |= ~0x0000FFFF;
+	i->cofun = w & 0x01FFFFFF;
+	i->target = w & 0x03FFFFFF;
+	i->w0 = w;
+	return 1;
+}
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	if (mnemonic)
+		format(0, i, mnemonic);
+	if (f == 0)
+		return;
+	if (i->curr < i->end)
+		*i->curr++ = '\t';
+	for ( ; *f && i->curr < i->end; f++) {
+		if (*f != '%') {
+			*i->curr++ = *f;
+			continue;
+		}
+		switch (*++f) {
+
+		case 's':
+			bprint(i, "%d", i->rs);
+			break;
+
+		case 't':
+			bprint(i, "%d", i->rt);
+			break;
+
+		case 'd':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'a':
+			bprint(i, "%d", i->sa);
+			break;
+
+		case 'l':
+			if (i->rs == 30) {
+				i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
+				bprint(i, "(SB)");
+			} else 
+				bprint(i, "%lx(r%d)", i->immediate, i->rs);
+			break;
+
+		case 'i':
+			bprint(i, "$%lx", i->immediate);
+			break;
+
+		case 'u':
+			*i->curr++ = '$';
+			i->curr += symoff(i->curr, i->end-i->curr, i->immediate, CANY);
+			bprint(i, "(SB)");
+			break;
+
+		case 'j':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				(i->target<<2)|(i->addr & 0xF0000000), CANY);
+			bprint(i, "(SB)");
+			break;
+
+		case 'b':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				(i->immediate<<2)+i->addr+4, CANY);
+			break;
+
+		case 'c':
+			bprint(i, "%lux", i->cofun);
+			break;
+
+		case 'w':
+			bprint(i, "[%lux]", i->w0);
+			break;
+
+		case 'f':
+			*i->curr++ = fsub[i->rs & 0x0F];
+			break;
+
+		case '\0':
+			*i->curr++ = '%';
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+}
+
+static void
+copz(int cop, Instr *i)
+{
+	char *f, *m, buf[16];
+
+	m = buf;
+	f = "%t,%d";
+	switch (i->rs) {
+
+	case 0:
+		sprint(buf, "mfc%d", cop);
+		break;
+
+	case 2:
+		sprint(buf, "cfc%d", cop);
+		break;
+
+	case 4:
+		sprint(buf, "mtc%d", cop);
+		break;
+
+	case 6:
+		sprint(buf, "ctc%d", cop);
+		break;
+
+	case 8:
+		f = "%b";
+		switch (i->rt) {
+
+		case 0:
+			sprint(buf, "bc%df", cop);
+			break;
+
+		case 1:
+			sprint(buf, "bc%dt", cop);
+			break;
+
+		case 2:
+			sprint(buf, "bc%dfl", cop);
+			break;
+
+		case 3:
+			sprint(buf, "bc%dtl", cop);
+			break;
+
+		default:
+			sprint(buf, "cop%d", cop);
+			f = mipscoxxx;
+			break;
+		}
+		break;
+
+	default:
+		sprint(buf, "cop%d", cop);
+		if (i->rs & 0x10)
+			f = "function %c";
+		else
+			f = mipscoxxx;
+		break;
+	}
+	format(m, i, f);
+}
+
+static void
+cop0(Instr *i)
+{
+	char *m = 0;
+
+	if (i->rs >= 0x10) {
+		switch (i->cofun) {
+	
+		case 1:
+			m = "tlbr";
+			break;
+	
+		case 2:
+			m = "tlbwi";
+			break;
+	
+		case 6:
+			m = "tlbwr";
+			break;
+	
+		case 8:
+			m = "tlbp";
+			break;
+	
+		case 16:
+			m = "rfe";
+			break;
+	
+		case 32:
+			m = "eret";
+			break;
+		}
+		if (m) {
+			format(m, i, 0);
+			if (i->curr < i->end)
+				*i->curr++ = 0;
+			return;
+		}
+	}
+	copz(0, i);
+}
+
+int
+_mipscoinst(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+	Opcode *o;
+	uchar op;
+
+	i.curr = buf;
+	i.end = buf+n-1;
+	if (mkinstr(&i, map, pc) < 0)
+		return -1;
+	switch (i.op) {
+
+	case 0x00:					/* SPECIAL */
+		o = sopcodes;
+		op = i.function;
+		break;
+
+	case 0x01:					/* REGIMM */
+		o = ropcodes;
+		op = i.rt;
+		break;
+
+	case 0x10:					/* COP0 */
+		cop0(&i);
+		return 4;
+
+	case 0x11:					/* COP1 */
+		if (i.rs & 0x10) {
+			o = fopcodes;
+			op = i.function;
+			break;
+		}
+		/*FALLTHROUGH*/
+	case 0x12:					/* COP2 */
+	case 0x13:					/* COP3 */
+		copz(i.op-0x10, &i);
+		return 4;
+
+	default:
+		o = opcodes;
+		op = i.op;
+		break;
+	}
+	format(o[op].mnemonic, &i, o[op].mipsco);
+	return 4;
+}
--- /dev/null
+++ b/utils/libmach/vdb.c
@@ -1,0 +1,1165 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+/*
+ * Mips-specific debugger interface
+ */
+
+static	char	*mipsexcep(Map*, Rgetter);
+static	int	mipsfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	mipsinst(Map*, uvlong, char, char*, int);
+static	int	mipsdas(Map*, uvlong, char*, int);
+static	int	mipsinstlen(Map*, uvlong);
+
+/*
+ *	Debugger interface
+ */
+Machdata mipsmach =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	beswab,			/* short to local byte order */
+	beswal,			/* long to local byte order */
+	beswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	beieeesftos,		/* single precision float printer */
+	beieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
+
+Machdata mipsmachle =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	leswab,			/* short to local byte order */
+	leswal,			/* long to local byte order */
+	leswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
+
+/*
+ *	mips r4k little-endian
+ */
+Machdata mipsmach2le =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	leswab,			/* short to local byte order */
+	leswal,			/* long to local byte order */
+	leswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
+
+/*
+ *	mips r4k big-endian
+ */
+Machdata mipsmach2be =
+{
+	{0, 0, 0, 0xD},		/* break point */
+	4,			/* break point size */
+
+	beswab,			/* short to local byte order */
+	beswal,			/* long to local byte order */
+	beswav,			/* vlong to local byte order */
+	risctrace,		/* C traceback */
+	riscframe,		/* Frame finder */
+	mipsexcep,		/* print exception */
+	0,			/* breakpoint fixup */
+	beieeesftos,		/* single precision float printer */
+	beieeedftos,		/* double precisioin float printer */
+	mipsfoll,		/* following addresses */
+	mipsinst,		/* print instruction */
+	mipsdas,		/* dissembler */
+	mipsinstlen,		/* instruction size */
+};
+
+
+static char *excname[] =
+{
+	"external interrupt",
+	"TLB modification",
+	"TLB miss (load or fetch)",
+	"TLB miss (store)",
+	"address error (load or fetch)",
+	"address error (store)",
+	"bus error (fetch)",
+	"bus error (data load or store)",
+	"system call",
+	"breakpoint",
+	"reserved instruction",
+	"coprocessor unusable",
+	"arithmetic overflow",
+	"undefined 13",
+	"undefined 14",
+	"system call",
+	/* the following is made up */
+	"floating point exception"		/* FPEXC */
+};
+
+static char*
+mipsexcep(Map *map, Rgetter rget)
+{
+	int e;
+	long c;
+
+	c = (*rget)(map, "CAUSE");
+	if(c & 0x00002000)	/* INTR3 */
+		e = 16;		/* Floating point exception */
+	else
+		e = (c>>2)&0x0F;
+	return excname[e];
+}
+
+	/* mips disassembler and related functions */
+
+static	char FRAMENAME[] = ".frame";
+
+typedef struct {
+	uvlong addr;
+	uchar op;			/* bits 31-26 */
+	uchar rs;			/* bits 25-21 */
+	uchar rt;			/* bits 20-16 */
+	uchar rd;			/* bits 15-11 */
+	uchar sa;			/* bits 10-6 */
+	uchar function;			/* bits 5-0 */
+	long immediate;			/* bits 15-0 */
+	ulong cofun;			/* bits 24-0 */
+	ulong target;			/* bits 25-0 */
+	long w0;
+	long w1;
+	int size;			/* instruction size */
+	char *curr;			/* fill point in buffer */
+	char *end;			/* end of buffer */
+	char *err;			/* error message */
+} Instr;
+
+static Map *mymap;
+
+static int
+decode(uvlong pc, Instr *i)
+{
+	ulong w;
+
+	if (get4(mymap, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+
+	i->addr = pc;
+	i->size = 1;
+	i->op = (w >> 26) & 0x3F;
+	i->rs = (w >> 21) & 0x1F;
+	i->rt = (w >> 16) & 0x1F;
+	i->rd = (w >> 11) & 0x1F;
+	i->sa = (w >> 6) & 0x1F;
+	i->function = w & 0x3F;
+	i->immediate = w & 0x0000FFFF;
+	if (i->immediate & 0x8000)
+		i->immediate |= ~0x0000FFFF;
+	i->cofun = w & 0x01FFFFFF;
+	i->target = w & 0x03FFFFFF;
+	i->w0 = w;
+	return 1;
+}
+
+static int
+mkinstr(uvlong pc, Instr *i)
+{
+	Instr x;
+
+	if (decode(pc, i) < 0)
+		return -1;
+	/*
+	 * if it's a LUI followed by an ORI,
+	 * it's an immediate load of a large constant.
+	 * fix the LUI immediate in any case.
+	 */
+	if (i->op == 0x0F) {
+		if (decode(pc+4, &x) < 0)
+			return 0;
+		i->immediate <<= 16;
+		if (x.op == 0x0D && x.rs == x.rt && x.rt == i->rt) {
+			i->immediate |= (x.immediate & 0xFFFF);
+			i->w1 = x.w0;
+			i->size++;
+			return 1;
+		}
+	}
+	/*
+	 * if it's a LWC1 followed by another LWC1
+	 * into an adjacent register, it's a load of
+	 * a floating point double.
+	 */
+	else if (i->op == 0x31 && (i->rt & 0x01)) {
+		if (decode(pc+4, &x) < 0)
+			return 0;
+		if (x.op == 0x31 && x.rt == (i->rt - 1) && x.rs == i->rs) {
+			i->rt -= 1;
+			i->w1 = x.w0;
+			i->size++;
+			return 1;
+		}
+	}
+	/*
+	 * similarly for double stores
+	 */
+	else if (i->op == 0x39 && (i->rt & 0x01)) {
+		if (decode(pc+4, &x) < 0)
+			return 0;
+		if (x.op == 0x39 && x.rt == (i->rt - 1) && x.rs == i->rs) {
+			i->rt -= 1;
+			i->w1 = x.w0;
+			i->size++;
+		}
+	}
+	return 1;
+}
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+typedef struct Opcode Opcode;
+
+struct Opcode {
+	char *mnemonic;
+	void (*f)(Opcode *, Instr *);
+	char *ken;
+};
+
+static void format(char *, Instr *, char *);
+
+static void
+branch(Opcode *o, Instr *i)
+{
+	if (i->rs == 0 && i->rt == 0)
+		format("JMP", i, "%b");
+	else if (i->rs == 0)
+		format(o->mnemonic, i, "R%t,%b");
+	else if (i->rt < 2)
+		format(o->mnemonic, i, "R%s,%b");
+	else
+		format(o->mnemonic, i, "R%s,R%t,%b");
+}
+
+static void
+addi(Opcode *o, Instr *i)
+{
+	if (i->rs == i->rt)
+		format(o->mnemonic, i, "%i,R%t");
+	else if (i->rs == 0)
+		format("MOVW", i, "%i,R%t");
+	else if (i->rs == 30) {
+		bprint(i, "MOVW\t$");
+		i->curr += symoff(i->curr, i->end-i->curr,
+					i->immediate+mach->sb, CANY);
+		bprint(i, "(SB),R%d", i->rt);
+	}
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+andi(Opcode *o, Instr *i)
+{
+	if (i->rs == i->rt)
+		format(o->mnemonic, i, "%i,R%t");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static int
+plocal(Instr *i, char *m, char r, int store)
+{
+	int offset;
+	char *reg;
+	Symbol s;
+
+	if (!findsym(i->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s))
+		return 0;
+	if (s.value > i->immediate) {
+		if(!getauto(&s, s.value-i->immediate, CAUTO, &s))
+			return 0;
+		reg = "(SP)";
+		offset = i->immediate;
+	} else {
+		offset = i->immediate-s.value;
+		if (!getauto(&s, offset-4, CPARAM, &s))
+			return 0;
+		reg = "(FP)";
+	}
+	if (store)
+		bprint(i, "%s\t%c%d,%s+%d%s", m, r, i->rt, s.name, offset, reg);
+	else
+		bprint(i, "%s\t%s+%d%s,%c%d", m, s.name, offset, reg, r, i->rt);
+	return 1;
+}
+
+static void
+lw(Opcode *o, Instr *i, char r)
+{
+	char *m;
+
+	if (r == 'F') {
+		if (i->size == 2)
+			m = "MOVD";
+		else
+			m = "MOVF";
+	}
+	else
+		m = o->mnemonic;
+	if (i->rs == 29 && plocal(i, m, r, 0))
+			return;
+
+	if (i->rs == 30 && mach->sb) {
+		bprint(i, "%s\t", m);
+		i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
+		bprint(i, "(SB),%c%d", r, i->rt);
+		return;
+	}
+	if (r == 'F')
+		format(m, i, "%l,F%t");
+	else
+		format(m, i, o->ken);
+}
+
+static void
+load(Opcode *o, Instr *i)
+{
+	lw(o, i, 'R');
+}
+
+static void
+lwc1(Opcode *o, Instr *i)
+{
+	lw(o, i, 'F');
+}
+
+static void
+sw(Opcode *o, Instr *i, char r)
+{
+	char *m;
+
+	if (r == 'F') {
+		if (i->size == 2)
+			m = "MOVD";
+		else
+			m = "MOVF";
+	}
+	else
+		m = o->mnemonic;
+	if (i->rs == 29 && plocal(i, m, r, 1))
+			return;
+
+	if (i->rs == 30 && mach->sb) {
+		bprint(i, "%s\t%c%d,", m, r, i->rt);
+		i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
+		bprint(i, "(SB)");
+		return;
+	}
+	if (r == 'F')
+		format(m, i, "F%t,%l");
+	else
+		format(m, i, o->ken);
+}
+
+static void
+store(Opcode *o, Instr *i)
+{
+	sw(o, i, 'R');
+}
+
+static void
+swc1(Opcode *o, Instr *i)
+{
+	sw(o, i, 'F');
+}
+
+static void
+sll(Opcode *o, Instr *i)
+{
+	if (i->w0 == 0)
+		bprint(i, "NOOP");
+	else if (i->rd == i->rt)
+		format(o->mnemonic, i, "$%a,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+sl32(Opcode *o, Instr *i)
+{
+	i->sa += 32;
+	if (i->rd == i->rt)
+		format(o->mnemonic, i, "$%a,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+sllv(Opcode *o, Instr *i)
+{
+	if (i->rd == i->rt)
+		format(o->mnemonic, i, "R%s,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+jal(Opcode *o, Instr *i)
+{
+	if (i->rd == 31)
+		format("JAL", i, "(R%s)");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+add(Opcode *o, Instr *i)
+{
+	if (i->rd == i->rs)
+		format(o->mnemonic, i, "R%t,R%d");
+	else if (i->rd == i->rt)
+		format(o->mnemonic, i, "R%s,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+sub(Opcode *o, Instr *i)
+{
+	if (i->rd == i->rs)
+		format(o->mnemonic, i, "R%t,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+or(Opcode *o, Instr *i)
+{
+	if (i->rs == 0 && i->rt == 0)
+		format("MOVW", i, "$0,R%d");
+	else if (i->rs == 0)
+		format("MOVW", i, "R%t,R%d");
+	else if (i->rt == 0)
+		format("MOVW", i, "R%s,R%d");
+	else
+		add(o, i);
+}
+
+static void
+nor(Opcode *o, Instr *i)
+{
+	if (i->rs == 0 && i->rt == 0 && i->rd == 0)
+		format("NOP", i, 0);
+	else
+		add(o, i);
+}
+
+static char mipscoload[] = "r%t,%l";
+static char mipsload[] = "%l,R%t";
+static char mipsstore[] = "R%t,%l";
+static char mipsalui[] = "%i,R%s,R%t";
+static char mipsalu3op[] = "R%t,R%s,R%d";
+static char mipsrtrs[] = "R%t,R%s";
+static char mipscorsrt[] = "r%s,r%t";
+static char mipscorsi[] = "r%s,%i";
+static char mipscoxxx[] = "%w";
+static char mipscofp3[] = "f%a,f%d,f%t";	/* fd,fs,ft */
+static char mipsfp3[] = "F%t,F%d,F%a";
+static char mipscofp2[] = "f%a,f%d";		/* fd,fs */
+static char mipsfp2[] = "F%d,F%a";
+static char mipscofpc[] = "f%d,f%t";		/* fs,ft */
+static char mipsfpc[] = "F%t,F%d";
+
+static Opcode opcodes[64] = {
+	0,		0,	0,
+	0,		0,	0,
+	"JMP",		0,	"%j",
+	"JAL",		0,	"%j",
+	"BEQ",	   branch,	0,
+	"BNE",	   branch,	0,
+	"BLEZ",	   branch,	0,
+	"BGTZ",	   branch,	0,
+	"ADD",	     addi,	mipsalui,
+	"ADDU",	     addi,	mipsalui,
+	"SGT",		0,	mipsalui,
+	"SGTU",		0,	mipsalui,
+	"AND",	     andi,	mipsalui,
+	"OR",	     andi,	mipsalui,
+	"XOR",	     andi,	mipsalui,
+	"MOVW",		0,	"$%u,R%t",
+	"cop0",		0,	0,
+	"cop1",		0,	0,
+	"cop2",		0,	0,
+	"cop3",		0,	0,
+	"BEQL",	   branch,	0,
+	"BNEL",	   branch,	0,
+	"BLEZL",   branch,	0,
+	"BGTZL",   branch,	0,
+	"instr18",	0,	mipscoxxx,
+	"instr19",	0,	mipscoxxx,
+	"MOVVL",     load,	mipsload,
+	"MOVVR",     load,	mipsload,
+	"instr1C",	0,	mipscoxxx,
+	"instr1D",	0,	mipscoxxx,
+	"instr1E",	0,	mipscoxxx,
+	"instr1F",	0,	mipscoxxx,
+	"MOVB",	     load,	mipsload,
+	"MOVH",	     load,	mipsload,
+	"lwl",		0,	mipscoload,
+	"MOVW",	     load,	mipsload,
+	"MOVBU",     load,	mipsload,
+	"MOVHU",     load,	mipsload,
+	"lwr",		0,	mipscoload,
+	"instr27",	0,	mipscoxxx,
+	"MOVB",	    store,	mipsstore,
+	"MOVH",	    store,	mipsstore,
+	"swl",		0,	mipscoload,
+	"MOVW",	    store,	mipsstore,
+	"MOVVL",    store,	mipsstore,
+	"MOVVR",    store,	mipsstore,
+	"swr",		0,	mipscoload,
+	"CACHE",	0,	"%C,%l",
+	"ll",		0,	mipscoload,
+	"MOVW",	     lwc1,	mipscoload,
+	"lwc2",		0,	mipscoload,
+	"lwc3",		0,	mipscoload,
+	"instr34",	0,	mipscoxxx,
+	"ldc1",		0,	mipscoload,
+	"ldc2",		0,	mipscoload,
+	"MOVV",	    load,	mipsload,
+	"sc",		0,	mipscoload,
+	"swc1",	     swc1,	mipscoload,
+	"swc2",		0,	mipscoload,
+	"swc3",		0,	mipscoload,
+	"instr3C",	0,	mipscoxxx,
+	"sdc1",		0,	mipscoload,
+	"sdc2",		0,	mipscoload,
+	"MOVV",	    store,	mipsstore,
+};
+
+static Opcode sopcodes[64] = {
+	"SLL",	      sll,	"$%a,R%t,R%d",
+	"special01",	0,	mipscoxxx,
+	"SRL",	      sll,	"$%a,R%t,R%d",
+	"SRA",	      sll,	"$%a,R%t,R%d",
+	"SLL",	     sllv,	"R%s,R%t,R%d",
+	"special05",	0,	mipscoxxx,
+	"SRL",	     sllv,	"R%s,R%t,R%d",
+	"SRA",	     sllv,	"R%s,R%t,R%d",
+	"JMP",		0,	"(R%s)",
+	"jal",	      jal,	"r%d,r%s",
+	"special0A",	0,	mipscoxxx,
+	"special0B",	0,	mipscoxxx,
+	"SYSCALL",	0,	0,
+	"BREAK",	0,	0,
+	"special0E",	0,	mipscoxxx,
+	"SYNC",		0,	0,
+	"MOVW",		0,	"HI,R%d",
+	"MOVW",		0,	"R%s,HI",
+	"MOVW",		0,	"LO,R%d",
+	"MOVW",		0,	"R%s,LO",
+	"SLLV",	     sllv,	"R%s,R%t,R%d",
+	"special15",	0,	mipscoxxx,
+	"SRLV",	     sllv,	"R%s,R%t,R%d",
+	"SRAV",	     sllv,	"R%s,R%t,R%d",
+	"MUL",		0,	mipsrtrs,
+	"MULU",		0,	mipsrtrs,
+	"DIV",		0,	mipsrtrs,
+	"DIVU",		0,	mipsrtrs,
+	"special1C",	0,	mipscoxxx,
+	"special1D",	0,	mipscoxxx,
+	"DDIV",		0,	"R%s,R%t",
+	"special1F",	0,	mipscoxxx,
+	"ADD",	      add,	mipsalu3op,
+	"ADDU",	      add,	mipsalu3op,
+	"SUB",	      sub,	mipsalu3op,
+	"SUBU",	      sub,	mipsalu3op,
+	"AND",	      add,	mipsalu3op,
+	"OR",	       or,	mipsalu3op,
+	"XOR",	      add,	mipsalu3op,
+	"NOR",	      nor,	mipsalu3op,
+	"special28",	0,	mipscoxxx,
+	"special29",	0,	mipscoxxx,
+	"SGT",		0,	mipsalu3op,
+	"SGTU",		0,	mipsalu3op,
+	"special2C",	0,	mipscoxxx,
+	"special2D",	0,	mipscoxxx,
+	"special2E",	0,	mipscoxxx,
+	"DSUBU",	0,	"R%s,R%t,R%d",
+	"tge",		0,	mipscorsrt,
+	"tgeu",		0,	mipscorsrt,
+	"tlt",		0,	mipscorsrt,
+	"tltu",		0,	mipscorsrt,
+	"teq",		0,	mipscorsrt,
+	"special35",	0,	mipscoxxx,
+	"tne",		0,	mipscorsrt,
+	"special37",	0,	mipscoxxx,
+	"SLLV",	      sll,	"$%a,R%t,R%d",
+	"special39",	0,	mipscoxxx,
+	"SRLV",	      sll,	"$%a,R%t,R%d",
+	"SRAV",	      sll,	"$%a,R%t,R%d",
+	"SLLV",	     sl32,	"$%a,R%t,R%d",
+	"special3D",	0,	mipscoxxx,
+	"SRLV",	     sl32,	"$%a,R%t,R%d",
+	"SRAV",	     sl32,	"$%a,R%t,R%d",
+};
+
+static Opcode ropcodes[32] = {
+	"BLTZ",	   branch,	0,
+	"BGEZ",	   branch,	0,
+	"BLTZL",   branch,	0,
+	"BGEZL",   branch,	0,
+	"regimm04",	0,	mipscoxxx,
+	"regimm05",	0,	mipscoxxx,
+	"regimm06",	0,	mipscoxxx,
+	"regimm07",	0,	mipscoxxx,
+	"tgei",		0,	mipscorsi,
+	"tgeiu",	0,	mipscorsi,
+	"tlti",		0,	mipscorsi,
+	"tltiu",	0,	mipscorsi,
+	"teqi",		0,	mipscorsi,
+	"regimm0D",	0,	mipscoxxx,
+	"tnei",		0,	mipscorsi,
+	"regimm0F",	0,	mipscoxxx,
+	"BLTZAL",  branch,	0,
+	"BGEZAL",  branch,	0,
+	"BLTZALL", branch,	0,
+	"BGEZALL", branch,	0,
+	"regimm14",	0,	mipscoxxx,
+	"regimm15",	0,	mipscoxxx,
+	"regimm16",	0,	mipscoxxx,
+	"regimm17",	0,	mipscoxxx,
+	"regimm18",	0,	mipscoxxx,
+	"regimm19",	0,	mipscoxxx,
+	"regimm1A",	0,	mipscoxxx,
+	"regimm1B",	0,	mipscoxxx,
+	"regimm1C",	0,	mipscoxxx,
+	"regimm1D",	0,	mipscoxxx,
+	"regimm1E",	0,	mipscoxxx,
+	"regimm1F",	0,	mipscoxxx,
+};
+
+static Opcode fopcodes[64] = {
+	"ADD%f",	0,	mipsfp3,
+	"SUB%f",	0,	mipsfp3,
+	"MUL%f",	0,	mipsfp3,
+	"DIV%f",	0,	mipsfp3,
+	"sqrt.%f",	0,	mipscofp2,
+	"ABS%f",	0,	mipsfp2,
+	"MOV%f",	0,	mipsfp2,
+	"NEG%f",	0,	mipsfp2,
+	"finstr08",	0,	mipscoxxx,
+	"finstr09",	0,	mipscoxxx,
+	"finstr0A",	0,	mipscoxxx,
+	"finstr0B",	0,	mipscoxxx,
+	"round.w.%f",	0,	mipscofp2,
+	"trunc.w%f",	0,	mipscofp2,
+	"ceil.w%f",	0,	mipscofp2,
+	"floor.w%f",	0,	mipscofp2,
+	"finstr10",	0,	mipscoxxx,
+	"finstr11",	0,	mipscoxxx,
+	"finstr12",	0,	mipscoxxx,
+	"finstr13",	0,	mipscoxxx,
+	"finstr14",	0,	mipscoxxx,
+	"finstr15",	0,	mipscoxxx,
+	"finstr16",	0,	mipscoxxx,
+	"finstr17",	0,	mipscoxxx,
+	"finstr18",	0,	mipscoxxx,
+	"finstr19",	0,	mipscoxxx,
+	"finstr1A",	0,	mipscoxxx,
+	"finstr1B",	0,	mipscoxxx,
+	"finstr1C",	0,	mipscoxxx,
+	"finstr1D",	0,	mipscoxxx,
+	"finstr1E",	0,	mipscoxxx,
+	"finstr1F",	0,	mipscoxxx,
+	"cvt.s.%f",	0,	mipscofp2,
+	"cvt.d.%f",	0,	mipscofp2,
+	"cvt.e.%f",	0,	mipscofp2,
+	"cvt.q.%f",	0,	mipscofp2,
+	"cvt.w.%f",	0,	mipscofp2,
+	"finstr25",	0,	mipscoxxx,
+	"finstr26",	0,	mipscoxxx,
+	"finstr27",	0,	mipscoxxx,
+	"finstr28",	0,	mipscoxxx,
+	"finstr29",	0,	mipscoxxx,
+	"finstr2A",	0,	mipscoxxx,
+	"finstr2B",	0,	mipscoxxx,
+	"finstr2C",	0,	mipscoxxx,
+	"finstr2D",	0,	mipscoxxx,
+	"finstr2E",	0,	mipscoxxx,
+	"finstr2F",	0,	mipscoxxx,
+	"c.f.%f",	0,	mipscofpc,
+	"c.un.%f",	0,	mipscofpc,
+	"CMPEQ%f",	0,	mipsfpc,
+	"c.ueq.%f",	0,	mipscofpc,
+	"c.olt.%f",	0,	mipscofpc,
+	"c.ult.%f",	0,	mipscofpc,
+	"c.ole.%f",	0,	mipscofpc,
+	"c.ule.%f",	0,	mipscofpc,
+	"c.sf.%f",	0,	mipscofpc,
+	"c.ngle.%f",	0,	mipscofpc,
+	"c.seq.%f",	0,	mipscofpc,
+	"c.ngl.%f",	0,	mipscofpc,
+	"CMPGT%f",	0,	mipsfpc,
+	"c.nge.%f",	0,	mipscofpc,
+	"CMPGE%f",	0,	mipsfpc,
+	"c.ngt.%f",	0,	mipscofpc,
+};
+
+static char *cop0regs[32] = {
+	"INDEX", "RANDOM", "TLBPHYS", "EntryLo0",
+	"CONTEXT", "PageMask", "Wired",	"Error",
+	"BADVADDR", "Count", "TLBVIRT", "Compare",
+	"STATUS", "CAUSE", "EPC", "PRID",
+	"Config", "LLadr", "WatchLo", "WatchHi",
+	"20", "21", "22", "23",
+	"24", "25", "26", "CacheErr",
+	"TagLo", "TagHi", "ErrorEPC", "31"
+};
+
+static char fsub[16] = {
+	'F', 'D', 'e', 'q', 'W', '?', '?', '?',
+	'?', '?', '?', '?', '?', '?', '?', '?'
+};
+
+static char *cacheps[] = {
+	"I", "D", "SI", "SD"
+};
+
+static char *cacheop[] = {
+	"IWBI", "ILT", "IST", "CDE", "HI", "HWBI", "HWB", "HSV"
+};
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	if (mnemonic)
+		format(0, i, mnemonic);
+	if (f == 0)
+		return;
+	if (mnemonic)
+		if (i->curr < i->end)
+			*i->curr++ = '\t';
+	for ( ; *f && i->curr < i->end; f++) {
+		if (*f != '%') {
+			*i->curr++ = *f;
+			continue;
+		}
+		switch (*++f) {
+
+		case 's':
+			bprint(i, "%d", i->rs);
+			break;
+
+		case 't':
+			bprint(i, "%d", i->rt);
+			break;
+
+		case 'd':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'a':
+			bprint(i, "%d", i->sa);
+			break;
+
+		case 'l':
+			bprint(i, "%lx(R%d)",i->immediate, i->rs);
+			break;
+
+		case 'i':
+			bprint(i, "$%lx", i->immediate);
+			break;
+
+		case 'u':
+			i->curr += symoff(i->curr, i->end-i->curr, i->immediate, CANY);
+			bprint(i, "(SB)");
+			break;
+
+		case 'j':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				(i->target<<2)|(i->addr & 0xF0000000), CANY);
+			bprint(i, "(SB)");
+			break;
+
+		case 'b':
+			i->curr += symoff(i->curr, i->end-i->curr,
+				(i->immediate<<2)+i->addr+4, CANY);
+			break;
+
+		case 'c':
+			bprint(i, "$%lx", i->cofun);
+			break;
+
+		case 'w':
+			bprint(i, "[%lux]", i->w0);
+			break;
+
+		case 'm':
+			bprint(i, "M(%s)", cop0regs[i->rd]);
+			break;
+
+		case 'f':
+			*i->curr++ = fsub[i->rs & 0x0F];
+			break;
+
+		case 'C':
+			bprint(i, "%s%s", cacheps[i->rt & 3], cacheop[(i->rt>>2) & 7]);
+			break;
+
+		case '\0':
+			*i->curr++ = '%';
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+	*i->curr = 0;
+}
+
+static void
+copz(int cop, Instr *i)
+{
+	char *f, *m, buf[16];
+
+	m = buf;
+	f = "%t,%d";
+	switch (i->rs) {
+
+	case 0:
+		sprint(buf, "mfc%d", cop);
+		break;
+
+	case 2:
+		sprint(buf, "cfc%d", cop);
+		break;
+
+	case 4:
+		sprint(buf, "mtc%d", cop);
+		break;
+
+	case 6:
+		sprint(buf, "ctc%d", cop);
+		break;
+
+	case 8:
+		f = "%b";
+		switch (i->rt) {
+
+		case 0:
+			sprint(buf, "bc%df", cop);
+			break;
+
+		case 1:
+			sprint(buf, "bc%dt", cop);
+			break;
+
+		case 2:
+			sprint(buf, "bc%dfl", cop);
+			break;
+
+		case 3:
+			sprint(buf, "bc%dtl", cop);
+			break;
+
+		default:
+			sprint(buf, "cop%d", cop);
+			f = mipscoxxx;
+			break;
+		}
+		break;
+
+	default:
+		sprint(buf, "cop%d", cop);
+		if (i->rs & 0x10)
+			f = "function %c";
+		else
+			f = mipscoxxx;
+		break;
+	}
+	format(m, i, f);
+}
+
+static void
+cop0(Instr *i)
+{
+	char *m = 0;
+
+	if (i->rs < 8) {
+		switch (i->rs) {
+
+		case 0:
+		case 1:
+			format("MOVW", i, "%m,R%t");
+			return;
+
+		case 4:
+		case 5:
+			format("MOVW", i, "R%t,%m");
+			return;
+		}
+	}
+	else if (i->rs >= 0x10) {
+		switch (i->cofun) {
+	
+		case 1:
+			m = "TLBR";
+			break;
+	
+		case 2:
+			m = "TLBWI";
+			break;
+	
+		case 6:
+			m = "TLBWR";
+			break;
+	
+		case 8:
+			m = "TLBP";
+			break;
+	
+		case 16:
+			m = "RFE";
+			break;
+	
+		case 32:
+			m = "ERET";
+			break;
+		}
+		if (m) {
+			format(m, i, 0);
+			return;
+		}
+	}
+	copz(0, i);
+}
+
+static void
+cop1(Instr *i)
+{
+	char *m = "MOVW";
+
+	switch (i->rs) {
+
+	case 0:
+		format(m, i, "F%d,R%t");
+		return;
+
+	case 2:
+		format(m, i, "FCR%d,R%t");
+		return;
+
+	case 4:
+		format(m, i, "R%t,F%d");
+		return;
+
+	case 6:
+		format(m, i, "R%t,FCR%d");
+		return;
+
+	case 8:
+		switch (i->rt) {
+
+		case 0:
+			format("BFPF", i, "%b");
+			return;
+
+		case 1:
+			format("BFPT", i, "%b");
+			return;
+		}
+		break;
+	}
+	copz(1, i);
+}
+
+static int
+printins(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+	Opcode *o;
+	uchar op;
+
+	i.curr = buf;
+	i.end = buf+n-1;
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	switch (i.op) {
+
+	case 0x00:					/* SPECIAL */
+		o = sopcodes;
+		op = i.function;
+		break;
+
+	case 0x01:					/* REGIMM */
+		o = ropcodes;
+		op = i.rt;
+		break;
+
+	case 0x10:					/* COP0 */
+		cop0(&i);
+		return i.size*4;
+
+	case 0x11:					/* COP1 */
+		if (i.rs & 0x10) {
+			o = fopcodes;
+			op = i.function;
+			break;
+		}
+		cop1(&i);
+		return i.size*4;
+
+	case 0x12:					/* COP2 */
+	case 0x13:					/* COP3 */
+		copz(i.op-0x10, &i);
+		return i.size*4;
+
+	default:
+		o = opcodes;
+		op = i.op;
+		break;
+	}
+	if (o[op].f)
+		(*o[op].f)(&o[op], &i);
+	else
+		format(o[op].mnemonic, &i, o[op].ken);
+	return i.size*4;
+}
+
+extern	int	_mipscoinst(Map *, uvlong, char*, int);
+
+	/* modifier 'I' toggles the default disassembler type */
+static int
+mipsinst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	if ((asstype == AMIPSCO && modifier == 'i')
+		|| (asstype == AMIPS && modifier == 'I'))
+		return _mipscoinst(map, pc, buf, n);
+	else
+		return printins(map, pc, buf, n);
+}
+
+static int
+mipsdas(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr i;
+
+	i.curr = buf;
+	i.end = buf+n;
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	if (i.end-i.curr > 8)
+		i.curr = _hexify(buf, i.w0, 7);
+	if (i.size == 2 && i.end-i.curr > 9) {
+		*i.curr++ = ' ';
+		i.curr = _hexify(i.curr, i.w1, 7);
+	}
+	*i.curr = 0;
+	return i.size*4;
+}
+
+static int
+mipsinstlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	return i.size*4;
+}
+
+static int
+mipsfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	ulong w, l;
+	char buf[8];
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	w = i.w0;
+	if((w&0xF3600000) == 0x41000000){	/* branch on coprocessor */
+    Conditional:
+		foll[0] = pc+8;
+		l = ((w&0xFFFF)<<2);
+		if(w & 0x8000)
+			l |= 0xFFFC0000;
+		foll[1] = pc+4 + l;
+		return 2;
+	}
+
+	l = (w&0xFC000000)>>26;
+	switch(l){
+	case 0:		/* SPECIAL */
+		if((w&0x3E) == 0x08){	/* JR, JALR */
+			sprint(buf, "R%ld", (w>>21)&0x1F);
+			foll[0] = (*rget)(map, buf);
+			return 1;
+		}
+		foll[0] = pc+i.size*4;
+		return 1;
+	case 0x30:	/* Load-Linked followed by NOP, STC */
+		foll[0] = pc+12;
+		return 1;
+	case 1:		/* BCOND */
+	case 4:		/* BEQ */
+	case 20:	/* BEQL */
+	case 5:		/* BNE */
+	case 21:	/* BNEL */
+	case 6:		/* BLEZ */
+	case 22:	/* BLEZL */
+	case 7:		/* BGTZ */
+	case 23:	/* BGTZL */
+		goto Conditional;
+	case 2:		/* J */
+	case 3:		/* JAL */
+		foll[0] = (pc&0xF0000000) | ((w&0x03FFFFFF)<<2);
+		return 1;
+	}
+
+	foll[0] = pc+i.size*4;
+	return 1;
+}
--- /dev/null
+++ b/utils/libmach/vobj.c
@@ -1,0 +1,133 @@
+/*
+ * vobj.c - identify and parse a mips object file
+ */
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+#include "vc/v.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_isv(char *s)
+{
+	return  s[0] == ANAME				/* ANAME */
+		&& s[1] == D_FILE			/* type */
+		&& s[2] == 1				/* sym */
+		&& s[3] == '<';				/* name of file */
+}
+
+int
+_readv(Biobuf *bp, Prog *p)
+{
+	int as, n;
+	Addr a;
+
+	as = Bgetc(bp);			/* as */
+	if(as < 0)
+		return 0;
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 5);		/* reg(1), lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE: case D_REG: case D_FREG: case D_MREG:
+	case D_FCREG: case D_LO: case D_HI:
+		break;
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/utils/libregexp/NOTICE
@@ -1,0 +1,28 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1994-1999 Lucent Technologies Inc.
+	Revisions Copyright © 2000-2003 Vita Nuova Holdings Limited (www.vitanuova.com).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/libregexp/mkfile
@@ -1,0 +1,22 @@
+<../../mkconfig
+
+#XXX
+<$ROOT/mkfiles/mkhost-$SYSHOST		# variables appropriate for host system
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE	# variables used to build target object type
+
+LIB=libregexp.a
+CFLAGS= $CFLAGS -I../include
+
+OFILES=\
+	regcomp.$O\
+	regerror.$O\
+	regexec.$O\
+	regsub.$O\
+	regaux.$O\
+	rregexec.$O\
+	rregsub.$O\
+
+HFILES=	../include/regexp.h\
+	regcomp.h\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
--- /dev/null
+++ b/utils/libregexp/regaux.c
@@ -1,0 +1,98 @@
+#include <lib9.h>
+#include "regexp.h"
+#include "regcomp.h"
+
+
+/*
+ *  save a new match in mp
+ */
+extern void
+_renewmatch(Resub *mp, int ms, Resublist *sp)
+{
+	int i;
+
+	if(mp==0 || ms<=0)
+		return;
+	if(mp[0].s.sp==0 || sp->m[0].s.sp<mp[0].s.sp ||
+	   (sp->m[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){
+		for(i=0; i<ms && i<NSUBEXP; i++)
+			mp[i] = sp->m[i];
+		for(; i<ms; i++)
+			mp[i].s.sp = mp[i].e.ep = 0;
+	}
+}
+
+/*
+ * Note optimization in _renewthread:
+ * 	*lp must be pending when _renewthread called; if *l has been looked
+ *		at already, the optimization is a bug.
+ */
+extern Relist*
+_renewthread(Relist *lp,	/* _relist to add to */
+	Reinst *ip,		/* instruction to add */
+	Resublist *sep)		/* pointers to subexpressions */
+{
+	Relist *p;
+
+	for(p=lp; p->inst; p++){
+		if(p->inst == ip){
+			if((sep)->m[0].s.sp < p->se.m[0].s.sp)
+				p->se = *sep;
+			return 0;
+		}
+	}
+	p->inst = ip;
+	p->se = *sep;
+	(++p)->inst = 0;
+	return p;
+}
+
+/*
+ * same as renewthread, but called with
+ * initial empty start pointer.
+ */
+extern Relist*
+_renewemptythread(Relist *lp,	/* _relist to add to */
+	Reinst *ip,		/* instruction to add */
+	char *sp)		/* pointers to subexpressions */
+{
+	Relist *p;
+
+	for(p=lp; p->inst; p++){
+		if(p->inst == ip){
+			if(sp < p->se.m[0].s.sp) {
+				memset((void *)&p->se, 0, sizeof(p->se));
+				p->se.m[0].s.sp = sp;
+			}
+			return 0;
+		}
+	}
+	p->inst = ip;
+	memset((void *)&p->se, 0, sizeof(p->se));
+	p->se.m[0].s.sp = sp;
+	(++p)->inst = 0;
+	return p;
+}
+
+extern Relist*
+_rrenewemptythread(Relist *lp,	/* _relist to add to */
+	Reinst *ip,		/* instruction to add */
+	Rune *rsp)		/* pointers to subexpressions */
+{
+	Relist *p;
+
+	for(p=lp; p->inst; p++){
+		if(p->inst == ip){
+			if(rsp < p->se.m[0].s.rsp) {
+				memset((void *)&p->se, 0, sizeof(p->se));
+				p->se.m[0].s.rsp = rsp;
+			}
+			return 0;
+		}
+	}
+	p->inst = ip;
+	memset((void *)&p->se, 0, sizeof(p->se));
+	p->se.m[0].s.rsp = rsp;
+	(++p)->inst = 0;
+	return p;
+}
--- /dev/null
+++ b/utils/libregexp/regcomp.c
@@ -1,0 +1,557 @@
+#include <lib9.h>
+#include "regexp.h"
+#include "regcomp.h"
+
+#define	TRUE	1
+#define	FALSE	0
+
+/*
+ * Parser Information
+ */
+typedef
+struct Node
+{
+	Reinst*	first;
+	Reinst*	last;
+}Node;
+
+Reprog	RePrOg;
+
+#define	NSTACK	20
+static	Node	andstack[NSTACK];
+static	Node	*andp;
+static	int	atorstack[NSTACK];
+static	int*	atorp;
+static	int	cursubid;		/* id of current subexpression */
+static	int	subidstack[NSTACK];	/* parallel to atorstack */
+static	int*	subidp;
+static	int	lastwasand;	/* Last token was operand */
+static	int	nbra;
+static	char*	exprp;		/* pointer to next character in source expression */
+static	int	lexdone;
+static	int	nclass;
+static	Reclass*classp;
+static	Reinst*	freep;
+static	int	errors;
+static	Rune	yyrune;		/* last lex'd rune */
+static	Reclass*yyclassp;	/* last lex'd class */
+
+/* predeclared crap */
+static	void	operator(int);
+static	void	pushand(Reinst*, Reinst*);
+static	void	pushator(int);
+static	void	evaluntil(int);
+static	int	bldcclass(void);
+
+static jmp_buf regkaboom;
+
+static	void
+rcerror(char *s)
+{
+	errors++;
+	regerror(s);
+	longjmp(regkaboom, 1);
+}
+
+static	Reinst*
+newinst(int t)
+{
+	freep->type = t;
+	freep->u2.left = 0;
+	freep->u1.right = 0;
+	return freep++;
+}
+
+static	void
+operand(int t)
+{
+	Reinst *i;
+
+	if(lastwasand)
+		operator(CAT);	/* catenate is implicit */
+	i = newinst(t);
+
+	if(t == CCLASS || t == NCCLASS)
+		i->u1.cp = yyclassp;
+	if(t == RUNE)
+		i->u1.r = yyrune;
+
+	pushand(i, i);
+	lastwasand = TRUE;
+}
+
+static	void
+operator(int t)
+{
+	if(t==RBRA && --nbra<0)
+		rcerror("unmatched right paren");
+	if(t==LBRA){
+		if(++cursubid >= NSUBEXP)
+			rcerror ("too many subexpressions");
+		nbra++;
+		if(lastwasand)
+			operator(CAT);
+	} else
+		evaluntil(t);
+	if(t != RBRA)
+		pushator(t);
+	lastwasand = FALSE;
+	if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
+		lastwasand = TRUE;	/* these look like operands */
+}
+
+static	void
+regerr2(char *s, int c)
+{
+	char buf[100];
+	char *cp = buf;
+	while(*s)
+		*cp++ = *s++;
+	*cp++ = c;
+	*cp = '\0'; 
+	rcerror(buf);
+}
+
+static	void
+cant(char *s)
+{
+	char buf[100];
+	strcpy(buf, "can't happen: ");
+	strcat(buf, s);
+	rcerror(buf);
+}
+
+static	void
+pushand(Reinst *f, Reinst *l)
+{
+	if(andp >= &andstack[NSTACK])
+		cant("operand stack overflow");
+	andp->first = f;
+	andp->last = l;
+	andp++;
+}
+
+static	void
+pushator(int t)
+{
+	if(atorp >= &atorstack[NSTACK])
+		cant("operator stack overflow");
+	*atorp++ = t;
+	*subidp++ = cursubid;
+}
+
+static	Node*
+popand(int op)
+{
+	Reinst *inst;
+
+	if(andp <= &andstack[0]){
+		regerr2("missing operand for ", op);
+		inst = newinst(NOP);
+		pushand(inst,inst);
+	}
+	return --andp;
+}
+
+static	int
+popator(void)
+{
+	if(atorp <= &atorstack[0])
+		cant("operator stack underflow");
+	--subidp;
+	return *--atorp;
+}
+
+static	void
+evaluntil(int pri)
+{
+	Node *op1, *op2;
+	Reinst *inst1, *inst2;
+
+	while(pri==RBRA || atorp[-1]>=pri){
+		switch(popator()){
+		default:
+			rcerror("unknown operator in evaluntil");
+			break;
+		case LBRA:		/* must have been RBRA */
+			op1 = popand('(');
+			inst2 = newinst(RBRA);
+			inst2->u1.subid = *subidp;
+			op1->last->u2.next = inst2;
+			inst1 = newinst(LBRA);
+			inst1->u1.subid = *subidp;
+			inst1->u2.next = op1->first;
+			pushand(inst1, inst2);
+			return;
+		case OR:
+			op2 = popand('|');
+			op1 = popand('|');
+			inst2 = newinst(NOP);
+			op2->last->u2.next = inst2;
+			op1->last->u2.next = inst2;
+			inst1 = newinst(OR);
+			inst1->u1.right = op1->first;
+			inst1->u2.left = op2->first;
+			pushand(inst1, inst2);
+			break;
+		case CAT:
+			op2 = popand(0);
+			op1 = popand(0);
+			op1->last->u2.next = op2->first;
+			pushand(op1->first, op2->last);
+			break;
+		case STAR:
+			op2 = popand('*');
+			inst1 = newinst(OR);
+			op2->last->u2.next = inst1;
+			inst1->u1.right = op2->first;
+			pushand(inst1, inst1);
+			break;
+		case PLUS:
+			op2 = popand('+');
+			inst1 = newinst(OR);
+			op2->last->u2.next = inst1;
+			inst1->u1.right = op2->first;
+			pushand(op2->first, inst1);
+			break;
+		case QUEST:
+			op2 = popand('?');
+			inst1 = newinst(OR);
+			inst2 = newinst(NOP);
+			inst1->u2.left = inst2;
+			inst1->u1.right = op2->first;
+			op2->last->u2.next = inst2;
+			pushand(inst1, inst2);
+			break;
+		}
+	}
+}
+
+static	Reprog*
+optimize(Reprog *pp)
+{
+	Reinst *inst, *target;
+	int size;
+	Reprog *npp;
+	Reclass *cl;
+	int diff;
+
+	/*
+	 *  get rid of NOOP chains
+	 */
+	for(inst=pp->firstinst; inst->type!=END; inst++){
+		target = inst->u2.next;
+		while(target->type == NOP)
+			target = target->u2.next;
+		inst->u2.next = target;
+	}
+
+	/*
+	 *  The original allocation is for an area larger than
+	 *  necessary.  Reallocate to the actual space used
+	 *  and then relocate the code.
+	 */
+	size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst);
+	npp = (Reprog *)realloc(pp, size);
+	if(npp==0 || npp==pp)
+		return pp;
+	diff = (char *)npp - (char *)pp;
+	freep = (Reinst *)((char *)freep + diff);
+	for(inst=npp->firstinst; inst<freep; inst++){
+		switch(inst->type){
+		case OR:
+		case STAR:
+		case PLUS:
+		case QUEST:
+			*(char **)&inst->u1.right += diff;
+			break;
+		case CCLASS:
+		case NCCLASS:
+			*(char **)&inst->u1.right += diff;
+			cl = inst->u1.cp;
+			*(char **)&cl->end += diff;
+			break;
+		}
+		*(char **)&inst->u2.left += diff;
+	}
+	*(char **)&npp->startinst += diff;
+	return npp;
+}
+
+#ifdef	DEBUG
+static	void
+dumpstack(void){
+	Node *stk;
+	int *ip;
+
+	print("operators\n");
+	for(ip=atorstack; ip<atorp; ip++)
+		print("0%o\n", *ip);
+	print("operands\n");
+	for(stk=andstack; stk<andp; stk++)
+		print("0%o\t0%o\n", stk->first->type, stk->last->type);
+}
+
+static	void
+dump(Reprog *pp)
+{
+	Reinst *l;
+	Rune *p;
+
+	l = pp->firstinst;
+	do{
+		print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type,
+			l->u2.left-pp->firstinst, l->u1.right-pp->firstinst);
+		if(l->type == RUNE)
+			print("\t%C\n", l->r);
+		else if(l->type == CCLASS || l->type == NCCLASS){
+			print("\t[");
+			if(l->type == NCCLASS)
+				print("^");
+			for(p = l->cp->spans; p < l->cp->end; p += 2)
+				if(p[0] == p[1])
+					print("%C", p[0]);
+				else
+					print("%C-%C", p[0], p[1]);
+			print("]\n");
+		} else
+			print("\n");
+	}while(l++->type);
+}
+#endif
+
+static	Reclass*
+newclass(void)
+{
+	if(nclass >= NCLASS)
+		regerr2("too many character classes; limit", NCLASS+'0');
+	return &(classp[nclass++]);
+}
+
+static	int
+nextc(Rune *rp)
+{
+	if(lexdone){
+		*rp = 0;
+		return 1;
+	}
+	exprp += chartorune(rp, exprp);
+	if(*rp == L'\\'){
+		exprp += chartorune(rp, exprp);
+		return 1;
+	}
+	if(*rp == 0)
+		lexdone = 1;
+	return 0;
+}
+
+static	int
+lex(int literal, int dot_type)
+{
+	int quoted;
+
+	quoted = nextc(&yyrune);
+	if(literal || quoted){
+		if(yyrune == 0)
+			return END;
+		return RUNE;
+	}
+
+	switch(yyrune){
+	case 0:
+		return END;
+	case L'*':
+		return STAR;
+	case L'?':
+		return QUEST;
+	case L'+':
+		return PLUS;
+	case L'|':
+		return OR;
+	case L'.':
+		return dot_type;
+	case L'(':
+		return LBRA;
+	case L')':
+		return RBRA;
+	case L'^':
+		return BOL;
+	case L'$':
+		return EOL;
+	case L'[':
+		return bldcclass();
+	}
+	return RUNE;
+}
+
+static int
+bldcclass(void)
+{
+	int type;
+	Rune r[NCCRUNE];
+	Rune *p, *ep, *np;
+	Rune rune;
+	int quoted;
+
+	/* we have already seen the '[' */
+	type = CCLASS;
+	yyclassp = newclass();
+
+	/* look ahead for negation */
+	/* SPECIAL CASE!!! negated classes don't match \n */
+	ep = r;
+	quoted = nextc(&rune);
+	if(!quoted && rune == L'^'){
+		type = NCCLASS;
+		quoted = nextc(&rune);
+		*ep++ = L'\n';
+		*ep++ = L'\n';
+	}
+
+	/* parse class into a set of spans */
+	for(; ep<&r[NCCRUNE];){
+		if(rune == 0){
+			rcerror("malformed '[]'");
+			return 0;
+		}
+		if(!quoted && rune == L']')
+			break;
+		if(!quoted && rune == L'-'){
+			if(ep == r){
+				rcerror("malformed '[]'");
+				return 0;
+			}
+			quoted = nextc(&rune);
+			if((!quoted && rune == L']') || rune == 0){
+				rcerror("malformed '[]'");
+				return 0;
+			}
+			*(ep-1) = rune;
+		} else {
+			*ep++ = rune;
+			*ep++ = rune;
+		}
+		quoted = nextc(&rune);
+	}
+
+	/* sort on span start */
+	for(p = r; p < ep; p += 2){
+		for(np = p; np < ep; np += 2)
+			if(*np < *p){
+				rune = np[0];
+				np[0] = p[0];
+				p[0] = rune;
+				rune = np[1];
+				np[1] = p[1];
+				p[1] = rune;
+			}
+	}
+
+	/* merge spans */
+	np = yyclassp->spans;
+	p = r;
+	if(r == ep)
+		yyclassp->end = np;
+	else {
+		np[0] = *p++;
+		np[1] = *p++;
+		for(; p < ep; p += 2)
+			if(p[0] <= np[1]){
+				if(p[1] > np[1])
+					np[1] = p[1];
+			} else {
+				np += 2;
+				np[0] = p[0];
+				np[1] = p[1];
+			}
+		yyclassp->end = np+2;
+	}
+
+	return type;
+}
+
+static	Reprog*
+regcomp1(char *s, int literal, int dot_type)
+{
+	int token;
+	Reprog *pp;
+
+	/* get memory for the program */
+	pp = (Reprog *)malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s));
+	if(pp == 0){
+		regerror("out of memory");
+		return 0;
+	}
+	freep = pp->firstinst;
+	classp = pp->class;
+	errors = 0;
+
+	if(setjmp(regkaboom))
+		goto out;
+
+	/* go compile the sucker */
+	lexdone = 0;
+	exprp = s;
+	nclass = 0;
+	nbra = 0;
+	atorp = atorstack;
+	andp = andstack;
+	subidp = subidstack;
+	lastwasand = FALSE;
+	cursubid = 0;
+
+	/* Start with a low priority operator to prime parser */
+	pushator(START-1);
+	while((token = lex(literal, dot_type)) != END){
+		if((token&0300) == OPERATOR)
+			operator(token);
+		else
+			operand(token);
+	}
+
+	/* Close with a low priority operator */
+	evaluntil(START);
+
+	/* Force END */
+	operand(END);
+	evaluntil(START);
+#ifdef DEBUG
+	dumpstack();
+#endif
+	if(nbra)
+		rcerror("unmatched left paren");
+	--andp;	/* points to first and only operand */
+	pp->startinst = andp->first;
+#ifdef DEBUG
+	dump(pp);
+#endif
+	pp = optimize(pp);
+#ifdef DEBUG
+	print("start: %d\n", andp->first-pp->firstinst);
+	dump(pp);
+#endif
+out:
+	if(errors){
+		free(pp);
+		pp = 0;
+	}
+	return pp;
+}
+
+extern	Reprog*
+regcomp(char *s)
+{
+	return regcomp1(s, 0, ANY);
+}
+
+extern	Reprog*
+regcomplit(char *s)
+{
+	return regcomp1(s, 1, ANY);
+}
+
+extern	Reprog*
+regcompnl(char *s)
+{
+	return regcomp1(s, 0, ANYNL);
+}
--- /dev/null
+++ b/utils/libregexp/regcomp.h
@@ -1,0 +1,70 @@
+/*
+ *  substitution list
+ */
+#define NSUBEXP 32
+typedef struct Resublist	Resublist;
+struct	Resublist
+{
+	Resub	m[NSUBEXP];
+};
+
+/* max character classes per program */
+extern Reprog	RePrOg;
+#define	NCLASS	(sizeof(RePrOg.class)/sizeof(Reclass))
+
+/* max rune ranges per character class */
+#define NCCRUNE	(sizeof(Reclass)/sizeof(Rune))
+
+/*
+ * Actions and Tokens (Reinst types)
+ *
+ *	02xx are operators, value == precedence
+ *	03xx are tokens, i.e. operands for operators
+ */
+#define RUNE		0177
+#define	OPERATOR	0200	/* Bitmask of all operators */
+#define	START		0200	/* Start, used for marker on stack */
+#define	RBRA		0201	/* Right bracket, ) */
+#define	LBRA		0202	/* Left bracket, ( */
+#define	OR		0203	/* Alternation, | */
+#define	CAT		0204	/* Concatentation, implicit operator */
+#define	STAR		0205	/* Closure, * */
+#define	PLUS		0206	/* a+ == aa* */
+#define	QUEST		0207	/* a? == a|nothing, i.e. 0 or 1 a's */
+#define	ANY		0300	/* Any character except newline, . */
+#define	ANYNL		0301	/* Any character including newline, . */
+#define	NOP		0302	/* No operation, internal use only */
+#define	BOL		0303	/* Beginning of line, ^ */
+#define	EOL		0304	/* End of line, $ */
+#define	CCLASS		0305	/* Character class, [] */
+#define	NCCLASS		0306	/* Negated character class, [] */
+#define	END		0377	/* Terminate: match found */
+
+/*
+ *  regexec execution lists
+ */
+#define LISTSIZE	10
+#define BIGLISTSIZE	(10*LISTSIZE)
+typedef struct Relist	Relist;
+struct Relist
+{
+	Reinst*		inst;		/* Reinstruction of the thread */
+	Resublist	se;		/* matched subexpressions in this thread */
+};
+typedef struct Reljunk	Reljunk;
+struct	Reljunk
+{
+	Relist*	relist[2];
+	Relist*	reliste[2];
+	int	starttype;
+	Rune	startchar;
+	char*	starts;
+	char*	eol;
+	Rune*	rstarts;
+	Rune*	reol;
+};
+
+extern Relist*	_renewthread(Relist*, Reinst*, Resublist*);
+extern void	_renewmatch(Resub*, int, Resublist*);
+extern Relist*	_renewemptythread(Relist*, Reinst*, char*);
+extern Relist*	_rrenewemptythread(Relist*, Reinst*, Rune*);
--- /dev/null
+++ b/utils/libregexp/regerror.c
@@ -1,0 +1,14 @@
+#include <lib9.h>
+#include "regexp.h"
+
+void
+regerror(char *s)
+{
+	char buf[132];
+
+	strcpy(buf, "regerror: ");
+	strcat(buf, s);
+	strcat(buf, "\n");
+	write(2, buf, strlen(buf));
+	exits("regerr");
+}
--- /dev/null
+++ b/utils/libregexp/regexec.c
@@ -1,0 +1,219 @@
+#include <lib9.h>
+#include "regexp.h"
+#include "regcomp.h"
+
+
+/*
+ *  return	0 if no match
+ *		>0 if a match
+ *		<0 if we ran out of _relist space
+ */
+static int
+regexec1(Reprog *progp,	/* program to run */
+	char *bol,	/* string to run machine on */
+	Resub *mp,	/* subexpression elements */
+	int ms,		/* number of elements at mp */
+	Reljunk *j
+)
+{
+	int flag=0;
+	Reinst *inst;
+	Relist *tlp;
+	char *s;
+	int i, checkstart;
+	Rune r, *rp, *ep;
+	int n;
+	Relist* tl;		/* This list, next list */
+	Relist* nl;
+	Relist* tle;		/* ends of this and next list */
+	Relist* nle;
+	int match;
+	char *p;
+
+	match = 0;
+	checkstart = j->starttype;
+	if(mp)
+		for(i=0; i<ms; i++) {
+			mp[i].s.sp = 0;
+			mp[i].e.ep = 0;
+		}
+	j->relist[0][0].inst = 0;
+	j->relist[1][0].inst = 0;
+
+	/* Execute machine once for each character, including terminal NUL */
+	s = j->starts;
+	do{
+		/* fast check for first char */
+		if(checkstart) {
+			switch(j->starttype) {
+			case RUNE:
+				p = utfrune(s, j->startchar);
+				if(p == 0 || s == j->eol)
+					return match;
+				s = p;
+				break;
+			case BOL:
+				if(s == bol)
+					break;
+				p = utfrune(s, '\n');
+				if(p == 0 || s == j->eol)
+					return match;
+				s = p;
+				break;
+			}
+		}
+		r = *(uchar*)s;
+		if(r < (Rune)Runeself)
+			n = 1;
+		else
+			n = chartorune(&r, s);
+
+		/* switch run lists */
+		tl = j->relist[flag];
+		tle = j->reliste[flag];
+		nl = j->relist[flag^=1];
+		nle = j->reliste[flag];
+		nl->inst = 0;
+
+		/* Add first instruction to current list */
+		if(match == 0)
+			_renewemptythread(tl, progp->startinst, s);
+
+		/* Execute machine until current list is empty */
+		for(tlp=tl; tlp->inst; tlp++){	/* assignment = */
+			for(inst = tlp->inst; ; inst = inst->u2.next){
+				switch(inst->type){
+				case RUNE:	/* regular character */
+					if(inst->u1.r == r){
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					}
+					break;
+				case LBRA:
+					tlp->se.m[inst->u1.subid].s.sp = s;
+					continue;
+				case RBRA:
+					tlp->se.m[inst->u1.subid].e.ep = s;
+					continue;
+				case ANY:
+					if(r != '\n')
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case ANYNL:
+					if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case BOL:
+					if(s == bol || *(s-1) == '\n')
+						continue;
+					break;
+				case EOL:
+					if(s == j->eol || r == 0 || r == '\n')
+						continue;
+					break;
+				case CCLASS:
+					ep = inst->u1.cp->end;
+					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+						if(r >= rp[0] && r <= rp[1]){
+							if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+								return -1;
+							break;
+						}
+					break;
+				case NCCLASS:
+					ep = inst->u1.cp->end;
+					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+						if(r >= rp[0] && r <= rp[1])
+							break;
+					if(rp == ep)
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case OR:
+					/* evaluate right choice later */
+					if(_renewthread(tlp, inst->u1.right, &tlp->se) == tle)
+						return -1;
+					/* efficiency: advance and re-evaluate */
+					continue;
+				case END:	/* Match! */
+					match = 1;
+					tlp->se.m[0].e.ep = s;
+					if(mp != 0)
+						_renewmatch(mp, ms, &tlp->se);
+					break;
+				}
+				break;
+			}
+		}
+		if(s == j->eol)
+			break;
+		checkstart = j->starttype && nl->inst==0;
+		s += n;
+	}while(r);
+	return match;
+}
+
+static int
+regexec2(Reprog *progp,	/* program to run */
+	char *bol,	/* string to run machine on */
+	Resub *mp,	/* subexpression elements */
+	int ms,		/* number of elements at mp */
+	Reljunk *j
+)
+{
+	Relist relist0[BIGLISTSIZE], relist1[BIGLISTSIZE];
+
+	/* mark space */
+	j->relist[0] = relist0;
+	j->relist[1] = relist1;
+	j->reliste[0] = relist0 + nelem(relist0) - 2;
+	j->reliste[1] = relist1 + nelem(relist1) - 2;
+
+	return regexec1(progp, bol, mp, ms, j);
+}
+
+extern int
+regexec(Reprog *progp,	/* program to run */
+	char *bol,	/* string to run machine on */
+	Resub *mp,	/* subexpression elements */
+	int ms)		/* number of elements at mp */
+{
+	Reljunk j;
+	Relist relist0[LISTSIZE], relist1[LISTSIZE];
+	int rv;
+
+	/*
+ 	 *  use user-specified starting/ending location if specified
+	 */
+	j.starts = bol;
+	j.eol = 0;
+	if(mp && ms>0){
+		if(mp->s.sp)
+			j.starts = mp->s.sp;
+		if(mp->e.ep)
+			j.eol = mp->e.ep;
+	}
+	j.starttype = 0;
+	j.startchar = 0;
+	if(progp->startinst->type == RUNE && progp->startinst->u1.r < (Rune)Runeself) {
+		j.starttype = RUNE;
+		j.startchar = progp->startinst->u1.r;
+	}
+	if(progp->startinst->type == BOL)
+		j.starttype = BOL;
+
+	/* mark space */
+	j.relist[0] = relist0;
+	j.relist[1] = relist1;
+	j.reliste[0] = relist0 + nelem(relist0) - 2;
+	j.reliste[1] = relist1 + nelem(relist1) - 2;
+
+	rv = regexec1(progp, bol, mp, ms, &j);
+	if(rv >= 0)
+		return rv;
+	rv = regexec2(progp, bol, mp, ms, &j);
+	if(rv >= 0)
+		return rv;
+	return -1;
+}
--- /dev/null
+++ b/utils/libregexp/regsub.c
@@ -1,0 +1,55 @@
+#include <lib9.h>
+#include "regexp.h"
+
+/* substitute into one string using the matches from the last regexec() */
+extern	void
+regsub(char *sp,	/* source string */
+	char *dp,	/* destination string */
+	Resub *mp,	/* subexpression elements */
+	int ms)		/* number of elements pointed to by mp */
+{
+	char *ssp;
+	int i;
+
+	while(*sp != '\0'){
+		if(*sp == '\\'){
+			switch(*++sp){
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				i = *sp-'0';
+				if(mp[i].s.sp != 0 && mp!=0 && ms>i)
+					for(ssp = mp[i].s.sp;
+					     ssp < mp[i].e.ep;
+					     ssp++)
+						*dp++ = *ssp;
+				break;
+			case '\\':
+				*dp++ = '\\';
+				break;
+			case '\0':
+				sp--;
+				break;
+			default:
+				*dp++ = *sp;
+				break;
+			}
+		}else if(*sp == '&'){				
+			if(mp[0].s.sp != 0 && mp!=0 && ms>0)
+			if(mp[0].s.sp != 0)
+				for(ssp = mp[0].s.sp;
+				     ssp < mp[0].e.ep; ssp++)
+					*dp++ = *ssp;
+		}else
+			*dp++ = *sp;
+		sp++;
+	}
+	*dp = '\0';
+}
--- /dev/null
+++ b/utils/libregexp/rregexec.c
@@ -1,0 +1,213 @@
+#include <lib9.h>
+#include "regexp.h"
+#include "regcomp.h"
+
+/*
+ *  return	0 if no match
+ *		>0 if a match
+ *		<0 if we ran out of _relist space
+ */
+static int
+rregexec1(Reprog *progp,	/* program to run */
+	Rune *bol,		/* string to run machine on */
+	Resub *mp,		/* subexpression elements */
+	int ms,			/* number of elements at mp */
+	Reljunk *j)
+{
+	int flag=0;
+	Reinst *inst;
+	Relist *tlp;
+	Rune *s;
+	int i, checkstart;
+	Rune r, *rp, *ep;
+	Relist* tl;		/* This list, next list */
+	Relist* nl;
+	Relist* tle;		/* ends of this and next list */
+	Relist* nle;
+	int match;
+
+	match = 0;
+	checkstart = j->startchar;
+	if(mp)
+		for(i=0; i<ms; i++) {
+			mp[i].s.rsp = 0;
+			mp[i].e.rep = 0;
+		}
+	j->relist[0][0].inst = 0;
+	j->relist[1][0].inst = 0;
+
+	/* Execute machine once for each character, including terminal NUL */
+	s = j->rstarts;
+	do{
+
+		/* fast check for first char */
+		if(checkstart) {
+			switch(j->starttype) {
+			case RUNE:
+				while(*s != j->startchar) {
+					if(*s == 0 || s == j->reol)
+						return match;
+					s++;
+				}
+				break;
+			case BOL:
+				if(s == bol)
+					break;
+				while(*s != '\n') {
+					if(*s == 0 || s == j->reol)
+						return match;
+					s++;
+				}
+				break;
+			}
+		}
+
+		r = *s;
+
+		/* switch run lists */
+		tl = j->relist[flag];
+		tle = j->reliste[flag];
+		nl = j->relist[flag^=1];
+		nle = j->reliste[flag];
+		nl->inst = 0;
+
+		/* Add first instruction to current list */
+		_rrenewemptythread(tl, progp->startinst, s);
+
+		/* Execute machine until current list is empty */
+		for(tlp=tl; tlp->inst; tlp++){
+			for(inst=tlp->inst; ; inst = inst->u2.next){
+				switch(inst->type){
+				case RUNE:	/* regular character */
+					if(inst->u1.r == r)
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case LBRA:
+					tlp->se.m[inst->u1.subid].s.rsp = s;
+					continue;
+				case RBRA:
+					tlp->se.m[inst->u1.subid].e.rep = s;
+					continue;
+				case ANY:
+					if(r != '\n')
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case ANYNL:
+					if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case BOL:
+					if(s == bol || *(s-1) == '\n')
+						continue;
+					break;
+				case EOL:
+					if(s == j->reol || r == 0 || r == '\n')
+						continue;
+					break;
+				case CCLASS:
+					ep = inst->u1.cp->end;
+					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+						if(r >= rp[0] && r <= rp[1]){
+							if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+								return -1;
+							break;
+						}
+					break;
+				case NCCLASS:
+					ep = inst->u1.cp->end;
+					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+						if(r >= rp[0] && r <= rp[1])
+							break;
+					if(rp == ep)
+						if(_renewthread(nl, inst->u2.next, &tlp->se)==nle)
+							return -1;
+					break;
+				case OR:
+					/* evaluate right choice later */
+					if(_renewthread(tlp, inst->u1.right, &tlp->se) == tle)
+						return -1;
+					/* efficiency: advance and re-evaluate */
+					continue;
+				case END:	/* Match! */
+					match = 1;
+					tlp->se.m[0].e.rep = s;
+					if(mp != 0)
+						_renewmatch(mp, ms, &tlp->se);
+					break;
+				}
+				break;
+			}
+		}
+		if(s == j->reol)
+			break;
+		checkstart = j->startchar && nl->inst==0;
+		s++;
+	}while(r);
+	return match;
+}
+
+static int
+rregexec2(Reprog *progp,	/* program to run */
+	Rune *bol,	/* string to run machine on */
+	Resub *mp,	/* subexpression elements */
+	int ms,		/* number of elements at mp */
+	Reljunk *j
+)
+{
+	Relist relist0[5*LISTSIZE], relist1[5*LISTSIZE];
+
+	/* mark space */
+	j->relist[0] = relist0;
+	j->relist[1] = relist1;
+	j->reliste[0] = relist0 + nelem(relist0) - 2;
+	j->reliste[1] = relist1 + nelem(relist1) - 2;
+
+	return rregexec1(progp, bol, mp, ms, j);
+}
+
+extern int
+rregexec(Reprog *progp,	/* program to run */
+	Rune *bol,	/* string to run machine on */
+	Resub *mp,	/* subexpression elements */
+	int ms)		/* number of elements at mp */
+{
+	Reljunk j;
+	Relist relist0[LISTSIZE], relist1[LISTSIZE];
+	int rv;
+
+	/*
+ 	 *  use user-specified starting/ending location if specified
+	 */
+	j.rstarts = bol;
+	j.reol = 0;
+	if(mp && ms>0){
+		if(mp->s.sp)
+			j.rstarts = mp->s.rsp;
+		if(mp->e.ep)
+			j.reol = mp->e.rep;
+	}
+	j.starttype = 0;
+	j.startchar = 0;
+	if(progp->startinst->type == RUNE && progp->startinst->u1.r < (Rune)Runeself) {
+		j.starttype = RUNE;
+		j.startchar = progp->startinst->u1.r;
+	}
+	if(progp->startinst->type == BOL)
+		j.starttype = BOL;
+
+	/* mark space */
+	j.relist[0] = relist0;
+	j.relist[1] = relist1;
+	j.reliste[0] = relist0 + nelem(relist0) - 2;
+	j.reliste[1] = relist1 + nelem(relist1) - 2;
+
+	rv = rregexec1(progp, bol, mp, ms, &j);
+	if(rv >= 0)
+		return rv;
+	rv = rregexec2(progp, bol, mp, ms, &j);
+	if(rv >= 0)
+		return rv;
+	return -1;
+}
--- /dev/null
+++ b/utils/libregexp/rregsub.c
@@ -1,0 +1,55 @@
+#include <lib9.h>
+#include "regexp.h"
+
+/* substitute into one string using the matches from the last regexec() */
+extern	void
+rregsub(Rune *sp,	/* source string */
+	Rune *dp,	/* destination string */
+	Resub *mp,	/* subexpression elements */
+	int ms)		/* number of elements pointed to by mp */
+{
+	Rune *ssp;
+	int i;
+
+	while(*sp != '\0'){
+		if(*sp == '\\'){
+			switch(*++sp){
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				i = *sp-'0';
+				if(mp[i].s.rsp != 0 && mp!=0 && ms>i)
+					for(ssp = mp[i].s.rsp;
+					     ssp < mp[i].e.rep;
+					     ssp++)
+						*dp++ = *ssp;
+				break;
+			case '\\':
+				*dp++ = '\\';
+				break;
+			case '\0':
+				sp--;
+				break;
+			default:
+				*dp++ = *sp;
+				break;
+			}
+		}else if(*sp == '&'){				
+			if(mp[0].s.rsp != 0 && mp!=0 && ms>0)
+			if(mp[0].s.rsp != 0)
+				for(ssp = mp[0].s.rsp;
+				     ssp < mp[0].e.rep; ssp++)
+					*dp++ = *ssp;
+		}else
+			*dp++ = *sp;
+		sp++;
+	}
+	*dp = '\0';
+}
--- /dev/null
+++ b/utils/libregexp/test.c
@@ -1,0 +1,46 @@
+#include <lib9.h>
+#include <regexp.h>
+
+struct x
+{
+	char *re;
+	char *s;
+	Reprog *p;
+};
+
+struct x t[] = {
+	{ "^[^!@]+$", "/bin/upas/aliasmail '&'", 0 },
+	{ "^local!(.*)$", "/mail/box/\\1/mbox", 0 },
+	{ "^plan9!(.*)$", "\\1", 0 },
+	{ "^helix!(.*)$", "\\1", 0 },
+	{ "^([^!]+)@([^!@]+)$", "\\2!\\1", 0 },
+	{ "^(uk\\.[^!]*)(!.*)$", "/bin/upas/uk2uk '\\1' '\\2'", 0 },
+	{ "^[^!]*\\.[^!]*!.*$", "inet!&", 0 },
+	{ "^(coma|research|pipe|pyxis|inet|hunny|gauss)!(.*)$", "/mail/lib/qmail '\s' 'net!\\1' '\\2'", 0 },
+	{ "^.*$", "/mail/lib/qmail '\s' 'net!research' '&'", 0 },
+	{ 0, 0, 0 },
+};
+
+
+
+main(int ac, char **av)
+{
+	Resub rs[10];
+	char dst[128];
+	int n;
+	struct x *tp;
+
+	for(tp = t; tp->re; tp++)
+		tp->p = regcomp(tp->re);
+
+
+	for(tp = t; tp->re; tp++){
+		print("%s VIA %s", av[1], tp->re);
+		if(regexec(tp->p, av[1], rs, 10)){
+			regsub(tp->s, dst, rs, 10);
+			print(" sub %s -> %s", tp->s, dst);
+		}
+		print("\n");
+	}
+	exits(0);
+}
--- /dev/null
+++ b/utils/libregexp/test2.c
@@ -1,0 +1,20 @@
+#include <lib9.h>
+#include <regexp.h>
+
+
+main(int ac, char **av)
+{
+	Resub rs[10];
+	Reprog *p;
+	char *s;
+	int i;
+
+	p = regcomp("[^a-z]");
+	s = "\n";
+	if(regexec(p, s, rs, 10))
+		print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
+	s = "0";
+	if(regexec(p, s, rs, 10))
+		print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
+	exits(0);
+}
--- /dev/null
+++ b/utils/md5sum/md5sum.c
@@ -1,0 +1,61 @@
+#include <lib9.h>
+#include <libsec.h>
+
+#pragma	varargck	type	"M"	uchar*
+
+static int
+digestfmt(Fmt *fmt)
+{
+	char buf[MD5dlen*2+1];
+	uchar *p;
+	int i;
+
+	p = va_arg(fmt->args, uchar*);
+	for(i=0; i<MD5dlen; i++)
+		sprint(buf+2*i, "%.2ux", p[i]);
+	return fmtstrcpy(fmt, buf);
+}
+
+static void
+sum(int fd, char *name)
+{
+	int n;
+	uchar buf[8192], digest[MD5dlen];
+	DigestState *s;
+
+	s = md5(nil, 0, nil, nil);
+	while((n = read(fd, buf, sizeof buf)) > 0)
+		md5(buf, n, nil, s);
+	md5(nil, 0, digest, s);
+	if(name == nil)
+		print("%M\n", digest);
+	else
+		print("%M\t%s\n", digest, name);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, fd;
+
+	ARGBEGIN{
+	default:
+		fprint(2, "usage: md5sum [file...]\n");
+		exits("usage");
+	}ARGEND
+
+	fmtinstall('M', digestfmt);
+
+	if(argc == 0)
+		sum(0, nil);
+	else for(i = 0; i < argc; i++){
+		fd = open(argv[i], OREAD);
+		if(fd < 0){
+			fprint(2, "md5sum: can't open %s: %r\n", argv[i]);
+			continue;
+		}
+		sum(fd, argv[i]);
+		close(fd);
+	}
+	exits(nil);
+}
--- /dev/null
+++ b/utils/md5sum/mkfile
@@ -1,0 +1,19 @@
+<../../mkconfig
+
+#
+#	the md5sum command is needed for the test suite
+#
+
+TARG=md5sum
+
+OFILES=	md5sum.$O\
+
+
+HFILES= 
+
+LIBS=9 sec
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
--- /dev/null
+++ b/utils/mk/Nt.c
@@ -1,0 +1,443 @@
+#define INFERNO_KEEPENVIRON
+#include	"mk.h"
+#include	<signal.h>
+#include	<sys/utime.h>
+
+#define Arc	My_Arc		/* avoid name conflicts */
+#undef DELETE
+
+#include	<windows.h>
+
+enum {
+	Nchild	= 100,
+};
+
+char *rootdir =		ROOT;
+char *shell =		"Nt/386/bin/rcsh.exe";	/* Path relative to root */
+
+typedef struct Child	Child;
+
+struct Child {
+	int	pid;
+	HANDLE	handle;
+};
+
+static Child child[Nchild];
+
+extern char **environ;
+
+DWORD WINAPI writecmd(LPVOID a);
+
+void
+readenv(void)
+{
+	char **p, *s;
+	Word *w;
+
+	for(p = environ; *p; p++){
+		s = shname(*p);
+		if(*s == '=') {
+			*s = 0;
+			w = newword(s+1);
+		} else
+			w = newword("");
+		if (symlook(*p, S_INTERNAL, 0))
+			continue;
+		s = strdup(*p);
+		setvar(s, (void *)w);
+		symlook(s, S_EXPORTED, (void *)"")->value = "";
+	}
+}
+
+char *
+exportenv(Envy *e)
+{
+	int i, n;
+	char *buf, *v;
+
+	buf = 0;
+	n = 0;
+	for(i = 0; e->name; e++, i++) {
+			/* word separator is shell-dependent */
+		if(e->values)
+			v = wtos(e->values, IWS);
+		else
+			v = "";
+		buf = Realloc(buf, n+strlen(e->name)+1+strlen(v)+1);
+		
+		n += sprint(buf+n, "%s=%s", e->name, v);
+		n++;	/* skip over null */
+		if(e->values)
+			free(v);
+	}
+	/* final null */
+	buf = Realloc(buf, n+1);
+	buf[n] = 0;
+
+	return buf;
+}
+
+int
+waitfor(char *msg)
+{
+	int pid, n, i, r, code;
+	HANDLE tab[Nchild];
+
+	for(i=0,n=0; i<Nchild; i++)
+		if(child[i].handle != 0)
+			tab[n++] = child[i].handle;
+
+	if(n == 0)
+		return -1;
+
+	r = WaitForMultipleObjects(n, tab, 0, INFINITE);
+
+	r -= WAIT_OBJECT_0;
+	if(r<0 || r>=n) {
+		perror("wait failed");
+		exits("wait failed");
+	}
+
+	for(i=0; i<Nchild; i++)
+		if(child[i].handle == tab[r])
+			break;
+	if(i == Nchild){
+		snprint(msg, ERRMAX, "unknown child (%lux)", tab[r]);
+		return -1;
+	}
+
+	if(msg) {
+		*msg = 0;
+		if(GetExitCodeProcess(child[i].handle, &code) == FALSE)
+			snprint(msg, ERRMAX, "unknown exit code");
+		else if(code != 0)
+			snprint(msg, ERRMAX, "exit(%d)", code);
+	}
+
+	CloseHandle(child[i].handle);
+	child[i].handle = 0;
+	pid = child[i].pid;
+	child[i].pid = 0;
+
+	return pid;
+}
+
+void
+expunge(int pid, char *msg)
+{
+/*
+	if(strcmp(msg, "interrupt"))
+		kill(pid, SIGINT);
+	else
+		kill(pid, SIGHUP);
+*/
+}
+
+HANDLE
+duphandle(HANDLE h)
+{
+	HANDLE r;
+
+	if(DuplicateHandle(GetCurrentProcess(), h,
+			GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS,
+			1, DUPLICATE_SAME_ACCESS) == FALSE) {
+		perror("dup handle");
+		Exit();
+	}
+
+	return r;
+}
+
+void
+childadd(HANDLE h, int pid)
+{
+	int i;
+	
+	for(i=0; i<Nchild; i++) {
+		if(child[i].handle == 0) {
+			child[i].handle = h;
+			child[i].pid = pid;
+			return;
+		}
+	}
+	perror("child table full");
+	Exit();
+}
+
+static DWORD WINAPI
+spinoff(HANDLE in, HANDLE out, char *args, char *cmd, Envy *e)
+{
+	char args2[4096], path[MAX_PATH], *s, *eb;
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	Symtab *sym;
+
+
+		/* set up the full path of the shell */
+	sym = symlook("MKSH", S_VAR, 0);
+	if(sym){
+		strncpy(path, ((Word*)(sym->value))->s, sizeof(path));
+		path[MAX_PATH-1] = 0;
+	}else{
+		sym = symlook("ROOT", S_VAR, 0);
+		if(sym)
+			rootdir = ((Word*)(sym->value))->s;
+		snprint(path, sizeof(path), "%s\\%s", rootdir, shell);
+	}
+		/* convert to backslash notation */
+	for(s = strchr(path,'/'); s; s = strchr(s+1, '/'))
+			*s = '\\';
+
+	s = args2;
+	s += snprint(args2, sizeof(args2)-1, "%s", path);
+	if(shflags)
+		s += snprint(s, args2+sizeof(args2)-s-1, " %s", shflags);
+	if(args)
+		s += snprint(s, args2+sizeof(args2)-s-1, " %s", args);
+	if(cmd)
+		s += snprint(s, args2+sizeof(args2)-s-1, " \"%s\"", cmd);
+
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+	si.wShowWindow = SW_SHOW;
+
+	if (e)
+		eb = exportenv(e);
+	else
+		eb = 0;
+	si.hStdInput = duphandle(in);
+	si.hStdOutput = duphandle(out);
+	si.hStdError = duphandle(GetStdHandle(STD_ERROR_HANDLE));
+	if(CreateProcess(path, args2, 0, 0, 1, 0, eb, 0, &si, &pi) == FALSE) {
+		perror("can't find shell");
+		Exit();
+	}
+
+	free(eb);
+
+	CloseHandle(si.hStdInput);
+	CloseHandle(si.hStdOutput);
+	CloseHandle(si.hStdError);
+
+	childadd(pi.hProcess, pi.dwProcessId);
+	return pi.dwProcessId;
+}
+
+int
+execsh(char *args, char *cmd, Bufblock *buf, Envy *e)
+{
+	int tot, n, tid, pid;
+	HANDLE outin, outout, inout, inin;
+	struct { char *cmd; HANDLE handle; } *arg;
+
+	if(buf == 0)
+		outout = GetStdHandle(STD_OUTPUT_HANDLE);
+	else
+	if(CreatePipe(&outin, &outout, 0, 0) == FALSE){
+		perror("pipe");
+		Exit();
+	}
+
+	if(CreatePipe(&inin, &inout, 0, 0) == FALSE){
+		perror("pipe");
+		Exit();
+	}
+
+	arg = malloc(sizeof(*arg));
+	arg->cmd = strdup(cmd);
+	arg->handle = inout;
+	if(CreateThread(0, 0, writecmd, arg, 0, &tid) == FALSE) {
+		perror("spawn writecmd");
+		Exit();
+	}
+
+	pid = spinoff(inin, outout, args, 0, e);
+	CloseHandle(inin);
+
+	if(DEBUG(D_EXEC))
+		fprint(1, "starting: %s\n", cmd);
+
+	if(buf){
+		CloseHandle(outout);
+		tot = 0;
+		for(;;){
+			if (buf->current >= buf->end)
+				growbuf(buf);
+			if(ReadFile(outin, buf->current, buf->end-buf->current, &n, 0) == FALSE)
+				break;
+			buf->current += n;
+			tot += n;
+		}
+		if (tot && buf->current[-1] == '\n')
+			buf->current--;
+		CloseHandle(outin);
+	}
+
+	return pid;
+}
+
+static DWORD WINAPI
+writecmd(LPVOID a)
+{
+	struct {char *cmd; HANDLE handle;} *arg;
+	char *cmd, *p;
+	int n;
+
+	arg = a;
+	cmd = arg->cmd;
+	p = cmd+strlen(cmd);
+	while(cmd < p){
+		if(WriteFile(arg->handle, cmd, p-cmd, &n, 0) == FALSE)
+			break;
+		cmd += n;
+	}	
+
+	free(arg->cmd);
+	CloseHandle(arg->handle);
+	free(arg);
+	ExitThread(0);
+	return 0;
+}
+
+int
+pipecmd(char *cmd, Envy *e, int *fd)
+{
+	int pid;
+	HANDLE pipein, pipeout;
+
+	if(fd){
+		if(CreatePipe(&pipein, &pipeout, 0, 0) == FALSE){
+			perror("pipe");
+			Exit();
+		}
+	} else 
+		pipeout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+
+	pid = spinoff(GetStdHandle(STD_INPUT_HANDLE), pipeout, "-c", cmd, e);
+
+	if(fd){
+		CloseHandle(pipeout);
+		*fd = _open_osfhandle((long)pipein, 0);
+	}
+	return pid;
+}
+
+void
+Exit(void)
+{
+	while(waitfor(0) != -1)
+		;
+	exits("error");
+}
+
+void
+catchnotes()
+{
+}
+
+char*
+maketmp(void)
+{
+	static char temp[] = "mkargsXXXXXXXXXXX";
+
+	mktemp(temp);
+	return temp;
+}
+
+Dir*
+mkdirstat(char *name)
+{
+	int c, n;
+	Dir *buf;
+
+	n = strlen(name)-1;
+	c = name[n];
+	if(c == '/' || c == '\\')
+		name[n] = 0;
+	buf = dirstat(name);
+	name[n] = c;
+	return buf;
+}
+
+int
+chgtime(char *name)
+{
+	Dir *sbuf;
+	struct utimbuf u;
+
+	if((sbuf = mkdirstat(name)) != nil){
+		u.actime = sbuf->atime;
+		u.modtime = time(0);
+		free(sbuf);
+		return utime(name, &u);
+	}
+	return close(create(name, OWRITE, 0666));
+}
+
+void
+rcopy(char **to, Resub *match, int n)
+{
+	int c;
+	char *p;
+
+	*to = match->s.sp;		/* stem0 matches complete target */
+	for(to++, match++; --n > 0; to++, match++){
+		if(match->s.sp && match->e.ep){
+			p = match->e.ep;
+			c = *p;
+			*p = 0;
+			*to = strdup(match->s.sp);
+			*p = c;
+		} else
+			*to = 0;
+	}
+}
+
+ulong
+mkmtime(char *name)
+{
+	Dir *buf;
+	ulong t;
+	int n;
+	char *s;
+
+	n = strlen(name)-1;
+	if(n >= 0 && (name[n] == '/' || name[n] == '\\')){
+		s = strdup(name);
+		s[n] = 0;
+	}else
+		s = name;
+	buf = dirstat(s);
+	if(buf == nil){
+		if(s != name)
+			free(s);
+		return 0;
+	}
+	t = buf->mtime;
+	free(buf);
+	if(s != name)
+		free(s);
+	return t;
+}
+
+char *stab;
+
+char *
+membername(char *s, int fd, char *sz)
+{
+	long t;
+
+	if(s[0] == '/' && s[1] == '\0'){	/* long file name string table */
+		t = atol(sz);
+		if(t&01) t++;
+		stab = malloc(t);
+		read(fd, stab, t);
+		return nil;
+	}
+	else if(s[0] == '/' && stab != nil)		/* index into string table */
+		return stab+atol(s+1);
+	else
+		return s;
+}
--- /dev/null
+++ b/utils/mk/Plan9.c
@@ -1,0 +1,363 @@
+#include	"mk.h"
+
+char 	*shell =	"/bin/rc";
+char 	*shellname =	"rc";
+
+static	Word	*encodenulls(char*, int);
+
+void
+readenv(void)
+{
+	char *p;
+	int envf, f;
+	Dir *e;
+	char nam[1024];
+	int i, n, len;
+	Word *w;
+
+	rfork(RFENVG);	/*  use copy of the current environment variables */
+
+	envf = open("/env", OREAD);
+	if(envf < 0)
+		return;
+	while((n = dirread(envf, &e)) > 0){
+		for(i = 0; i < n; i++){
+			len = e[i].length;
+				/* don't import funny names, NULL values,
+				 * or internal mk variables
+				 */
+			if(len <= 0 || *shname(e[i].name) != '\0')
+				continue;
+			if (symlook(e[i].name, S_INTERNAL, 0))
+				continue;
+			sprint(nam, "/env/%s", e[i].name);
+			f = open(nam, OREAD);
+			if(f < 0)
+				continue;
+			p = Malloc(len+1);
+			if(read(f, p, len) != len){
+				perror(nam);
+				close(f);
+				continue;
+			}
+			close(f);
+			if (p[len-1] == 0)
+				len--;
+			else
+				p[len] = 0;
+			w = encodenulls(p, len);
+			free(p);
+			p = strdup(e[i].name);
+			setvar(p, (void *) w);
+			symlook(p, S_EXPORTED, (void*)"")->value = (void*)"";
+		}
+		free(e);
+	}
+	close(envf);
+}
+
+/* break string of values into words at 01's or nulls*/
+static Word *
+encodenulls(char *s, int n)
+{
+	Word *w, *head;
+	char *cp;
+
+	head = w = 0;
+	while (n-- > 0) {
+		for (cp = s; *cp && *cp != '\0'; cp++)
+				n--;
+		*cp = 0;
+		if (w) {
+			w->next = newword(s);
+			w = w->next;
+		} else
+			head = w = newword(s);
+		s = cp+1;
+	}
+	if (!head)
+		head = newword("");
+	return head;
+}
+
+/* as well as 01's, change blanks to nulls, so that rc will
+ * treat the words as separate arguments
+ */
+void
+exportenv(Envy *e)
+{
+	int f, n, hasvalue, first;
+	Word *w;
+	Symtab *sy;
+	char nam[256];
+
+	for(;e->name; e++){
+		sy = symlook(e->name, S_VAR, 0);
+		if (e->values == 0 || e->values->s == 0 || e->values->s[0] == 0)
+			hasvalue = 0;
+		else
+			hasvalue = 1;
+		if(sy == 0 && !hasvalue)	/* non-existant null symbol */
+			continue;
+		sprint(nam, "/env/%s", e->name);
+		if (sy != 0 && !hasvalue) {	/* Remove from environment */
+				/* we could remove it from the symbol table
+				 * too, but we're in the child copy, and it
+				 * would still remain in the parent's table.
+				 */
+			remove(nam);
+			delword(e->values);
+			e->values = 0;		/* memory leak */
+			continue;
+		}
+	
+		f = create(nam, OWRITE, 0666L);
+		if(f < 0) {
+			fprint(2, "can't create %s, f=%d\n", nam, f);
+			perror(nam);
+			continue;
+		}
+		first = 1;
+		for (w = e->values; w; w = w->next) {
+			n = strlen(w->s);
+			if (n) {
+				if(first)
+					first = 0;
+				else{
+					if (write (f, "\0", 1) != 1)
+						perror(nam);
+				}
+				if (write(f, w->s, n) != n)
+					perror(nam);
+			}
+		}
+		close(f);
+	}
+}
+
+int
+waitfor(char *msg)
+{
+	Waitmsg *w;
+	int pid;
+
+	if((w=wait()) == nil)
+		return -1;
+	strecpy(msg, msg+ERRMAX, w->msg);
+	pid = w->pid;
+	free(w);
+	return pid;
+}
+
+void
+expunge(int pid, char *msg)
+{
+	postnote(PNPROC, pid, msg);
+}
+
+int
+execsh(char *args, char *cmd, Bufblock *buf, Envy *e)
+{
+	char *p;
+	int tot, n, pid, in[2], out[2];
+
+	if(buf && pipe(out) < 0){
+		perror("pipe");
+		Exit();
+	}
+	pid = rfork(RFPROC|RFFDG|RFENVG);
+	if(pid < 0){
+		perror("mk rfork");
+		Exit();
+	}
+	if(pid == 0){
+		if(buf)
+			close(out[0]);
+		if(pipe(in) < 0){
+			perror("pipe");
+			Exit();
+		}
+		pid = fork();
+		if(pid < 0){
+			perror("mk fork");
+			Exit();
+		}
+		if(pid != 0){
+			dup(in[0], 0);
+			if(buf){
+				dup(out[1], 1);
+				close(out[1]);
+			}
+			close(in[0]);
+			close(in[1]);
+			if (e)
+				exportenv(e);
+			if(shflags)
+				execl(shell, shellname, shflags, args, nil);
+			else
+				execl(shell, shellname, args, nil);
+			perror(shell);
+			_exits("exec");
+		}
+		close(out[1]);
+		close(in[0]);
+		p = cmd+strlen(cmd);
+		while(cmd < p){
+			n = write(in[1], cmd, p-cmd);
+			if(n < 0)
+				break;
+			cmd += n;
+		}
+		close(in[1]);
+		_exits(0);
+	}
+	if(buf){
+		close(out[1]);
+		tot = 0;
+		for(;;){
+			if (buf->current >= buf->end)
+				growbuf(buf);
+			n = read(out[0], buf->current, buf->end-buf->current);
+			if(n <= 0)
+				break;
+			buf->current += n;
+			tot += n;
+		}
+		if (tot && buf->current[-1] == '\n')
+			buf->current--;
+		close(out[0]);
+	}
+	return pid;
+}
+
+int
+pipecmd(char *cmd, Envy *e, int *fd)
+{
+	int pid, pfd[2];
+
+	if(DEBUG(D_EXEC))
+		fprint(1, "pipecmd='%s'\n", cmd);/**/
+
+	if(fd && pipe(pfd) < 0){
+		perror("pipe");
+		Exit();
+	}
+	pid = rfork(RFPROC|RFFDG|RFENVG);
+	if(pid < 0){
+		perror("mk fork");
+		Exit();
+	}
+	if(pid == 0){
+		if(fd){
+			close(pfd[0]);
+			dup(pfd[1], 1);
+			close(pfd[1]);
+		}
+		if(e)
+			exportenv(e);
+		if(shflags)
+			execl(shell, shellname, shflags, "-c", cmd, nil);
+		else
+			execl(shell, shellname, "-c", cmd, nil);
+		perror(shell);
+		_exits("exec");
+	}
+	if(fd){
+		close(pfd[1]);
+		*fd = pfd[0];
+	}
+	return pid;
+}
+
+void
+Exit(void)
+{
+	while(waitpid() >= 0)
+		;
+	exits("error");
+}
+
+int
+notifyf(void *a, char *msg)
+{
+	static int nnote;
+
+	USED(a);
+	if(++nnote > 100){	/* until andrew fixes his program */
+		fprint(2, "mk: too many notes\n");
+		notify(0);
+		abort();
+	}
+	if(strcmp(msg, "interrupt")!=0 && strcmp(msg, "hangup")!=0)
+		return 0;
+	killchildren(msg);
+	return -1;
+}
+
+void
+catchnotes()
+{
+	atnotify(notifyf, 1);
+}
+
+char*
+maketmp(void)
+{
+	static char temp[] = "/tmp/mkargXXXXXXXXXXX";
+
+	mktemp(temp);
+	return temp;
+}
+
+int
+chgtime(char *name)
+{
+	Dir sbuf;
+
+	if(access(name, AEXIST) >= 0) {
+		nulldir(&sbuf);
+		sbuf.mtime = time(0);
+		return dirwstat(name, &sbuf);
+	}
+	return close(create(name, OWRITE, 0666));
+}
+
+void
+rcopy(char **to, Resub *match, int n)
+{
+	int c;
+	char *p;
+
+	*to = match->s.sp;		/* stem0 matches complete target */
+	for(to++, match++; --n > 0; to++, match++){
+		if(match->s.sp && match->e.ep){
+			p = match->e.ep;
+			c = *p;
+			*p = 0;
+			*to = strdup(match->s.sp);
+			*p = c;
+		}
+		else
+			*to = 0;
+	}
+}
+
+ulong
+mkmtime(char *name)
+{
+	Dir *buf;
+	ulong t;
+
+	buf = dirstat(name);
+	if(buf == nil)
+		return 0;
+	t = buf->mtime;
+	free(buf);
+	return t;
+}
+
+char *
+membername(char *s, int, char*)
+{
+	return s;
+}
--- /dev/null
+++ b/utils/mk/Posix.c
@@ -1,0 +1,332 @@
+#include	"mk.h"
+#include	<dirent.h>
+#include	<signal.h>
+#include	<sys/wait.h>
+#include	<utime.h>
+#include	<stdio.h>
+
+char 	*shell =	"/bin/sh";
+char 	*shellname =	"sh";
+
+extern char **environ;
+
+void
+readenv(void)
+{
+	char **p, *s;
+	Word *w;
+
+	for(p = environ; *p; p++){
+		s = shname(*p);
+		if(*s == '=') {
+			*s = 0;
+			w = newword(s+1);
+		} else
+			w = newword("");
+		if (symlook(*p, S_INTERNAL, 0))
+			continue;
+		s = strdup(*p);
+		setvar(s, (void *)w);
+		symlook(s, S_EXPORTED, (void*)"")->value = (void*)"";
+	}
+}
+
+/*
+ *	done on child side of fork, so parent's env is not affected
+ *	and we don't care about freeing memory because we're going
+ *	to exec immediately after this.
+ */
+void
+exportenv(Envy *e)
+{
+	int i;
+	char **p;
+	char *values;
+
+	p = 0;
+	for(i = 0; e->name; e++, i++) {
+		p = (char**) Realloc(p, (i+2)*sizeof(char*));
+		if (e->values)
+			values = wtos(e->values, IWS);
+		else
+			values = "";
+		p[i] = malloc(strlen(e->name) + strlen(values) + 2);
+		sprint(p[i], "%s=%s", e->name,  values);
+	}
+	p[i] = 0;
+	environ = p;
+}
+
+int
+waitfor(char *msg)
+{
+	int status;
+	int pid;
+
+	*msg = 0;
+	pid = wait(&status);
+	if(pid > 0) {
+		if(status&0x7f) {
+			if(status&0x80)
+				snprint(msg, ERRMAX, "signal %d, core dumped", status&0x7f);
+			else
+				snprint(msg, ERRMAX, "signal %d", status&0x7f);
+		} else if(status&0xff00)
+			snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff);
+	}
+	return pid;
+}
+
+void
+expunge(int pid, char *msg)
+{
+	if(strcmp(msg, "interrupt"))
+		kill(pid, SIGINT);
+	else
+		kill(pid, SIGHUP);
+}
+
+int
+execsh(char *args, char *cmd, Bufblock *buf, Envy *e)
+{
+	char *p;
+	int tot, n, pid, in[2], out[2];
+
+	if(buf && pipe(out) < 0){
+		perror("pipe");
+		Exit();
+	}
+	pid = fork();
+	if(pid < 0){
+		perror("mk fork");
+		Exit();
+	}
+	if(pid == 0){
+		if(buf)
+			close(out[0]);
+		if(pipe(in) < 0){
+			perror("pipe");
+			Exit();
+		}
+		pid = fork();
+		if(pid < 0){
+			perror("mk fork");
+			Exit();
+		}
+		if(pid != 0){
+			dup2(in[0], 0);
+			if(buf){
+				dup2(out[1], 1);
+				close(out[1]);
+			}
+			close(in[0]);
+			close(in[1]);
+			if (e)
+				exportenv(e);
+			if(shflags)
+				execl(shell, shellname, shflags, args, nil);
+			else
+				execl(shell, shellname, args, nil);
+			perror(shell);
+			_exits("exec");
+		}
+		close(out[1]);
+		close(in[0]);
+		if(DEBUG(D_EXEC))
+			fprint(1, "starting: %s\n", cmd);
+		p = cmd+strlen(cmd);
+		while(cmd < p){
+			n = write(in[1], cmd, p-cmd);
+			if(n < 0)
+				break;
+			cmd += n;
+		}
+		close(in[1]);
+		_exits(0);
+	}
+	if(buf){
+		close(out[1]);
+		tot = 0;
+		for(;;){
+			if (buf->current >= buf->end)
+				growbuf(buf);
+			n = read(out[0], buf->current, buf->end-buf->current);
+			if(n <= 0)
+				break;
+			buf->current += n;
+			tot += n;
+		}
+		if (tot && buf->current[-1] == '\n')
+			buf->current--;
+		close(out[0]);
+	}
+	return pid;
+}
+
+int
+pipecmd(char *cmd, Envy *e, int *fd)
+{
+	int pid, pfd[2];
+
+	if(DEBUG(D_EXEC))
+		fprint(1, "pipecmd='%s'\n", cmd);/**/
+
+	if(fd && pipe(pfd) < 0){
+		perror("pipe");
+		Exit();
+	}
+	pid = fork();
+	if(pid < 0){
+		perror("mk fork");
+		Exit();
+	}
+	if(pid == 0){
+		if(fd){
+			close(pfd[0]);
+			dup2(pfd[1], 1);
+			close(pfd[1]);
+		}
+		if(e)
+			exportenv(e);
+		if(shflags)
+			execl(shell, shellname, shflags, "-c", cmd, nil);
+		else
+			execl(shell, shellname, "-c", cmd, nil);
+		perror(shell);
+		_exits("exec");
+	}
+	if(fd){
+		close(pfd[1]);
+		*fd = pfd[0];
+	}
+	return pid;
+}
+
+void
+Exit(void)
+{
+	while(wait(0) >= 0)
+		;
+	exits("error");
+}
+
+static	struct
+{
+	int	sig;
+	char	*msg;
+}	sigmsgs[] =
+{
+	SIGALRM,	"alarm",
+	SIGFPE,		"sys: fp: fptrap",
+	SIGPIPE,	"sys: write on closed pipe",
+	SIGILL,		"sys: trap: illegal instruction",
+	SIGSEGV,	"sys: segmentation violation",
+	0,		0
+};
+
+static void
+notifyf(int sig)
+{
+	int i;
+
+	for(i = 0; sigmsgs[i].msg; i++)
+		if(sigmsgs[i].sig == sig)
+			killchildren(sigmsgs[i].msg);
+
+	/* should never happen */
+	signal(sig, SIG_DFL);
+	kill(getpid(), sig);
+}
+
+void
+catchnotes()
+{
+	int i;
+
+	for(i = 0; sigmsgs[i].msg; i++)
+		signal(sigmsgs[i].sig, notifyf);
+}
+
+char*
+maketmp(void)
+{
+	static char temp[L_tmpnam];
+
+	return tmpnam(temp);
+}
+
+int
+chgtime(char *name)
+{
+	Dir *sbuf;
+	struct utimbuf u;
+
+	if((sbuf = dirstat(name)) != nil) {
+		u.actime = sbuf->atime;
+		free(sbuf);
+		u.modtime = time(0);
+		return utime(name, &u);
+	}
+	return close(create(name, OWRITE, 0666));
+}
+
+void
+rcopy(char **to, Resub *match, int n)
+{
+	int c;
+	char *p;
+
+	*to = match->s.sp;		/* stem0 matches complete target */
+	for(to++, match++; --n > 0; to++, match++){
+		if(match->s.sp && match->e.ep){
+			p = match->e.ep;
+			c = *p;
+			*p = 0;
+			*to = strdup(match->s.sp);
+			*p = c;
+		}
+		else
+			*to = 0;
+	}
+}
+
+ulong
+mkmtime(char *name)
+{
+	Dir *buf;
+	ulong t;
+
+	buf = dirstat(name);
+	if(buf == nil)
+		return 0;
+	t = buf->mtime;
+	free(buf);
+	return t;
+}
+
+
+char *stab;
+
+char *
+membername(char *s, int fd, char *sz)
+{
+	long t;
+	char *p, *q;
+
+	if(s[0] == '/' && s[1] == '\0'){	/* long file name string table */
+		t = atol(sz);
+		if(t&01) t++;
+		stab = malloc(t);
+		if(read(fd, stab, t) != t)
+			{}
+		return nil;
+	}
+	else if(s[0] == '/' && stab != nil)	{	/* index into string table */
+		p = stab+atol(s+1);
+		q = strchr(p, '/');
+		if (q)
+			*q = 0;				/* terminate string here */
+		return p;
+	}else
+		return s;
+}
--- /dev/null
+++ b/utils/mk/README
@@ -1,0 +1,26 @@
+Using the delivered mk to rebuild mk.
+
+You should be able to use the delivered executable of mk to
+build a new executable.  This may be of particular interest
+on Windows NT/Win95 where the path of the shell program
+can be hard-coded by changing the variable named "shell"
+near the beginning of source file Nt.c.
+
+Mk uses the regular expression library, so build
+the program as follows:
+
+1.  ensure that ../../mkconfig contains the proper system definitions
+
+2.  ensure that the system libraries lib9, libbio, and libregexp have
+	been built.  you can do this by hand by changing to ../lib9,
+	../libbio, and ../libregexp and doing "mk nuke" and a "mk install"
+	in each.
+
+3. in this directory
+	mk nuke
+	mk install
+
+4. on NT/Win95 the executable must be installed by hand because the current
+	executable is locked down while it is executing:
+
+	mv obj.out ../../Nt/386/bin/mk.exe
--- /dev/null
+++ b/utils/mk/arc.c
@@ -1,0 +1,52 @@
+#include	"mk.h"
+
+Arc *
+newarc(Node *n, Rule *r, char *stem, Resub *match)
+{
+	Arc *a;
+
+	a = (Arc *)Malloc(sizeof(Arc));
+	a->n = n;
+	a->r = r;
+	a->stem = strdup(stem);
+	rcopy(a->match, match, NREGEXP);
+	a->next = 0;
+	a->flag = 0;
+	a->prog = r->prog;
+	return(a);
+}
+
+void
+dumpa(char *s, Arc *a)
+{
+	char buf[1024];
+
+	Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'",
+		s, a, a->n, a->r, a->flag, a->stem);
+	if(a->prog)
+		Bprint(&bout, " prog='%s'", a->prog);
+	Bprint(&bout, "\n");
+
+	if(a->n){
+		snprint(buf, sizeof(buf), "%s    ", (*s == ' ')? s:"");
+		dumpn(buf, a->n);
+	}
+}
+
+void
+nrep(void)
+{
+	Symtab *sym;
+	Word *w;
+
+	sym = symlook("NREP", S_VAR, 0);
+	if(sym){
+		w = (Word *) sym->value;
+		if (w && w->s && *w->s)
+			nreps = atoi(w->s);
+	}
+	if(nreps < 1)
+		nreps = 1;
+	if(DEBUG(D_GRAPH))
+		Bprint(&bout, "nreps = %d\n", nreps);
+}
--- /dev/null
+++ b/utils/mk/archive.c
@@ -1,0 +1,169 @@
+#include	"mk.h"
+#include	<ar.h>
+
+static int dolong;
+
+static void atimes(char *);
+static char *split(char*, char**);
+
+long
+atimeof(int force, char *name)
+{
+	Symtab *sym;
+	long t;
+	char *archive, *member, buf[512];
+
+	archive = split(name, &member);
+	if(archive == 0)
+		Exit();
+
+	t = mtime(archive);
+	sym = symlook(archive, S_AGG, 0);
+	if(sym){
+		if(force || (t > (long)sym->value)){
+			atimes(archive);
+			sym->value = (void *)t;
+		}
+	}
+	else{
+		atimes(archive);
+		/* mark the aggegate as having been done */
+		symlook(strdup(archive), S_AGG, "")->value = (void *)t;
+	}
+		/* truncate long member name to sizeof of name field in archive header */
+	if(dolong)
+		snprint(buf, sizeof(buf), "%s(%s)", archive, member);
+	else
+		snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member);
+	sym = symlook(buf, S_TIME, 0);
+	if (sym)
+		return (long)sym->value;	/* uggh */
+	return 0;
+}
+
+void
+atouch(char *name)
+{
+	char *archive, *member;
+	int fd, i;
+	struct ar_hdr h;
+	long t;
+
+	archive = split(name, &member);
+	if(archive == 0)
+		Exit();
+
+	fd = open(archive, ORDWR);
+	if(fd < 0){
+		fd = create(archive, OWRITE, 0666);
+		if(fd < 0){
+			perror(archive);
+			Exit();
+		}
+		write(fd, ARMAG, SARMAG);
+	}
+	if(symlook(name, S_TIME, 0)){
+		/* hoon off and change it in situ */
+		LSEEK(fd, SARMAG, 0);
+		while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){
+			for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--)
+					;
+			h.name[i+1]=0;
+			if(strcmp(member, h.name) == 0){
+				t = SARNAME-sizeof(h);	/* ughgghh */
+				LSEEK(fd, t, 1);
+				fprint(fd, "%-12ld", time(0));
+				break;
+			}
+			t = atol(h.size);
+			if(t&01) t++;
+			LSEEK(fd, t, 1);
+		}
+	}
+	close(fd);
+}
+
+static void
+atimes(char *ar)
+{
+	struct ar_hdr h;
+	long t;
+	int fd, i;
+	char buf[BIGBLOCK];
+	char *n, name[sizeof(h.name)+1];
+
+	fd = open(ar, OREAD);
+	if(fd < 0)
+		return;
+
+	if(read(fd, buf, SARMAG) != SARMAG){
+		close(fd);
+		return;
+	}
+	while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){
+		t = atol(h.date);
+		if(t == 0)	/* as it sometimes happens; thanks ken */
+			t = 1;
+		strncpy(name, h.name, sizeof(h.name));
+		for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--)
+				;
+		if(name[i] == '/')		/* system V bug */
+			i--;
+		name[i+1]=0;
+		n = membername(name, fd, h.size);
+		if(n == nil){
+			dolong = 1;
+			continue;
+		}
+		sprint(buf, "%s(%s)", ar, n);
+		symlook(strdup(buf), S_TIME, (void *)t)->value = (void *)t;
+		t = atol(h.size);
+		if(t&01) t++;
+		LSEEK(fd, t, 1);
+	}
+	close(fd);
+}
+
+static int
+type(char *file)
+{
+	int fd;
+	char buf[SARMAG];
+
+	fd = open(file, OREAD);
+	if(fd < 0){
+		if(symlook(file, S_BITCH, 0) == 0){
+			Bprint(&bout, "%s doesn't exist: assuming it will be an archive\n", file);
+			symlook(file, S_BITCH, (void *)file);
+		}
+		return 1;
+	}
+	if(read(fd, buf, SARMAG) != SARMAG){
+		close(fd);
+		return 0;
+	}
+	close(fd);
+	return !strncmp(ARMAG, buf, SARMAG);
+}
+
+static char*
+split(char *name, char **member)
+{
+	char *p, *q;
+
+	p = strdup(name);
+	q = utfrune(p, '(');
+	if(q){
+		*q++ = 0;
+		if(member)
+			*member = q;
+		q = utfrune(q, ')');
+		if (q)
+			*q = 0;
+		if(type(p))
+			return p;
+		free(p);
+		fprint(2, "mk: '%s' is not an archive\n", name);
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/mk/bufblock.c
@@ -1,0 +1,88 @@
+#include	"mk.h"
+
+static Bufblock *freelist;
+#define	QUANTA	4096
+
+Bufblock *
+newbuf(void)
+{
+	Bufblock *p;
+
+	if (freelist) {
+		p = freelist;
+		freelist = freelist->next;
+	} else {
+		p = (Bufblock *) Malloc(sizeof(Bufblock));
+		p->start = Malloc(QUANTA*sizeof(*p->start));
+		p->end = p->start+QUANTA;
+	}
+	p->current = p->start;
+	*p->start = 0;
+	p->next = 0;
+	return p;
+}
+
+void
+freebuf(Bufblock *p)
+{
+	p->next = freelist;
+	freelist = p;
+}
+
+void
+growbuf(Bufblock *p)
+{
+	int n;
+	Bufblock *f;
+	char *cp;
+
+	n = p->end-p->start+QUANTA;
+		/* search the free list for a big buffer */
+	for (f = freelist; f; f = f->next) {
+		if (f->end-f->start >= n) {
+			memcpy(f->start, p->start, p->end-p->start);
+			cp = f->start;
+			f->start = p->start;
+			p->start = cp;
+			cp = f->end;
+			f->end = p->end;
+			p->end = cp;
+			f->current = f->start;
+			break;
+		}
+	}
+	if (!f) {		/* not found - grow it */
+		p->start = Realloc(p->start, n);
+		p->end = p->start+n;
+	}
+	p->current = p->start+n-QUANTA;
+}
+
+void
+bufcpy(Bufblock *buf, char *cp, int n)
+{
+
+	while (n--)
+		insert(buf, *cp++);
+}
+
+void
+insert(Bufblock *buf, int c)
+{
+
+	if (buf->current >= buf->end)
+		growbuf(buf);
+	*buf->current++ = c;
+}
+
+void
+rinsert(Bufblock *buf, Rune r)
+{
+	int n;
+
+	n = runelen(r);
+	if (buf->current+n > buf->end)
+		growbuf(buf);
+	runetochar(buf->current, &r);
+	buf->current += n;
+}
--- /dev/null
+++ b/utils/mk/env.c
@@ -1,0 +1,149 @@
+#include	"mk.h"
+
+enum {
+	ENVQUANTA=10
+};
+
+Envy	*envy;
+static int nextv;
+
+static char	*myenv[] =
+{
+	"target",
+	"stem",
+	"prereq",
+	"pid",
+	"nproc",
+	"newprereq",
+	"alltarget",
+	"newmember",
+	"stem0",		/* must be in order from here */
+	"stem1",
+	"stem2",
+	"stem3",
+	"stem4",
+	"stem5",
+	"stem6",
+	"stem7",
+	"stem8",
+	"stem9",
+	0,
+};
+
+void
+initenv(void)
+{
+	char **p;
+
+	for(p = myenv; *p; p++)
+		symlook(*p, S_INTERNAL, (void *)"");
+	readenv();				/* o.s. dependent */
+}
+
+static void
+envinsert(char *name, Word *value)
+{
+	static int envsize;
+
+	if (nextv >= envsize) {
+		envsize += ENVQUANTA;
+		envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy));
+	}
+	envy[nextv].name = name;
+	envy[nextv++].values = value;
+}
+
+static void
+envupd(char *name, Word *value)
+{
+	Envy *e;
+
+	for(e = envy; e->name; e++)
+		if(strcmp(name, e->name) == 0){
+			delword(e->values);
+			e->values = value;
+			return;
+		}
+	e->name = name;
+	e->values = value;
+	envinsert(0,0);
+}
+
+static void
+ecopy(Symtab *s)
+{
+	char **p;
+
+	if(symlook(s->name, S_NOEXPORT, 0))
+		return;
+	for(p = myenv; *p; p++)
+		if(strcmp(*p, s->name) == 0)
+			return;
+	envinsert(s->name, (Word *) s->value);
+}
+
+void
+execinit(void)
+{
+	char **p;
+
+	nextv = 0;
+	for(p = myenv; *p; p++)
+		envinsert(*p, stow(""));
+
+	symtraverse(S_VAR, ecopy);
+	envinsert(0, 0);
+}
+
+Envy*
+buildenv(Job *j, int slot)
+{
+	char **p, *cp, *qp;
+	Word *w, *v, **l;
+	int i;
+	char buf[256];
+
+	envupd("target", wdup(j->t));
+	if(j->r->attr&REGEXP)
+		envupd("stem",newword(""));
+	else
+		envupd("stem", newword(j->stem));
+	envupd("prereq", wdup(j->p));
+	sprint(buf, "%d", getpid());
+	envupd("pid", newword(buf));
+	sprint(buf, "%d", slot);
+	envupd("nproc", newword(buf));
+	envupd("newprereq", wdup(j->np));
+	envupd("alltarget", wdup(j->at));
+	l = &v;
+	v = w = wdup(j->np);
+	while(w){
+		cp = strchr(w->s, '(');
+		if(cp){
+			qp = strchr(cp+1, ')');
+			if(qp){
+				*qp = 0;
+				strcpy(w->s, cp+1);
+				l = &w->next;
+				w = w->next;
+				continue;
+			}
+		}
+		*l = w->next;
+		free(w->s);
+		free(w);
+		w = *l;
+	}
+	envupd("newmember", v);
+		/* update stem0 -> stem9 */
+	for(p = myenv; *p; p++)
+		if(strcmp(*p, "stem0") == 0)
+			break;
+	for(i = 0; *p; i++, p++){
+		if((j->r->attr&REGEXP) && j->match[i])
+			envupd(*p, newword(j->match[i]));
+		else 
+			envupd(*p, newword(""));
+	}
+	return envy;
+}
--- /dev/null
+++ b/utils/mk/file.c
@@ -1,0 +1,90 @@
+#include	"mk.h"
+
+/* table-driven version in bootes dump of 12/31/96 */
+
+long
+mtime(char *name)
+{
+	return mkmtime(name);
+}
+
+long
+timeof(char *name, int force)
+{
+	Symtab *sym;
+	long t;
+
+	if(utfrune(name, '('))
+		return atimeof(force, name);	/* archive */
+
+	if(force)
+		return mtime(name);
+
+
+	sym = symlook(name, S_TIME, 0);
+	if (sym)
+		return (long) sym->value;		/* uggh */
+
+	t = mtime(name);
+	if(t == 0)
+		return 0;
+
+	symlook(name, S_TIME, (void*)t);		/* install time in cache */
+	return t;
+}
+
+void
+touch(char *name)
+{
+	Bprint(&bout, "touch(%s)\n", name);
+	if(nflag)
+		return;
+
+	if(utfrune(name, '('))
+		atouch(name);		/* archive */
+	else if(chgtime(name) < 0) {
+		perror(name);
+		Exit();
+	}
+}
+
+void
+delete(char *name)
+{
+	if(utfrune(name, '(') == 0) {		/* file */
+		if(remove(name) < 0)
+			perror(name);
+	} else
+		fprint(2, "hoon off; mk can'tdelete archive members\n");
+}
+
+void
+timeinit(char *s)
+{
+	long t;
+	char *cp;
+	Rune r;
+	int c, n;
+
+	t = time(0);
+	while (*s) {
+		cp = s;
+		do{
+			n = chartorune(&r, s);
+			if (r == ' ' || r == ',' || r == '\n')
+				break;
+			s += n;
+		} while(*s);
+		c = *s;
+		*s = 0;
+		symlook(strdup(cp), S_TIME, (void *)t)->value = (void *)t;
+		if (c)
+			*s++ = c;
+		while(*s){
+			n = chartorune(&r, s);
+			if(r != ' ' && r != ',' && r != '\n')
+				break;
+			s += n;
+		}
+	}
+}
--- /dev/null
+++ b/utils/mk/fns.h
@@ -1,0 +1,84 @@
+void	addrule(char*, Word*, char*, Word*, int, int, char*);
+void	addrules(Word*, Word*, char*, int, int, char*);
+void	addw(Word*, char*);
+void	assert(char*, int);
+int	assline(Biobuf *, Bufblock *);
+long	atimeof(int,char*);
+void	atouch(char*);
+void	bufcpy(Bufblock *, char *, int);
+Envy	*buildenv(Job*, int);
+void	catchnotes(void);
+char 	*charin(char *, char *);
+int	chgtime(char*);
+void	clrmade(Node*);
+char	*copyq(char*, Rune, Bufblock*);
+void	delete(char*);
+void	delword(Word*);
+int	dorecipe(Node*);
+void	dumpa(char*, Arc*);
+void	dumpj(char*, Job*, int);
+void	dumpn(char*, Node*);
+void	dumpr(char*, Rule*);
+void	dumpv(char*);
+void	dumpw(char*, Word*);
+int	escapetoken(Biobuf*, Bufblock*, int, int);
+void	execinit(void);
+int	execsh(char*, char*, Bufblock*, Envy*);
+void	Exit(void);
+char	*expandquote(char*, Rune, Bufblock*);
+void	expunge(int, char*);
+void	freebuf(Bufblock*);
+void	front(char*);
+Node	*graph(char*);
+void	growbuf(Bufblock *);
+void	initenv(void);
+void	insert(Bufblock *, int);
+void	ipop(void);
+void	ipush(void);
+void	killchildren(char*);
+void	*Malloc(int);
+char	*maketmp(void);
+int	match(char*, char*, char*);
+char *membername(char*, int, char*);
+void	mk(char*);
+ulong	mkmtime(char*);
+long	mtime(char*);
+Arc	*newarc(Node*, Rule*, char*, Resub*);
+Bufblock *newbuf(void);
+Job	*newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*);
+Word	*newword(char*);
+int	nextrune(Biobuf*, int);
+int	nextslot(void);
+void	nproc(void);
+void	nrep(void);
+int	outofdate(Node*, Arc*, int);
+void	parse(char*, int, int);
+int	pipecmd(char*, Envy*, int*);
+void	prusage(void);
+void	rcopy(char**, Resub*, int);
+void	readenv(void);
+void	*Realloc(void*, int);
+void	rinsert(Bufblock *, Rune);
+char	*rulecnt(void);
+void	run(Job*);
+void	setvar(char*, void*);
+char	*shname(char*);
+void	shprint(char*, Envy*, Bufblock*);
+Word	*stow(char*);
+void	subst(char*, char*, char*);
+void	symdel(char*, int);
+void	syminit(void);
+Symtab	*symlook(char*, int, void*);
+void	symstat(void);
+void	symtraverse(int, void(*)(Symtab*));
+void	timeinit(char*);
+long	timeof(char*, int);
+void	touch(char*);
+void	update(int, Node*);
+void	usage(void);
+Word	*varsub(char**);
+int	waitfor(char*);
+int	waitup(int, int*);
+Word	*wdup(Word*);
+int	work(Node*, Node*, Arc*);
+char	*wtos(Word*, int);
--- /dev/null
+++ b/utils/mk/graph.c
@@ -1,0 +1,279 @@
+#include	"mk.h"
+
+static Node *applyrules(char *, char *);
+static void togo(Node *);
+static int vacuous(Node *);
+static Node *newnode(char *);
+static void trace(char *, Arc *);
+static void cyclechk(Node *);
+static void ambiguous(Node *);
+static void attribute(Node *);
+
+Node *
+graph(char *target)
+{
+	Node *node;
+	char *cnt;
+
+	cnt = rulecnt();
+	node = applyrules(target, cnt);
+	free(cnt);
+	cyclechk(node);
+	node->flags |= PROBABLE;	/* make sure it doesn't get deleted */
+	vacuous(node);
+	ambiguous(node);
+	attribute(node);
+	return(node);
+}
+
+static Node *
+applyrules(char *target, char *cnt)
+{
+	Symtab *sym;
+	Node *node;
+	Rule *r;
+	Arc head, *a = &head;
+	Word *w;
+	char stem[NAMEBLOCK], buf[NAMEBLOCK];
+	Resub rmatch[NREGEXP];
+
+/*	print("applyrules(%lux='%s')\n", target, target);/**/
+	sym = symlook(target, S_NODE, 0);
+	if(sym)
+		return (Node *)(sym->value);
+	target = strdup(target);
+	node = newnode(target);
+	head.n = 0;
+	head.next = 0;
+	sym = symlook(target, S_TARGET, 0);
+	memset((char*)rmatch, 0, sizeof(rmatch));
+	for(r = sym? (Rule *)(sym->value):0; r; r = r->chain){
+		if(r->attr&META) continue;
+		if(strcmp(target, r->target)) continue;
+		if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue;	/* no effect; ignore */
+		if(cnt[r->rule] >= nreps) continue;
+		cnt[r->rule]++;
+		node->flags |= PROBABLE;
+
+/*		if(r->attr&VIR)
+ *			node->flags |= VIRTUAL;
+ *		if(r->attr&NOREC)
+ *			node->flags |= NORECIPE;
+ *		if(r->attr&DEL)
+ *			node->flags |= DELETE;
+ */
+		if(!r->tail || !r->tail->s || !*r->tail->s) {
+			a->next = newarc((Node *)0, r, "", rmatch);
+			a = a->next;
+		} else
+			for(w = r->tail; w; w = w->next){
+				a->next = newarc(applyrules(w->s, cnt), r, "", rmatch);
+				a = a->next;
+		}
+		cnt[r->rule]--;
+		head.n = node;
+	}
+	for(r = metarules; r; r = r->next){
+		if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue;	/* no effect; ignore */
+		if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR))
+			continue;
+		if(r->attr&REGEXP){
+			stem[0] = 0;
+			patrule = r;
+			memset((char*)rmatch, 0, sizeof(rmatch));
+			if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
+				continue;
+		} else {
+			if(!match(node->name, r->target, stem)) continue;
+		}
+		if(cnt[r->rule] >= nreps) continue;
+		cnt[r->rule]++;
+
+/*		if(r->attr&VIR)
+ *			node->flags |= VIRTUAL;
+ *		if(r->attr&NOREC)
+ *			node->flags |= NORECIPE;
+ *		if(r->attr&DEL)
+ *			node->flags |= DELETE;
+ */
+
+		if(!r->tail || !r->tail->s || !*r->tail->s) {
+			a->next = newarc((Node *)0, r, stem, rmatch);
+			a = a->next;
+		} else
+			for(w = r->tail; w; w = w->next){
+				if(r->attr&REGEXP)
+					regsub(w->s, buf, rmatch, NREGEXP);
+				else
+					subst(stem, w->s, buf);
+				a->next = newarc(applyrules(buf, cnt), r, stem, rmatch);
+				a = a->next;
+			}
+		cnt[r->rule]--;
+	}
+	a->next = node->prereqs;
+	node->prereqs = head.next;
+	return(node);
+}
+
+static void
+togo(Node *node)
+{
+	Arc *la, *a;
+
+	/* delete them now */
+	la = 0;
+	for(a = node->prereqs; a; la = a, a = a->next)
+		if(a->flag&TOGO){
+			if(a == node->prereqs)
+				node->prereqs = a->next;
+			else
+				la->next = a->next, a = la;
+		}
+}
+
+static int
+vacuous(Node *node)
+{
+	Arc *la, *a;
+	int vac = !(node->flags&PROBABLE);
+
+	if(node->flags&READY)
+		return(node->flags&VACUOUS);
+	node->flags |= READY;
+	for(a = node->prereqs; a; a = a->next)
+		if(a->n && vacuous(a->n) && (a->r->attr&META))
+			a->flag |= TOGO;
+		else
+			vac = 0;
+	/* if a rule generated arcs that DON'T go; no others from that rule go */
+	for(a = node->prereqs; a; a = a->next)
+		if((a->flag&TOGO) == 0)
+			for(la = node->prereqs; la; la = la->next)
+				if((la->flag&TOGO) && (la->r == a->r)){
+					la->flag &= ~TOGO;
+				}
+	togo(node);
+	if(vac)
+		node->flags |= VACUOUS;
+	return(vac);
+}
+
+static Node *
+newnode(char *name)
+{
+	register Node *node;
+
+	node = (Node *)Malloc(sizeof(Node));
+	symlook(name, S_NODE, (void *)node);
+	node->name = name;
+	node->time = timeof(name, 0);
+	node->prereqs = 0;
+	node->flags = node->time? PROBABLE : 0;
+	node->next = 0;
+	return(node);
+}
+
+void
+dumpn(char *s, Node *n)
+{
+	char buf[1024];
+	Arc *a;
+
+	sprint(buf, "%s   ", (*s == ' ')? s:"");
+	Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n",
+		s, n->name, n, n->time, n->flags, n->next);
+	for(a = n->prereqs; a; a = a->next)
+		dumpa(buf, a);
+}
+
+static void
+trace(char *s, Arc *a)
+{
+	fprint(2, "\t%s", s);
+	while(a){
+		fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line,
+			a->n? a->n->name:"");
+		if(a->n){
+			for(a = a->n->prereqs; a; a = a->next)
+				if(*a->r->recipe) break;
+		} else
+			a = 0;
+	}
+	fprint(2, "\n");
+}
+
+static void
+cyclechk(Node *n)
+{
+	Arc *a;
+
+	if((n->flags&CYCLE) && n->prereqs){
+		fprint(2, "mk: cycle in graph detected at target %s\n", n->name);
+		Exit();
+	}
+	n->flags |= CYCLE;
+	for(a = n->prereqs; a; a = a->next)
+		if(a->n)
+			cyclechk(a->n);
+	n->flags &= ~CYCLE;
+}
+
+static void
+ambiguous(Node *n)
+{
+	Arc *a;
+	Rule *r = 0;
+	Arc *la;
+	int bad = 0;
+
+	la = 0;
+	for(a = n->prereqs; a; a = a->next){
+		if(a->n)
+			ambiguous(a->n);
+		if(*a->r->recipe == 0) continue;
+		if(r == 0)
+			r = a->r, la = a;
+		else{
+			if(r->recipe != a->r->recipe){
+				if((r->attr&META) && !(a->r->attr&META)){
+					la->flag |= TOGO;
+					r = a->r, la = a;
+				} else if(!(r->attr&META) && (a->r->attr&META)){
+					a->flag |= TOGO;
+					continue;
+				}
+			}
+			if(r->recipe != a->r->recipe){
+				if(bad == 0){
+					fprint(2, "mk: ambiguous recipes for %s:\n", n->name);
+					bad = 1;
+					trace(n->name, la);
+				}
+				trace(n->name, a);
+			}
+		}
+	}
+	if(bad)
+		Exit();
+	togo(n);
+}
+
+static void
+attribute(Node *n)
+{
+	register Arc *a;
+
+	for(a = n->prereqs; a; a = a->next){
+		if(a->r->attr&VIR)
+			n->flags |= VIRTUAL;
+		if(a->r->attr&NOREC)
+			n->flags |= NORECIPE;
+		if(a->r->attr&DEL)
+			n->flags |= DELETE;
+		if(a->n)
+			attribute(a->n);
+	}
+	if(n->flags&VIRTUAL)
+		n->time = 0;
+}
--- /dev/null
+++ b/utils/mk/job.c
@@ -1,0 +1,33 @@
+#include	"mk.h"
+
+Job *
+newjob(Rule *r, Node *nlist, char *stem, char **match, Word *pre, Word *npre, Word *tar, Word *atar)
+{
+	register Job *j;
+
+	j = (Job *)Malloc(sizeof(Job));
+	j->r = r;
+	j->n = nlist;
+	j->stem = stem;
+	j->match = match;
+	j->p = pre;
+	j->np = npre;
+	j->t = tar;
+	j->at = atar;
+	j->nproc = -1;
+	j->next = 0;
+	return(j);
+}
+
+void
+dumpj(char *s, Job *j, int all)
+{
+	Bprint(&bout, "%s\n", s);
+	while(j){
+		Bprint(&bout, "job@%ld: r=%ld n=%ld stem='%s' nproc=%d\n",
+			j, j->r, j->n, j->stem, j->nproc);
+		Bprint(&bout, "\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n",
+			wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wtos(j->np, ' '));
+		j = all? j->next : 0;
+	}
+}
--- /dev/null
+++ b/utils/mk/lex.c
@@ -1,0 +1,147 @@
+#include	"mk.h"
+
+static	int	bquote(Biobuf*, Bufblock*);
+
+/*
+ *	Assemble a line skipping blank lines, comments, and eliding
+ *	escaped newlines
+ */
+int
+assline(Biobuf *bp, Bufblock *buf)
+{
+	int c;
+	int lastc;
+
+	buf->current=buf->start;
+	while ((c = nextrune(bp, 1)) >= 0){
+		switch(c)
+		{
+		case '\r':		/* consumes CRs for Win95 */
+			continue;
+		case '\n':
+			if (buf->current != buf->start) {
+				insert(buf, 0);
+				return 1;
+			}
+			break;		/* skip empty lines */
+		case '\\':
+		case '\'':
+		case '"':
+			rinsert(buf, c);
+			if (escapetoken(bp, buf, 1, c) == 0)
+				Exit();
+			break;
+		case '`':
+			if (bquote(bp, buf) == 0)
+				Exit();
+			break;
+		case '#':
+			lastc = '#';
+			while ((c = Bgetc(bp)) != '\n') {
+				if (c < 0)
+					goto eof;
+				if(c != '\r')
+					lastc = c;
+			}
+			mkinline++;
+			if (lastc == '\\')
+				break;		/* propagate escaped newlines??*/
+			if (buf->current != buf->start) {
+				insert(buf, 0);
+				return 1;
+			}
+			break;
+		default:
+			rinsert(buf, c);
+			break;
+		}
+	}
+eof:
+	insert(buf, 0);
+	return *buf->start != 0;
+}
+
+/*
+ *	assemble a back-quoted shell command into a buffer
+ */
+static int
+bquote(Biobuf *bp, Bufblock *buf)
+{
+	int c, line, term;
+	int start;
+
+	line = mkinline;
+	while((c = Bgetrune(bp)) == ' ' || c == '\t')
+			;
+	if(c == '{'){
+		term = '}';		/* rc style */
+		while((c = Bgetrune(bp)) == ' ' || c == '\t')
+			;
+	} else
+		term = '`';		/* sh style */
+
+	start = buf->current-buf->start;
+	for(;c > 0; c = nextrune(bp, 0)){
+		if(c == term){
+			insert(buf, '\n');
+			insert(buf,0);
+			buf->current = buf->start+start;
+			execinit();
+			execsh(0, buf->current, buf, envy);
+			return 1;
+		}
+		if(c == '\n')
+			break;
+		if(c == '\'' || c == '"' || c == '\\'){
+			insert(buf, c);
+			if(!escapetoken(bp, buf, 1, c))
+				return 0;
+			continue;
+		}
+		rinsert(buf, c);
+	}
+	SYNERR(line);
+	fprint(2, "missing closing %c after `\n", term);
+	return 0;
+}
+
+/*
+ *	get next character stripping escaped newlines
+ *	the flag specifies whether escaped newlines are to be elided or
+ *	replaced with a blank.
+ */
+int
+nextrune(Biobuf *bp, int elide)
+{
+	int c, c2;
+	static int savec;
+
+	if(savec){
+		c = savec;
+		savec = 0;
+		return c;
+	}
+
+	for (;;) {
+		c = Bgetrune(bp);
+		if (c == '\\') {
+			c2 = Bgetrune(bp);
+			if(c2 == '\r'){
+				savec = c2;
+				c2 = Bgetrune(bp);
+			}
+			if (c2 == '\n') {
+				savec = 0;
+				mkinline++;
+				if (elide)
+					continue;
+				return ' ';
+			}
+			Bungetrune(bp);
+		}
+		if (c == '\n')
+			mkinline++;
+		return c;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/mk/main.c
@@ -1,0 +1,291 @@
+#include	"mk.h"
+
+#define		MKFILE		"mkfile"
+
+static char *version = "@(#)mk general release 4 (plan 9)";
+int debug;
+Rule *rules, *metarules;
+int nflag = 0;
+int tflag = 0;
+int iflag = 0;
+int kflag = 0;
+int aflag = 0;
+int uflag = 0;
+char *explain = 0;
+Word *target1;
+int nreps = 1;
+Job *jobs;
+Biobuf bout;
+Rule *patrule;
+void badusage(void);
+#ifdef	PROF
+short buf[10000];
+#endif
+
+void
+main(int argc, char **argv)
+{
+	Word *w;
+	char *s, *temp;
+	char *files[256], **f = files, **ff;
+	int sflag = 0;
+	int i;
+	int tfd = -1;
+	Biobuf tb;
+	Bufblock *buf;
+	Bufblock *whatif;
+
+	/*
+	 *  start with a copy of the current environment variables
+	 *  instead of sharing them
+	 */
+
+	Binit(&bout, 1, OWRITE);
+	buf = newbuf();
+	whatif = 0;
+	USED(argc);
+	for(argv++; *argv && (**argv == '-'); argv++)
+	{
+		bufcpy(buf, argv[0], strlen(argv[0]));
+		insert(buf, ' ');
+		switch(argv[0][1])
+		{
+		case 'a':
+			aflag = 1;
+			break;
+		case 'd':
+			if(*(s = &argv[0][2]))
+				while(*s) switch(*s++)
+				{
+				case 'p':	debug |= D_PARSE; break;
+				case 'g':	debug |= D_GRAPH; break;
+				case 'e':	debug |= D_EXEC; break;
+				}
+			else
+				debug = 0xFFFF;
+			break;
+		case 'e':
+			explain = &argv[0][2];
+			break;
+		case 'f':
+			if(*++argv == 0)
+				badusage();
+			*f++ = *argv;
+			bufcpy(buf, argv[0], strlen(argv[0]));
+			insert(buf, ' ');
+			break;
+		case 'i':
+			iflag = 1;
+			break;
+		case 'k':
+			kflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 's':
+			sflag = 1;
+			break;
+		case 't':
+			tflag = 1;
+			break;
+		case 'u':
+			uflag = 1;
+			break;
+		case 'w':
+			if(whatif == 0)
+				whatif = newbuf();
+			else
+				insert(whatif, ' ');
+			if(argv[0][2])
+				bufcpy(whatif, &argv[0][2], strlen(&argv[0][2]));
+			else {
+				if(*++argv == 0)
+					badusage();
+				bufcpy(whatif, &argv[0][0], strlen(&argv[0][0]));
+			}
+			break;
+		default:
+			badusage();
+		}
+	}
+#ifdef	PROF
+	{
+		extern etext();
+		monitor(main, etext, buf, sizeof buf, 300);
+	}
+#endif
+
+	if(aflag)
+		iflag = 1;
+	usage();
+	syminit();
+	initenv();
+	usage();
+
+	/*
+		assignment args become null strings
+	*/
+	temp = 0;
+	for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){
+		bufcpy(buf, argv[i], strlen(argv[i]));
+		insert(buf, ' ');
+		if(tfd < 0){
+			temp = maketmp();
+			if(temp == 0) {
+				perror("temp file");
+				Exit();
+			}
+			close(create(temp, OWRITE, 0600));
+			if((tfd = open(temp, 2)) < 0){
+				perror(temp);
+				Exit();
+			}
+			Binit(&tb, tfd, OWRITE);
+		}
+		Bprint(&tb, "%s\n", argv[i]);
+		*argv[i] = 0;
+	}
+	if(tfd >= 0){
+		Bflush(&tb);
+		LSEEK(tfd, 0L, 0);
+		parse("command line args", tfd, 1);
+		remove(temp);
+	}
+
+	if (buf->current != buf->start) {
+		buf->current--;
+		insert(buf, 0);
+	}
+	symlook("MKFLAGS", S_VAR, (void *) stow(buf->start));
+	buf->current = buf->start;
+	for(i = 0; argv[i]; i++){
+		if(*argv[i] == 0) continue;
+		if(i)
+			insert(buf, ' ');
+		bufcpy(buf, argv[i], strlen(argv[i]));
+	}
+	insert(buf, 0);
+	symlook("MKARGS", S_VAR, (void *) stow(buf->start));
+	freebuf(buf);
+
+	if(f == files){
+		if(access(MKFILE, 4) == 0)
+			parse(MKFILE, open(MKFILE, 0), 0);
+	} else
+		for(ff = files; ff < f; ff++)
+			parse(*ff, open(*ff, 0), 0);
+	if(DEBUG(D_PARSE)){
+		dumpw("default targets", target1);
+		dumpr("rules", rules);
+		dumpr("metarules", metarules);
+		dumpv("variables");
+	}
+	if(whatif){
+		insert(whatif, 0);
+		timeinit(whatif->start);
+		freebuf(whatif);
+	}
+	execinit();
+	/* skip assignment args */
+	while(*argv && (**argv == 0))
+		argv++;
+
+	catchnotes();
+	if(*argv == 0){
+		if(target1)
+			for(w = target1; w; w = w->next)
+				mk(w->s);
+		else {
+			fprint(2, "mk: nothing to mk\n");
+			Exit();
+		}
+	} else {
+		if(sflag){
+			for(; *argv; argv++)
+				if(**argv)
+					mk(*argv);
+		} else {
+			Word *head, *tail, *t;
+
+			/* fake a new rule with all the args as prereqs */
+			tail = 0;
+			t = 0;
+			for(; *argv; argv++)
+				if(**argv){
+					if(tail == 0)
+						tail = t = newword(*argv);
+					else {
+						t->next = newword(*argv);
+						t = t->next;
+					}
+				}
+			if(tail->next == 0)
+				mk(tail->s);
+			else {
+				head = newword("command line arguments");
+				addrules(head, tail, strdup(""), VIR, mkinline, 0);
+				mk(head->s);
+			}
+		}
+	}
+	if(uflag)
+		prusage();
+	exits(0);
+}
+
+void
+badusage(void)
+{
+
+	fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n");
+	Exit();
+}
+
+void *
+Malloc(int n)
+{
+	register void *s;
+
+	s = malloc(n);
+	if(!s) {
+		fprint(2, "mk: cannot alloc %d bytes\n", n);
+		Exit();
+	}
+	return(s);
+}
+
+void *
+Realloc(void *s, int n)
+{
+	if(s)
+		s = realloc(s, n);
+	else
+		s = malloc(n);
+	if(!s) {
+		fprint(2, "mk: cannot alloc %d bytes\n", n);
+		Exit();
+	}
+	return(s);
+}
+
+void
+assert(char *s, int n)
+{
+	if(!n){
+		fprint(2, "mk: Assertion ``%s'' failed.\n", s);
+		Exit();
+	}
+}
+
+void
+regerror(char *s)
+{
+	if(patrule)
+		fprint(2, "mk: %s:%d: regular expression error; %s\n",
+			patrule->file, patrule->line, s);
+	else
+		fprint(2, "mk: %s:%d: regular expression error; %s\n",
+			infile, mkinline, s);
+	Exit();
+}
--- /dev/null
+++ b/utils/mk/match.c
@@ -1,0 +1,49 @@
+#include	"mk.h"
+
+int
+match(char *name, char *template, char *stem)
+{
+	Rune r;
+	int n;
+
+	while(*name && *template){
+		n = chartorune(&r, template);
+		if (PERCENT(r))
+			break;
+		while (n--)
+			if(*name++ != *template++)
+				return 0;
+	}
+	if(!PERCENT(*template))
+		return 0;
+	n = strlen(name)-strlen(template+1);
+	if (n < 0)
+		return 0;
+	if (strcmp(template+1, name+n))
+		return 0;
+	strncpy(stem, name, n);
+	stem[n] = 0;
+	if(*template == '&')
+		return !charin(stem, "./");
+	return 1;
+}
+
+void
+subst(char *stem, char *template, char *dest)
+{
+	Rune r;
+	char *s;
+	int n;
+
+	while(*template){
+		n = chartorune(&r, template);
+		if (PERCENT(r)) {
+			template += n;
+			for (s = stem; *s; s++)
+				*dest++ = *s;
+		} else
+			while (n--)
+				*dest++ = *template++;
+	}
+	*dest = 0;
+}
binary files /dev/null b/utils/mk/mk differ
--- /dev/null
+++ b/utils/mk/mk.c
@@ -1,0 +1,226 @@
+#include	"mk.h"
+
+int runerrs;
+
+void
+mk(char *target)
+{
+	Node *node;
+	int did = 0;
+
+	nproc();		/* it can be updated dynamically */
+	nrep();			/* it can be updated dynamically */
+	runerrs = 0;
+	node = graph(target);
+	if(DEBUG(D_GRAPH)){
+		dumpn("new target\n", node);
+		Bflush(&bout);
+	}
+	clrmade(node);
+	while(node->flags&NOTMADE){
+		if(work(node, (Node *)0, (Arc *)0))
+			did = 1;	/* found something to do */
+		else {
+			if(waitup(1, (int *)0) > 0){
+				if(node->flags&(NOTMADE|BEINGMADE)){
+					assert("must be run errors", runerrs);
+					break;	/* nothing more waiting */
+				}
+			}
+		}
+	}
+	if(node->flags&BEINGMADE)
+		waitup(-1, (int *)0);
+	while(jobs)
+		waitup(-2, (int *)0);
+	assert("target didn't get done", runerrs || (node->flags&MADE));
+	if(did == 0)
+		Bprint(&bout, "mk: '%s' is up to date\n", node->name);
+}
+
+void
+clrmade(Node *n)
+{
+	Arc *a;
+
+	n->flags &= ~(CANPRETEND|PRETENDING);
+	if(strchr(n->name, '(') ==0 || n->time)
+		n->flags |= CANPRETEND;
+	MADESET(n, NOTMADE);
+	for(a = n->prereqs; a; a = a->next)
+		if(a->n)
+			clrmade(a->n);
+}
+
+static void
+unpretend(Node *n)
+{
+	MADESET(n, NOTMADE);
+	n->flags &= ~(CANPRETEND|PRETENDING);
+	n->time = 0;
+}
+
+int
+work(Node *node, Node *p, Arc *parc)
+{
+	Arc *a, *ra;
+	int weoutofdate;
+	int ready;
+	int did = 0;
+
+	/*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time);/**/
+	if(node->flags&BEINGMADE)
+		return(did);
+	if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){
+		if(explain)
+			fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n",
+				node->name, node->time, p->name, p->time);
+		unpretend(node);
+	}
+	/*
+		have a look if we are pretending in case
+		someone has been unpretended out from underneath us
+	*/
+	if(node->flags&MADE){
+		if(node->flags&PRETENDING){
+			node->time = 0;
+		}else
+			return(did);
+	}
+	/* consider no prerequsite case */
+	if(node->prereqs == 0){
+		if(node->time == 0){
+			fprint(2, "mk: don't know how to make '%s'\n", node->name);
+			if(kflag){
+				node->flags |= BEINGMADE;
+				runerrs++;
+			} else
+				Exit();
+		} else
+			MADESET(node, MADE);
+		return(did);
+	}
+	/*
+		now see if we are out of date or what
+	*/
+	ready = 1;
+	weoutofdate = aflag;
+	ra = 0;
+	for(a = node->prereqs; a; a = a->next)
+		if(a->n){
+			did = work(a->n, node, a) || did;
+			if(a->n->flags&(NOTMADE|BEINGMADE))
+				ready = 0;
+			if(outofdate(node, a, 0)){
+				weoutofdate = 1;
+				if((ra == 0) || (ra->n == 0)
+						|| (ra->n->time < a->n->time))
+					ra = a;
+			}
+		} else {
+			if(node->time == 0){
+				if(ra == 0)
+					ra = a;
+				weoutofdate = 1;
+			}
+		}
+	if(ready == 0)	/* can't do anything now */
+		return(did);
+	if(weoutofdate == 0){
+		MADESET(node, MADE);
+		return(did);
+	}
+	/*
+		can we pretend to be made?
+	*/
+	if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND))
+			&& p && ra->n && !outofdate(p, ra, 0)){
+		node->flags &= ~CANPRETEND;
+		MADESET(node, MADE);
+		if(explain && ((node->flags&PRETENDING) == 0))
+			fprint(1, "pretending %s has time %ld\n", node->name, node->time);
+		node->flags |= PRETENDING;
+		return(did);
+	}
+	/*
+		node is out of date and we REALLY do have to do something.
+		quickly rescan for pretenders
+	*/
+	for(a = node->prereqs; a; a = a->next)
+		if(a->n && (a->n->flags&PRETENDING)){
+			if(explain)
+				Bprint(&bout, "unpretending %s because of %s because of %s\n",
+				a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites");
+
+			unpretend(a->n);
+			did = work(a->n, node, a) || did;
+			ready = 0;
+		}
+	if(ready == 0)	/* try later unless nothing has happened for -k's sake */
+		return(did || work(node, p, parc));
+	did = dorecipe(node) || did;
+	return(did);
+}
+
+void
+update(int fake, Node *node)
+{
+	Arc *a;
+
+	MADESET(node, fake? BEINGMADE : MADE);
+	if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){
+		node->time = timeof(node->name, 1);
+		node->flags &= ~(CANPRETEND|PRETENDING);
+		for(a = node->prereqs; a; a = a->next)
+			if(a->prog)
+				outofdate(node, a, 1);
+	} else {
+		node->time = 1;
+		for(a = node->prereqs; a; a = a->next)
+			if(a->n && outofdate(node, a, 1))
+				node->time = a->n->time;
+	}
+/*	print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);/**/
+}
+
+static int
+pcmp(char *prog, char *p, char *q)
+{
+	char buf[3*NAMEBLOCK];
+	int pid;
+
+	Bflush(&bout);
+	sprint(buf, "%s '%s' '%s'\n", prog, p, q);
+	pid = pipecmd(buf, 0, 0);
+	while(waitup(-3, &pid) >= 0)
+		;
+	return(pid? 2:1);
+}
+
+int
+outofdate(Node *node, Arc *arc, int eval)
+{
+	char buf[3*NAMEBLOCK], *str;
+	Symtab *sym;
+	int ret;
+
+	str = 0;
+	if(arc->prog){
+		sprint(buf, "%s%c%s", node->name, 0377, arc->n->name);
+		sym = symlook(buf, S_OUTOFDATE, 0);
+		if(sym == 0 || eval){
+			if(sym == 0)
+				str = strdup(buf);
+			ret = pcmp(arc->prog, node->name, arc->n->name);
+			if(sym)
+				sym->value = (void *)ret;
+			else
+				symlook(str, S_OUTOFDATE, (void *)ret);
+		} else
+			ret = (int)sym->value;
+		return(ret-1);
+	} else if(strchr(arc->n->name, '(') && arc->n->time == 0)  /* missing archive member */
+		return 1;
+	else
+		return node->time <= arc->n->time;
+}
--- /dev/null
+++ b/utils/mk/mk.h
@@ -1,0 +1,171 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	<regexp.h>
+
+#undef assert
+#define	assert	mkassert
+extern Biobuf bout;
+
+typedef struct Bufblock
+{
+	struct Bufblock *next;
+	char 		*start;
+	char 		*end;
+	char 		*current;
+} Bufblock;
+
+typedef struct Word
+{
+	char 		*s;
+	struct Word 	*next;
+} Word;
+
+typedef struct Envy
+{
+	char 		*name;
+	Word 		*values;
+} Envy;
+
+extern Envy *envy;
+
+typedef struct Rule
+{
+	char 		*target;	/* one target */
+	Word 		*tail;		/* constituents of targets */
+	char 		*recipe;	/* do it ! */
+	short 		attr;		/* attributes */
+	short 		line;		/* source line */
+	char 		*file;		/* source file */
+	Word 		*alltargets;	/* all the targets */
+	int 		rule;		/* rule number */
+	Reprog		*pat;		/* reg exp goo */
+	char		*prog;		/* to use in out of date */
+	struct Rule	*chain;		/* hashed per target */
+	struct Rule	*next;
+} Rule;
+
+extern Rule *rules, *metarules, *patrule;
+
+/*	Rule.attr	*/
+#define		META		0x0001
+#define		UNUSED		0x0002
+#define		UPD		0x0004
+#define		QUIET		0x0008
+#define		VIR		0x0010
+#define		REGEXP		0x0020
+#define		NOREC		0x0040
+#define		DEL		0x0080
+#define		NOVIRT		0x0100
+
+#define		NREGEXP		10
+
+typedef struct Arc
+{
+	short		flag;
+	struct Node	*n;
+	Rule		*r;
+	char		*stem;
+	char		*prog;
+	char		*match[NREGEXP];
+	struct Arc	*next;
+} Arc;
+
+	/* Arc.flag */
+#define		TOGO		1
+
+typedef struct Node
+{
+	char		*name;
+	long		time;
+	unsigned short	flags;
+	Arc		*prereqs;
+	struct Node	*next;		/* list for a rule */
+} Node;
+
+	/* Node.flags */
+#define		VIRTUAL		0x0001
+#define		CYCLE		0x0002
+#define		READY		0x0004
+#define		CANPRETEND	0x0008
+#define		PRETENDING	0x0010
+#define		NOTMADE		0x0020
+#define		BEINGMADE	0x0040
+#define		MADE		0x0080
+#define		MADESET(n,m)	n->flags = (n->flags&~(NOTMADE|BEINGMADE|MADE))|(m)
+#define		PROBABLE	0x0100
+#define		VACUOUS		0x0200
+#define		NORECIPE	0x0400
+#define		DELETE		0x0800
+#define		NOMINUSE	0x1000
+
+typedef struct Job
+{
+	Rule		*r;	/* master rule for job */
+	Node		*n;	/* list of node targets */
+	char		*stem;
+	char		**match;
+	Word		*p;	/* prerequistes */
+	Word		*np;	/* new prerequistes */
+	Word		*t;	/* targets */
+	Word		*at;	/* all targets */
+	int		nproc;	/* slot number */
+	struct Job	*next;
+} Job;
+extern Job *jobs;
+
+typedef struct Symtab
+{
+	short		space;
+	char		*name;
+	void		*value;
+	struct Symtab	*next;
+} Symtab;
+
+enum {
+	S_VAR,		/* variable -> value */
+	S_TARGET,	/* target -> rule */
+	S_TIME,		/* file -> time */
+	S_PID,		/* pid -> products */
+	S_NODE,		/* target name -> node */
+	S_AGG,		/* aggregate -> time */
+	S_BITCH,	/* bitched about aggregate not there */
+	S_NOEXPORT,	/* var -> noexport */
+	S_OVERRIDE,	/* can't override */
+	S_OUTOFDATE,	/* n1\377n2 -> 2(outofdate) or 1(not outofdate) */
+	S_MAKEFILE,	/* target -> node */
+	S_MAKEVAR,	/* dumpable mk variable */
+	S_EXPORTED,	/* var -> current exported value */
+	S_WESET,	/* variable; we set in the mkfile */
+	S_INTERNAL	/* an internal mk variable (e.g., stem, target) */
+};
+
+extern	int	debug;
+extern	int	nflag, tflag, iflag, kflag, aflag, mflag;
+extern	int	mkinline;
+extern	char	*infile;
+extern	int	nreps;
+extern	char	*explain;
+extern	char	*termchars;
+extern	int	IWS;
+extern	char 	*shell;
+extern	char 	*shellname;
+extern	char 	*shflags;
+
+#define	SYNERR(l)	(fprint(2, "mk: %s:%d: syntax error; ", infile, ((l)>=0)?(l):mkinline))
+#define	RERR(r)		(fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line))
+#define	NAMEBLOCK	1000
+#define	BIGBLOCK	20000
+
+#define	SEP(c)		(((c)==' ')||((c)=='\t')||((c)=='\n'))
+#define WORDCHR(r)	((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r)))
+
+#define	DEBUG(x)	(debug&(x))
+#define		D_PARSE		0x01
+#define		D_GRAPH		0x02
+#define		D_EXEC		0x04
+
+#define	LSEEK(f,o,p)	seek(f,o,p)
+
+#define	PERCENT(ch)	(((ch) == '%') || ((ch) == '&'))
+
+#include	"fns.h"
--- /dev/null
+++ b/utils/mk/mkfile
@@ -1,0 +1,40 @@
+<../../mkconfig
+
+TARG=mk
+
+OFILES=	arc.$O\
+	archive.$O\
+	bufblock.$O\
+	env.$O\
+	file.$O\
+	graph.$O\
+	job.$O\
+	lex.$O\
+	main.$O\
+	match.$O\
+	mk.$O\
+	parse.$O\
+	$TARGMODEL.$O\
+	recipe.$O\
+	rule.$O\
+	run.$O\
+	$TARGSHTYPE.$O\
+	shprint.$O\
+	symtab.$O\
+	var.$O\
+	varsub.$O\
+	word.$O\
+
+HFILES=	fns.h\
+	../include/ar.h\
+	mk.h\
+
+LIBS=	regexp bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=$CFLAGS -I../include -DROOT'="'$ROOT'"'
+
+<mkfile-$HOSTMODEL
--- /dev/null
+++ b/utils/mk/mkfile-Nt
@@ -1,0 +1,12 @@
+#
+#	install rule for NT & windows 95
+#
+#	since we can't reliably copy the new executable
+#	onto the already executing copy, we make the user
+#	do it manually
+
+$BIN/%:Q:	$O.out
+	echo 'mk must be installed manually on Windows systems'
+	echo use: cp $O.out $target
+	cp $O.out $target
+
--- /dev/null
+++ b/utils/mk/mkfile-Plan9
@@ -1,0 +1,3 @@
+#
+#	install rule for Inferno/Plan9 - use the default
+#
--- /dev/null
+++ b/utils/mk/mkfile-Posix
@@ -1,0 +1,7 @@
+#
+#	install rule for Posix systems
+#
+
+$BIN/%:	$O.out
+	test -x $target && mv $target $BIN/mk.save		#because we are executing it
+	cp $O.out $target
--- /dev/null
+++ b/utils/mk/parse.c
@@ -1,0 +1,309 @@
+#include	"mk.h"
+
+char *infile;
+int mkinline;
+static int rhead(char *, Word **, Word **, int *, char **);
+static char *rbody(Biobuf*);
+extern Word *target1;
+
+void
+parse(char *f, int fd, int varoverride)
+{
+	int hline;
+	char *body;
+	Word *head, *tail;
+	int attr, set, pid;
+	char *prog, *p;
+	int newfd;
+	Biobuf in;
+	Bufblock *buf;
+
+	if(fd < 0){
+		perror(f);
+		Exit();
+	}
+	ipush();
+	infile = strdup(f);
+	mkinline = 1;
+	Binit(&in, fd, OREAD);
+	buf = newbuf();
+	while(assline(&in, buf)){
+		hline = mkinline;
+		switch(rhead(buf->start, &head, &tail, &attr, &prog))
+		{
+		case '<':
+			p = wtos(tail, ' ');
+			if(*p == 0){
+				SYNERR(-1);
+				fprint(2, "missing include file name\n");
+				Exit();
+			}
+			newfd = open(p, OREAD);
+			if(newfd < 0){
+				fprint(2, "warning: skipping missing include file: ");
+				perror(p);
+			} else
+				parse(p, newfd, 0);
+			break;
+		case '|':
+			p = wtos(tail, ' ');
+			if(*p == 0){
+				SYNERR(-1);
+				fprint(2, "missing include program name\n");
+				Exit();
+			}
+			execinit();
+			pid=pipecmd(p, envy, &newfd);
+			if(newfd < 0){
+				fprint(2, "warning: skipping missing program file: ");
+				perror(p);
+			} else
+				parse(p, newfd, 0);
+			while(waitup(-3, &pid) >= 0)
+				;
+			if(pid != 0){
+				fprint(2, "bad include program status\n");
+				Exit();
+			}
+			break;
+		case ':':
+			body = rbody(&in);
+			addrules(head, tail, body, attr, hline, prog);
+			break;
+		case '=':
+			if(head->next){
+				SYNERR(-1);
+				fprint(2, "multiple vars on left side of assignment\n");
+				Exit();
+			}
+			if(symlook(head->s, S_OVERRIDE, 0)){
+				set = varoverride;
+			} else {
+				set = 1;
+				if(varoverride)
+					symlook(head->s, S_OVERRIDE, (void *)"");
+			}
+			if(set){
+/*
+char *cp;
+dumpw("tail", tail);
+cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
+*/
+				setvar(head->s, (void *) tail);
+				symlook(head->s, S_WESET, (void *)"");
+			}
+			if(attr)
+				symlook(head->s, S_NOEXPORT, (void *)"");
+			break;
+		default:
+			SYNERR(hline);
+			fprint(2, "expected one of :<=\n");
+			Exit();
+			break;
+		}
+	}
+	close(fd);
+	freebuf(buf);
+	ipop();
+}
+
+void
+addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog)
+{
+	Word *w;
+
+	assert("addrules args", head && body);
+		/* tuck away first non-meta rule as default target*/
+	if(target1 == 0 && !(attr&REGEXP)){
+		for(w = head; w; w = w->next)
+			if(charin(w->s, "%&"))
+				break;
+		if(w == 0)
+			target1 = wdup(head);
+	}
+	for(w = head; w; w = w->next)
+		addrule(w->s, tail, body, head, attr, hline, prog);
+}
+
+static int
+rhead(char *line, Word **h, Word **t, int *attr, char **prog)
+{
+	char *p;
+	char *pp;
+	int sep;
+	Rune r;
+	int n;
+	Word *w;
+
+	p = charin(line,":=<");
+	if(p == 0)
+		return('?');
+	sep = *p;
+	*p++ = 0;
+	if(sep == '<' && *p == '|'){
+		sep = '|';
+		p++;
+	}
+	*attr = 0;
+	*prog = 0;
+	if(sep == '='){
+		pp = charin(p, termchars);	/* termchars is shell-dependent */
+		if (pp && *pp == '=') {
+			while (p != pp) {
+				n = chartorune(&r, p);
+				switch(r)
+				{
+				default:
+					SYNERR(-1);
+					fprint(2, "unknown attribute '%c'\n",*p);
+					Exit();
+				case 'U':
+					*attr = 1;
+					break;
+				}
+				p += n;
+			}
+			p++;		/* skip trailing '=' */
+		}
+	}
+	if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){
+		while (*p) {
+			n = chartorune(&r, p);
+			if (r == ':')
+				break;
+			p += n;
+			switch(r)
+			{
+			default:
+				SYNERR(-1);
+				fprint(2, "unknown attribute '%c'\n", p[-1]);
+				Exit();
+			case 'D':
+				*attr |= DEL;
+				break;
+			case 'E':
+				*attr |= NOMINUSE;
+				break;
+			case 'n':
+				*attr |= NOVIRT;
+				break;
+			case 'N':
+				*attr |= NOREC;
+				break;
+			case 'P':
+				pp = utfrune(p, ':');
+				if (pp == 0 || *pp == 0)
+					goto eos;
+				*pp = 0;
+				*prog = strdup(p);
+				*pp = ':';
+				p = pp;
+				break;
+			case 'Q':
+				*attr |= QUIET;
+				break;
+			case 'R':
+				*attr |= REGEXP;
+				break;
+			case 'U':
+				*attr |= UPD;
+				break;
+			case 'V':
+				*attr |= VIR;
+				break;
+			}
+		}
+		if (*p++ != ':') {
+	eos:
+			SYNERR(-1);
+			fprint(2, "missing trailing :\n");
+			Exit();
+		}
+	}
+	*h = w = stow(line);
+	if(*w->s == 0 && sep != '<' && sep != '|') {
+		SYNERR(mkinline-1);
+		fprint(2, "no var on left side of assignment/rule\n");
+		Exit();
+	}
+	*t = stow(p);
+	return(sep);
+}
+
+static char *
+rbody(Biobuf *in)
+{
+	Bufblock *buf;
+	int r, lastr;
+	char *p;
+
+	lastr = '\n';
+	buf = newbuf();
+	for(;;){
+		r = Bgetrune(in);
+		if (r < 0)
+			break;
+		if (lastr == '\n') {
+			if (r == '#')
+				rinsert(buf, r);
+			else if (r != ' ' && r != '\t') {
+				Bungetrune(in);
+				break;
+			}
+		} else
+			rinsert(buf, r);
+		lastr = r;
+		if (r == '\n')
+			mkinline++;
+	}
+	insert(buf, 0);
+	p = strdup(buf->start);
+	freebuf(buf);
+	return p;
+}
+
+struct input
+{
+	char *file;
+	int line;
+	struct input *next;
+};
+static struct input *inputs = 0;
+
+void
+ipush(void)
+{
+	struct input *in, *me;
+
+	me = (struct input *)Malloc(sizeof(*me));
+	me->file = infile;
+	me->line = mkinline;
+	me->next = 0;
+	if(inputs == 0)
+		inputs = me;
+	else {
+		for(in = inputs; in->next; )
+			in = in->next;
+		in->next = me;
+	}
+}
+
+void
+ipop(void)
+{
+	struct input *in, *me;
+
+	assert("pop input list", inputs != 0);
+	if(inputs->next == 0){
+		me = inputs;
+		inputs = 0;
+	} else {
+		for(in = inputs; in->next->next; )
+			in = in->next;
+		me = in->next;
+		in->next = 0;
+	}
+	infile = me->file;
+	mkinline = me->line;
+	free((char *)me);
+}
--- /dev/null
+++ b/utils/mk/rc.c
@@ -1,0 +1,175 @@
+#include	"mk.h"
+
+char	*termchars = "'= \t";	/*used in parse.c to isolate assignment attribute*/
+char	*shflags = "-I";	/* rc flag to force non-interactive mode */
+int	IWS = '\1';		/* inter-word separator in env - not used in plan 9 */
+
+/*
+ *	This file contains functions that depend on rc's syntax.  Most
+ *	of the routines extract strings observing rc's escape conventions
+ */
+
+
+/*
+ *	skip a token in single quotes.
+ */
+static char *
+squote(char *cp)
+{
+	Rune r;
+	int n;
+
+	while(*cp){
+		n = chartorune(&r, cp);
+		if(r == '\'') {
+			n += chartorune(&r, cp+n);
+			if(r != '\'')
+				return(cp);
+		}
+		cp += n;
+	}
+	SYNERR(-1);		/* should never occur */
+	fprint(2, "missing closing '\n");
+	return 0;
+}
+
+/*
+ *	search a string for characters in a pattern set
+ *	characters in quotes and variable generators are escaped
+ */
+char *
+charin(char *cp, char *pat)
+{
+	Rune r;
+	int n, vargen;
+
+	vargen = 0;
+	while(*cp){
+		n = chartorune(&r, cp);
+		switch(r){
+		case '\'':			/* skip quoted string */
+			cp = squote(cp+1);	/* n must = 1 */
+			if(!cp)
+				return 0;
+			break;
+		case '$':
+			if(*(cp+1) == '{')
+				vargen = 1;
+			break;
+		case '}':
+			if(vargen)
+				vargen = 0;
+			else if(utfrune(pat, r))
+				return cp;
+			break;
+		default:
+			if(vargen == 0 && utfrune(pat, r))
+				return cp;
+			break;
+		}
+		cp += n;
+	}
+	if(vargen){
+		SYNERR(-1);
+		fprint(2, "missing closing } in pattern generator\n");
+	}
+	return 0;
+}
+
+/*
+ *	extract an escaped token.  Possible escape chars are single-quote,
+ *	double-quote,and backslash.  Only the first is valid for rc. the
+ *	others are just inserted into the receiving buffer.
+ */
+char*
+expandquote(char *s, Rune r, Bufblock *b)
+{
+	if (r != '\'') {
+		rinsert(b, r);
+		return s;
+	}
+
+	while(*s){
+		s += chartorune(&r, s);
+		if(r == '\'') {
+			if(*s == '\'')
+				s++;
+			else
+				return s;
+		}
+		rinsert(b, r);
+	}
+	return 0;
+}
+
+/*
+ *	Input an escaped token.  Possible escape chars are single-quote,
+ *	double-quote and backslash.  Only the first is a valid escape for
+ *	rc; the others are just inserted into the receiving buffer.
+ */
+int
+escapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+	int c, line;
+
+	if(esc != '\'')
+		return 1;
+
+	line = mkinline;
+	while((c = nextrune(bp, 0)) > 0){
+		if(c == '\''){
+			if(preserve)
+				rinsert(buf, c);
+			c = Bgetrune(bp);
+			if (c < 0)
+				break;
+			if(c != '\''){
+				Bungetrune(bp);
+				return 1;
+			}
+		}
+		rinsert(buf, c);
+	}
+	SYNERR(line); fprint(2, "missing closing %c\n", esc);
+	return 0;
+}
+
+/*
+ *	copy a single-quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Bufblock *buf)
+{
+	Rune r;
+
+	while(*s){
+		s += chartorune(&r, s);
+		rinsert(buf, r);
+		if(r == '\'')
+			break;
+	}
+	return s;
+}
+/*
+ *	check for quoted strings.  backquotes are handled here; single quotes above.
+ *	s points to char after opening quote, q.
+ */
+char *
+copyq(char *s, Rune q, Bufblock *buf)
+{
+	if(q == '\'')				/* copy quoted string */
+		return copysingle(s, buf);
+
+	if(q != '`')				/* not quoted */
+		return s;
+
+	while(*s){				/* copy backquoted string */
+		s += chartorune(&q, s);
+		rinsert(buf, q);
+		if(q == '}')
+			break;
+		if(q == '\'')
+			s = copysingle(s, buf);	/* copy quoted string */
+	}
+	return s;
+}
--- /dev/null
+++ b/utils/mk/recipe.c
@@ -1,0 +1,117 @@
+#include	"mk.h"
+
+int
+dorecipe(Node *node)
+{
+	char buf[BIGBLOCK];
+	register Node *n;
+	Rule *r = 0;
+	Arc *a, *aa;
+	Word head, ahead, lp, ln, *w, *ww, *aw;
+	Symtab *s;
+	int did = 0;
+
+	aa = 0;
+	/*
+		pick up the rule
+	*/
+	for(a = node->prereqs; a; a = a->next)
+		if(*a->r->recipe)
+			r = (aa = a)->r;
+	/*
+		no recipe? go to buggery!
+	*/
+	if(r == 0){
+		if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){
+			fprint(2, "mk: no recipe to make '%s'\n", node->name);
+			Exit();
+		}
+		if(strchr(node->name, '(') && node->time == 0)
+			MADESET(node, MADE);
+		else
+			update(0, node);
+		if(tflag){
+			if(!(node->flags&VIRTUAL))
+				touch(node->name);
+			else if(explain)
+				Bprint(&bout, "no touch of virtual '%s'\n", node->name);
+		}
+		return(did);
+	}
+	/*
+		build the node list
+	*/
+	node->next = 0;
+	head.next = 0;
+	ww = &head;
+	ahead.next = 0;
+	aw = &ahead;
+	if(r->attr&REGEXP){
+		ww->next = newword(node->name);
+		aw->next = newword(node->name);
+	} else {
+		for(w = r->alltargets; w; w = w->next){
+			if(r->attr&META)
+				subst(aa->stem, w->s, buf);
+			else
+				strcpy(buf, w->s);
+			aw->next = newword(buf);
+			aw = aw->next;
+			if((s = symlook(buf, S_NODE, 0)) == 0)
+				continue;	/* not a node we are interested in */
+			n = (Node *)s->value;
+			if(aflag == 0 && n->time) {
+				for(a = n->prereqs; a; a = a->next)
+					if(a->n && outofdate(n, a, 0))
+						break;
+				if(a == 0)
+					continue;
+			}
+			ww->next = newword(buf);
+			ww = ww->next;
+			if(n == node) continue;
+			n->next = node->next;
+			node->next = n;
+		}
+	}
+	for(n = node; n; n = n->next)
+		if((n->flags&READY) == 0)
+			return(did);
+	/*
+		gather the params for the job
+	*/
+	lp.next = ln.next = 0;
+	for(n = node; n; n = n->next){
+		for(a = n->prereqs; a; a = a->next){
+			if(a->n){
+				addw(&lp, a->n->name);
+				if(outofdate(n, a, 0)){
+					addw(&ln, a->n->name);
+					if(explain)
+						fprint(1, "%s(%ld) < %s(%ld)\n",
+							n->name, n->time, a->n->name, a->n->time);
+				}
+			} else {
+				if(explain)
+					fprint(1, "%s has no prerequisites\n",
+							n->name);
+			}
+		}
+		MADESET(n, BEINGMADE);
+	}
+	/*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));/**/
+	run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next));
+	return(1);
+}
+
+void
+addw(Word *w, char *s)
+{
+	Word *lw;
+
+	for(lw = w; w = w->next; lw = w){
+		if(strcmp(s, w->s) == 0)
+			return;
+	}
+	lw->next = newword(s);
+}
--- /dev/null
+++ b/utils/mk/rule.c
@@ -1,0 +1,107 @@
+#include	"mk.h"
+
+static Rule *lr, *lmr;
+static int rcmp(Rule *r, char *target, Word *tail);
+static int nrules = 0;
+
+void
+addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, char *prog)
+{
+	Rule *r;
+	Rule *rr;
+	Symtab *sym;
+	int reuse;
+
+	r = 0;
+	reuse = 0;
+	if(sym = symlook(head, S_TARGET, 0)){
+		for(r = (Rule *)sym->value; r; r = r->chain)
+			if(rcmp(r, head, tail) == 0){
+				reuse = 1;
+				break;
+			}
+	}
+	if(r == 0)
+		r = (Rule *)Malloc(sizeof(Rule));
+	r->target = head;
+	r->tail = tail;
+	r->recipe = body;
+	r->line = hline;
+	r->file = infile;
+	r->attr = attr;
+	r->alltargets = ahead;
+	r->prog = prog;
+	r->rule = nrules++;
+	if(!reuse){
+		rr = (Rule *)symlook(head, S_TARGET, (void *)r)->value;
+		if(rr != r){
+			r->chain = rr->chain;
+			rr->chain = r;
+		} else
+			r->chain = 0;
+	}
+	if(!reuse)
+		r->next = 0;
+	if((attr&REGEXP) || charin(head, "%&")){
+		r->attr |= META;
+		if(reuse)
+			return;
+		if(attr&REGEXP){
+			patrule = r;
+			r->pat = regcomp(head);
+		}
+		if(metarules == 0)
+			metarules = lmr = r;
+		else {
+			lmr->next = r;
+			lmr = r;
+		}
+	} else {
+		if(reuse)
+			return;
+		r->pat = 0;
+		if(rules == 0)
+			rules = lr = r;
+		else {
+			lr->next = r;
+			lr = r;
+		}
+	}
+}
+
+void
+dumpr(char *s, Rule *r)
+{
+	Bprint(&bout, "%s: start=%ld\n", s, r);
+	for(; r; r = r->next){
+		Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'",
+			r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' '));
+		if(r->prog)
+			Bprint(&bout, " prog='%s'", r->prog);
+		Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' '));
+		Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe);
+	}
+}
+
+static int
+rcmp(Rule *r, char *target, Word *tail)
+{
+	Word *w;
+
+	if(strcmp(r->target, target))
+		return 1;
+	for(w = r->tail; w && tail; w = w->next, tail = tail->next)
+		if(strcmp(w->s, tail->s))
+			return 1;
+	return(w || tail);
+}
+
+char *
+rulecnt(void)
+{
+	char *s;
+
+	s = Malloc(nrules);
+	memset(s, 0, nrules);
+	return(s);
+}
--- /dev/null
+++ b/utils/mk/run.c
@@ -1,0 +1,297 @@
+#include	"mk.h"
+
+typedef struct Event
+{
+	int pid;
+	Job *job;
+} Event;
+static Event *events;
+static int nevents, nrunning, nproclimit;
+
+typedef struct Process
+{
+	int pid;
+	int status;
+	struct Process *b, *f;
+} Process;
+static Process *phead, *pfree;
+static void sched(void);
+static void pnew(int, int), pdelete(Process *);
+
+int pidslot(int);
+
+void
+run(Job *j)
+{
+	Job *jj;
+
+	if(jobs){
+		for(jj = jobs; jj->next; jj = jj->next)
+			;
+		jj->next = j;
+	} else 
+		jobs = j;
+	j->next = 0;
+	/* this code also in waitup after parse redirect */
+	if(nrunning < nproclimit)
+		sched();
+}
+
+static void
+sched(void)
+{
+	char *flags;
+	Job *j;
+	Bufblock *buf;
+	int slot;
+	Node *n;
+	Envy *e;
+
+	if(jobs == 0){
+		usage();
+		return;
+	}
+	j = jobs;
+	jobs = j->next;
+	if(DEBUG(D_EXEC))
+		fprint(1, "firing up job for target %s\n", wtos(j->t, ' '));
+	slot = nextslot();
+	events[slot].job = j;
+	buf = newbuf();
+	e = buildenv(j, slot);
+	shprint(j->r->recipe, e, buf);
+	if(!tflag && (nflag || !(j->r->attr&QUIET)))
+		Bwrite(&bout, buf->start, (long)strlen(buf->start));
+	freebuf(buf);
+	if(nflag||tflag){
+		for(n = j->n; n; n = n->next){
+			if(tflag){
+				if(!(n->flags&VIRTUAL))
+					touch(n->name);
+				else if(explain)
+					Bprint(&bout, "no touch of virtual '%s'\n", n->name);
+			}
+			n->time = time(0);
+			MADESET(n, MADE);
+		}
+	} else {
+		if(DEBUG(D_EXEC))
+			fprint(1, "recipe='%s'", j->r->recipe);/**/
+		Bflush(&bout);
+		if(j->r->attr&NOMINUSE)
+			flags = 0;
+		else
+			flags = "-e";
+		events[slot].pid = execsh(flags, j->r->recipe, 0, e);
+		usage();
+		nrunning++;
+		if(DEBUG(D_EXEC))
+			fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid);
+	}
+}
+
+int
+waitup(int echildok, int *retstatus)
+{
+	Envy *e;
+	int pid;
+	int slot;
+	Symtab *s;
+	Word *w;
+	Job *j;
+	char buf[ERRMAX];
+	Bufblock *bp;
+	int uarg = 0;
+	int done;
+	Node *n;
+	Process *p;
+	extern int runerrs;
+
+	/* first check against the proces slist */
+	if(retstatus)
+		for(p = phead; p; p = p->f)
+			if(p->pid == *retstatus){
+				*retstatus = p->status;
+				pdelete(p);
+				return(-1);
+			}
+again:		/* rogue processes */
+	pid = waitfor(buf);
+	if(pid == -1){
+		if(echildok > 0)
+			return(1);
+		else {
+			fprint(2, "mk: (waitup %d) ", echildok);
+			perror("mk wait");
+			Exit();
+		}
+	}
+	if(DEBUG(D_EXEC))
+		fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf);
+	if(retstatus && pid == *retstatus){
+		*retstatus = buf[0]? 1:0;
+		return(-1);
+	}
+	slot = pidslot(pid);
+	if(slot < 0){
+		if(DEBUG(D_EXEC))
+			fprint(2, "mk: wait returned unexpected process %d\n", pid);
+		pnew(pid, buf[0]? 1:0);
+		goto again;
+	}
+	j = events[slot].job;
+	usage();
+	nrunning--;
+	events[slot].pid = -1;
+	if(buf[0]){
+		e = buildenv(j, slot);
+		bp = newbuf();
+		shprint(j->r->recipe, e, bp);
+		front(bp->start);
+		fprint(2, "mk: %s: exit status=%s", bp->start, buf);
+		freebuf(bp);
+		for(n = j->n, done = 0; n; n = n->next)
+			if(n->flags&DELETE){
+				if(done++ == 0)
+					fprint(2, ", deleting");
+				fprint(2, " '%s'", n->name);
+				delete(n->name);
+			}
+		fprint(2, "\n");
+		if(kflag){
+			runerrs++;
+			uarg = 1;
+		} else {
+			jobs = 0;
+			Exit();
+		}
+	}
+	for(w = j->t; w; w = w->next){
+		if((s = symlook(w->s, S_NODE, 0)) == 0)
+			continue;	/* not interested in this node */
+		update(uarg, (Node *)s->value);
+	}
+	if(nrunning < nproclimit)
+		sched();
+	return(0);
+}
+
+void
+nproc(void)
+{
+	Symtab *sym;
+	Word *w;
+
+	if(sym = symlook("NPROC", S_VAR, 0)) {
+		w = (Word *) sym->value;
+		if (w && w->s && w->s[0])
+			nproclimit = atoi(w->s);
+	}
+	if(nproclimit < 1)
+		nproclimit = 1;
+	if(DEBUG(D_EXEC))
+		fprint(1, "nprocs = %d\n", nproclimit);
+	if(nproclimit > nevents){
+		if(nevents)
+			events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event));
+		else
+			events = (Event *)Malloc(nproclimit*sizeof(Event));
+		while(nevents < nproclimit)
+			events[nevents++].pid = 0;
+	}
+}
+
+int
+nextslot(void)
+{
+	int i;
+
+	for(i = 0; i < nproclimit; i++)
+		if(events[i].pid <= 0) return i;
+	assert("out of slots!!", 0);
+	return 0;	/* cyntax */
+}
+
+int
+pidslot(int pid)
+{
+	int i;
+
+	for(i = 0; i < nevents; i++)
+		if(events[i].pid == pid) return(i);
+	if(DEBUG(D_EXEC))
+		fprint(2, "mk: wait returned unexpected process %d\n", pid);
+	return(-1);
+}
+
+
+static void
+pnew(int pid, int status)
+{
+	Process *p;
+
+	if(pfree){
+		p = pfree;
+		pfree = p->f;
+	} else
+		p = (Process *)Malloc(sizeof(Process));
+	p->pid = pid;
+	p->status = status;
+	p->f = phead;
+	phead = p;
+	if(p->f)
+		p->f->b = p;
+	p->b = 0;
+}
+
+static void
+pdelete(Process *p)
+{
+	if(p->f)
+		p->f->b = p->b;
+	if(p->b)
+		p->b->f = p->f;
+	else
+		phead = p->f;
+	p->f = pfree;
+	pfree = p;
+}
+
+void
+killchildren(char *msg)
+{
+	Process *p;
+
+	kflag = 1;	/* to make sure waitup doesn't exit */
+	jobs = 0;	/* make sure no more get scheduled */
+	for(p = phead; p; p = p->f)
+		expunge(p->pid, msg);
+	while(waitup(1, (int *)0) == 0)
+		;
+	Bprint(&bout, "mk: %s\n", msg);
+	Exit();
+}
+
+static long tslot[1000];
+static long tick;
+
+void
+usage(void)
+{
+	long t;
+
+	t = time(0);
+	if(tick)
+		tslot[nrunning] += (t-tick);
+	tick = t;
+}
+
+void
+prusage(void)
+{
+	int i;
+
+	usage();
+	for(i = 0; i <= nevents; i++)
+		fprint(1, "%d: %ld\n", i, tslot[i]);
+}
--- /dev/null
+++ b/utils/mk/sh.c
@@ -1,0 +1,189 @@
+#include	"mk.h"
+
+char	*termchars = "\"'= \t";	/*used in parse.c to isolate assignment attribute*/
+char	*shflags = 0;
+int	IWS = ' ';		/* inter-word separator in env */
+
+/*
+ *	This file contains functions that depend on the shell's syntax.  Most
+ *	of the routines extract strings observing the shell's escape conventions.
+ */
+
+
+/*
+ *	skip a token in quotes.
+ */
+static char *
+squote(char *cp, int c)
+{
+	Rune r;
+	int n;
+
+	while(*cp){
+		n = chartorune(&r, cp);
+		if(r == c)
+			return cp;
+		if(r == '\\')
+			n += chartorune(&r, cp+n);
+		cp += n;
+	}
+	SYNERR(-1);		/* should never occur */
+	fprint(2, "missing closing '\n");
+	return 0;
+}
+/*
+ *	search a string for unescaped characters in a pattern set
+ */
+char *
+charin(char *cp, char *pat)
+{
+	Rune r;
+	int n, vargen;
+
+	vargen = 0;
+	while(*cp){
+		n = chartorune(&r, cp);
+		switch(r){
+		case '\\':			/* skip escaped char */
+			cp += n;
+			n = chartorune(&r, cp);
+			break;
+		case '\'':			/* skip quoted string */
+		case '"':
+			cp = squote(cp+1, r);	/* n must = 1 */
+			if(!cp)
+				return 0;
+			break;
+		case '$':
+			if(*(cp+1) == '{')
+				vargen = 1;
+			break;
+		case '}':
+			if(vargen)
+				vargen = 0;
+			else if(utfrune(pat, r))
+				return cp;
+			break;
+		default:
+			if(vargen == 0 && utfrune(pat, r))
+				return cp;
+			break;
+		}
+		cp += n;
+	}
+	if(vargen){
+		SYNERR(-1);
+		fprint(2, "missing closing } in pattern generator\n");
+	}
+	return 0;
+}
+
+/*
+ *	extract an escaped token.  Possible escape chars are single-quote,
+ *	double-quote,and backslash.
+ */
+char*
+expandquote(char *s, Rune esc, Bufblock *b)
+{
+	Rune r;
+
+	if (esc == '\\') {
+		s += chartorune(&r, s);
+		rinsert(b, r);
+		return s;
+	}
+
+	while(*s){
+		s += chartorune(&r, s);
+		if(r == esc)
+			return s;
+		if (r == '\\') {
+			rinsert(b, r);
+			s += chartorune(&r, s);
+		}
+		rinsert(b, r);
+	}
+	return 0;
+}
+
+/*
+ *	Input an escaped token.  Possible escape chars are single-quote,
+ *	double-quote and backslash.
+ */
+int
+escapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+	int c, line;
+
+	if(esc == '\\') {
+		c = Bgetrune(bp);
+		if(c == '\r')
+			c = Bgetrune(bp);
+		if (c == '\n')
+			mkinline++;
+		rinsert(buf, c);
+		return 1;
+	}
+
+	line = mkinline;
+	while((c = nextrune(bp, 0)) >= 0){
+		if(c == esc){
+			if(preserve)
+				rinsert(buf, c);
+			return 1;
+		}
+		if(c == '\\') {
+			rinsert(buf, c);
+			c = Bgetrune(bp);
+			if(c == '\r')
+				c = Bgetrune(bp);
+			if (c < 0)
+				break;
+			if (c == '\n')
+				mkinline++;
+		}
+		rinsert(buf, c);
+	}
+	SYNERR(line); fprint(2, "missing closing %c\n", esc);
+	return 0;
+}
+
+/*
+ *	copy a quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Rune q, Bufblock *buf)
+{
+	Rune r;
+
+	while(*s){
+		s += chartorune(&r, s);
+		rinsert(buf, r);
+		if(r == q)
+			break;
+	}
+	return s;
+}
+/*
+ *	check for quoted strings.  backquotes are handled here; single quotes above.
+ *	s points to char after opening quote, q.
+ */
+char *
+copyq(char *s, Rune q, Bufblock *buf)
+{
+	if(q == '\'' || q == '"')		/* copy quoted string */
+		return copysingle(s, q, buf);
+
+	if(q != '`')				/* not quoted */
+		return s;
+
+	while(*s){				/* copy backquoted string */
+		s += chartorune(&q, s);
+		rinsert(buf, q);
+		if(q == '`')
+			break;
+		if(q == '\'' || q == '"')
+			s = copysingle(s, q, buf);	/* copy quoted string */
+	}
+	return s;
+}
--- /dev/null
+++ b/utils/mk/shprint.c
@@ -1,0 +1,88 @@
+#include	"mk.h"
+
+static char *vexpand(char*, Envy*, Bufblock*);
+
+void
+shprint(char *s, Envy *env, Bufblock *buf)
+{
+	int n;
+	Rune r;
+
+	while(*s) {
+		n = chartorune(&r, s);
+		if (r == '$')
+			s = vexpand(s, env, buf);
+		else {
+			rinsert(buf, r);
+			s += n;
+			s = copyq(s, r, buf);	/*handle quoted strings*/
+		}
+	}
+	insert(buf, 0);
+}
+
+static char *
+mygetenv(char *name, Envy *env)
+{
+	if (!env)
+		return 0;
+	if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == 0)
+		return 0;
+		/* only resolve internal variables and variables we've set */
+	for(; env->name; env++){
+		if (strcmp(env->name, name) == 0)
+			return wtos(env->values, ' ');
+	}
+	return 0;
+}
+
+static char *
+vexpand(char *w, Envy *env, Bufblock *buf)
+{
+	char *s, carry, *p, *q;
+
+	assert("vexpand no $", *w == '$');
+	p = w+1;	/* skip dollar sign */
+	if(*p == '{') {
+		p++;
+		q = utfrune(p, '}');
+		if (!q)
+			q = strchr(p, 0);
+	} else
+		q = shname(p);
+	carry = *q;
+	*q = 0;
+	s = mygetenv(p, env);
+	*q = carry;
+	if (carry == '}')
+		q++;
+	if (s) {
+		bufcpy(buf, s, strlen(s));
+		free(s);
+	} else 		/* copy name intact*/
+		bufcpy(buf, w, q-w);
+	return(q);
+}
+
+void
+front(char *s)
+{
+	char *t, *q;
+	int i, j;
+	char *flds[512];
+
+	q = strdup(s);
+	i = getfields(q, flds, 512, 0, " \t\n");
+	if(i > 5){
+		flds[4] = flds[i-1];
+		flds[3] = "...";
+		i = 5;
+	}
+	t = s;
+	for(j = 0; j < i; j++){
+		for(s = flds[j]; *s; *t++ = *s++);
+		*t++ = ' ';
+	}
+	*t = 0;
+	free(q);
+}
--- /dev/null
+++ b/utils/mk/symtab.c
@@ -1,0 +1,97 @@
+#include	"mk.h"
+
+#define	NHASH	4099
+#define	HASHMUL	79L	/* this is a good value */
+static Symtab *hash[NHASH];
+
+void
+syminit(void)
+{
+	Symtab **s, *ss, *next;
+
+	for(s = hash; s < &hash[NHASH]; s++){
+		for(ss = *s; ss; ss = next){
+			next = ss->next;
+			free((char *)ss);
+		}
+		*s = 0;
+	}
+}
+
+Symtab *
+symlook(char *sym, int space, void *install)
+{
+	long h;
+	char *p;
+	Symtab *s;
+
+	for(p = sym, h = space; *p; h += *p++)
+		h *= HASHMUL;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h]; s; s = s->next)
+		if((s->space == space) && (strcmp(s->name, sym) == 0))
+			return(s);
+	if(install == 0)
+		return(0);
+	s = (Symtab *)Malloc(sizeof(Symtab));
+	s->space = space;
+	s->name = sym;
+	s->value = install;
+	s->next = hash[h];
+	hash[h] = s;
+	return(s);
+}
+
+void
+symdel(char *sym, int space)
+{
+	long h;
+	char *p;
+	Symtab *s, *ls;
+
+	/* multiple memory leaks */
+
+	for(p = sym, h = space; *p; h += *p++)
+		h *= HASHMUL;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h], ls = 0; s; ls = s, s = s->next)
+		if((s->space == space) && (strcmp(s->name, sym) == 0)){
+			if(ls)
+				ls->next = s->next;
+			else
+				hash[h] = s->next;
+			free((char *)s);
+		}
+}
+
+void
+symtraverse(int space, void (*fn)(Symtab*))
+{
+	Symtab **s, *ss;
+
+	for(s = hash; s < &hash[NHASH]; s++)
+		for(ss = *s; ss; ss = ss->next)
+			if(ss->space == space)
+				(*fn)(ss);
+}
+
+void
+symstat(void)
+{
+	Symtab **s, *ss;
+	int n;
+	int l[1000];
+
+	memset((char *)l, 0, sizeof(l));
+	for(s = hash; s < &hash[NHASH]; s++){
+		for(ss = *s, n = 0; ss; ss = ss->next)
+			n++;
+		l[n]++;
+	}
+	for(n = 0; n < 1000; n++)
+		if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n);
+}
--- /dev/null
+++ b/utils/mk/var.c
@@ -1,0 +1,41 @@
+#include	"mk.h"
+
+void
+setvar(char *name, void *value)
+{
+	symlook(name, S_VAR, value)->value = value;
+	symlook(name, S_MAKEVAR, (void*)"");
+}
+
+static void
+print1(Symtab *s)
+{
+	Word *w;
+
+	Bprint(&bout, "\t%s=", s->name);
+	for (w = (Word *) s->value; w; w = w->next)
+		Bprint(&bout, "'%s'", w->s);
+	Bprint(&bout, "\n");
+}
+
+void
+dumpv(char *s)
+{
+	Bprint(&bout, "%s:\n", s);
+	symtraverse(S_VAR, print1);
+}
+
+char *
+shname(char *a)
+{
+	Rune r;
+	int n;
+
+	while (*a) {
+		n = chartorune(&r, a);
+		if (!WORDCHR(r))
+			break;
+		a += n;
+	}
+	return a;
+}
--- /dev/null
+++ b/utils/mk/varsub.c
@@ -1,0 +1,256 @@
+#include	"mk.h"
+
+static	Word		*subsub(Word*, char*, char*);
+static	Word		*expandvar(char**);
+static	Bufblock	*varname(char**);
+static	Word		*extractpat(char*, char**, char*, char*);
+static	int		submatch(char*, Word*, Word*, int*, char**);
+static	Word		*varmatch(char *, char**);
+
+Word *
+varsub(char **s)
+{
+	Bufblock *b;
+	Word *w;
+
+	if(**s == '{')		/* either ${name} or ${name: A%B==C%D}*/
+		return expandvar(s);
+
+	b = varname(s);
+	if(b == 0)
+		return 0;
+
+	w = varmatch(b->start, s);
+	freebuf(b);
+	return w;
+}
+
+/*
+ *	extract a variable name
+ */
+static Bufblock*
+varname(char **s)
+{
+	Bufblock *b;
+	char *cp;
+	Rune r;
+	int n;
+
+	b = newbuf();
+	cp = *s;
+	for(;;){
+		n = chartorune(&r, cp);
+		if (!WORDCHR(r))
+			break;
+		rinsert(b, r);
+		cp += n;
+	}
+	if (b->current == b->start){
+		SYNERR(-1);
+		fprint(2, "missing variable name <%s>\n", *s);
+		freebuf(b);
+		return 0;
+	}
+	*s = cp;
+	insert(b, 0);
+	return b;
+}
+
+static Word*
+varmatch(char *name, char **s)
+{
+	Word *w;
+	Symtab *sym;
+	char *cp;
+	
+	sym = symlook(name, S_VAR, 0);
+	if(sym){
+			/* check for at least one non-NULL value */
+		for (w = (Word*)sym->value; w; w = w->next)
+			if(w->s && *w->s)
+				return wdup(w);
+	}
+	for(cp = *s; *cp == ' ' || *cp == '\t'; cp++)	/* skip trailing whitespace */
+			;
+	*s = cp;
+	return 0;
+}
+
+static Word*
+expandvar(char **s)
+{
+	Word *w;
+	Bufblock *buf;
+	Symtab *sym;
+	char *cp, *begin, *end;
+
+	begin = *s;
+	(*s)++;						/* skip the '{' */
+	buf = varname(s);
+	if (buf == 0)
+		return 0;
+	cp = *s;
+	if (*cp == '}') {				/* ${name} variant*/
+		(*s)++;					/* skip the '}' */
+		w = varmatch(buf->start, s);
+		freebuf(buf);
+		return w;
+	}
+	if (*cp != ':') {
+		SYNERR(-1);
+		fprint(2, "bad variable name <%s>\n", buf->start);
+		freebuf(buf);
+		return 0;
+	}
+	cp++;
+	end = charin(cp , "}");
+	if(end == 0){
+		SYNERR(-1);
+		fprint(2, "missing '}': %s\n", begin);
+		Exit();
+	}
+	*end = 0;
+	*s = end+1;
+	
+	sym = symlook(buf->start, S_VAR, 0);
+	if(sym == 0 || sym->value == 0)
+		w = newword(buf->start);
+	else
+		w = subsub((Word*) sym->value, cp, end);
+	freebuf(buf);
+	return w;
+}
+
+static Word*
+extractpat(char *s, char **r, char *term, char *end)
+{
+	int save;
+	char *cp;
+	Word *w;
+
+	cp = charin(s, term);
+	if(cp){
+		*r = cp;
+		if(cp == s)
+			return 0;
+		save = *cp;
+		*cp = 0;
+		w = stow(s);
+		*cp = save;
+	} else {
+		*r = end;
+		w = stow(s);
+	}
+	return w;
+}
+
+static Word*
+subsub(Word *v, char *s, char *end)
+{
+	int nmid;
+	Word *head, *tail, *w, *h;
+	Word *a, *b, *c, *d;
+	Bufblock *buf;
+	char *cp, *enda;
+
+	a = extractpat(s, &cp, "=%&", end);
+	b = c = d = 0;
+	if(PERCENT(*cp))
+		b = extractpat(cp+1, &cp, "=", end);
+	if(*cp == '=')
+		c = extractpat(cp+1, &cp, "&%", end);
+	if(PERCENT(*cp))
+		d = stow(cp+1);
+	else if(*cp)
+		d = stow(cp);
+
+	head = tail = 0;
+	buf = newbuf();
+	for(; v; v = v->next){
+		h = w = 0;
+		if(submatch(v->s, a, b, &nmid, &enda)){
+			/* enda points to end of A match in source;
+			 * nmid = number of chars between end of A and start of B
+			 */
+			if(c){
+				h = w = wdup(c);
+				while(w->next)
+					w = w->next;
+			}
+			if(PERCENT(*cp) && nmid > 0){	
+				if(w){
+					bufcpy(buf, w->s, strlen(w->s));
+					bufcpy(buf, enda, nmid);
+					insert(buf, 0);
+					free(w->s);
+					w->s = strdup(buf->start);
+				} else {
+					bufcpy(buf, enda, nmid);
+					insert(buf, 0);
+					h = w = newword(buf->start);
+				}
+				buf->current = buf->start;
+			}
+			if(d && *d->s){
+				if(w){
+
+					bufcpy(buf, w->s, strlen(w->s));
+					bufcpy(buf, d->s, strlen(d->s));
+					insert(buf, 0);
+					free(w->s);
+					w->s = strdup(buf->start);
+					w->next = wdup(d->next);
+					while(w->next)
+						w = w->next;
+					buf->current = buf->start;
+				} else
+					h = w = wdup(d);
+			}
+		}
+		if(w == 0)
+			h = w = newword(v->s);
+	
+		if(head == 0)
+			head = h;
+		else
+			tail->next = h;
+		tail = w;
+	}
+	freebuf(buf);
+	delword(a);
+	delword(b);
+	delword(c);
+	delword(d);
+	return head;
+}
+
+static int
+submatch(char *s, Word *a, Word *b, int *nmid, char **enda)
+{
+	Word *w;
+	int n;
+	char *end;
+
+	n = 0;
+	for(w = a; w; w = w->next){
+		n = strlen(w->s);
+		if(strncmp(s, w->s, n) == 0)
+			break;
+	}
+	if(a && w == 0)		/*  a == NULL matches everything*/
+		return 0;
+
+	*enda = s+n;		/* pointer to end a A part match */
+	*nmid = strlen(s)-n;	/* size of remainder of source */
+	end = *enda+*nmid;
+	for(w = b; w; w = w->next){
+		n = strlen(w->s);
+		if(strcmp(w->s, end-n) == 0){
+			*nmid -= n;
+			break;
+		}
+	}
+	if(b && w == 0)		/* b == NULL matches everything */
+		return 0;
+	return 1;
+}
--- /dev/null
+++ b/utils/mk/word.c
@@ -1,0 +1,180 @@
+#include	"mk.h"
+
+static	Word	*nextword(char**);
+
+Word*
+newword(char *s)
+{
+	Word *w;
+
+	w = (Word *)Malloc(sizeof(Word));
+	w->s = strdup(s);
+	w->next = 0;
+	return(w);
+}
+
+Word *
+stow(char *s)
+{
+	Word *head, *w, *new;
+
+	w = head = 0;
+	while(*s){
+		new = nextword(&s);
+		if(new == 0)
+			break;
+		if (w)
+			w->next = new;
+		else
+			head = w = new;
+		while(w->next)
+			w = w->next;
+		
+	}
+	if (!head)
+		head = newword("");
+	return(head);
+}
+
+char *
+wtos(Word *w, int sep)
+{
+	Bufblock *buf;
+	char *cp;
+
+	buf = newbuf();
+	for(; w; w = w->next){
+		for(cp = w->s; *cp; cp++)
+			insert(buf, *cp);
+		if(w->next)
+			insert(buf, sep);
+	}
+	insert(buf, 0);
+	cp = strdup(buf->start);
+	freebuf(buf);
+	return(cp);
+}
+
+Word*
+wdup(Word *w)
+{
+	Word *v, *new, *base;
+
+	v = base = 0;
+	while(w){
+		new = newword(w->s);
+		if(v)
+			v->next = new;
+		else
+			base = new;
+		v = new;
+		w = w->next;
+	}
+	return base;
+}
+
+void
+delword(Word *w)
+{
+	Word *v;
+
+	while(v = w){
+		w = w->next;
+		if(v->s)
+			free(v->s);
+		free(v);
+	}
+}
+
+/*
+ *	break out a word from a string handling quotes, executions,
+ *	and variable expansions.
+ */
+static Word*
+nextword(char **s)
+{
+	Bufblock *b;
+	Word *head, *tail, *w;
+	Rune r;
+	char *cp;
+
+	cp = *s;
+	b = newbuf();
+	head = tail = 0;
+	while(*cp == ' ' || *cp == '\t')		/* leading white space */
+		cp++;
+	while(*cp){
+		cp += chartorune(&r, cp);
+		switch(r)
+		{
+		case ' ':
+		case '\t':
+		case '\n':
+			goto out;
+		case '\\':
+		case '\'':
+		case '"':
+			cp = expandquote(cp, r, b);
+			if(cp == 0){
+				fprint(2, "missing closing quote: %s\n", *s);
+				Exit();
+			}
+			break;
+		case '$':
+			w = varsub(&cp);
+			if(w == 0)
+				break;
+			if(b->current != b->start){
+				bufcpy(b, w->s, strlen(w->s));
+				insert(b, 0);
+				free(w->s);
+				w->s = strdup(b->start);
+				b->current = b->start;
+			}
+			if(head){
+				bufcpy(b, tail->s, strlen(tail->s));
+				bufcpy(b, w->s, strlen(w->s));
+				insert(b, 0);
+				free(tail->s);
+				tail->s = strdup(b->start);
+				tail->next = w->next;
+				free(w->s);
+				free(w);
+				b->current = b->start;
+			} else
+				tail = head = w;
+			while(tail->next)
+				tail = tail->next;
+			break;
+		default:
+			rinsert(b, r);
+			break;
+		}
+	}
+out:
+	*s = cp;
+	if(b->current != b->start){
+		if(head){
+			cp = b->current;
+			bufcpy(b, tail->s, strlen(tail->s));
+			bufcpy(b, b->start, cp-b->start);
+			insert(b, 0);
+			free(tail->s);
+			tail->s = strdup(cp);
+		} else {
+			insert(b, 0);
+			head = newword(b->start);
+		}
+	}
+	freebuf(b);
+	return head;
+}
+
+void
+dumpw(char *s, Word *w)
+{
+	Bprint(&bout, "%s", s);
+	for(; w; w = w->next)
+		Bprint(&bout, " '%s'", w->s);
+	Bputc(&bout, '\n');
+}
--- /dev/null
+++ b/utils/mkdir/mkdir.c
@@ -1,0 +1,60 @@
+#include <lib9.h>
+
+static void
+usage(void)
+{
+	fprint(2, "usage: mkdir [-p] dir ...\n");
+	exits("usage");
+}
+
+static int
+mkdirp(char *s, int pflag)
+{
+	char *p;
+
+	if(!pflag) {
+		if(access(s, 0) == 0){
+			fprint(2, "mkdir: %s already exists\n", s);
+			exits("exists");
+		}
+		return mkdir(s);
+	}
+
+	/* create intermediate directories */
+	p = strchr(s+1, '/');
+	while(p != nil) {
+		*p = '\0';
+		if(access(s, 0) != 0 && mkdir(s) != 0)
+			return -1;
+		*p = '/';
+		p = strchr(p+1, '/');
+	}
+
+	/* create final directory */
+	if(access(s, 0) == 0)
+		return 0;
+	return mkdir(s);
+}
+
+void
+main(int argc, char **argv)
+{
+	int pflag;
+
+	pflag = 0;
+	ARGBEGIN{
+	case 'p':
+		pflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+	for(; *argv; argv++){
+		if(mkdirp(*argv, pflag) < 0){
+			fprint(2, "mkdir: can't create %s\n", *argv);
+			perror(0);
+			exits("error");
+		}
+	}
+	exits(0);
+}
--- /dev/null
+++ b/utils/mkdir/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=mkdir
+
+OFILES=	mkdir.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/mkext/mkext.c
@@ -1,0 +1,313 @@
+#include <lib9.h>
+#include <bio.h>
+
+#define	dirfwstat(b,d) 0	/* lib9 doesn't implement it */
+#define	mkdir extmkdir
+
+enum{
+	LEN	= 8*1024,
+	NFLDS	= 6,		/* filename, modes, uid, gid, mtime, bytes */
+};
+
+int	selected(char*, int, char*[]);
+void	mkdirs(char*, char*);
+void	mkdir(char*, ulong, ulong, char*, char*);
+void	extract(char*, ulong, ulong, char*, char*, ulong);
+void	seekpast(ulong);
+void	error(char*, ...);
+void	warn(char*, ...);
+void	usage(void);
+#pragma varargck argpos warn 1
+#pragma varargck argpos error 1
+
+Biobuf bin;
+uchar	binbuf[2*LEN];
+char	linebuf[LEN];
+int	uflag;
+int	hflag;
+int	vflag;
+int	Tflag;
+
+void
+main(int argc, char **argv)
+{
+	Biobuf bout;
+	char *fields[NFLDS], name[2*LEN], *p, *namep;
+	char *uid, *gid;
+	ulong mode, bytes, mtime;
+
+	setbinmode();
+	quotefmtinstall();
+	namep = name;
+	ARGBEGIN{
+	case 'd':
+		p = ARGF();
+		if(strlen(p) >= LEN)
+			error("destination fs name too long\n");
+		strcpy(name, p);
+		namep = name + strlen(name);
+		break;
+	case 'h':
+		hflag = 1;
+		Binit(&bout, 1, OWRITE);
+		break;
+	case 'u':
+		uflag = 1;
+		Tflag = 1;
+		break;
+	case 'T':
+		Tflag = 1;
+		break;
+	case 'v':
+		vflag = 1;
+		break;
+	default:
+		usage();
+	}ARGEND
+	
+	Binits(&bin, 0, OREAD, binbuf, sizeof binbuf);
+	while(p = Brdline(&bin, '\n')){
+		p[Blinelen(&bin)-1] = '\0';
+		strcpy(linebuf, p);
+		p = linebuf;
+		if(strcmp(p, "end of archive") == 0){
+			Bterm(&bout);
+			fprint(2, "done\n");
+			exits(0);
+		}
+		if(getfields(p, fields, NFLDS, 0, " \t") != NFLDS){
+			warn("too few fields in file header");
+			continue;
+		}
+		strcpy(namep, fields[0]);
+		mode = strtoul(fields[1], 0, 8);
+		uid = fields[2];
+		gid = fields[3];
+		mtime = strtoul(fields[4], 0, 10);
+		bytes = strtoul(fields[5], 0, 10);
+		if(argc){
+			if(!selected(namep, argc, argv)){
+				if(bytes)
+					seekpast(bytes);
+				continue;
+			}
+			mkdirs(name, namep);
+		}
+		if(hflag){
+			Bprint(&bout, "%q %luo %q %q %lud %lud\n",
+				name, mode, uid, gid, mtime, bytes);
+			if(bytes)
+				seekpast(bytes);
+			continue;
+		}
+		if(mode & DMDIR)
+			mkdir(name, mode, mtime, uid, gid);
+		else
+			extract(name, mode, mtime, uid, gid, bytes);
+	}
+	fprint(2, "premature end of archive\n");
+	exits("premature end of archive");
+}
+
+int
+fileprefix(char *prefix, char *s)
+{
+	while(*prefix)
+		if(*prefix++ != *s++)
+			return 0;
+	if(*s && *s != '/')
+		return 0;
+	return 1;
+}
+
+int
+selected(char *s, int argc, char *argv[])
+{
+	int i;
+
+	for(i=0; i<argc; i++)
+		if(fileprefix(argv[i], s))
+			return 1;
+	return 0;
+}
+
+void
+mkdirs(char *name, char *namep)
+{
+	char buf[2*LEN], *p;
+	int fd;
+
+	strcpy(buf, name);
+	for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){
+		if(p[1] == '\0')
+			return;
+		*p = 0;
+		fd = create(buf, OREAD, 0775|DMDIR);
+		close(fd);
+		*p = '/';
+	}
+}
+
+void
+mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
+{
+	Dir *d, xd;
+	int fd;
+	char *p;
+	char olderr[256];
+
+	fd = create(name, OREAD, mode);
+	if(fd < 0){
+		rerrstr(olderr, sizeof(olderr));
+		if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
+			free(d);
+			warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
+			return;
+		}
+		free(d);
+	}
+	close(fd);
+
+	d = &xd;
+	nulldir(d);
+	p = utfrrune(name, L'/');
+	if(p)
+		p++;
+	else
+		p = name;
+	d->name = p;
+	if(uflag){
+		d->uid = uid;
+		d->gid = gid;
+	}
+	if(Tflag)
+		d->mtime = mtime;
+	d->mode = mode;
+	if(dirwstat(name, d) < 0)
+		warn("can't set modes for %q: %r", name);
+
+	if(uflag||Tflag){
+		if((d = dirstat(name)) == nil){
+			warn("can't reread modes for %q: %r", name);
+			return;
+		}
+		if(Tflag && d->mtime != mtime)
+			warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime);
+		if(uflag && strcmp(uid, d->uid))
+			warn("%q: uid mismatch %q %q", name, uid, d->uid);
+		if(uflag && strcmp(gid, d->gid))
+			warn("%q: gid mismatch %q %q", name, gid, d->gid);
+	}
+}
+
+void
+extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, ulong bytes)
+{
+	Dir d, *nd;
+	Biobuf *b;
+	char buf[LEN];
+	char *p;
+	ulong n, tot;
+
+	if(vflag)
+		print("x %q %lud bytes\n", name, bytes);
+
+	b = Bopen(name, OWRITE);
+	if(!b){
+		warn("can't make file %q: %r", name);
+		seekpast(bytes);
+		return;
+	}
+	for(tot = 0; tot < bytes; tot += n){
+		n = sizeof buf;
+		if(tot + n > bytes)
+			n = bytes - tot;
+		n = Bread(&bin, buf, n);
+		if(n <= 0)
+			error("premature eof reading %q", name);
+		if(Bwrite(b, buf, n) != n)
+			warn("error writing %q: %r", name);
+	}
+
+	nulldir(&d);
+	p = utfrrune(name, '/');
+	if(p)
+		p++;
+	else
+		p = name;
+	d.name = p;
+	if(uflag){
+		d.uid = uid;
+		d.gid = gid;
+	}
+	if(Tflag)
+		d.mtime = mtime;
+	d.mode = mode;
+	Bflush(b);
+	if(dirfwstat(Bfildes(b), &d) < 0)
+		warn("can't set modes for %q: %r", name);
+	if(uflag||Tflag){
+		if((nd = dirfstat(Bfildes(b))) == nil)
+			warn("can't reread modes for %q: %r", name);
+		else{
+			if(Tflag && nd->mtime != mtime)
+				warn("%q: time mismatch %lud %lud\n", name, mtime, nd->mtime);
+			if(uflag && strcmp(uid, nd->uid))
+				warn("%q: uid mismatch %q %q", name, uid, nd->uid);
+			if(uflag && strcmp(gid, nd->gid))
+				warn("%q: gid mismatch %q %q", name, gid, nd->gid);
+			free(nd);
+		}
+	}
+	Bterm(b);
+}
+
+void
+seekpast(ulong bytes)
+{
+	char buf[LEN];
+	ulong tot, n;
+
+	for(tot = 0; tot < bytes; tot += n){
+		n = sizeof buf;
+		if(tot + n > bytes)
+			n = bytes - tot;
+		n = Bread(&bin, buf, n);
+		if(n < 0)
+			error("premature eof");
+	}
+}
+
+void
+error(char *fmt, ...)
+{
+	char buf[1024];
+	va_list arg;
+
+	sprint(buf, "%q: ", argv0);
+	va_start(arg, fmt);
+	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s\n", buf);
+	exits(0);
+}
+
+void
+warn(char *fmt, ...)
+{
+	char buf[1024];
+	va_list arg;
+
+	sprint(buf, "%q: ", argv0);
+	va_start(arg, fmt);
+	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s\n", buf);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
+	exits("usage");
+}
--- /dev/null
+++ b/utils/mkext/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=mkext
+
+OFILES=	mkext.$O\
+
+HFILES= 
+
+LIBS=bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/mkfile
@@ -1,0 +1,110 @@
+<../mkconfig
+
+#
+#	Utils we build everywhere, because the Plan 9 versions don't yet
+#	contain our changes (or they don't exist on Plan 9).
+#	Fairly soon the Plan 9 compilers will be updated to match.
+#
+ALWAYS=\
+	libmach\
+	libregexp\
+	iyacc\
+	iar\
+	cc\
+	5coff\
+	5cv\
+	sqz\
+	acid\
+	srclist\
+	ftl\
+	ms2\
+	data2c\
+	data2s\
+	idea\
+	kprof\
+	c2l\
+	mkppcimage\
+	nm\
+
+#
+#	Utils we build on Posix and Nt, which already exist on Plan 9.
+#
+NOTPLAN9=\
+	5a\
+	5c\
+	5l\
+	8a\
+	8c\
+	8l\
+	qa\
+	qc\
+	ql\
+	va\
+	vc\
+	vl\
+	ka\
+	kc\
+	kl\
+	mk\
+	ksize\
+	kstrip\
+	md5sum\
+	mkext\
+	ndate\
+
+#
+#	Utils we build on Nt, for build environment compatibility.
+#
+NTONLY=\
+	cat\
+	cp\
+	echo\
+	format\
+	mkdir\
+	mv\
+	ntsrv\
+	rcsh\
+	rm\
+	sed\
+	test\
+	tr\
+
+#
+# Thumb-1
+#
+OBSOLETE=\
+	ta\
+	tc\
+	tl\
+
+all:QV:		all-$TARGMODEL
+clean:QV:	clean-$TARGMODEL
+install:QV:	install-$TARGMODEL
+installall:QV:	installall-$TARGMODEL
+nuke:QV:	nuke-$TARGMODEL
+
+%-Plan9:QV:
+	for (j in $ALWAYS)
+	{
+		test -d $j && {
+			echo '@{cd' $j '; mk $MKFLAGS $stem}'
+			@{cd $j; mk $MKFLAGS $stem }
+		} || test ! -e $j
+	}
+
+%-Posix:QV:
+	for j in  $ALWAYS $NOTPLAN9
+	do
+		test -d $j || continue
+		echo "(cd $j; mk $MKFLAGS $stem)"
+		(cd $j; mk $MKFLAGS $stem) || exit 1
+	done
+
+%-Nt:QV:
+	for (j in  $ALWAYS $NTONLY $NOTPLAN9)
+	{
+		test -d $j && {
+			echo.exe '@{cd' $j '; mk $MKFLAGS $stem}'
+			@{cd $j; mk $MKFLAGS $stem }
+		} || test ! -e $j
+	}
--- /dev/null
+++ b/utils/mkppcimage/mkfile
@@ -1,0 +1,18 @@
+<../../mkconfig
+
+TARG=mkppcimage
+
+OFILES=	mkppcimage.$O\
+
+HFILES=\
+	a.out.h\
+	bio.h\
+	mach.h\
+
+LIBS=mach bio 9	# order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/mkppcimage/mkppcimage.c
@@ -1,0 +1,261 @@
+/*
+ * generate the special header needed by DENX's ppcboot
+ */
+
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * the following values are all determined by PPCBOOT
+ */
+enum {
+	/* OS code */
+	IH_OS_NCR = 12,	/* we'll use this for the time being; there isn't a general purpose one */
+
+	/* CPU code */
+	IH_CPU_ARM = 2,
+	IH_CPU_PPC = 7,
+
+	/* image type */
+	IH_TYPE_KERNEL = 2,	/* OS kernel */
+
+	/* compression type */
+	IH_COMP_NONE = 0,	/* none: if i need one, i'll use sqz internally, much faster */
+
+	IH_MAGIC = 0x27051956,	/* Image Magic Number		*/
+
+	IH_NMLEN = 32	/* image name length */
+};
+
+typedef struct Imagehdr Imagehdr;
+struct Imagehdr
+{
+	ulong	magic;	/* image header magic number	*/
+	ulong	hcrc;		/* image header crc	*/
+	ulong	time;		/* image creation timestamp	*/
+	ulong	size;		/* image data size */
+	ulong	load;		/* data  load address */
+	ulong	entry;		/* entry point address */
+	ulong	dcrc;		/* image data crc */
+	uchar	os;		/* operating system */
+	uchar	arch;	/* cpu type */
+	uchar	type;	/* image type */
+	uchar	comp;	/* compression type */
+	char		name[IH_NMLEN];	/* image name */
+};
+
+
+static Imagehdr ih;
+static char *fname;
+
+static ulong crc32(ulong, uchar*, unsigned int);
+static long Read(int, void*, long);
+static void	Write(int, void*, long);
+
+/*
+ * data is big endian
+ */
+static ulong
+swal(ulong l)
+{
+	return beswal(l);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: mkppcimage [-l loadaddr] q.out ppcboot.out\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int fd, ofd;
+	long len;
+	uchar *data;
+	ulong load, entry;
+	Exec ex;
+	int arch;
+
+	load = ~0;
+	entry = ~0;
+	ARGBEGIN{
+	case 'l':
+		load = strtoul(EARGF(usage()), nil, 0);
+		entry = load + sizeof(Exec);
+		break;
+	}ARGEND
+	if(*argv == nil || argv[1] == nil)
+		usage();
+	fname = *argv;
+	fd = open(*argv, OREAD);
+	if(fd < 0){
+		fprint(2, "mkppcimage: can't open %s: %r\n", *argv);
+		exits("image");
+	}
+	Read(fd, &ex, sizeof(Exec));
+	switch(beswal(ex.magic)){
+	case Q_MAGIC:	/* powerpc */
+		arch = IH_CPU_PPC;
+		break;
+	case E_MAGIC:	/* arm */
+		arch = IH_CPU_ARM;
+		break;
+	default:
+		arch = 0;
+		fprint(2, "mkppcboot: unknown magic: %8.8lux\n", beswal(ex.magic));
+		exits("bad magic");
+	}
+	len = beswal(ex.text);
+	len += beswal(ex.data);
+	if(load == ~0){
+		entry = beswal(ex.entry);
+		entry &= ~0xFF000000;	/* physical address */
+		load = entry - sizeof(Exec);
+	}
+	data = malloc(len+sizeof(Exec));
+	if(data == nil){
+		fprint(2, "mkppcimage: can't allocate data: %r\n");
+		exits("mem");
+	}
+	memmove(data, &ex, sizeof(Exec));
+	Read(fd, data+sizeof(Exec), len);
+	len += sizeof(Exec);
+	ih.magic = swal(IH_MAGIC);
+	ih.hcrc = 0;
+	ih.time = swal(time(0));
+	ih.size = swal(len);
+	ih.load = swal(load);
+	ih.entry = swal(entry);
+	ih.dcrc = swal(crc32(0, data, len));
+	ih.os = IH_OS_NCR;	/* why not */
+	ih.arch = arch;
+	ih.type = IH_TYPE_KERNEL;
+	ih.comp = IH_COMP_NONE;
+	strcpy(ih.name, "Himmel!");
+	ih.hcrc = swal(crc32(0, (uchar*)&ih, sizeof(ih)));
+	ofd = create(argv[1], OWRITE, 0666);
+	if(ofd < 0){
+		fprint(2, "mkppcimage: can't create %s: %r\n", argv[1]);
+		exits("image");
+	}
+	Write(ofd, &ih, sizeof(ih));
+	Write(ofd, data, len);
+	exits(nil);
+}
+
+static long
+Read(int fd, void *buf, long nb)
+{
+	long n;
+
+	n = readn(fd, buf, nb);
+	if(n < 0){
+		fprint(2, "mkppcimage: %s: read error: %r\n", fname);
+		exits("read");
+	}
+	if(n < nb){
+		fprint(2, "mkppcimage: %s: unexpected end-of-file\n", fname);
+		exits("read");
+	}
+	return n;
+}
+
+static void
+Write(int fd, void *buf, long nb)
+{
+	if(write(fd, buf, nb) != nb){
+		fprint(2, "mkppcboot: write error: %r\n");
+		exits("write err");
+	}
+}
+
+/*
+ * The remainder of this file is derived from crc32.c from the zlib-1.1.3 distribution
+ * by Jean-loup Gailly and Mark Adler.
+ */
+
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* ========================================================================
+ * Table of CRC-32's of all single-byte values (made by make_crc_table)
+ */
+static ulong crc_table[256] = {
+  0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+  0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+  0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+  0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+  0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+  0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+  0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+  0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+  0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+  0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+  0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+  0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+  0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+  0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+  0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+  0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+  0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+  0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+  0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+  0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+  0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+  0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+  0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+  0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+  0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+  0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+  0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+  0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+  0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+  0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+  0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+  0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+  0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+  0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+  0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+  0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+  0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+  0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+  0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+  0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+  0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+  0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+  0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+  0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+  0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+  0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+  0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+  0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+  0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+  0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+  0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+  0x2d02ef8dL
+};
+
+
+#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+#define DO2(buf)  DO1(buf); DO1(buf);
+#define DO4(buf)  DO2(buf); DO2(buf);
+#define DO8(buf)  DO4(buf); DO4(buf);
+
+static ulong
+crc32(ulong crc, uchar *buf, unsigned len)
+{
+	crc = ~crc;
+	while (len >= 8) {
+		DO8(buf);
+		len -= 8;
+	}
+	if (len) do {
+		DO1(buf);
+	} while (--len);
+	return ~crc;
+}
--- /dev/null
+++ b/utils/ms2/mkfile
@@ -1,0 +1,16 @@
+<../../mkconfig
+CFLAGS=$CFLAGS -I../include
+
+TARG=ms2
+
+OFILES=	ms2.$O\
+
+HFILES=	\
+	$ROOT/include/bio.h\
+	../include/mach.h\
+
+LIBS=mach bio 9			#order matters
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/ms2/ms2.c
@@ -1,0 +1,186 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+void	record(uchar*, int);
+void	usage(void);
+void	dosegment(long, int);
+void trailer(ulong);
+void header(void);
+
+enum
+{
+	Recordsize = 32
+};
+
+int	dsegonly;
+int	supressend;
+int	binary;
+ulong	addr;
+ulong 	psize = 4096;
+ulong	startaddr = 0x030000;
+Biobuf 	bout;
+Biobuf	bio;
+
+void
+main(int argc, char **argv)
+{
+	Dir *dir;
+	Fhdr f;
+	int fd;
+
+	ARGBEGIN{
+	case 'd':
+		dsegonly++;
+		break;
+	case 's':
+		supressend++;
+		break;
+	case 'a':
+	case 'T':
+		addr = strtoul(ARGF(), 0, 0);
+		break;
+	case 'p':
+	case 'R':
+		psize = strtoul(ARGF(), 0, 0);
+		break;
+	case 'b':
+		binary++;
+		break;
+	case 'S':
+		startaddr = strtoul(ARGF(), 0, 0);
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	Binit(&bout, 1, OWRITE);
+
+	fd = open(argv[0], OREAD);
+	if(fd < 0) {
+		fprint(2, "ms2: open %s: %r\n", argv[0]);
+		exits("open");
+	}
+
+	if(binary) {
+		if((dir = dirfstat(fd)) == nil) {
+			fprint(2, "ms2: stat failed %r");
+			exits("dirfstat");
+		}
+		Binit(&bio, fd, OREAD);
+		header();
+		dosegment(0, dir->length);
+		if(supressend == 0)
+			trailer(startaddr);
+		Bterm(&bout);
+		Bterm(&bio);
+		free(dir);
+		exits(0);
+	}
+
+	if(crackhdr(fd, &f) == 0){
+		fprint(2, "ms2: bad magic: %r\n");
+		exits("magic");
+	}
+	seek(fd, 0, 0);
+
+	Binit(&bio, fd, OREAD);
+
+	header();
+	if(dsegonly)
+		dosegment(f.datoff, f.datsz);
+	else {
+		dosegment(f.txtoff, f.txtsz);
+		addr = (addr+(psize-1))&~(psize-1);
+		dosegment(f.datoff, f.datsz);
+	}
+
+	if(supressend == 0)
+		trailer(startaddr);
+
+	Bterm(&bout);
+	Bterm(&bio);
+	exits(0);
+}
+
+void
+dosegment(long foff, int len)
+{
+	int l, n;
+	uchar buf[2*Recordsize];
+
+	Bseek(&bio, foff, 0);
+	for(;;) {
+		l = len;
+		if(l > Recordsize)
+			l = Recordsize;
+		n = Bread(&bio, buf, l);
+		if(n == 0)
+			break;
+		if(n < 0) {
+			fprint(2, "ms2: read error: %r\n");
+			exits("read");
+		}
+		record(buf, l);
+		len -= l;
+	}
+}
+
+void
+record(uchar *s, int l)
+{
+	int i;
+	ulong cksum;
+
+	if(addr & (0xFF<<24)){
+		Bprint(&bout, "S3%.2X%.8lX", l+5, addr);
+		cksum = l+5;
+		cksum += (addr>>24)&0xff;
+	}else{
+		Bprint(&bout, "S2%.2X%.6lX", l+4, addr);
+		cksum = l+4;
+	}
+	cksum += addr&0xff;
+	cksum += (addr>>8)&0xff;
+	cksum += (addr>>16)&0xff;
+
+	for(i = 0; i < l; i++) {
+		cksum += *s;
+		Bprint(&bout, "%.2X", *s++);
+	}
+	Bprint(&bout, "%.2lX\n", (~cksum)&0xff);
+	addr += l;
+}
+
+void
+header(void)
+{
+	Bprint(&bout, "S0030000FC\n");
+}
+
+void
+trailer(ulong a)
+{
+	ulong cksum;
+
+	cksum = 0;
+	if(a & (0xFF<<24)){
+		Bprint(&bout, "S7%.8lX", a);
+		cksum += (a>>24)&0xff;
+	}else
+		Bprint(&bout, "S9%.6lX", a);
+	cksum += a&0xff;
+	cksum += (a>>8)&0xff;
+	cksum += (a>>16)&0xff;
+	Bprint(&bout, "%.2lX\n", (~cksum)&0xff);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: ms2 [-dsb] [-T address] [-R pagesize] [-S startaddress] ?.out\n");
+	exits("usage");
+}
--- /dev/null
+++ b/utils/mv/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=mv
+
+OFILES=	mv.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/mv/mv.c
@@ -1,0 +1,210 @@
+#include <lib9.h>
+
+void	split(char *, char **, char **);
+int	samefile(char *, char *);
+int	mv(char *from, char *todir, char *toelem);
+int	copy1(int fdf, int fdt, char *from, char *to);
+void	hardremove(char *);
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	int failed;
+	Dir *dirto, *dirfrom;
+	char *todir, *toelem;
+
+	if(argc<3){
+		fprint(2, "usage: mv fromfile tofile\n");
+		fprint(2, "	  mv fromfile ... todir\n");
+		exits("bad usage");
+	}
+	if((dirto = dirstat(argv[argc-1])) != nil && (dirto->mode&DMDIR)){
+		if(argc == 3
+		&& (dirfrom = dirstat(argv[1])) !=nil
+		&& (dirfrom->mode & DMDIR))
+			split(argv[argc-1], &todir, &toelem);
+		else{
+			todir = argv[argc-1];
+			toelem = 0;	/* toelem will be fromelem */
+		}
+	}else
+		split(argv[argc-1], &todir, &toelem);
+	if(argc>3 && toelem != 0){
+		fprint(2, "mv: %s not a directory\n", argv[argc-1]);
+		exits("bad usage");
+	}
+	failed = 0;
+	for(i=1; i < argc-1; i++)
+		if (mv(argv[i], todir, toelem) < 0)
+			failed++;
+	if(failed)
+		exits("failure");
+	exits(0);
+}
+
+int
+mv(char *from, char *todir, char *toelem)
+{
+	Dir *dirb, *dirt, null;
+	char toname[4096], fromname[4096];
+	int fdf, fdt, i, j;
+	int stat;
+	char *fromdir, *fromelem;
+
+	dirb = dirstat(from);
+	if(dirb == nil){
+		fprint(2, "mv: can't stat %s: %r\n", from);
+		return -1;
+	}
+	strncpy(fromname, from, sizeof fromname);
+	split(from, &fromdir, &fromelem);
+	if(toelem == 0)
+		toelem = fromelem;
+	i = strlen(toelem);
+	if(i==0){
+		fprint(2, "mv: null last name element moving %s\n", fromname);
+		return -1;
+	}
+	j = strlen(todir);
+	if(i + j + 2 > sizeof toname){
+		fprint(2, "mv: path too big (max %d): %s/%s\n", sizeof toname, todir, toelem);
+		return -1;
+	}
+	memmove(toname, todir, j);
+	toname[j] = '/';
+	memmove(toname+j+1, toelem, i);
+	toname[i+j+1] = 0;
+	if(samefile(fromdir, todir)){
+		if(samefile(fromname, toname)){
+			fprint(2, "mv: %s and %s are the same\n", fromname, toname);
+			return -1;
+		}
+		dirt = dirstat(toname);
+		if(dirt != nil){
+			free(dirt);
+			hardremove(toname);
+		}
+		nulldir(&null);
+		null.name = toelem;
+		if(dirwstat(fromname, &null) >= 0)
+			return 0;
+		if(dirb->mode & DMDIR){
+			fprint(2, "mv: can't rename directory %s: %r\n", fromname);
+			return -1;
+		}
+	}
+	/*
+	 * Renaming won't work --- must copy
+	 */
+	if(dirb->mode & DMDIR){
+		fprint(2, "mv: %s is a directory, not copied to %s\n", fromname, toname);
+		return -1;
+	}
+	fdf = open(fromname, OREAD);
+	if(fdf < 0){
+		fprint(2, "mv: can't open %s: %r\n", fromname);
+		return -1;
+	}
+	dirt = dirstat(toname);
+	if(dirt != nil && (dirt->mode & DMAPPEND))
+		hardremove(toname);	/* because create() won't truncate file */
+	free(dirt);
+	fdt = create(toname, OWRITE, dirb->mode);
+	if(fdt < 0){
+		fprint(2, "mv: can't create %s: %r\n", toname);
+		close(fdf);
+		return -1;
+	}
+	if ((stat = copy1(fdf, fdt, fromname, toname)) != -1) {
+		nulldir(&null);
+		null.mtime = dirb->mtime;
+		null.mode = dirb->mode;
+		dirfwstat(fdt, &null);	/* ignore errors; e.g. user none always fails */
+		close(fdf);
+		close(fdt);
+		if (remove(fromname) < 0) {
+			fprint(2, "mv: can't remove %s: %r\n", fromname);
+			return -1;
+		}
+	}
+	close(fdf);
+	close(fdt);
+	return stat;
+}
+
+int
+copy1(int fdf, int fdt, char *from, char *to)
+{
+	char buf[8192];
+	long n, n1;
+
+	for(;;) {
+		n = read(fdf, buf, sizeof buf);
+		if(n >= 0) {
+			if(n == 0)
+				break;
+			n1 = write(fdt, buf, n);
+			if(n1 != n) {
+				fprint(2, "mv: error writing %s: %r\n", to);
+				return -1;
+			}
+		}
+	}
+	if(n < 0) {
+		fprint(2, "mv: error reading %s: %r\n", from);
+		return -1;
+	}
+	return 0;
+}
+
+void
+split(char *name, char **pdir, char **pelem)
+{
+	char *s;
+
+	s = utfrrune(name, '/');
+	if(s){
+		*s = 0;
+		*pelem = s+1;
+		*pdir = name;
+	}else if(strcmp(name, "..") == 0){
+		*pdir = "..";
+		*pelem = ".";
+	}else{
+		*pdir = ".";
+		*pelem = name;
+	}
+}
+
+int
+samefile(char *a, char *b)
+{
+	Dir *da, *db;
+	int ret;
+
+	if(strcmp(a, b) == 0)
+		return 1;
+	da = dirstat(a);
+	db = dirstat(b);
+	ret = (da !=nil ) &&
+		(db != nil) &&
+		(da->qid.type==db->qid.type) &&
+		(da->qid.path==db->qid.path) &&
+		(da->qid.vers==db->qid.vers) &&
+		(da->dev==db->dev) &&
+		da->type==db->type;
+	free(da);
+	free(db);
+	return ret;
+}
+
+void
+hardremove(char *a)
+{
+	if(remove(a) == -1){
+		fprint(2, "mv: can't remove %s: %r\n", a);
+		exits("mv");
+	}
+	do; while(remove(a) != -1);
+}
--- /dev/null
+++ b/utils/na/mkfile
@@ -1,0 +1,18 @@
+<../../mkconfig
+
+TARG=na
+HFILES=\
+	na.h\
+
+OFILES=\
+	y.tab.$O\
+
+YFILES=na.y\
+
+LIBS=9
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/na/na.h
@@ -1,0 +1,13 @@
+#ifndef NA_H
+#define NA_H
+
+struct na_patch {
+	unsigned lwoff;
+	unsigned char type;
+};
+
+int na_fixup(unsigned long *script, unsigned long pa_script, unsigned long pa_reg,
+    struct na_patch *patch, int patches,
+    int (*externval)(int x, unsigned long *v));
+
+#endif
--- /dev/null
+++ b/utils/na/na.man
@@ -1,0 +1,33 @@
+.TH NA 8
+.SH NAME
+na \- assembler for the Symbios Logic PCI-SCSI I/O Processors
+.SH SYNOPSIS
+.B aux/na file
+.SH DESCRIPTION
+The SYM53C8XX series of PCI-SCSI I/O Processors contain
+loadable microcode to control their operation.
+The microcode is written in a language called SCRIPTS.
+.I Aux/na
+is an assembler for the SCRIPTS programming language.
+It assembles SCRIPTS code in
+.I file
+into an array of assembled SCRIPTS
+instructions, patches, defines and enums
+that can be included in a C device driver.
+.SH SOURCE
+.TF /sys/src/cmd/aux/na
+.TP
+.B /sys/src/cmd/aux/na
+.SH "SEE ALSO"
+Symbios Logic,
+``PCI-SCSI I/O Processors Programming Guide Version 2.1''
+.br
+.TF /sys/src/9/*/sd53c8xx.c
+.TP
+.B /sys/src/9/*/sd53c8xx.n
+SCRIPTS source code
+.TP
+.B /sys/src/9/*/sd53c8xx.c
+driver for the SYM53C8XX series of PCI-SCSI controllers
+.SH AUTHOR
+Nigel Roles (ngr@9fs.org)
--- /dev/null
+++ b/utils/na/na.y
@@ -1,0 +1,1231 @@
+/* NCR53c8xx assembler */
+%{
+#include <lib9.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "na.h"
+
+#define COND_WAIT (1L << 16)
+#define COND_TRUE (1L << 19)
+#define COND_INTFLY (1L << 20)
+#define COND_CARRY (1L << 21)
+#define COND_REL (1L << 23)
+#define COND_PHASE (1L << 17)
+#define COND_DATA (1L << 18)
+
+#define IO_REL (1L << 26)
+
+#define MOVE_MODE (1L << 27)
+
+int yylex(void);
+int yyparse(void);
+void assemble(void);
+void yyerror(char *, ...);
+void yywarn(char *, ...);
+void p2error(int line, char *);
+
+struct addr {
+	int type; /* 0 - direct, 1 - indirect 2 - table indirect */
+	unsigned long offset;
+};
+
+typedef enum Type { Const, Addr, Table, Extern, Reg, Unknown, Error } Type;
+
+struct sym {
+	char *name;
+	int set;
+	Type t;
+	long value;
+	struct sym *next;
+};
+
+struct sym *findsym(char *name);
+struct sym *symlist;
+
+void newsym(struct sym *s, Type t, long v);
+
+struct binary {
+	char len;
+	unsigned long data[3];
+	unsigned char patch[3];
+};
+
+#define MAXCPPOPTS 30
+#define MAX_PATCHES 1000
+struct na_patch patch[MAX_PATCHES];
+int patches;
+
+struct binary out;
+
+struct expval {
+	Type t;
+	long value;
+};
+
+struct expval eval(struct expval a, struct expval b, char op);
+
+int patchtype(Type t);
+void fixup(void);
+
+unsigned dot;
+unsigned externs;
+int errors, warnings;
+struct sym *externp[100];
+
+void regmove(unsigned char src_reg, unsigned char op,
+    unsigned char dst_reg, struct expval *imm);
+
+void preprocess(char *in, FILE *out);
+
+int mk24bitssigned(long *l);
+long mkreladdr(long value, int len);
+long chkreladdr(int d, struct expval *e, int len, long relrv);
+int pass2;
+FILE *in_f;
+
+int yyline = 0;
+char yyfilename[200];
+char line[500];
+char *cppopts[MAXCPPOPTS];
+int ncppopts;
+int wflag;
+%}
+
+%union {
+	long n;
+	struct sym *s;
+	struct expval e;
+}
+
+%token NUM MOVE WHEN SYMBOL SELECT WAIT DISCONNECT RESELECT SET CLEAR
+%token DATA_OUT DATA_IN COMMAND STATUS RESERVED_OUT RESERVED_IN MESSAGE_OUT
+%token MESSAGE_IN WITH ATN FAIL CARRY TARGET ACK COMMENT TO
+%token SCNTL0 SCNTL1 SCNTL2 SCNTL3 SCID SXFER SDID GPREG
+%token SFBR SOCL SSID SBCL DSTAT SSTAT0 SSTAT1 SSTAT2
+%token ISTAT CTEST0 CTEST1 CTEST2 CTEST3 TEMP DFIFO CTEST4 CTEST5 CTEST6
+%token DBC DCMD DNAD DSP DSPS DMODE DIEN DWT DCNTL ADDER
+%token SIEN0 SIEN1 SIST0 SIST1 SLPAR MACNTL GPCNTL STIME0 STIME1 RESPID
+%token STEST0 STEST1 STEST2 STEST3 SIDL SODL SBDL
+%token SHL SHR AND OR XOR ADD ADDC
+%token JUMP CALL RETURN INT INTFLY NOT ABSOLUTE MASK IF REL PTR
+%token TABLE FROM MEMORY NOP EXTERN
+%token SCRATCHA0 SCRATCHA1 SCRATCHA2 SCRATCHA3
+%token SCRATCHB0 SCRATCHB1 SCRATCHB2 SCRATCHB3
+%token SCRATCHC0 SCRATCHC1 SCRATCHC2 SCRATCHC3
+%token DSA0 DSA1 DSA2 DSA3
+%token DEFW
+
+%left '-' '+'
+%left '*' '/'
+%left NEG     /* negation--unary minus */
+%right '^'    /* exponentiation        */
+%type <n> NUM phase .atn set_list set_bit regA reg
+%type <n> set_cmd .cond condsfbr condphase
+%type <n> jump_or_call .ptr
+%type <s> SYMBOL
+%type <e> exp byteexp regexp
+
+/* Grammar follows */
+%%
+input:    /* empty string */
+        | input line
+;
+
+line:	.label .opcode .comment '\n'
+	{
+		if (pass2) {
+			int x;
+			for (x = 0; x < out.len; x++) {
+				printf("/* %.4x */ 0x%.8lxL,",
+				    dot, out.data[x]);
+				if (x == 0) {
+					printf(" /*\t");
+					fwrite(line,
+					    strlen(line) - 1, 1, stdout);
+					printf(" */");
+				}
+				printf("\n");
+				if (out.patch[x]) {
+					patch[patches].lwoff = dot / 4;
+					patch[patches].type = out.patch[x];
+					patches++;
+				}
+				dot += 4;
+			}
+		}
+		else
+			dot += 4 * out.len;
+	}
+	| ABSOLUTE SYMBOL '=' exp .comment '\n'
+	{
+		setsym($2, $4.t, $4.value);
+		if (pass2) {
+			printf("\t\t\t/*\t");
+			fwrite(line, strlen(line) - 1, 1, stdout);
+			printf(" */\n");
+		}
+	}
+	| SYMBOL '=' exp .comment '\n'
+	{
+		setsym($1, $3.t, $3.value);
+		if (pass2) {
+			printf("\t\t\t/*\t");
+			fwrite(line, strlen(line) - 1, 1, stdout);
+			printf(" */\n");
+		}
+	}
+	| EXTERN SYMBOL {
+		if (pass2) {
+			printf("\t\t\t/*\t");
+			fwrite(line, strlen(line) - 1, 1, stdout);
+			printf(" */\n");
+		}
+		else {
+			if (!pass2)
+				externp[externs] = $2;
+			setsym($2, Extern, externs++);
+		}
+	}
+	;
+
+.comment: COMMENT
+	| /* nothing */
+	;
+
+.label:	SYMBOL ':' {
+		if ($1->t != Unknown)
+		{
+			if (!pass2)
+				yyerror("multiply defined symbol");
+		}
+		else {
+			$1->t = Addr;
+			$1->value = dot;
+		}
+	}
+	| /* nothing */
+	;
+
+set_cmd: SET { $$ = 3; }
+	| CLEAR { $$ = 4; }
+	;
+
+set_bit: CARRY { $$ = 0x400; }
+	| TARGET { $$ = 0x200; }
+	| ACK { $$ = 0x40; }
+	| ATN { $$ = 0x8; }
+	;
+	
+set_list: set_list ',' set_bit { $$ = $1 | $3; }
+	| set_list AND set_bit { $$ = $1 | $3; }
+	| set_bit { $$ = $1; }
+	;
+
+opcode: set_cmd set_list {
+		out.len = 2;
+		out.data[0] = (1L << 30) | ((long)$1 << 27) | $2;
+		out.data[1] = 0;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| DISCONNECT
+	{
+		out.len = 2;
+		out.data[0] = 0x48020000L;
+		out.data[1] = 0;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| INT exp .cond {
+		out.len = 2;
+		out.data[0] = $3 | 0x98000000L;
+		out.data[1] = $2.value;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| INTFLY exp .cond {
+		out.len = 2;
+		out.data[0] = $3 | 0x98000000L | COND_INTFLY;
+		out.data[1] = $2.value;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| jump_or_call exp .cond {
+		out.len = 2;
+		out.data[0] = $1 | $3 | chkreladdr(1, &$2, 2, COND_REL);
+		out.patch[0] = 0;
+	}
+	| jump_or_call REL '(' exp ')' .cond {
+		out.len = 2;
+		out.data[0] = $1 | $6 | COND_REL;
+		out.data[1] = mkreladdr($4.value, 2);
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| MOVE exp ',' .ptr regexp ',' with_or_when phase {
+		out.len = 2;
+		out.data[0] = ($8 << 24) | $2.value | ($4 << 29) | MOVE_MODE;
+		out.data[1] = $5.value;
+		out.patch[0] = 0;
+		out.patch[1] = patchtype($5.t);
+	}
+	| MOVE FROM exp ',' with_or_when phase {
+		out.len = 2;
+		out.data[0] = ($6 << 24) | (1L << 28) | MOVE_MODE;
+		out.data[1] = $3.value;
+		out.patch[0] = 0;
+		out.patch[1] = patchtype($3.t);
+	}
+	| MOVE MEMORY exp ',' regexp ',' regexp {
+		out.len = 3;
+		out.data[0] = 0xc0000000L | $3.value;
+		out.data[1] = $5.value;
+		out.data[2] = $7.value;
+		out.patch[0] = 0;
+		out.patch[1] = patchtype($5.t);
+		out.patch[2] = patchtype($7.t);
+	}
+	| MOVE regA TO regA		{ regmove($2, 2, $4, 0); }	/* do reg to sfbr moves using or 0 */
+	| MOVE exp TO regA		{ regmove($4, 0, $4, &$2); }
+	| MOVE regA '|' exp TO regA	{ regmove($2, 2, $6, &$4); }
+	| MOVE regA '&' exp TO regA	{ regmove($2, 4, $6, &$4); }
+	| MOVE regA '+' exp TO regA	{ regmove($2, 6, $6, &$4); }
+	| MOVE regA '-' exp TO regA	{ regmove($2, 6, $6, &$4); }
+	| MOVE regA '+' exp TO regA WITH CARRY	{
+		regmove($2, 7, $6, &$4);
+	}
+	| MOVE regA '-' exp TO regA WITH CARRY	{
+		$4.value = -$4.value;
+		regmove($2, 7, $6, &$4);
+	}
+	| MOVE regA SHL TO regA		{ regmove($2, 1, $5, 0); }
+	| MOVE regA SHR TO regA		{ regmove($2, 5, $5, 0); }
+	| MOVE regA XOR exp TO regA	{ regmove($2, 3, $6, &$4); }
+	| NOP {
+		out.len = 2;
+		out.data[0] = 0x80000000L;
+		out.data[1] = 0;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| RESELECT exp ',' exp {
+		out.len = 2;
+		out.data[0] = 0x40000000L | ((long)$2.value << 16) | (1L << 9) | chkreladdr(1, &$4, 2, IO_REL);
+		out.patch[0] = 0;
+	}
+	| RESELECT exp ',' REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x40000000L | IO_REL
+		    | ((long)$2.value << 16) | (1L << 9);
+		out.data[1] = mkreladdr($6.value, 2);
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| RESELECT FROM exp ',' exp {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 25) | $3.value | chkreladdr(1, &$5, 2, IO_REL);
+		out.patch[0] = 5;
+	}
+	| RESELECT FROM exp ',' REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 25) | IO_REL | $3.value;
+		out.patch[0] = 5;
+		out.data[1] = mkreladdr($7.value, 2);
+		out.patch[1] = 0;
+	}
+	| RETURN .cond {
+		
+		out.len = 2;
+		out.data[0] = 0x90000000L | $2;
+		out.data[1] = 0;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| SELECT .atn exp ',' exp {
+		out.len = 2;
+		out.data[0] =
+		    0x40000000L | ((long)$3.value << 16) | (1L << 9) | $2 | chkreladdr(1, &$5, 2, IO_REL);
+		out.patch[0] = 0;
+	}
+	| SELECT .atn exp ',' REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 26)
+		    | ((long)$3.value << 16) | (1L << 9) | $2;
+		out.data[1] = mkreladdr($7.value, 2);
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| SELECT .atn FROM exp ',' exp {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 25) | $4.value | $2 | chkreladdr(1, &$6, 2, IO_REL);
+		out.patch[0] = 5;
+	}
+	| SELECT .atn FROM exp ',' REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 25) | IO_REL | $4.value | $2;
+		out.patch[0] = 5;
+		out.data[1] = mkreladdr($8.value, 2);
+		out.patch[1] = 0;
+	}
+	| WAIT DISCONNECT {
+		out.len = 2;
+		out.data[0] = 0x48000000L;
+		out.data[1] = 0;
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| WAIT RESELECT exp {
+		out.len = 2;
+		out.data[0] = 0x50000000L | chkreladdr(1, &$3, 2, IO_REL);
+		out.patch[0] = 0;
+	}
+	| WAIT RESELECT REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x50000000L | (1L << 26);
+		out.data[1] = mkreladdr($5.value, 2);
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| WAIT SELECT exp {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 9) | chkreladdr(1, &$3, 2, IO_REL);
+		out.patch[0] = 0;
+	}
+	| WAIT SELECT REL '(' exp ')' {
+		out.len = 2;
+		out.data[0] = 0x40000000L | (1L << 26) | (1L << 9);
+		out.data[1] = mkreladdr($5.value, 2);
+		out.patch[0] = out.patch[1] = 0;
+	}
+	| DEFW exp {
+		out.len = 1;
+		out.data[0] = $2.value;
+		out.patch[0] = patchtype($2.t);
+	}
+	;
+
+.ptr:	PTR { $$ = 1; }
+	| { $$ = 0; }
+	;
+
+with_or_when: WITH
+	| WHEN
+	;
+	
+jump_or_call: JUMP	 { $$ = 0x80000000L; }
+	| CALL		 { $$ = 0x88000000L; }
+	;
+
+condsfbr: byteexp { $$ = $1.value | COND_DATA; }
+	| byteexp AND MASK byteexp { $$ = ($4.value << 8) | $1.value | COND_DATA; }
+	;
+
+condphase: phase { $$ = ($1 << 24) | COND_PHASE; }
+
+.cond:    ',' IF ATN { $$ = COND_TRUE; }
+	| ',' IF condphase { $$ = $3 | COND_TRUE; }
+	| ',' IF CARRY { $$ = COND_CARRY | COND_TRUE; }
+	| ',' IF condsfbr { $$ = $3 | COND_TRUE; }
+	| ',' IF ATN AND condsfbr { $$ = $5 | COND_TRUE; }
+	| ',' IF condphase AND condsfbr { $$ = $3 | $5 | COND_TRUE; }
+	| ',' WHEN condphase { $$ = $3 | COND_WAIT | COND_TRUE; }
+	| ',' WHEN CARRY { $$ = COND_CARRY | COND_WAIT | COND_TRUE; }
+	| ',' WHEN condsfbr { $$ = $3 | COND_WAIT | COND_TRUE; }
+	| ',' WHEN condphase AND condsfbr { $$ = $3 | $5 | COND_WAIT | COND_TRUE; }
+	| ',' IF NOT ATN { $$ = 0; }
+	| ',' IF NOT condphase { $$ = $4; }
+	| ',' IF NOT CARRY { $$ = COND_CARRY; }
+	| ',' IF NOT condsfbr { $$ = $4; }
+	| ',' IF NOT ATN OR condsfbr { $$ = $6; }
+	| ',' IF NOT condphase OR condsfbr { $$ = $4 | $6; }
+	| ',' WHEN NOT condphase { $$ = $4 | COND_WAIT; }
+	| ',' WHEN NOT CARRY { $$ = COND_CARRY | COND_WAIT; }
+	| ',' WHEN NOT condsfbr { $$ = $4 | COND_WAIT; }
+	| ',' WHEN NOT condphase OR condsfbr { $$ = $4 | $6 | COND_WAIT; }
+	| { $$ = COND_TRUE; }
+	;
+
+.opcode: opcode
+	| { out.len = 0; }
+	;
+
+regA:	reg
+	| SFBR { $$ = 8; }
+	;
+
+reg:	  SCNTL0	{ $$ = 0; }
+	| SCNTL1	{ $$ = 1; }
+	| SCNTL2	{ $$ = 2; }
+	| SCNTL3	{ $$ = 3; }
+	| SCID		{ $$ = 4; }
+	| SXFER		{ $$ = 5; }
+	| SDID		{ $$ = 6; }
+	| GPREG		{ $$ = 7; }
+	| SOCL		{ $$ = 9; }
+	| SSID		{ $$ = 0xa; }
+	| SBCL		{ $$ = 0xb; }
+	| DSTAT		{ $$ = 0xc; }
+	| SSTAT0	{ $$ = 0xd; }
+	| SSTAT1	{ $$ = 0xe; }
+	| SSTAT2	{ $$ = 0xf; }
+	| DSA0		{ $$ = 0x10; }
+	| DSA1		{ $$ = 0x11; }
+	| DSA2		{ $$ = 0x12; }
+	| DSA3		{ $$ = 0x13; }
+	| ISTAT		{ $$ = 0x14; }
+	| CTEST0	{ $$ = 0x18; }
+	| CTEST1	{ $$ = 0x19; }
+	| CTEST2	{ $$ = 0x1a; }
+	| CTEST3	{ $$ = 0x1b; }
+	| TEMP		{ $$ = 0x1c; }
+	| DFIFO		{ $$ = 0x20; }
+	| CTEST4	{ $$ = 0x21; }
+	| CTEST5	{ $$ = 0x22; }
+	| CTEST6	{ $$ = 0x23; }
+	| DBC		{ $$ = 0x24; }
+	| DCMD		{ $$ = 0x27; }
+	| DNAD		{ $$ = 0x28; }
+	| DSP		{ $$ = 0x2c; }
+	| DSPS		{ $$ = 0x30; }
+	| SCRATCHA0	{ $$ = 0x34; }
+	| SCRATCHA1	{ $$ = 0x35; }
+	| SCRATCHA2	{ $$ = 0x36; }
+	| SCRATCHA3	{ $$ = 0x37; }
+	| DMODE		{ $$ = 0x38; }
+	| DIEN		{ $$ = 0x39; }
+	| DWT		{ $$ = 0x3a; }
+	| DCNTL		{ $$ = 0x3b; }
+	| ADDER		{ $$ = 0x3c; }
+	| SIEN0		{ $$ = 0x40; }
+	| SIEN1		{ $$ = 0x41; }
+	| SIST0		{ $$ = 0x42; }
+	| SIST1		{ $$ = 0x43; }
+	| SLPAR		{ $$ = 0x44; }
+	| MACNTL	{ $$ = 0x46; }
+	| GPCNTL	{ $$ = 0x47; }
+	| STIME0	{ $$ = 0x48; }
+	| STIME1	{ $$ = 0x49; }
+	| RESPID	{ $$ = 0x4a; }
+	| STEST0	{ $$ = 0x4c; }
+	| STEST1	{ $$ = 0x4d; }
+	| STEST2	{ $$ = 0x4e; }
+	| STEST3	{ $$ = 0x4f; }
+	| SIDL		{ $$ = 0x50; }
+	| SODL		{ $$ = 0x54; }
+	| SBDL		{ $$ = 0x58; }
+	| SCRATCHB0	{ $$ = 0x5c; }
+	| SCRATCHB1	{ $$ = 0x5d; }
+	| SCRATCHB2	{ $$ = 0x5e; }
+	| SCRATCHB3	{ $$ = 0x5f; }
+	| SCRATCHC0	{ $$ = 0x60; }
+	| SCRATCHC1	{ $$ = 0x61; }
+	| SCRATCHC2	{ $$ = 0x62; }
+	| SCRATCHC3	{ $$ = 0x63; }
+	;
+
+.atn:	ATN		{ $$ = (1 << 24); }
+	| /* nothing */ { $$ = 0; }
+;
+
+phase:	DATA_OUT 	{ $$ = 0; }
+	| DATA_IN	{ $$ = 1; }
+	| COMMAND 	{ $$ = 2; }
+	| STATUS	{ $$ = 3; }
+	| RESERVED_OUT 	{ $$ = 4; }
+	| RESERVED_IN	{ $$ = 5; }
+	| MESSAGE_OUT   { $$ = 6; }
+	| MESSAGE_IN	{ $$ = 7; }
+;
+
+byteexp: exp
+	{
+		if (pass2 && ($1.value < 0 || $1.value > 255)) {
+			if (wflag)
+				yywarn("conversion causes truncation");
+			$$.value = $1.value & 0xff;
+		}
+		else
+			$$.value = $1.value;
+	}
+	;
+
+regexp:	exp
+	| regA { $$.t = Reg; $$.value = $1; }
+	;
+
+exp:	NUM { $$.t = Const; $$.value = $1; }
+	| SYMBOL {
+		$$.t = $1->t; $$.value = $1->value;
+		if (pass2 && $1->t == Unknown)
+		{
+			yyerror("Undefined symbol %s", $1->name);
+			$1->t = Error;
+			$1->value = 0;
+			$$.t = Error;
+			$$.value = 0;
+		}
+	}
+        | exp '+' exp { $$ = eval($1, $3, '+'); }
+        | exp '-' exp { $$ = eval($1, $3, '-'); }
+        | exp '*' exp { $$ = eval($1, $3, '*'); }
+        | exp '/' exp { $$ = eval($1, $3, '/'); }
+        | '-' exp  %prec NEG { $$ = eval($2, $2, '_'); }
+        | '(' exp ')'        { $$ = $2; }
+	| '~' exp %prec NEG { $$ = eval($2, $2, '~'); }
+	;
+%%
+
+struct {
+	char *name;
+	int tok;
+} toktab[] =
+{
+	{ "when", WHEN },
+	{ "data_out", DATA_OUT },
+	{ "data_in", DATA_IN },
+	{ "msg_out", MESSAGE_OUT },
+	{ "msg_in", MESSAGE_IN },
+	{ "cmd", COMMAND },
+	{ "command", COMMAND },
+	{ "status", STATUS },
+	{ "move", MOVE },
+	{ "select", SELECT },
+	{ "reselect", RESELECT },
+	{ "disconnect", DISCONNECT },
+	{ "wait", WAIT },
+	{ "set", SET },
+	{ "clear", CLEAR },
+	{ "with", WITH },
+	{ "atn", ATN },
+	{ "fail", FAIL },
+	{ "carry", CARRY },
+	{ "target", TARGET },
+	{ "ack", ACK },
+	{ "scntl0", SCNTL0 },
+	{ "scntl1", SCNTL1 },
+	{ "scntl2", SCNTL2 },
+	{ "scntl3", SCNTL3 },
+	{ "scid", SCID },
+	{ "sxfer", SXFER },
+	{ "sdid", SDID },
+	{ "gpreg", GPREG },
+	{ "sfbr", SFBR },
+	{ "socl", SOCL },
+	{ "ssid", SSID },
+	{ "sbcl", SBCL },
+	{ "dstat", DSTAT },
+	{ "sstat0", SSTAT0 },
+	{ "sstat1", SSTAT1 },
+	{ "sstat2", SSTAT2 },
+	{ "dsa", DSA0 },
+	{ "dsa0", DSA0 },
+	{ "dsa1", DSA1 },
+	{ "dsa2", DSA2 },
+	{ "dsa3", DSA3 },
+	{ "istat", ISTAT },
+	{ "ctest0", CTEST0 },
+	{ "ctest1", CTEST1 },
+	{ "ctest2", CTEST2 },
+	{ "ctest3", CTEST3 },
+	{ "temp", TEMP },
+	{ "dfifo", DFIFO },
+	{ "ctest4", CTEST4 },
+	{ "ctest5", CTEST5 },
+	{ "ctest6", CTEST6 },
+	{ "dbc", DBC },
+	{ "dcmd", DCMD },
+	{ "dnad", DNAD },
+	{ "dsp", DSP },
+	{ "dsps", DSPS },
+	{ "scratcha", SCRATCHA0 },
+	{ "scratcha0", SCRATCHA0 },
+	{ "scratcha1", SCRATCHA1 },
+	{ "scratcha2", SCRATCHA2 },
+	{ "scratcha3", SCRATCHA3 },
+	{ "dmode", DMODE },
+	{ "dien", DIEN },
+	{ "dwt", DWT },
+	{ "dcntl", DCNTL },
+	{ "adder", ADDER },
+	{ "sien0", SIEN0 },
+	{ "sien1", SIEN1 },
+	{ "sist0", SIST0 },
+	{ "sist1", SIST1 },
+	{ "slpar", SLPAR },
+	{ "macntl", MACNTL },
+	{ "gpcntl", GPCNTL },
+	{ "stime0", STIME0 },
+	{ "stime1", STIME1 },
+	{ "respid", RESPID },
+	{ "stest0", STEST0 },
+	{ "stest1", STEST1 },
+	{ "stest2", STEST2 },
+	{ "stest3", STEST3 },
+	{ "sidl", SIDL },
+	{ "sodl", SODL },
+	{ "sbdl", SBDL },
+	{ "scratchb", SCRATCHB0 },
+	{ "scratchb0", SCRATCHB0 },
+	{ "scratchb1", SCRATCHB1 },
+	{ "scratchb2", SCRATCHB2 },
+	{ "scratchb3", SCRATCHB3 },
+	{ "scratchc", SCRATCHC0 },
+	{ "scratchc0", SCRATCHC0 },
+	{ "scratchc1", SCRATCHC1 },
+	{ "scratchc2", SCRATCHC2 },
+	{ "scratchc3", SCRATCHC3 },
+	{ "add", ADD },
+	{ "addc", ADDC },
+	{ "and", AND },
+	{ "or", OR },
+	{ "xor", XOR },
+	{ "shl", SHL },
+	{ "shr", SHR },
+	{ "jump", JUMP },
+	{ "call", CALL },
+	{ "return", RETURN },
+	{ "int", INT },
+	{ "intfly", INTFLY },
+	{ "not", NOT },
+	{ "absolute", ABSOLUTE },
+	{ "mask", MASK },
+	{ "if", IF },
+	{ "rel", REL },
+	{ "ptr", PTR },
+	{ "table", TABLE },
+	{ "from", FROM },
+	{ "memory", MEMORY },
+	{ "to", TO },
+	{ "nop", NOP },
+	{ "extern", EXTERN },
+	{ "defw", DEFW },
+};
+
+#define TOKS (sizeof(toktab)/sizeof(toktab[0]))
+
+int lc;
+int ll;
+
+void
+yyrewind(void)
+{
+	rewind(in_f);
+	ll = lc = 0;
+	yyline = 0;
+	dot = 0;
+}
+
+int
+yygetc(void)
+{
+	if (lc == ll)
+	{
+	next:
+		if (fgets(line, 500, in_f) == 0)
+			return EOF;
+		/* do nasty check for #line directives */
+		if (strncmp(line, "#line", 5) == 0) {
+			/* #line n "filename" */
+			sscanf(line, "#line %d \"%[^\"]", &yyline, yyfilename);
+			yyline--;
+			goto next;
+		}
+		yyline++;
+		ll = strlen(line);
+		lc = 0;
+	}
+	return line[lc++];
+}
+
+void
+yyungetc(void)
+{
+	if (lc <= 0)
+		exits("ungetc");
+	lc--;
+}
+
+int
+yylex(void)
+{
+	char token[100];
+	int tl = 0;
+	int c;
+
+	while ((c = yygetc()) != EOF && (c == ' ' || c == '\t'))
+		;
+	if (c == EOF)
+		return 0;
+	if(c == '/'){
+		int x;
+		x = yygetc();
+		if(x != '/')
+			yyungetc();
+		else{
+			lc -= 2;
+			while(lc >= 0 && (line[lc-1]==' ' || line[lc-1]=='\t'))
+				lc--;
+			line[lc++] = '\n';
+			line[lc] = 0;
+			ll = lc;
+			return '\n';
+		}
+	}
+	if (isalpha(c) || c == '_')
+	{
+		int x;
+		do {
+			token[tl++] = c;
+		} while ((c = yygetc()) != EOF && (isalnum(c) || c == '_'));
+		if (c == EOF)
+			return 0;
+		yyungetc();
+		token[tl] = 0;
+		for (x = 0; x < TOKS; x++)
+			if (strcmp(toktab[x].name, token) == 0)
+				return toktab[x].tok;
+		/* must be a symbol */
+		yylval.s = findsym(token);
+		return SYMBOL;
+	}
+	else if (isdigit(c))
+	{
+		/* accept 0x<digits> or 0b<digits> 0<digits> or <digits> */
+		int prefix = c == '0';
+		unsigned long n = c - '0';
+		int base = 10;
+		for (;;)
+		{
+			c = yygetc();
+			if (c == EOF)
+				return 0;
+			if (prefix)
+			{
+				prefix = 0;
+				if (c == 'x') {
+					base = 16;
+					continue;
+				}
+				else if (c == 'b')
+				{
+					base = 2;
+					continue;
+				}
+				else
+					base = 8;
+			}
+			if (isdigit(c))
+				c -= '0';
+			else if (isalpha(c) && base > 10)
+			{
+				if (isupper(c))
+					c = tolower(c);
+				c = c - 'a' + 10;
+			}
+			else {
+				yyungetc();
+				yylval.n = n;
+				return NUM;
+			}
+			if (c >= base)
+				yyerror("illegal format number");
+			n = n * base + c;
+		}
+	}
+	else if (c == ';') {
+		/* skip to end of line */
+		while ((c = yygetc()) != EOF && c != '\n')
+			;
+		if (c != EOF)
+			yyungetc();
+		return COMMENT;
+	}
+	return c;
+}
+
+void
+yyerror(char *s, ...)
+{
+	va_list ap;
+
+	va_start(ap, s);
+	fprintf(stderr, "%s: %d: ", yyfilename, yyline);
+	vfprintf(stderr, s, ap);
+	if (putc('\n', stderr) < 0)
+		exits("io");
+	errors++;
+	va_end(ap);
+}
+
+void
+yywarn(char *s, ...)
+{
+	va_list ap;
+
+	va_start(ap, s);
+	fprintf(stderr, "%s: %d: warning: ", yyfilename, yyline);
+	vfprintf(stderr, s, ap);
+	if (putc('\n', stderr) < 0)
+		exits("io");
+	warnings++;
+	va_end(ap);
+}
+
+void
+p2error(int line, char *s)
+{
+	USED(line);
+	printf("/*\t%s */\n", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int a;
+	for (a = 1; a < argc; a++)
+	{
+		if (argv[a][0] == '-')
+			switch (argv[a][1]) {
+			case 'D':
+				/* #defines for cpp */
+				if (ncppopts >= MAXCPPOPTS) {
+					fprintf(stderr, "too many cpp options\n");
+					exits("options");
+				}
+				cppopts[ncppopts++] = argv[a];
+				break;
+			default:
+				fprintf(stderr, "unrecognised option %s\n",
+				    argv[a]);
+				exits("options");
+			}
+		else
+			break;
+	}
+	if (a != argc - 1)
+	{
+		fprintf(stderr, "usage: na [options] file\n");
+		exits("options");
+	}
+	if (access(argv[a], 4) < 0) {
+		fprintf(stderr, "can't read %s\n", argv[a]);
+		exits("");
+	}
+	in_f = tmpfile();
+	preprocess(argv[a], in_f);
+	rewind(in_f);
+	strcpy(yyfilename, argv[a]);
+	yyparse();
+	if (errors)
+		exits("pass1");
+	pass2 = 1;
+	printf("unsigned long na_script[] = {\n");
+	yyrewind();
+	yyparse();
+	printf("};\n");
+	printf("\n");
+	printf("#define NA_SCRIPT_SIZE %d\n", dot / 4);
+	printf("\n");
+	fixup();
+/*
+	assemble();
+*/
+	exits(errors ? "pass2" : "");
+}
+
+void
+preprocess(char *in, FILE *out)
+{
+#ifdef USECPP
+	Waitmsg *w;
+	char **argv;
+
+	if (fork() == 0) {
+		/* child */
+		dup(fileno(out), 1);
+		argv = (char **)malloc(sizeof(char *) * (ncppopts + 5));
+		argv[0] = "cpp";
+		memcpy(&argv[1], cppopts, sizeof(char *) * ncppopts);
+		argv[ncppopts + 1] = "-+";
+		argv[ncppopts + 2] = "-N";
+		argv[ncppopts + 3] = in;
+		argv[ncppopts + 4] = 0;
+		if (exec("/bin/cpp", argv) < 0) {
+			fprintf(stderr, "failed to exec cpp (%R)\n");
+			exits("exec");
+		}
+		exits("");
+	}
+	w = wait();
+	free(w);
+#else
+	FILE *fi;
+	int c;
+
+
+	fi = fopen(in, "r");
+	if(fi == NULL){
+		fprintf(stderr, "na: can't open %s\n", in);
+		exits("open");
+	}
+	while((c = fgetc(fi)) != EOF)
+		fputc(c, out);
+	fclose(fi);
+#endif
+}
+
+struct sym *
+findsym(char *name)
+{
+	struct sym *s;
+	for (s = symlist; s; s = s->next)
+		if (strcmp(name, s->name) == 0)
+			return s;
+	s = (struct sym *)malloc(sizeof(*s));
+	s->name = strdup(name);
+	s->t = Unknown;
+	s->set = 0;
+	s->next = symlist;
+	symlist = s;
+	return s;
+}
+
+void
+setsym(struct sym *s, Type t, long v)
+{
+	if (pass2) {
+		if (t == Unknown || t == Error)
+			yyerror("can't resolve symbol");
+		else {
+			s->t = t;
+			s->value = v;
+		}
+	}
+	else {
+		if (s->set)
+			yyerror("multiply defined symbol");
+		s->set = 1;
+		s->t = t;
+		s->value = v;
+	}
+}
+
+int
+mk24bitssigned(long *l)
+{
+	if (*l < 0) {
+		if ((*l & 0xff800000L) != 0xff800000L) {
+			*l = 0;
+			return 0;
+		}
+		else
+			*l = (*l) & 0xffffffL;
+	}
+	else if (*l > 0xffffffL) {
+		*l = 0;
+		return 0;
+	}
+	return 1;
+}
+
+static Type addresult[5][5] = {
+/*		Const	Addr	Table   Extern	Reg */
+/* Const */	Const,	Addr,	Table,	Error,  Reg,
+/* Addr */	Addr,	Error,	Error,	Error,  Error,
+/* Table */	Table,	Error,	Error,	Error,  Error,
+/* Extern */	Error,	Error,	Error,	Error,	Error,
+/* Reg */	Reg,	Error,  Error,  Error,	Error,
+};
+
+static Type subresult[5][5] = {
+/*		Const	Addr	Table   Extern	Reg */
+/* Const */	Const,	Error,	Error,	Error,  Error,
+/* Addr */	Addr,	Const,	Error,	Error,	Error,
+/* Table */	Table,	Error,	Const,	Error,	Error,
+/* Extern */	Error,	Error,	Error,	Const,	Error,
+/* Reg */	Error,	Error,  Error,  Error,	Error,
+};
+
+static Type muldivresult[5][5] = {
+/*		Const	Addr	Table   Extern */
+/* Const */	Const,	Error,	Error,	Error,	Error,
+/* Addr */	Error,	Error,	Error,	Error,	Error,
+/* Table */	Error,	Error,	Error,	Error,	Error,
+/* Extern */	Error,	Error,	Error,	Error,	Error,
+/* Reg */	Error,	Error,	Error,	Error,	Error,
+};
+
+static Type negresult[] = {
+/* Const */	Const,
+/* Addr */	Error,
+/* Table */	Error,
+/* Extern */	Error,
+/* Reg */	Error,
+};
+
+int
+patchtype(Type t)
+{
+	switch (t) {
+	case Addr:
+		return 1;
+	case Reg:
+		return 2;
+	case Extern:
+		return 4;
+	default:
+		return 0;
+	}
+}
+
+struct expval
+eval(struct expval a, struct expval b, char op)
+{
+	struct expval c;
+	
+	if (a.t == Unknown || b.t == Unknown) {
+		c.t = Unknown;
+		c.value = 0;
+	}
+	else if (a.t == Error || b.t == Error) {
+		c.t = Error;
+		c.value = 0;
+	}
+	else {
+		switch (op) {
+		case '+':
+			c.t = addresult[a.t][b.t];
+			break;
+		case '-':
+			c.t = subresult[a.t][b.t];
+			break;
+		case '*':
+		case '/':
+			c.t = muldivresult[a.t][b.t];
+			break;
+		case '_':
+		case '~':
+			c.t = negresult[a.t];
+			break;
+		default:
+			c.t = Error;
+			break;
+		}
+		if (c.t == Error) {
+			if (pass2)
+				yyerror("type clash in evaluation");
+			c.value = 0;
+		}
+		else {
+			switch (op) {
+			case '+':
+				c.value = a.value + b.value;
+				break;
+			case '-':
+				c.value = a.value - b.value;
+				break;
+			case '*':
+				c.value = a.value * b.value;
+				break;
+			case '/':
+				c.value = a.value / b.value;
+				break;
+			case '_':
+				c.value = -a.value;
+				break;
+			case '~':
+				c.value = ~a.value;
+				break;
+			}
+		}
+	}
+	return c;
+}
+
+void
+regmove(unsigned char src_reg, unsigned char op,
+    unsigned char dst_reg, struct expval *imm)
+{
+	unsigned char func, reg;
+	int immdata;
+	out.len = 2;
+	if (src_reg == 8) {
+		func = 5;
+		reg = dst_reg;
+	}
+	else if (dst_reg == 8) {
+		func = 6;
+		reg = src_reg;
+	}
+	else {
+		if (pass2 && src_reg != dst_reg)
+			yyerror("Registers must be the same");
+		func = 7;
+		reg = src_reg;
+	}
+	immdata = imm ? (imm->value & 0xff) : 0;
+	out.data[0] = 0x40000000L
+	    | ((long)func << 27)
+	    | ((long)op << 24)
+	    | ((long)reg << 16)
+	    | ((long)(immdata) << 8);
+	out.data[1] = 0;
+	out.patch[0] = (imm && imm->t == Extern) ? 3 : 0;
+	out.patch[1] = 0;
+}
+
+long
+mkreladdr(long addr, int len)
+{
+	long rel;
+	rel = addr - (dot + 4 * len);
+	mk24bitssigned(&rel);
+	return rel;
+}
+
+long
+chkreladdr(int d, struct expval *e, int len, long relrv)
+{
+	if (e->t == Addr) {
+		out.data[d] = mkreladdr(e->value, len);
+		out.patch[d] = 0;
+		return relrv;
+	} else {
+		out.data[d] = e->value;
+		out.patch[d] = patchtype(e->t);
+		return 0;
+	}
+}
+
+void
+fixup(void)
+{
+	struct sym *s;
+	int p;
+	printf("struct na_patch na_patches[] = {\n");
+	for (p = 0; p < patches; p++) {
+		printf("\t{ 0x%.4x, %d }, /* %.8lx */\n",
+		    patch[p].lwoff, patch[p].type, patch[p].lwoff * 4L);
+	}
+	if (patches == 0) {
+		printf("\t{ 0, 0 },\n");
+	}
+	printf("};\n");
+	printf("#define NA_PATCHES %d\n", patches);
+	printf("\n");
+	if (externs) {
+		printf("enum na_external {\n");
+		for (p = 0; p < externs; p++) {
+			printf("\tX_%s,\n", externp[p]->name);
+		}
+		printf("};\n");
+	}
+	/* dump all labels (symbols of type Addr) as E_<Name> */
+	for (s = symlist; s; s = s->next)
+		if (s->t == Addr)
+			break;
+	if (s) {
+		printf("\nenum {\n");
+		while (s) {
+			if (s->t == Addr)
+				printf("\tE_%s = %ld,\n", s->name, s->value);
+			s = s->next;
+		}
+		printf("};\n");
+	}
+	/* dump all Consts as #define A_<Name> value */
+	for (s = symlist; s; s = s->next)
+		if (s->t == Const)
+			printf("#define A_%s %ld\n", s->name, s->value);
+}
+
--- /dev/null
+++ b/utils/ndate/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=ndate
+
+OFILES=	ndate.$O\
+
+HFILES=
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/ndate/ndate.c
@@ -1,0 +1,11 @@
+#include	<lib9.h>
+
+void
+main(void)
+{
+	ulong t;
+
+	t = time(0);
+	print("%lud\n", t);
+	exits(0);
+}
--- /dev/null
+++ b/utils/nm/mkfile
@@ -1,0 +1,14 @@
+<../../mkconfig
+
+TARG=inm	# inferno nm
+
+OFILES=\
+	nm.$O\
+
+LIBS=mach bio 9		# order is important.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I. -I../include
--- /dev/null
+++ b/utils/nm/nm.c
@@ -1,0 +1,302 @@
+/*
+ * nm.c -- drive nm
+ */
+#include <lib9.h>
+#include <ar.h>
+#include <bio.h>
+#include <mach.h>
+
+enum{
+	CHUNK	=	256	/* must be power of 2 */
+};
+
+char	*errs;			/* exit status */
+char	*filename;		/* current file */
+char	symname[]="__.SYMDEF";	/* table of contents file name */
+int	multifile;		/* processing multiple files */
+int	aflag;
+int	gflag;
+int	hflag;
+int	nflag;
+int	sflag;
+int	uflag;
+int	Tflag;
+
+Sym	**fnames;		/* file path translation table */
+Sym	**symptr;
+int	nsym;
+Biobuf	bout;
+char*	argv0;
+
+void	error(char*, ...);
+void	execsyms(int);
+void	psym(Sym*, void*);
+void	printsyms(Sym**, long);
+void	doar(Biobuf*);
+void	dofile(Biobuf*);
+void	zenter(Sym*);
+
+void
+usage(void)
+{
+	fprint(2, "usage: nm [-aghnsTu] file ...\n");
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	Biobuf	*bin;
+
+	Binit(&bout, 1, OWRITE);
+	argv0 = argv[0];
+	ARGBEGIN {
+	default:	usage();
+	case 'a':	aflag = 1; break;
+	case 'g':	gflag = 1; break;
+	case 'h':	hflag = 1; break;
+	case 'n':	nflag = 1; break;
+	case 's':	sflag = 1; break;
+	case 'u':	uflag = 1; break;
+	case 'T':	Tflag = 1; break;
+	} ARGEND
+	if (argc == 0)
+		usage();
+	if (argc > 1)
+		multifile++;
+	for(i=0; i<argc; i++){
+		filename = argv[i];
+		bin = Bopen(filename, OREAD);
+		if(bin == 0){
+			error("cannot open %s", filename);
+			continue;
+		}
+		if (isar(bin))
+			doar(bin);
+		else{
+			Bseek(bin, 0, 0);
+			dofile(bin);
+		}
+		Bterm(bin);
+	}
+	exits(errs);
+}
+
+/*
+ * read an archive file,
+ * processing the symbols for each intermediate file in it.
+ */
+void
+doar(Biobuf *bp)
+{
+	vlong offset;
+	int size, obj;
+	char membername[SARNAME];
+
+	multifile = 1;
+	for (offset = Boffset(bp);;offset += size) {
+		size = nextar(bp, offset, membername);
+		if (size < 0) {
+			error("phase error on ar header %lld", offset);
+			return;
+		}
+		if (size == 0)
+			return;
+		if (strcmp(membername, symname) == 0)
+			continue;
+		obj = objtype(bp, 0);
+		if (obj < 0) {
+			error("inconsistent file %s in %s",
+					membername, filename);
+			return;
+		}
+		if (!readar(bp, obj, offset+size, 1)) {
+			error("invalid symbol reference in file %s",
+					membername);
+			return;
+		}
+		filename = membername;
+		nsym=0;
+		objtraverse(psym, 0);
+		printsyms(symptr, nsym);
+	}
+}
+
+/*
+ * process symbols in a file
+ */
+void
+dofile(Biobuf *bp)
+{
+	int obj;
+
+	obj = objtype(bp, 0);
+	if (obj < 0)
+		execsyms(Bfildes(bp));
+	else
+	if (readobj(bp, obj)) {
+		nsym = 0;
+		objtraverse(psym, 0);
+		printsyms(symptr, nsym);
+	}
+}
+
+/*
+ * comparison routine for sorting the symbol table
+ *	this screws up on 'z' records when aflag == 1
+ */
+int
+cmp(void *vs, void *vt)
+{
+	Sym **s, **t;
+
+	s = (Sym**)vs;
+	t = (Sym**)vt;
+	if(nflag)
+		if((*s)->value < (*t)->value)
+			return -1;
+		else
+			return (*s)->value > (*t)->value;
+	return strcmp((*s)->name, (*t)->name);
+}
+/*
+ * enter a symbol in the table of filename elements
+ */
+void
+zenter(Sym *s)
+{
+	static int maxf = 0;
+
+	if (s->value >= maxf) {
+		maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
+		fnames = realloc(fnames, maxf*sizeof(*fnames));
+		if(fnames == 0) {
+			error("out of memory", argv0);
+			exits("memory");
+		}
+	}
+	fnames[s->value] = s;
+}
+
+/*
+ * get the symbol table from an executable file, if it has one
+ */
+void
+execsyms(int fd)
+{
+	Fhdr f;
+	Sym *s;
+	long n;
+
+	seek(fd, 0, 0);
+	if (crackhdr(fd, &f) == 0) {
+		error("Can't read header for %s", filename);
+		return;
+	}
+	if (syminit(fd, &f) < 0)
+		return;
+	s = symbase(&n);
+	nsym = 0;
+	while(n--)
+		psym(s++, 0);
+
+	printsyms(symptr, nsym);
+}
+
+void
+psym(Sym *s, void* p)
+{
+	USED(p);
+	switch(s->type) {
+	case 'T':
+	case 'L':
+	case 'D':
+	case 'B':
+		if (uflag)
+			return;
+		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+			return;
+		break;
+	case 'b':
+	case 'd':
+	case 'l':
+	case 't':
+		if (uflag || gflag)
+			return;
+		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+			return;
+		break;
+	case 'U':
+		if (gflag)
+			return;
+		break;
+	case 'Z':
+		if (!aflag)
+			return;
+		break;
+	case 'm':
+	case 'f':	/* we only see a 'z' when the following is true*/
+		if(!aflag || uflag || gflag)
+			return;
+		if (strcmp(s->name, ".frame"))
+			zenter(s);
+		break;
+	case 'a':
+	case 'p':
+	case 'z':
+	default:
+		if(!aflag || uflag || gflag)
+			return;
+		break;
+	}
+	symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
+	if (symptr == 0) {
+		error("out of memory");
+		exits("memory");
+	}
+	symptr[nsym++] = s;
+}
+
+void
+printsyms(Sym **symptr, long nsym)
+{
+	Sym *s;
+	char *cp;
+	char path[512];
+
+	if(!sflag)
+		qsort(symptr, nsym, sizeof(*symptr), cmp);
+	while (nsym-- > 0) {
+		s = *symptr++;
+		if (multifile && !hflag)
+			Bprint(&bout, "%s:", filename);
+		if (s->type == 'z') {
+			fileelem(fnames, (uchar *) s->name, path, 512);
+			cp = path;
+		} else
+			cp = s->name;
+		if (Tflag)
+			Bprint(&bout, "%8ux ", s->sig);
+		if (s->value || s->type == 'a' || s->type == 'p')
+			Bprint(&bout, "%8llux %c %s\n", s->value, s->type, cp);
+		else
+			Bprint(&bout, "         %c %s\n", s->type, cp);
+	}
+}
+
+void
+error(char *fmt, ...)
+{
+	char buf[4096], *s;
+	va_list arg;
+
+	s = buf;
+	s += sprint(s, "%s: ", argv0);
+	va_start(arg, fmt);
+	s = vseprint(s, buf + sizeof(buf) / sizeof(*buf), fmt, arg);
+	va_end(arg);
+	*s++ = '\n';
+	write(2, buf, s - buf);
+	errs = "errors";
+}
--- /dev/null
+++ b/utils/ntsrv/domk
@@ -1,0 +1,2 @@
+os -d $emuroot\utils\ntsrv mk | sed 's/\(([0-9]*)\)/:\1/
+s/
//'
--- /dev/null
+++ b/utils/ntsrv/mkfile
@@ -1,0 +1,25 @@
+#uncomment following line for full Microsoft debug symbols
+#LDEBUG=-debug -debugtype:cv -pdb:none
+<../../mkconfig
+SYSTARG=Nt
+SYSHOST=Nt
+OBJTYPE=386
+OBJDIR=$SYSTARG/$OBJTYPE
+<$ROOT/mkfiles/mkhost-$SYSHOST
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+
+TARG=ntsrv
+
+OFILES=	ntsrv.$O\
+
+HFILES=
+
+LIBS=9
+
+CFLAGS=$CFLAGS -I.
+
+BIN=$ROOT/$OBJDIR/bin
+
+SYSLIBS=	$SYSLIBS wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/ntsrv/ntsrv.c
@@ -1,0 +1,573 @@
+#ifndef ForNT4
+/* only for XP, 2000 and above - JobObject only available on these*/
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include "lib9.h"
+#include <windows.h>
+#include <winsvc.h>
+#include <stdarg.h>
+
+#ifdef JOB_OBJECT_TERMINATE
+#define	Jobs
+#endif
+
+#ifdef ForNT4
+#undef Jobs
+#endif
+
+/*
+ * ntsrv add Name Inferno-root Cmds
+ * ntsrv del Name
+ * ntsrv run Name Inferno-root Cmds
+ *
+ * 'add' registers service: Name with args "run Name Inferno-root Cmds"
+ * 'del' unregisters service Name
+ * 'run' - only given by NT service manager when starting the service (see 'add')
+ *
+ * Cmds are cat'd (with space separator) and requoted for CreateProcess()
+ *
+ * There must be an ntsrv.exe in Inferno-root/Nt/386/bin
+ */
+
+
+SERVICE_STATUS	status = {
+	SERVICE_WIN32_OWN_PROCESS,	/* dwServiceType */
+	0,							/* dwCurrentState */
+	SERVICE_ACCEPT_STOP			/* dwControlsAccepted */
+};
+
+typedef struct Emu Emu;
+struct Emu {
+	HANDLE	proc;		/* NULL if error */
+	HANDLE	job;			/* job for all children */
+	HANDLE	stdin;		/* stdio pipes */
+	HANDLE	stdout;
+	DWORD	notepg;		/* process group ID (in case we lack Jobs) */
+};
+
+typedef struct Copyargs Copyargs;
+struct Copyargs {
+	HANDLE in;
+	HANDLE out;
+};
+
+#ifdef Jobs
+static char *myname = "ntsrv.exe";
+#else
+static char *myname = "ntsrv4.exe";
+#endif
+#define LOGFILE "grid\\slave\\svclog"
+static char *name;
+static char *root;
+static char *cmds;
+static SERVICE_STATUS_HANDLE	statush;
+static HANDLE	emujob;		/* win32 job object for emu session */
+static DWORD	emugroup;		/* process group ID for emu session */
+static HANDLE emuin;		/* stdin pipe of emu */
+static HANDLE logfile;
+
+HANDLE openlog(char*);
+void logmsg(char*, ...);
+void WINAPI infmain(ulong, LPTSTR[]);
+void WINAPI infctl(ulong);
+Emu runemu(char*);
+HANDLE exporthandle(HANDLE, int);
+DWORD WINAPI copy(LPVOID);
+int shuttingdown = 0;
+int nice = 0;
+
+static void
+usage()
+{
+	fprint(2, "usage: ntsrv [-n] add name root cmds | del name\n");
+}
+
+/* (from rcsh)
+ * windows quoting rules - I think
+ * Words are seperated by space or tab
+ * Words containing a space or tab can be quoted using "
+ * 2N backslashes + " ==> N backslashes and end quote
+ * 2N+1 backslashes + " ==> N backslashes + literal "
+ * N backslashes not followed by " ==> N backslashes
+ */
+static char *
+dblquote(char *cmd, char *s)
+{
+	int nb;
+	char *p;
+
+	for(p=s; *p; p++)
+		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
+			break;
+
+	if(*p == 0){				/* easy case */
+		strcpy(cmd, s);
+		return cmd+(p-s);
+	}
+
+	*cmd++ = '"';
+	for(;;) {
+		for(nb=0; *s=='\\'; nb++)
+			*cmd++ = *s++;
+
+		if(*s == 0) {			/* trailing backslashes -> 2N */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			break;
+		}
+
+		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			*cmd++ = '\\';		/* escape the quote */
+		}
+		*cmd++ = *s++;
+	}
+
+	*cmd++ = '"';
+	*cmd = 0;
+
+	return cmd;
+}
+
+static char *
+proccmd(char **argv)
+{
+	int i, n;
+	char *cmd, *p;
+
+	/* conservatively calculate length of command;
+	 * backslash expansion can cause growth in dblquote().
+	 */
+	for(i=0,n=0; argv[i]; i++) {
+		n += 2*strlen(argv[i]);
+	}
+	n++;
+	
+	cmd = malloc(n);
+	for(i=0,p=cmd; argv[i]; i++) {
+		p = dblquote(p, argv[i]);
+		*p++ = ' ';
+	}
+	if(p != cmd)
+		p--;
+	*p = 0;
+
+	return cmd;
+}
+
+int
+installnewemu()
+{
+	LPCTSTR currpath, newpath;
+	currpath = smprint("%s\\Nt\\386\\bin\\emu.exe", root);
+	newpath = smprint("%s\\Nt\\386\\bin\\newemu.exe", root);
+	if(GetFileAttributes(newpath) == 0xffffffff)	// INVALID_FILE_ATTRIBUTES is not defined
+		return 0;
+	DeleteFile(currpath);			// ignore error message - it might not be there.
+	if(MoveFile(newpath, currpath) == 0){
+		logmsg("cannot rename %s to %s: %r", newpath, currpath);
+		return -1;
+	}
+	return 0;
+}	
+
+void WINAPI
+infmain(ulong argc, char *argv[])
+{
+	HANDLE cpt;
+	Emu emu;
+	Copyargs cp;
+	DWORD tid;
+	char *cmd;
+
+	argc--;
+	argv++;
+	cmd = smprint("%s\\%s", root, LOGFILE);
+	logfile = openlog(cmd);
+	free(cmd);
+	statush = RegisterServiceCtrlHandler(name, infctl);
+	if (statush == 0)
+		return;
+
+	status.dwCurrentState = SERVICE_START_PENDING;
+	SetServiceStatus(statush, &status);
+
+	while(installnewemu() != -1){
+		/* start the service */
+		cmd = smprint("%s\\Nt\\386\\bin\\emu.exe -r%s %s", root, root, cmds);
+		logmsg("starting %s", cmd);
+		emu = runemu(cmd);
+		free(cmd);
+		if (emu.proc == NULL) {
+			logmsg("runemu failed: %r");
+			status.dwCurrentState = SERVICE_STOPPED;
+			SetServiceStatus(statush, &status);
+			return;
+		}
+	
+		cp.in = emu.stdout;
+		cp.out = logfile;
+		cpt = CreateThread(NULL, 0, copy, (void*)&cp, 0, &tid);
+		if (cpt == NULL) {
+			logmsg("failed to create copy thread: %r");
+			CloseHandle(emu.stdout);
+		}
+	
+		logmsg("infmain blocking on emu proc");
+		emujob = emu.job;
+		emugroup = emu.notepg;
+		status.dwCurrentState = SERVICE_RUNNING;
+		SetServiceStatus(statush, &status);
+		WaitForSingleObject(emu.proc, INFINITE);
+		logmsg("infmain emu proc terminated");
+		emujob = NULL;
+		emugroup = 0;
+#ifdef Jobs
+		logmsg("terminating job");
+		TerminateJobObject(emu.job, 0);
+#else
+		logmsg("notepg (%d)", emu.notepg);
+		if(emu.notepg)
+			GenerateConsoleCtrlEvent(CTRL_C_EVENT, emu.notepg);
+#endif
+		if (cpt) {
+			/* copy() sees eof on emu.stdout and exits */
+			WaitForSingleObject(cpt, INFINITE);
+			CloseHandle(cpt);
+			CloseHandle(emu.stdout);
+		}
+		CloseHandle(emu.proc);
+		if(emu.job != NULL)
+			CloseHandle(emu.job);
+		CloseHandle(emu.stdin);
+		// XXX should check to see that we're not starting up again too quickly, as
+		// it's quite possible to get into an infinite loop here.
+		// but what are good criteria? 5 times? 100 times?
+		// 10 times within a minute?
+		// for the moment, just sleep for a while before restarting...
+		if(shuttingdown)
+			break;
+		SleepEx(10000, FALSE);
+	}
+	logmsg("infmain done");
+	if (logfile)
+		CloseHandle(logfile);
+	status.dwCurrentState = SERVICE_STOPPED;
+	SetServiceStatus(statush, &status);
+	return;
+}
+
+void WINAPI
+infctl(ulong op)
+{
+	if (op != SERVICE_CONTROL_STOP)
+		return;
+
+	/* stop the service (status set by infmain()
+	 *
+	 * NOTE: there is a race for emujob - may have been closed
+	 * after test, but before TerminateJobObject()
+	 * MSDN is unclear as to whether TerminatJobObject() handles
+	 * NULL job ptr - should probably use a mutex
+	 */
+	shuttingdown = 1;
+#ifdef Jobs
+	logmsg("svc stop: stopping job");
+	if (emujob)
+		TerminateJobObject(emujob, 0);
+#else
+	logmsg("svc stop: interrupting emu");
+	if (emugroup)
+		GenerateConsoleCtrlEvent(CTRL_C_EVENT, emugroup);
+#endif
+}
+
+void
+printerror(char *s)
+{
+	char *msg;
+	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
+		FORMAT_MESSAGE_FROM_SYSTEM|
+		FORMAT_MESSAGE_IGNORE_INSERTS,
+		NULL,
+		GetLastError(),
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPTSTR)&msg,
+		0,
+		NULL);
+	fprint(2, "%s: %s\n", s, msg);
+	LocalFree(msg);
+}
+
+int
+add(char *name, char *root, char *cmds)
+{
+	char *path;
+	int r;
+	SC_HANDLE scm, scs;
+	char *nopt;
+
+	nopt = nice ? " -n" : "";
+	path = smprint("%s\\Nt\\386\\bin\\%s%s run %s %s %s", root, myname, nopt, name, root, cmds);
+	r = 0;
+	scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+	if (scm == NULL) {
+		printerror("cannot open service control manager");
+		return -1;
+	}
+	scs = CreateService(scm,
+		name,
+		name,
+		SERVICE_START|SERVICE_STOP,
+		SERVICE_WIN32_OWN_PROCESS,
+		SERVICE_AUTO_START,
+		SERVICE_ERROR_IGNORE,
+		path,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		NULL
+	);
+	if (scs == NULL) {
+		printerror("cannot create service");
+		r = -1;
+	} else {
+		CloseServiceHandle(scs);
+	}
+	CloseServiceHandle(scm);
+	return r;
+}
+
+int
+del(char *name)
+{
+	SC_HANDLE scm, scs;
+
+	scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+	if (scm == NULL) {
+		printerror("cannot open service control manager");
+		return -1;
+	}
+
+	scs = OpenService(scm, name, DELETE);
+	if (scs == NULL) {
+		printerror("cannot open service");
+		CloseServiceHandle(scm);
+		return -1;
+	}
+	if (!DeleteService(scs)) {
+		printerror("cannot delete Iservice");
+		CloseServiceHandle(scs);
+		CloseServiceHandle(scm);
+		return -1;
+	}
+	CloseServiceHandle(scs);
+	CloseServiceHandle(scm);
+	return 0;
+}
+
+HANDLE
+openlog(char *p)
+{
+	HANDLE h;
+	h = CreateFile(p, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+	if (h == INVALID_HANDLE_VALUE)
+		return NULL;
+	SetFilePointer(h, 0, NULL, FILE_END);
+	return h;
+}
+
+void
+logmsg(char *fmt, ...)
+{
+	int n;
+	char *p;
+	va_list args;
+	if(logfile == 0)
+		return;
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+	n = strlen(p);
+	if (n)
+		WriteFile(logfile, p, n, &n, NULL);
+	WriteFile(logfile, "\n", 1, &n, NULL);
+}
+
+Emu
+runemu(char *cmd)
+{
+	Emu r = {NULL, NULL, NULL};
+	STARTUPINFO si;
+	PROCESS_INFORMATION pinfo;
+	HANDLE job, emu, emut, stdin, stdout, stderr, emui, emuo;
+	SECURITY_ATTRIBUTES sec;
+	DWORD flags;
+
+	job = emu = emut = stdin = stdout = stderr = emui = emuo = NULL;
+#ifdef Jobs
+	job = CreateJobObject(NULL, NULL);
+	if (job == NULL) {
+		logmsg("cannot create job object: %r");
+		goto error;
+	}
+#endif
+
+	/* set up pipes */
+	sec.nLength = sizeof(sec);
+	sec.lpSecurityDescriptor = 0;
+	sec.bInheritHandle = 0;
+	if (!CreatePipe(&stdin, &emui, &sec, 0)) {
+		logmsg("cannot create stdin pipe: %r");
+		goto error;
+	}
+	if (!CreatePipe(&emuo, &stdout, &sec, 0)) {
+		logmsg("cannot create stdout pipe: %r");
+		goto error;
+	}
+	stdin = exporthandle(stdin, 1);
+	stdout = exporthandle(stdout, 1);
+	stderr = exporthandle(stdout, 0);
+
+	/* create emu process (suspended) */
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESTDHANDLES;
+	si.hStdInput = stdin;
+	si.hStdOutput = stdout;
+	si.hStdError = stderr;
+
+	flags = CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|CREATE_SUSPENDED;
+	if(nice)
+		flags |= IDLE_PRIORITY_CLASS;
+	if(!CreateProcess(0, cmd, 0, 0, 1, flags, 0, 0, &si, &pinfo)) {
+		logmsg("cannot create process: %r");
+		goto error;
+	}
+	emu = pinfo.hProcess;
+	emut = pinfo.hThread;
+	CloseHandle(stdin);
+	stdin = NULL;
+	CloseHandle(stdout);
+	stdout = NULL;
+	CloseHandle(stderr);
+	stderr = NULL;
+
+#ifdef Jobs
+	if(!AssignProcessToJobObject(job, emu)) {
+		logmsg("failed to assign emu to job: %r");
+		goto error;
+	}
+#endif
+	ResumeThread(emut);
+	CloseHandle(emut);
+
+	r.proc = emu;
+	r.notepg = pinfo.dwProcessId;
+	r.job = job;	/* will be NULL if not implemented (NT4) */
+	r.stdin = emui;
+	r.stdout = emuo;
+	return r;
+
+error:
+	if (stdin)
+		CloseHandle(stdin);
+	if (stdout)
+		CloseHandle(stdout);
+	if (stderr)
+		CloseHandle(stderr);
+	if (emui)
+		CloseHandle(emuin);
+	if (emuo)
+		CloseHandle(emuo);
+	if (emut)
+		CloseHandle(emut);
+	if (emu) {
+		TerminateProcess(emu, 0);
+		CloseHandle(emu);
+	}
+	if (job)
+		CloseHandle(job);
+	return r;
+}
+
+HANDLE
+exporthandle(HANDLE h, int close)
+{
+	HANDLE cp, dh;
+	DWORD flags = DUPLICATE_SAME_ACCESS;
+	if (close)
+		flags |= DUPLICATE_CLOSE_SOURCE;
+	cp = GetCurrentProcess();
+	if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))
+		return nil;
+	return dh;
+}
+
+DWORD WINAPI
+copy(void *arg)
+{
+	Copyargs *cp = (Copyargs*)arg;
+	char buf[1024];
+	DWORD n;
+
+	while (ReadFile(cp->in, buf, sizeof(buf), &n, NULL)) {
+		if (n && cp->out)
+			WriteFile(cp->out, buf, n, &n, NULL);
+	}
+	return 0;
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *verb;
+	SERVICE_TABLE_ENTRY services[2];
+
+	memset(services, 0, sizeof(services));
+
+	ARGBEGIN{
+	case 'n':
+		nice = 1;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if (argc < 2) {
+		usage();
+		return;
+	}
+
+	verb = argv[0];
+	name = argv[1];
+	if (argc > 2)
+		root = argv[2];
+	if (argc > 3)
+		cmds = proccmd(argv+3);
+
+	if (strcmp(verb, "del") == 0)
+		exit(del(name));
+	if (strcmp(verb, "add") == 0) {
+		if (root == NULL || cmds == NULL) {
+			usage();
+			return;
+		}
+		exit(add(name, root, cmds));
+	}
+	if (strcmp(verb, "run") == 0) {
+		if (root == NULL || cmds == NULL || *cmds == '\0') {
+			usage();
+			return;
+		}
+		services[0].lpServiceName = name;
+		services[0].lpServiceProc = infmain;
+		StartServiceCtrlDispatcher(services);
+		exit(0);
+	}
+	usage();
+}
--- /dev/null
+++ b/utils/qa/a.h
@@ -1,0 +1,199 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../qc/q.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		8192
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+#define	ALLOC(lhs, type)\
+	while(nhunk < sizeof(type))\
+		gethunk();\
+	lhs = (type*)hunk;\
+	nhunk -= sizeof(type);\
+	hunk += sizeof(type);
+
+#define	ALLOCN(lhs, len, n)\
+	if(lhs+len != hunk || nhunk < n) {\
+		while(nhunk <= len)\
+			gethunk();\
+		memmove(hunk, lhs, len);\
+		lhs = hunk;\
+		hunk += len;\
+		nhunk -= len;\
+	}\
+	hunk += n;\
+	nhunk -= n;
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym*	sym;
+	long	offset;
+	short	type;
+	short	reg;
+	short	xreg;
+	short	name;
+	ushort	mask;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	nosched;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+void	outcode(int, Gen*, int, Gen*);
+void	outgcode(int, Gen*, int, Gen*, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	macprag(void);
+void	maclin(void);
+void	macif(int);
+void	macend(void);
+void	dodefine(char*);
+void	prfile(long);
+void	outhist(void);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/qa/a.y
@@ -1,0 +1,998 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	long	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LMOVW LMOVB LABS LLOGW LSHW LADDW LCMP LCROP
+%token	<lval>	LBRA LFMOV LFCONV LFCMP LFADD LFMA LTRAP LXORW
+%token	<lval>	LNOP LEND LRETT LWORD LTEXT LDATA LRETRN
+%token	<lval>	LCONST LSP LSB LFP LPC LCREG LFLUSH
+%token	<lval>	LREG LFREG LR LCR LF LFPSCR
+%token	<lval>	LLR LCTR LSPR LSPREG LSEG LMSR LDCR
+%token	<lval>	LSCHED LXLD LXST LXOP LXMV
+%token	<lval>	LRLWM LMOVMW LMOVEM LMOVFL LMTFSB LMA LFMOVX
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg
+%type	<gen>	addr rreg regaddr name creg freg xlreg lr ctr
+%type	<gen>	imm ximm fimm rel psr lcr cbit fpscr fpscrf seg msr mask
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LSCHED ';'
+	{
+		nosched = $1;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * load ints and bytes
+ */
+	LMOVW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW regaddr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB regaddr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * load and store floats
+ */
+|	LFMOV addr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV regaddr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV fimm ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV freg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOV freg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * load and store floats, indexed only
+ */
+|	LFMOVX regaddr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFMOVX freg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * store ints and bytes
+ */
+|	LMOVW rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW rreg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVB rreg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * store floats
+ */
+|	LMOVW freg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW freg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * floating point status
+ */
+|	LMOVW fpscr ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW freg ','  fpscr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW freg ',' imm ',' fpscr
+	{
+		outgcode($1, &$2, NREG, &$4, &$6);
+	}
+|	LMOVW fpscr ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW imm ',' fpscrf
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMTFSB imm ',' con
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+/*
+ * field moves (mtcrf)
+ */
+|	LMOVW rreg ',' imm ',' lcr
+	{
+		outgcode($1, &$2, NREG, &$4, &$6);
+	}
+|	LMOVW rreg ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW rreg ',' lcr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * integer operations
+ * logical instructions
+ * shift instructions
+ * unary instructions
+ */
+|	LADDW rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LADDW imm ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LADDW rreg ',' imm ',' rreg
+	{
+		outgcode($1, &$2, NREG, &$4, &$6);
+	}
+|	LADDW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LADDW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LLOGW rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LLOGW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LSHW rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LSHW rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LSHW imm ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LSHW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LABS rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LABS rreg
+	{
+		outcode($1, &$2, NREG, &$2);
+	}
+/*
+ * multiply-accumulate
+ */
+|	LMA rreg ',' sreg ',' rreg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * move immediate: macro for cau+or, addi, addis, and other combinations
+ */
+|	LMOVW imm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW ximm ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * condition register operations
+ */
+|	LCROP cbit ',' cbit
+	{
+		outcode($1, &$2, $4.reg, &$4);
+	}
+|	LCROP cbit ',' con ',' cbit
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * condition register moves
+ * move from machine state register
+ */
+|	LMOVW creg ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW psr ',' creg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW lcr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW psr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW seg ',' rreg
+	{
+		int r;
+		r = $2.offset;
+		$2.offset = 0;
+		outcode($1, &$2, r, &$4);
+	}
+|	LMOVW rreg ',' seg
+	{
+		int r;
+		r = $4.offset;
+		$4.offset = 0;
+		outcode($1, &$2, r, &$4);
+	}
+|	LMOVW xlreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW rreg ',' xlreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW creg ',' psr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVW rreg ',' psr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * branch, branch conditional
+ * branch conditional register
+ * branch conditional to count register
+ */
+|	LBRA rel
+	{
+		outcode($1, &nullgen, NREG, &$2);
+	}
+|	LBRA addr
+	{
+		outcode($1, &nullgen, NREG, &$2);
+	}
+|	LBRA '(' xlreg ')'
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LBRA ',' rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LBRA ',' addr
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LBRA ',' '(' xlreg ')'
+	{
+		outcode($1, &nullgen, NREG, &$4);
+	}
+|	LBRA creg ',' rel
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LBRA creg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LBRA creg ',' '(' xlreg ')'
+	{
+		outcode($1, &$2, NREG, &$5);
+	}
+|	LBRA con ',' rel
+	{
+		outcode($1, &nullgen, $2, &$4);
+	}
+|	LBRA con ',' addr
+	{
+		outcode($1, &nullgen, $2, &$4);
+	}
+|	LBRA con ',' '(' xlreg ')'
+	{
+		outcode($1, &nullgen, $2, &$5);
+	}
+|	LBRA con ',' con ',' rel
+	{
+		Gen g;
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $2;
+		outcode($1, &g, $4, &$6);
+	}
+|	LBRA con ',' con ',' addr
+	{
+		Gen g;
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $2;
+		outcode($1, &g, $4, &$6);
+	}
+|	LBRA con ',' con ',' '(' xlreg ')'
+	{
+		Gen g;
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $2;
+		outcode($1, &g, $4, &$7);
+	}
+/*
+ * conditional trap
+ */
+|	LTRAP rreg ',' sreg
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTRAP imm ',' sreg
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTRAP rreg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LTRAP comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * floating point operate
+ */
+|	LFCONV freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFADD freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFADD freg ',' freg ',' freg
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+|	LFMA freg ',' freg ',' freg ',' freg
+	{
+		outgcode($1, &$2, $4.reg, &$6, &$8);
+	}
+|	LFCMP freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LFCMP freg ',' freg ',' creg
+	{
+		outcode($1, &$2, $6.reg, &$4);
+	}
+/*
+ * CMP
+ */
+|	LCMP rreg ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LCMP rreg ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LCMP rreg ',' rreg ',' creg
+	{
+		outcode($1, &$2, $6.reg, &$4);
+	}
+|	LCMP rreg ',' imm ',' creg
+	{
+		outcode($1, &$2, $6.reg, &$4);
+	}
+/*
+ * rotate and mask
+ */
+|	LRLWM  imm ',' rreg ',' imm ',' rreg
+	{
+		outgcode($1, &$2, $4.reg, &$6, &$8);
+	}
+|	LRLWM  imm ',' rreg ',' mask ',' rreg
+	{
+		outgcode($1, &$2, $4.reg, &$6, &$8);
+	}
+|	LRLWM  rreg ',' rreg ',' imm ',' rreg
+	{
+		outgcode($1, &$2, $4.reg, &$6, &$8);
+	}
+|	LRLWM  rreg ',' rreg ',' mask ',' rreg
+	{
+		outgcode($1, &$2, $4.reg, &$6, &$8);
+	}
+/*
+ * load/store multiple
+ */
+|	LMOVMW addr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVMW rreg ',' addr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * various indexed load/store
+ * indexed unary (eg, cache clear)
+ */
+|	LXLD regaddr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXLD regaddr ',' imm ',' rreg
+	{
+		outgcode($1, &$2, NREG, &$4, &$6);
+	}
+|	LXST rreg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXST rreg ',' imm ',' regaddr
+	{
+		outgcode($1, &$2, NREG, &$4, &$6);
+	}
+|	LXMV regaddr ',' rreg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXMV rreg ',' regaddr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LXOP regaddr
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * NOP
+ */
+|	LNOP comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LNOP rreg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LNOP freg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LNOP ',' rreg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LNOP ',' freg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * word
+ */
+|	LWORD imm comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LWORD ximm comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * END
+ */
+|	LEND comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTEXT name ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTEXT name ',' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTEXT name ',' imm ':' imm
+	{
+		outgcode($1, &$2, NREG, &$6, &$4);
+	}
+|	LTEXT name ',' con ',' imm ':' imm
+	{
+		outgcode($1, &$2, $4, &$8, &$6);
+	}
+/*
+ * DATA
+ */
+|	LDATA name '/' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LDATA name '/' con ',' ximm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LDATA name '/' con ',' fimm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * RETURN
+ */
+|	LRETRN	comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+rreg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+xlreg:
+	lr
+|	ctr
+
+lr:
+	LLR
+	{
+		$$ = nullgen;
+		$$.type = D_SPR;
+		$$.offset = $1;
+	}
+
+lcr:
+	LCR
+	{
+		$$ = nullgen;
+		$$.type = D_CREG;
+		$$.reg = NREG;	/* whole register */
+	}
+
+ctr:
+	LCTR
+	{
+		$$ = nullgen;
+		$$.type = D_SPR;
+		$$.offset = $1;
+	}
+
+msr:
+	LMSR
+	{
+		$$ = nullgen;
+		$$.type = D_MSR;
+	}
+
+psr:
+	LSPREG
+	{
+		$$ = nullgen;
+		$$.type = D_SPR;
+		$$.offset = $1;
+	}
+|	LSPR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = $1;
+		$$.offset = $3;
+	}
+|	LDCR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = $1;
+		$$.offset = $3;
+	}
+|	LDCR '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = $1;
+		$$.reg = $3;
+		$$.offset = 0;
+	}
+|	msr
+
+seg:
+	LSEG '(' con ')'
+	{
+		if($3 < 0 || $3 > 15)
+			yyerror("segment register number out of range");
+		$$ = nullgen;
+		$$.type = D_SREG;
+		$$.reg = $3;
+		$$.offset = NREG;
+	}
+|	LSEG '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_SREG;
+		$$.reg = NREG;
+		$$.offset = $3;
+	}
+
+fpscr:
+	LFPSCR
+	{
+		$$ = nullgen;
+		$$.type = D_FPSCR;
+		$$.reg = NREG;
+	}
+
+fpscrf:
+	LFPSCR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FPSCR;
+		$$.reg = $3;
+	}
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+creg:
+	LCREG
+	{
+		$$ = nullgen;
+		$$.type = D_CREG;
+		$$.reg = $1;
+	}
+|	LCR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_CREG;
+		$$.reg = $3;
+	}
+
+
+cbit:	con
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+mask:
+	con ',' con
+	{
+		int mb, me;
+		ulong v;
+
+		$$ = nullgen;
+		$$.type = D_CONST;
+		mb = $1;
+		me = $3;
+		if(mb < 0 || mb > 31 || me < 0 || me > 31){
+			yyerror("illegal mask start/end value(s)");
+			mb = me = 0;
+		}
+		if(mb <= me)
+			v = ((ulong)~0L>>mb) & (~0L<<(31-me));
+		else
+			v = ~(((ulong)~0L>>(me+1)) & (~0L<<(31-(mb-1))));
+		$$.offset = v;
+	}
+
+ximm:
+	'$' addr
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+
+fimm:
+	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+sreg:
+	LREG
+|	LR '(' con ')'
+	{
+		if($$ < 0 || $$ >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+regaddr:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	'(' sreg '+' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.xreg = $4;
+		$$.offset = 0;
+	}
+
+addr:
+	name
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+comma:
+|	','
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/qa/lex.c
@@ -1,0 +1,940 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = 'q';
+	thestring = "power";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	if(p = strrchr(ofile, pathchar())) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(p = strrchr(outfile, '.'))
+			if(p[1] == 's' && p[2] == 0)
+				p[0] = 0;
+		p = strrchr(outfile, 0);
+		p[0] = '.';
+		p[1] = thechar;
+		p[2] = 0;
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	nosched = 0;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	nosched = 0;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"LR",		LLR,	D_LR,
+	"CTR",		LCTR,	D_CTR,
+
+	"XER",		LSPREG,	D_XER,
+	"MSR",		LMSR,	D_MSR,
+	"FPSCR",	LFPSCR,	D_FPSCR,
+	"SPR",		LSPR,	D_SPR,
+	"DCR",		LDCR,	D_DCR,
+
+	"SEG",		LSEG,	D_SREG,
+
+	"CR",		LCR,	0,
+	"CR0",		LCREG,	0,
+	"CR1",		LCREG,	1,
+	"CR2",		LCREG,	2,
+	"CR3",		LCREG,	3,
+	"CR4",		LCREG,	4,
+	"CR5",		LCREG,	5,
+	"CR6",		LCREG,	6,
+	"CR7",		LCREG,	7,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+	"R16",		LREG,	16,
+	"R17",		LREG,	17,
+	"R18",		LREG,	18,
+	"R19",		LREG,	19,
+	"R20",		LREG,	20,
+	"R21",		LREG,	21,
+	"R22",		LREG,	22,
+	"R23",		LREG,	23,
+	"R24",		LREG,	24,
+	"R25",		LREG,	25,
+	"R26",		LREG,	26,
+	"R27",		LREG,	27,
+	"R28",		LREG,	28,
+	"R29",		LREG,	29,
+	"R30",		LREG,	30,
+	"R31",		LREG,	31,
+
+	"F",		LF,	0,
+	"F0",		LFREG,	0,
+	"F1",		LFREG,	1,
+	"F2",		LFREG,	2,
+	"F3",		LFREG,	3,
+	"F4",		LFREG,	4,
+	"F5",		LFREG,	5,
+	"F6",		LFREG,	6,
+	"F7",		LFREG,	7,
+	"F8",		LFREG,	8,
+	"F9",		LFREG,	9,
+	"F10",		LFREG,	10,
+	"F11",		LFREG,	11,
+	"F12",		LFREG,	12,
+	"F13",		LFREG,	13,
+	"F14",		LFREG,	14,
+	"F15",		LFREG,	15,
+	"F16",		LFREG,	16,
+	"F17",		LFREG,	17,
+	"F18",		LFREG,	18,
+	"F19",		LFREG,	19,
+	"F20",		LFREG,	20,
+	"F21",		LFREG,	21,
+	"F22",		LFREG,	22,
+	"F23",		LFREG,	23,
+	"F24",		LFREG,	24,
+	"F25",		LFREG,	25,
+	"F26",		LFREG,	26,
+	"F27",		LFREG,	27,
+	"F28",		LFREG,	28,
+	"F29",		LFREG,	29,
+	"F30",		LFREG,	30,
+	"F31",		LFREG,	31,
+
+	"CREQV",	LCROP, ACREQV,
+	"CRXOR",	LCROP, ACRXOR,
+	"CRAND",	LCROP, ACRAND,
+	"CROR",		LCROP, ACROR,
+	"CRANDN",	LCROP, ACRANDN,
+	"CRORN",	LCROP, ACRORN,
+	"CRNAND",	LCROP, ACRNAND,
+	"CRNOR",	LCROP, ACRNOR,
+
+	"ADD",		LADDW, AADD,
+	"ADDV",		LADDW, AADDV,
+	"ADDCC",	LADDW, AADDCC,
+	"ADDVCC",	LADDW, AADDVCC,
+	"ADDC",		LADDW, AADDC,
+	"ADDCV",	LADDW, AADDCV,
+	"ADDCCC",	LADDW, AADDCCC,
+	"ADDCVCC",	LADDW, AADDCVCC,
+	"ADDE",		LLOGW, AADDE,
+	"ADDEV",	LLOGW, AADDEV,
+	"ADDECC",	LLOGW, AADDECC,
+	"ADDEVCC",	LLOGW, AADDEVCC,
+
+	"ADDME",	LABS, AADDME,
+	"ADDMEV",	LABS, AADDMEV,
+	"ADDMECC",	LABS, AADDMECC,
+	"ADDMEVCC",	LABS, AADDMEVCC,
+	"ADDZE",	LABS, AADDZE,
+	"ADDZEV",	LABS, AADDZEV,
+	"ADDZECC",	LABS, AADDZECC,
+	"ADDZEVCC",	LABS, AADDZEVCC,
+
+	"SUB",		LADDW, ASUB,
+	"SUBV",		LADDW, ASUBV,
+	"SUBCC",	LADDW, ASUBCC,
+	"SUBVCC",	LADDW, ASUBVCC,
+	"SUBE",		LLOGW, ASUBE,
+	"SUBECC",	LLOGW, ASUBECC,
+	"SUBEV",	LLOGW, ASUBEV,
+	"SUBEVCC",	LLOGW, ASUBEVCC,
+	"SUBC",		LADDW, ASUBC,
+	"SUBCCC",	LADDW, ASUBCCC,
+	"SUBCV",	LADDW, ASUBCV,
+	"SUBCVCC",	LADDW, ASUBCVCC,
+
+	"SUBME",	LABS, ASUBME,
+	"SUBMEV",	LABS, ASUBMEV,
+	"SUBMECC",	LABS, ASUBMECC,
+	"SUBMEVCC",	LABS, ASUBMEVCC,
+	"SUBZE",	LABS, ASUBZE,
+	"SUBZEV",	LABS, ASUBZEV,
+	"SUBZECC",	LABS, ASUBZECC,
+	"SUBZEVCC",	LABS, ASUBZEVCC,
+
+	"AND",		LADDW, AAND,
+	"ANDCC",	LADDW, AANDCC,	/* includes andil & andiu */
+	"ANDN",		LLOGW, AANDN,
+	"ANDNCC",	LLOGW, AANDNCC,
+	"EQV",		LLOGW, AEQV,
+	"EQVCC",	LLOGW, AEQVCC,
+	"NAND",		LLOGW, ANAND,
+	"NANDCC",	LLOGW, ANANDCC,
+	"NOR",		LLOGW, ANOR,
+	"NORCC",	LLOGW, ANORCC,
+	"OR",		LADDW, AOR,	/* includes oril & oriu */
+	"ORCC",		LADDW, AORCC,
+	"ORN",		LLOGW, AORN,
+	"ORNCC",	LLOGW, AORNCC,
+	"XOR",		LADDW, AXOR,	/* includes xoril & xoriu */
+	"XORCC",	LLOGW, AXORCC,
+
+	"EXTSB",	LABS,	AEXTSB,
+	"EXTSBCC",	LABS,	AEXTSBCC,
+	"EXTSH",	LABS, AEXTSH,
+	"EXTSHCC",	LABS, AEXTSHCC,
+
+	"CNTLZW",	LABS, ACNTLZW,
+	"CNTLZWCC",	LABS, ACNTLZWCC,
+
+	"RLWMI",	LRLWM, ARLWMI,
+	"RLWMICC",	LRLWM, ARLWMICC,
+	"RLWNM",	LRLWM, ARLWNM,
+	"RLWNMCC", LRLWM, ARLWNMCC,
+
+	"SLW",		LSHW, ASLW,
+	"SLWCC",	LSHW, ASLWCC,
+	"SRW",		LSHW, ASRW,
+	"SRWCC",	LSHW, ASRWCC,
+	"SRAW",		LSHW, ASRAW,
+	"SRAWCC",	LSHW, ASRAWCC,
+
+	"BR",		LBRA, ABR,
+	"BC",		LBRA, ABC,
+	"BCL",		LBRA, ABC,
+	"BL",		LBRA, ABL,
+	"BEQ",		LBRA, ABEQ,
+	"BNE",		LBRA, ABNE,
+	"BGT",		LBRA, ABGT,
+	"BGE",		LBRA, ABGE,
+	"BLT",		LBRA, ABLT,
+	"BLE",		LBRA, ABLE,
+	"BVC",		LBRA, ABVC,
+	"BVS",		LBRA, ABVS,
+
+	"CMP",		LCMP, ACMP,
+	"CMPU",		LCMP, ACMPU,
+
+	"DIVW",		LLOGW, ADIVW,
+	"DIVWV",	LLOGW, ADIVWV,
+	"DIVWCC",	LLOGW, ADIVWCC,
+	"DIVWVCC",	LLOGW, ADIVWVCC,
+	"DIVWU",	LLOGW, ADIVWU,
+	"DIVWUV",	LLOGW, ADIVWUV,
+	"DIVWUCC",	LLOGW, ADIVWUCC,
+	"DIVWUVCC",	LLOGW, ADIVWUVCC,
+
+	"FABS",		LFCONV,	AFABS,
+	"FABSCC",	LFCONV,	AFABSCC,
+	"FNEG",		LFCONV,	AFNEG,
+	"FNEGCC",	LFCONV,	AFNEGCC,
+	"FNABS",	LFCONV,	AFNABS,
+	"FNABSCC",	LFCONV,	AFNABSCC,
+
+	"FADD",		LFADD,	AFADD,
+	"FADDCC",	LFADD,	AFADDCC,
+	"FSUB",		LFADD,  AFSUB,
+	"FSUBCC",	LFADD,	AFSUBCC,
+	"FMUL",		LFADD,	AFMUL,
+	"FMULCC",	LFADD,	AFMULCC,
+	"FDIV",		LFADD,	AFDIV,
+	"FDIVCC",	LFADD,	AFDIVCC,
+	"FRSP",		LFCONV,	AFRSP,
+	"FRSPCC",	LFCONV,	AFRSPCC,
+
+	"FMADD",	LFMA, AFMADD,
+	"FMADDCC",	LFMA, AFMADDCC,
+	"FMSUB",	LFMA, AFMSUB,
+	"FMSUBCC",	LFMA, AFMSUBCC,
+	"FNMADD",	LFMA, AFNMADD,
+	"FNMADDCC",	LFMA, AFNMADDCC,
+	"FNMSUB",	LFMA, AFNMSUB,
+	"FNMSUBCC",	LFMA, AFNMSUBCC,
+	"FMADDS",	LFMA, AFMADDS,
+	"FMADDSCC",	LFMA, AFMADDSCC,
+	"FMSUBS",	LFMA, AFMSUBS,
+	"FMSUBSCC",	LFMA, AFMSUBSCC,
+	"FNMADDS",	LFMA, AFNMADDS,
+	"FNMADDSCC",	LFMA, AFNMADDSCC,
+	"FNMSUBS",	LFMA, AFNMSUBS,
+	"FNMSUBSCC",	LFMA, AFNMSUBSCC,
+
+	"FCMPU",	LFCMP, AFCMPU,
+	"FCMPO",	LFCMP, AFCMPO,
+	"MTFSB0",	LMTFSB, AMTFSB0,
+	"MTFSB1",	LMTFSB,	AMTFSB1,
+
+	"FMOVD",	LFMOV, AFMOVD,
+	"FMOVS",	LFMOV, AFMOVS,
+	"FMOVDCC",	LFCONV,	AFMOVDCC,	/* fmr. */
+
+	"GLOBL",	LTEXT, AGLOBL,
+
+	"MOVB",		LMOVB, AMOVB,
+	"MOVBZ",	LMOVB, AMOVBZ,
+	"MOVBU",	LMOVB, AMOVBU,
+	"MOVBZU", LMOVB, AMOVBZU,
+	"MOVH",		LMOVB, AMOVH,
+	"MOVHZ",	LMOVB, AMOVHZ,
+	"MOVHU",	LMOVB, AMOVHU,
+	"MOVHZU", LMOVB, AMOVHZU,
+	"MOVHBR", 	LXMV, AMOVHBR,
+	"MOVWBR",	LXMV, AMOVWBR,
+	"MOVW",		LMOVW, AMOVW,
+	"MOVWU",	LMOVW, AMOVWU,
+	"MOVMW",	LMOVMW, AMOVMW,
+	"MOVFL",	LMOVW,	AMOVFL,
+
+	"MULLW",	LADDW, AMULLW,		/* includes multiply immediate 10-139 */
+	"MULLWV",	LLOGW, AMULLWV,
+	"MULLWCC",	LLOGW, AMULLWCC,
+	"MULLWVCC",	LLOGW, AMULLWVCC,
+
+	"MULHW",	LLOGW, AMULHW,
+	"MULHWCC",	LLOGW, AMULHWCC,
+	"MULHWU",	LLOGW, AMULHWU,
+	"MULHWUCC",	LLOGW, AMULHWUCC,
+
+	"NEG",		LABS, ANEG,
+	"NEGV",		LABS, ANEGV,
+	"NEGCC",	LABS, ANEGCC,
+	"NEGVCC",	LABS, ANEGVCC,
+
+	"NOP",		LNOP, ANOP,	/* ori 0,0,0 */
+	"SYSCALL",	LNOP, ASYSCALL,
+
+	"RETURN",	LRETRN, ARETURN,
+	"RFI",		LRETRN,	ARFI,
+	"RFCI",		LRETRN,	ARFCI,
+
+	"DATA",		LDATA, ADATA,
+	"END",		LEND, AEND,
+	"TEXT",		LTEXT, ATEXT,
+
+	/* IBM powerpc embedded  */
+	"MACCHW", LMA, AMACCHW,
+	"MACCHWCC", LMA, AMACCHWCC,
+	"MACCHWS", LMA, AMACCHWS,
+	"MACCHWSCC", LMA, AMACCHWSCC,
+	"MACCHWSU", LMA, AMACCHWSU,
+	"MACCHWSUCC", LMA, AMACCHWSUCC,
+	"MACCHWSUV", LMA, AMACCHWSUV,
+	"MACCHWSUVCC", LMA, AMACCHWSUVCC,
+	"MACCHWSV", LMA, AMACCHWSV,
+	"MACCHWSVCC", LMA, AMACCHWSVCC,
+	"MACCHWU", LMA, AMACCHWU,
+	"MACCHWUCC", LMA, AMACCHWUCC,
+	"MACCHWUV", LMA, AMACCHWUV,
+	"MACCHWUVCC", LMA, AMACCHWUVCC,
+	"MACCHWV", LMA, AMACCHWV,
+	"MACCHWVCC", LMA, AMACCHWVCC,
+	"MACHHW", LMA, AMACHHW,
+	"MACHHWCC", LMA, AMACHHWCC,
+	"MACHHWS", LMA, AMACHHWS,
+	"MACHHWSCC", LMA, AMACHHWSCC,
+	"MACHHWSU", LMA, AMACHHWSU,
+	"MACHHWSUCC", LMA, AMACHHWSUCC,
+	"MACHHWSUV", LMA, AMACHHWSUV,
+	"MACHHWSUVCC", LMA, AMACHHWSUVCC,
+	"MACHHWSV", LMA, AMACHHWSV,
+	"MACHHWSVCC", LMA, AMACHHWSVCC,
+	"MACHHWU", LMA, AMACHHWU,
+	"MACHHWUCC", LMA, AMACHHWUCC,
+	"MACHHWUV", LMA, AMACHHWUV,
+	"MACHHWUVCC", LMA, AMACHHWUVCC,
+	"MACHHWV", LMA, AMACHHWV,
+	"MACHHWVCC", LMA, AMACHHWVCC,
+	"MACLHW", LMA, AMACLHW,
+	"MACLHWCC", LMA, AMACLHWCC,
+	"MACLHWS", LMA, AMACLHWS,
+	"MACLHWSCC", LMA, AMACLHWSCC,
+	"MACLHWSU", LMA, AMACLHWSU,
+	"MACLHWSUCC", LMA, AMACLHWSUCC,
+	"MACLHWSUV", LMA, AMACLHWSUV,
+	"MACLHWSUVCC", LMA, AMACLHWSUVCC,
+	"MACLHWSV", LMA, AMACLHWSV,
+	"MACLHWSVCC", LMA, AMACLHWSVCC,
+	"MACLHWU", LMA, AMACLHWU,
+	"MACLHWUCC", LMA, AMACLHWUCC,
+	"MACLHWUV", LMA, AMACLHWUV,
+	"MACLHWUVCC", LMA, AMACLHWUVCC,
+	"MACLHWV", LMA, AMACLHWV,
+	"MACLHWVCC", LMA, AMACLHWVCC,
+	"MULCHW",	LLOGW, AMULCHW,
+	"MULCHWCC",	LLOGW, AMULCHWCC,
+	"MULCHWU",	LLOGW, AMULCHWU,
+	"MULCHWUCC",	LLOGW, AMULCHWUCC,
+	"MULHHW",	LLOGW, AMULHHW,
+	"MULHHWCC",	LLOGW, AMULHHWCC,
+	"MULHHWU",	LLOGW, AMULHHWU,
+	"MULHHWUCC",	LLOGW, AMULHHWUCC,
+	"MULLHW",	LLOGW, AMULLHW,
+	"MULLHWCC",	LLOGW, AMULLHWCC,
+	"MULLHWU",	LLOGW, AMULLHWU,
+	"MULLHWUCC",	LLOGW, AMULLHWUCC,
+	"NMACCHW", LMA, ANMACCHW,
+	"NMACCHWCC", LMA, ANMACCHWCC,
+	"NMACCHWS", LMA, ANMACCHWS,
+	"NMACCHWSCC", LMA, ANMACCHWSCC,
+	"NMACCHWSV", LMA, ANMACCHWSV,
+	"NMACCHWSVCC", LMA, ANMACCHWSVCC,
+	"NMACCHWV", LMA, ANMACCHWV,
+	"NMACCHWVCC", LMA, ANMACCHWVCC,
+	"NMACHHW", LMA, ANMACHHW,
+	"NMACHHWCC", LMA, ANMACHHWCC,
+	"NMACHHWS", LMA, ANMACHHWS,
+	"NMACHHWSCC", LMA, ANMACHHWSCC,
+	"NMACHHWSV", LMA, ANMACHHWSV,
+	"NMACHHWSVCC", LMA, ANMACHHWSVCC,
+	"NMACHHWV", LMA, ANMACHHWV,
+	"NMACHHWVCC", LMA, ANMACHHWVCC,
+	"NMACLHW", LMA, ANMACLHW,
+	"NMACLHWCC", LMA, ANMACLHWCC,
+	"NMACLHWS", LMA, ANMACLHWS,
+	"NMACLHWSCC", LMA, ANMACLHWSCC,
+	"NMACLHWSV", LMA, ANMACLHWSV,
+	"NMACLHWSVCC", LMA, ANMACLHWSVCC,
+	"NMACLHWV", LMA, ANMACLHWV,
+	"NMACLHWVCC", LMA, ANMACLHWVCC,
+
+	/* optional on 32-bit */
+	"FRES", LFCONV, AFRES,
+	"FRESCC", LFCONV, AFRESCC,
+	"FRSQRTE", LFCONV, AFRSQRTE,
+	"FRSQRTECC", LFCONV, AFRSQRTECC,
+	"FSEL", LFMA, AFSEL,
+	"FSELCC", LFMA, AFSELCC,
+	"FSQRT", LFCONV, AFSQRT,
+	"FSQRTCC", LFCONV, AFSQRTCC,
+	"FSQRTS", LFCONV, AFSQRTS,
+	"FSQRTSCC", LFCONV, AFSQRTSCC,
+
+	/* parallel, cross, and secondary (fp2) */
+	"FPSEL", LFMA, AFPSEL,
+	"FPMUL", LFADD, AFPMUL,
+	"FXMUL", LFADD, AFXMUL,
+	"FXPMUL", LFADD, AFXPMUL,
+	"FXSMUL", LFADD, AFXSMUL,
+	"FPADD", LFADD, AFPADD,
+	"FPSUB", LFADD, AFPSUB,
+	"FPRE", LFCONV, AFPRE,
+	"FPRSQRTE", LFCONV, AFPRSQRTE,
+	"FPMADD", LFMA, AFPMADD,
+	"FXMADD", LFMA, AFXMADD,
+	"FXCPMADD", LFMA, AFXCPMADD,
+	"FXCSMADD", LFMA, AFXCSMADD,
+	"FPNMADD", LFMA, AFPNMADD,
+	"FXNMADD", LFMA, AFXNMADD,
+	"FXCPNMADD", LFMA, AFXCPNMADD,
+	"FXCSNMADD", LFMA, AFXCSNMADD,
+	"FPMSUB", LFMA, AFPMSUB,
+	"FXMSUB", LFMA, AFXMSUB,
+	"FXCPMSUB", LFMA, AFXCPMSUB,
+	"FXCSMSUB", LFMA, AFXCSMSUB,
+	"FPNMSUB", LFMA, AFPNMSUB,
+	"FXNMSUB", LFMA, AFXNMSUB,
+	"FXCPNMSUB", LFMA, AFXCPNMSUB,
+	"FXCSNMSUB", LFMA, AFXCSNMSUB,
+	"FPABS", LFCONV, AFPABS,
+	"FPNEG", LFCONV, AFPNEG,
+	"FPRSP", LFCONV, AFPRSP,
+	"FPNABS", LFCONV, AFPNABS,
+	"FSMOVD", LFMOV, AFSMOVD,
+	"FSCMP", LFCMP, AFSCMP,
+	"FSABS", LFCONV, AFSABS,
+	"FSNEG", LFCONV, AFSNEG,
+	"FSNABS", LFCONV, AFSNABS,
+	"FPCTIW", LFCONV, AFPCTIW,
+	"FPCTIWZ", LFCONV, AFPCTIWZ,
+	"FMOVSPD", LFCONV, AFMOVSPD,
+	"FMOVPSD", LFCONV, AFMOVPSD,
+	"FXCPNPMA", LFMA, AFXCPNPMA,
+	"FXCSNPMA", LFMA, AFXCSNPMA,
+	"FXCPNSMA", LFMA, AFXCPNSMA,
+	"FXCSNSMA", LFMA, AFXCSNSMA,
+	"FXCXNPMA", LFMA, AFXCXNPMA,
+	"FXCXNSMA", LFMA, AFXCXNSMA,
+	"FXCXMA", LFMA, AFXCXMA,
+	"FXCXNMS", LFMA, AFXCXNMS,
+
+	/* parallel, cross, and secondary load and store (fp2) */
+	"FSMOVS", LFMOVX, AFSMOVS,
+	"FSMOVSU", LFMOVX, AFSMOVSU,
+	"FSMOVD", LFMOVX, AFSMOVD,
+	"FSMOVDU", LFMOVX, AFSMOVDU,
+	"FXMOVS", LFMOVX, AFXMOVS,
+	"FXMOVSU", LFMOVX, AFXMOVSU,
+	"FXMOVD", LFMOVX, AFXMOVD,
+	"FXMOVDU", LFMOVX, AFXMOVDU,
+	"FPMOVS", LFMOVX, AFPMOVS,
+	"FPMOVSU", LFMOVX, AFPMOVSU,
+	"FPMOVD", LFMOVX, AFPMOVD,
+	"FPMOVDU", LFMOVX, AFPMOVDU,
+	"FPMOVIW", LFMOVX, AFPMOVIW,
+
+	"AFMOVSPD",	LFMOV,	AFMOVSPD,
+	"AFMOVPSD",	LFMOV,	AFMOVPSD,
+
+	/* special instructions */
+	"DCBF",		LXOP,	ADCBF,
+	"DCBI",		LXOP,	ADCBI,
+	"DCBST",	LXOP,	ADCBST,
+	"DCBT",		LXOP,	ADCBT,
+	"DCBTST",	LXOP,	ADCBTST,
+	"DCBZ",		LXOP,	ADCBZ,
+	"ICBI",		LXOP,	AICBI,
+
+	"ECIWX",	LXLD,	AECIWX,
+	"ECOWX",	LXST,	AECOWX,
+	"LWAR", LXLD, ALWAR,
+	"STWCCC", LXST, ASTWCCC,
+	"EIEIO",	LRETRN,	AEIEIO,
+	"TLBIE",	LNOP,	ATLBIE,
+	"LSW",	LXLD, ALSW,
+	"STSW",	LXST, ASTSW,
+	
+	"ISYNC",	LRETRN, AISYNC,
+	"SYNC",		LRETRN, ASYNC,
+/*	"TW",		LADDW,	ATW,*/
+
+	"WORD",		LWORD, AWORD,
+	"SCHED",	LSCHED, 0,
+	"NOSCHED",	LSCHED,	0x80,
+
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	nullgen.xreg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+	ALLOCN(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		ALLOCN(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, ANAME>>8);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+	case D_FPSCR:
+	case D_MSR:
+	case D_SREG:
+	case D_OPT:
+		break;
+
+	case D_DCR:
+	case D_SPR:
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+int
+outsim(Gen *g)
+{
+	Sym *s;
+	int sno, t;
+
+	s = g->sym;
+	if(s == S)
+		return 0;
+	sno = s->sym;
+	if(sno < 0 || sno >= NSYM)
+		sno = 0;
+	t = g->name;
+	if(h[sno].type == t && h[sno].sym == s)
+		return sno;
+	zname(s->name, t, sym);
+	s->sym = sym;
+	h[sym].sym = s;
+	h[sym].type = t;
+	sno = sym;
+	sym++;
+	if(sym >= NSYM)
+		sym = 1;
+	return sno;
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st;
+
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+	if(pass == 1)
+		return;
+	if(g1->xreg != NREG) {
+		if(reg != NREG || g2->xreg != NREG)
+			yyerror("bad addressing modes");
+		reg = g1->xreg;
+	} else
+	if(g2->xreg != NREG) {
+		if(reg != NREG)
+			yyerror("bad addressing modes");
+		reg = g2->xreg;
+	}
+	do {
+		sf = outsim(g1);
+		st = outsim(g2);
+	} while(sf != 0 && st == sf);
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, reg|nosched);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+}
+
+void
+outgcode(int a, Gen *g1, int reg, Gen *g2, Gen *g3)
+{
+	int s1, s2, s3, flag; 
+
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+	if(pass == 1)
+		return;
+	do {
+		s1 = outsim(g1);
+		s2 = outsim(g2);
+		s3 = outsim(g3);
+	} while(s1 && (s2 && s1 == s2 || s3 && s1 == s3) || s2 && (s3 && s2 == s3));
+	flag = 0;
+	if(g2->type != D_NONE)
+		flag = 0x40;	/* flags extra operand */
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, reg | nosched | flag);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, s1);
+	if(flag)
+		zaddr(g2, s2);
+	zaddr(g3, s3);
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, ANAME>>8);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, AHISTORY>>8);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/qa/mkfile
@@ -1,0 +1,29 @@
+<../../mkconfig
+
+TARG=qa
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../qc/q.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/qc/cgen.c
@@ -1,0 +1,1459 @@
+#include "gc.h"
+
+static void cmpv(Node*, int, Node*);
+static void testv(Node*, int);
+static void cgen64(Node*, Node*);
+static int isvconstable(int, vlong);
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesu[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	if(typev[n->type->etype]) {
+		switch(n->op) {
+		case OCONST:
+		case OFUNC:
+			cgen64(n, nn);
+			return;
+		}
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		if(!typev[r->type->etype]) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gmove(&nod, &nod1);
+			regfree(&nod);
+		} else {
+			regsalloc(&nod1, r);
+			cgen(r, &nod1);
+		}
+
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case ONEG:
+	case OCOM:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		gopcode(o, &nod, Z, &nod);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED) {
+			if(nn != Z || r->addable < INDEXED) {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OXOR:
+		if(nn != Z)
+		if(r->op == OCONST && r->vconst == -1){
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(OCOM, &nod, Z, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+
+	case OADD:
+	case OSUB:
+	case OAND:
+	case OOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z && r->op == OCONST && !typefd[n->type->etype] &&
+		    (!typev[n->type->etype] || isvconstable(o, r->vconst))) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(o == OAND || r->vconst != 0)
+				gopcode(o, r, Z, &nod);
+			gmove(&nod, nn);
+			regfree(&nod);
+			break;
+		}
+
+	case OMUL:
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case ODIV:
+	case OMOD:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if((o == OMUL || o == OLMUL) && !typev[n->type->etype]) {
+			if(mulcon(n, nn))
+				break;
+			if(debug['M'])
+				print("%L multiply\n", n->lineno);
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			if(o != OMUL || typev[n->type->etype] || !sconst(r)) {
+				regalloc(&nod1, r, Z);
+				cgen(r, &nod1);
+				gopcode(o, &nod1, Z, &nod);
+				regfree(&nod1);
+			} else
+				gopcode(o, r, Z, &nod);
+		} else {
+			regalloc(&nod1, r, nn);
+			cgen(r, &nod1);
+			regalloc(&nod, l, Z);
+			cgen(l, &nod);
+			gopcode(o, &nod1, Z, &nod);
+			regfree(&nod1);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST && !typefd[r->type->etype] && !typefd[n->type->etype] &&
+		   (!typev[n->type->etype] || isvconstable(o, r->vconst))) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, l, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+		regalloc(&nod1, n, nn);
+		gopcode(OAS, &nod2, Z, &nod1);
+		gopcode(o, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+		if(nn != Z)
+			gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		regalloc(&nod3, r, Z);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			cgen(r, &nod3);
+		} else {
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(n->op, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			cgen(l, nn);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+			/* both null, gen l->nn */
+			cgen(l, nn);
+			break;
+		}
+		if(typev[l->type->etype] || typev[n->type->etype]) {
+			cgen64(n, nn);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gmove(&nod, &nod1);
+		gmove(&nod1, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		if(nn && l->op == ONAME)	/* in x=++i, emit USED(i) */
+			gins(ANOP, l, Z);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o, uns;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	uns = 0;
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		if(n->op == OCONST) {
+			o = vconst(n);
+			if(!true)
+				o = !o;
+			gbranch(OGOTO);
+			if(o) {
+				p1 = p;
+				gbranch(OGOTO);
+				patch(p1, pc);
+			}
+			goto com;
+		}
+		if(typev[n->type->etype]) {
+			testv(n, true);
+			goto com;
+		}
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		o = ONE;
+		if(true)
+			o = comrel[relindex(o)];
+		if(typefd[n->type->etype]) {
+			nodreg(&nod1, n, NREG+FREGZERO);
+			gopcode(o, &nod, Z, &nod1);
+		} else
+			gopcode(o, &nod, Z, nodconst(0));
+		regfree(&nod);
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		uns = 1;
+		/* fall through */
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+		if(typev[l->type->etype]){
+			cmpv(n, true, Z);
+			goto com;
+		}
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(!uns && sconst(r) || (uns || o == OEQ || o == ONE) && uconst(r)) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, &nod, Z, r);
+			regfree(&nod);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod1, Z, &nod);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1L), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0L), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(typev[n->type->etype]) {
+		diag(n, "old vlong sugen: %O", n->op);
+		return;
+	}
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no side effects
+		 */
+		if(nn != Z && side(nn)) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regalloc(&nod2, &nod1, Z);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			/* prtree(&nod0, "hand craft"); /* */
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		/* BOTCH -- functions can clobber rathole */
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		/* this transformation should probably be done earlier */
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->complex = FNX;
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gmove(&nod1, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 5) {
+		layout(&nod1, &nod2, w, 0, Z);
+		goto out;
+	}
+
+	/*
+	 * minimize space for unrolling loop
+	 * 3,4,5 times. (6 or more is never minimum)
+	 * if small structure, try 2 also.
+	 */
+	c = 0; /* set */
+	m = 100;
+	i = 3;
+	if(w <= 15)
+		i = 2;
+	for(; i<=5; i++)
+		if(i + w%i <= m) {
+			c = i;
+			m = c + w%c;
+		}
+
+	regalloc(&nod3, &regnode, Z);
+	layout(&nod1, &nod2, w%c, w/c, &nod3);
+	
+	pc1 = pc;
+	layout(&nod1, &nod2, c, 0, Z);
+
+	gopcode(OSUB, nodconst(1L), Z, &nod3);
+	nod1.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1);
+	nod2.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2);
+	
+	gopcode(OGT, &nod3, Z, nodconst(0));
+	patch(p, pc1);
+
+	regfree(&nod3);
+out:
+	regfree(&nod1);
+	regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &regnode, Z);
+	regalloc(&t2, &regnode, Z);
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
+
+/*
+ * is the vlong's value directly addressible?
+ */
+int
+isvdirect(Node *n)
+{
+	return n->op == ONAME || n->op == OCONST || n->op == OINDREG;
+}
+
+/*
+ * can the constant be used with given vlong op?
+ */
+static int
+isvconstable(int o, vlong v)
+{
+	switch(o) {
+	case OADD:
+	case OASADD:
+		/* there isn't an immediate form for ADDE/SUBE, but there are special ADDME/ADDZE etc */
+		return v == 0 || v == -1;
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * most 64-bit operations: cgen into a register pair, then operate.
+ * 64-bit comparisons are handled a little differently because the two underlying
+ * comparisons can be compiled separately, since the calculations don't interact.
+ */
+
+static void
+vcgen(Node *n, Node *o, int *f)
+{
+	*f = 0;
+	if(!isvdirect(n)) {
+		if(n->complex >= FNX) {
+			regsalloc(o, n);
+			cgen(n, o);
+			return;
+		}
+		*f = 1;
+		if(n->addable < INDEXED && n->op != OIND && n->op != OINDEX) {
+			regalloc(o, n, Z);
+			cgen(n, o);
+		} else
+			reglcgen(o, n, Z);
+	} else
+		*o = *n;
+}
+
+static int
+isuns(int op)
+{
+	switch(op){
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static void
+gcmpv(Node *l, Node *r, void (*mov)(Node*, Node*, int), int op)
+{
+	Node vl, vr;
+
+	regalloc(&vl, &regnode, Z);
+	mov(l, &vl, 0);
+	regalloc(&vr, &regnode, Z);
+	mov(r, &vr, 1+isuns(op));
+	gopcode(op, &vl, Z, &vr);
+	if(vl.op == OREGISTER)
+		regfree(&vl);
+	if(vr.op == OREGISTER)
+		regfree(&vr);
+}
+
+static void
+brcondv(Node *l, Node *r, int chi, int clo)
+{
+	Prog *p1, *p2, *p3, *p4;
+
+	gcmpv(l, r, gloadhi, chi);
+	p1 = p;
+	gins(ABNE, Z, Z);
+	p2 = p;
+	gcmpv(l, r, gloadlo, clo);
+	p3 = p;
+	gbranch(OGOTO);
+	p4 = p;
+	patch(p1, pc);
+	patch(p3, pc);
+	gbranch(OGOTO);
+	patch(p2, pc);
+	patch(p4, pc);
+}
+
+static void
+testv(Node *n, int true)
+{
+	Node nod;
+
+	nod = znode;
+	nod.op = ONE;
+	nod.left = n;
+	nod.right = new1(0, Z, Z);
+	*nod.right = *nodconst(0);
+	nod.right->type = n->type;
+	nod.type = types[TLONG];
+	cmpv(&nod, true, Z);
+}
+
+/*
+ * comparison for vlong does high and low order parts separately,
+ * which saves loading the latter if the high order comparison suffices
+ */
+static void
+cmpv(Node *n, int true, Node *nn)
+{
+	Node *l, *r, nod, nod1;
+	int o, f1, f2;
+	Prog *p1, *p2;
+	long curs;
+
+	if(debug['g']) {
+		if(nn != nil)
+			prtree(nn, "cmpv lhs");
+		prtree(n, "cmpv");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	if(l->complex >= FNX && r->complex >= FNX) {
+		regsalloc(&nod1, r);
+		cgen(r, &nod1);
+		nod = *n;
+		nod.right = &nod1;
+		cmpv(&nod, true, nn);
+		cursafe = curs;
+		return;
+	}
+	if(l->complex >= r->complex) {
+		vcgen(l, &nod1, &f1);
+		vcgen(r, &nod, &f2);
+	} else {
+		vcgen(r, &nod, &f2);
+		vcgen(l, &nod1, &f1);
+	}
+	nod.type = types[TLONG];
+	nod1.type = types[TLONG];
+	o = n->op;
+	if(true)
+		o = comrel[relindex(o)];
+	switch(o){
+	case OEQ:
+		gcmpv(&nod1, &nod, gloadhi, ONE);
+		p1 = p;
+		gcmpv(&nod1, &nod, gloadlo, ONE);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		break;
+	case ONE:
+		gcmpv(&nod1, &nod, gloadhi, ONE);
+		p1 = p;
+		gcmpv(&nod1, &nod, gloadlo, OEQ);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		break;
+	case OLE:
+		brcondv(&nod1, &nod, OLT, OLS);
+		break;
+	case OGT:
+		brcondv(&nod1, &nod, OGT, OHI);
+		break;
+	case OLS:
+		brcondv(&nod1, &nod, OLO, OLS);
+		break;
+	case OHI:
+		brcondv(&nod1, &nod, OHI, OHI);
+		break;
+	case OLT:
+		brcondv(&nod1, &nod, OLT, OLO);
+		break;
+	case OGE:
+		brcondv(&nod1, &nod, OGT, OHS);
+		break;
+	case OLO:
+		brcondv(&nod1, &nod, OLO, OLO);
+		break;
+	case OHS:
+		brcondv(&nod1, &nod, OHI, OHS);
+		break;
+	default:
+		diag(n, "bad cmpv");
+		return;
+	}
+	if(f1)
+		regfree(&nod1);
+	if(f2)
+		regfree(&nod);
+	cursafe = curs;
+}
+
+static void
+cgen64(Node *n, Node *nn)
+{
+	Node *l, *r, *d;
+	Node nod, nod1;
+	long curs;
+	Type *t;
+	int o, m;
+
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	switch(o) {
+
+	case OCONST:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+		if(nn->op != OREGPAIR) {
+//prtree(n, "cgen64 const");
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gmove(nod32const(n->vconst>>32), &nod1);
+			else
+				gmove(nod32const(n->vconst), &nod1);
+			nod1.xoffset += SZ_LONG;
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gmove(nod32const(n->vconst), &nod1);
+			else
+				gmove(nod32const(n->vconst>>32), &nod1);
+
+			regfree(&nod1);
+		} else
+			gmove(n, nn);
+		break;
+
+	case OCAST:
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(typev[l->type->etype]){
+			/* vlong to non-vlong */
+			if(!isvdirect(l)) {
+				if(l->addable < INDEXED && l->op != OIND && l->op != OINDEX) {
+					regalloc(&nod, l, l);
+					cgen(l, &nod);
+					regalloc(&nod1, n, nn);
+					gmove(nod.right, &nod1);
+				} else {
+					reglcgen(&nod, l, Z);
+					regalloc(&nod1, n, nn);
+					gloadlo(&nod, &nod1, 0);	/* TO DO: not correct for typefd */
+				}
+				regfree(&nod);
+			} else {
+				regalloc(&nod1, n, nn);
+				gloadlo(l, &nod1, 0);	/* TO DO: not correct for typefd */
+			}
+		}else{
+			/* non-vlong to vlong */
+			regalloc(&nod, l, Z);
+			cgen(l, &nod);
+			regalloc(&nod1, n, nn);
+			gmove(&nod, nod1.right);
+			if(typeu[l->type->etype])
+				gmove(nodconst(0), nod1.left);
+			else
+				gopcode(OASHR, nodconst(31), nod1.right, nod1.left);
+			regfree(&nod);
+		}
+		gmove(&nod1, nn);
+		regfree(&nod1);
+		break;
+
+	case OFUNC:
+		/* this transformation should probably be done earlier */
+		if(nn == Z) {
+			regsalloc(&nod1, n);
+			nn = &nod1;
+		}
+		m = 0;
+		if(nn->op != OIND) {
+			if(nn->op == OREGPAIR) {
+				m = 1;
+				regsalloc(&nod1, nn);
+				d = &nod1;
+			}else
+				d = nn;
+			d = new1(OADDR, d, Z);
+			d->type = types[TIND];
+			d->addable = 0;
+		} else
+			d = nn->left;
+		n = new(OFUNC, l, new(OLIST, d, r));
+		n->complex = FNX;
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		if(m)
+			gmove(&nod1, nn);
+		break;
+
+	default:
+		diag(n, "bad cgen64 %O", o);
+		break;
+	}
+	cursafe = curs;
+}
--- /dev/null
+++ b/utils/qc/enam.c
@@ -1,0 +1,383 @@
+char	*anames[] =
+{
+	"XXX",
+	"ADD",
+	"ADDCC",
+	"ADDV",
+	"ADDVCC",
+	"ADDC",
+	"ADDCCC",
+	"ADDCV",
+	"ADDCVCC",
+	"ADDME",
+	"ADDMECC",
+	"ADDMEVCC",
+	"ADDMEV",
+	"ADDE",
+	"ADDECC",
+	"ADDEVCC",
+	"ADDEV",
+	"ADDZE",
+	"ADDZECC",
+	"ADDZEVCC",
+	"ADDZEV",
+	"AND",
+	"ANDCC",
+	"ANDN",
+	"ANDNCC",
+	"BC",
+	"BCL",
+	"BEQ",
+	"BGE",
+	"BGT",
+	"BL",
+	"BLE",
+	"BLT",
+	"BNE",
+	"BR",
+	"BVC",
+	"BVS",
+	"CMP",
+	"CMPU",
+	"CNTLZW",
+	"CNTLZWCC",
+	"CRAND",
+	"CRANDN",
+	"CREQV",
+	"CRNAND",
+	"CRNOR",
+	"CROR",
+	"CRORN",
+	"CRXOR",
+	"DIVW",
+	"DIVWCC",
+	"DIVWVCC",
+	"DIVWV",
+	"DIVWU",
+	"DIVWUCC",
+	"DIVWUVCC",
+	"DIVWUV",
+	"EQV",
+	"EQVCC",
+	"EXTSB",
+	"EXTSBCC",
+	"EXTSH",
+	"EXTSHCC",
+	"FABS",
+	"FABSCC",
+	"FADD",
+	"FADDCC",
+	"FADDS",
+	"FADDSCC",
+	"FCMPO",
+	"FCMPU",
+	"FCTIW",
+	"FCTIWCC",
+	"FCTIWZ",
+	"FCTIWZCC",
+	"FDIV",
+	"FDIVCC",
+	"FDIVS",
+	"FDIVSCC",
+	"FMADD",
+	"FMADDCC",
+	"FMADDS",
+	"FMADDSCC",
+	"FMOVD",
+	"FMOVDCC",
+	"FMOVDU",
+	"FMOVS",
+	"FMOVSU",
+	"FMSUB",
+	"FMSUBCC",
+	"FMSUBS",
+	"FMSUBSCC",
+	"FMUL",
+	"FMULCC",
+	"FMULS",
+	"FMULSCC",
+	"FNABS",
+	"FNABSCC",
+	"FNEG",
+	"FNEGCC",
+	"FNMADD",
+	"FNMADDCC",
+	"FNMADDS",
+	"FNMADDSCC",
+	"FNMSUB",
+	"FNMSUBCC",
+	"FNMSUBS",
+	"FNMSUBSCC",
+	"FRSP",
+	"FRSPCC",
+	"FSUB",
+	"FSUBCC",
+	"FSUBS",
+	"FSUBSCC",
+	"MOVMW",
+	"LSW",
+	"LWAR",
+	"MOVWBR",
+	"MOVB",
+	"MOVBU",
+	"MOVBZ",
+	"MOVBZU",
+	"MOVH",
+	"MOVHBR",
+	"MOVHU",
+	"MOVHZ",
+	"MOVHZU",
+	"MOVW",
+	"MOVWU",
+	"MOVFL",
+	"MOVCRFS",
+	"MTFSB0",
+	"MTFSB0CC",
+	"MTFSB1",
+	"MTFSB1CC",
+	"MULHW",
+	"MULHWCC",
+	"MULHWU",
+	"MULHWUCC",
+	"MULLW",
+	"MULLWCC",
+	"MULLWVCC",
+	"MULLWV",
+	"NAND",
+	"NANDCC",
+	"NEG",
+	"NEGCC",
+	"NEGVCC",
+	"NEGV",
+	"NOR",
+	"NORCC",
+	"OR",
+	"ORCC",
+	"ORN",
+	"ORNCC",
+	"REM",
+	"REMCC",
+	"REMV",
+	"REMVCC",
+	"REMU",
+	"REMUCC",
+	"REMUV",
+	"REMUVCC",
+	"RFI",
+	"RLWMI",
+	"RLWMICC",
+	"RLWNM",
+	"RLWNMCC",
+	"SLW",
+	"SLWCC",
+	"SRW",
+	"SRAW",
+	"SRAWCC",
+	"SRWCC",
+	"STSW",
+	"STWCCC",
+	"SUB",
+	"SUBCC",
+	"SUBVCC",
+	"SUBC",
+	"SUBCCC",
+	"SUBCV",
+	"SUBCVCC",
+	"SUBME",
+	"SUBMECC",
+	"SUBMEVCC",
+	"SUBMEV",
+	"SUBV",
+	"SUBE",
+	"SUBECC",
+	"SUBEV",
+	"SUBEVCC",
+	"SUBZE",
+	"SUBZECC",
+	"SUBZEVCC",
+	"SUBZEV",
+	"SYNC",
+	"XOR",
+	"XORCC",
+	"DCBF",
+	"DCBI",
+	"DCBST",
+	"DCBT",
+	"DCBTST",
+	"DCBZ",
+	"ECIWX",
+	"ECOWX",
+	"EIEIO",
+	"ICBI",
+	"ISYNC",
+	"TLBIE",
+	"TW",
+	"SYSCALL",
+	"DATA",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"NAME",
+	"NOP",
+	"RETURN",
+	"TEXT",
+	"WORD",
+	"END",
+	"DYNT",
+	"INIT",
+	"SIGNAME",
+	"MACCHW",
+	"MACCHWCC",
+	"MACCHWS",
+	"MACCHWSCC",
+	"MACCHWSU",
+	"MACCHWSUCC",
+	"MACCHWSUV",
+	"MACCHWSUVCC",
+	"MACCHWSV",
+	"MACCHWSVCC",
+	"MACCHWU",
+	"MACCHWUCC",
+	"MACCHWUV",
+	"MACCHWUVCC",
+	"MACCHWV",
+	"MACCHWVCC",
+	"MACHHW",
+	"MACHHWCC",
+	"MACHHWV",
+	"MACHHWVCC",
+	"MACHHWS",
+	"MACHHWSCC",
+	"MACHHWSV",
+	"MACHHWSVCC",
+	"MACHHWSU",
+	"MACHHWSUCC",
+	"MACHHWSUV",
+	"MACHHWSUVCC",
+	"MACHHWU",
+	"MACHHWUCC",
+	"MACHHWUV",
+	"MACHHWUVCC",
+	"MACLHW",
+	"MACLHWCC",
+	"MACLHWS",
+	"MACLHWSCC",
+	"MACLHWSU",
+	"MACLHWSUCC",
+	"MACLHWSUV",
+	"MACLHWSUVCC",
+	"MACLHWSV",
+	"MACLHWSVCC",
+	"MACLHWU",
+	"MACLHWUCC",
+	"MACLHWUV",
+	"MACLHWUVCC",
+	"MACLHWV",
+	"MACLHWVCC",
+	"MULCHW",
+	"MULCHWCC",
+	"MULCHWU",
+	"MULCHWUCC",
+	"MULHHW",
+	"MULHHWCC",
+	"MULHHWU",
+	"MULHHWUCC",
+	"MULLHW",
+	"MULLHWCC",
+	"MULLHWU",
+	"MULLHWUCC",
+	"NMACCHW",
+	"NMACCHWCC",
+	"NMACCHWS",
+	"NMACCHWSCC",
+	"NMACCHWSV",
+	"NMACCHWSVCC",
+	"NMACCHWV",
+	"NMACCHWVCC",
+	"NMACHHW",
+	"NMACHHWCC",
+	"NMACHHWS",
+	"NMACHHWSCC",
+	"NMACHHWSV",
+	"NMACHHWSVCC",
+	"NMACHHWV",
+	"NMACHHWVCC",
+	"NMACLHW",
+	"NMACLHWCC",
+	"NMACLHWS",
+	"NMACLHWSCC",
+	"NMACLHWSV",
+	"NMACLHWSVCC",
+	"NMACLHWV",
+	"NMACLHWVCC",
+	"RFCI",
+	"FRES",
+	"FRESCC",
+	"FRSQRTE",
+	"FRSQRTECC",
+	"FSEL",
+	"FSELCC",
+	"FSQRT",
+	"FSQRTCC",
+	"FSQRTS",
+	"FSQRTSCC",
+	"FPSEL",
+	"FPMUL",
+	"FXMUL",
+	"FXPMUL",
+	"FXSMUL",
+	"FPADD",
+	"FPSUB",
+	"FPRE",
+	"FPRSQRTE",
+	"FPMADD",
+	"FXMADD",
+	"FXCPMADD",
+	"FXCSMADD",
+	"FPNMADD",
+	"FXNMADD",
+	"FXCPNMADD",
+	"FXCSNMADD",
+	"FPMSUB",
+	"FXMSUB",
+	"FXCPMSUB",
+	"FXCSMSUB",
+	"FPNMSUB",
+	"FXNMSUB",
+	"FXCPNMSUB",
+	"FXCSNMSUB",
+	"FPABS",
+	"FPNEG",
+	"FPRSP",
+	"FPNABS",
+	"FSCMP",
+	"FSABS",
+	"FSNEG",
+	"FSNABS",
+	"FPCTIW",
+	"FPCTIWZ",
+	"FMOVSPD",
+	"FMOVPSD",
+	"FXCPNPMA",
+	"FXCSNPMA",
+	"FXCPNSMA",
+	"FXCSNSMA",
+	"FXCXNPMA",
+	"FXCXNSMA",
+	"FXCXMA",
+	"FXCXNMS",
+	"FSMOVS",
+	"FSMOVSU",
+	"FSMOVD",
+	"FSMOVDU",
+	"FXMOVS",
+	"FXMOVSU",
+	"FXMOVD",
+	"FXMOVDU",
+	"FPMOVS",
+	"FPMOVSU",
+	"FPMOVD",
+	"FPMOVDU",
+	"FPMOVIW",
+	"LAST",
+};
--- /dev/null
+++ b/utils/qc/gc.h
@@ -1,0 +1,356 @@
+#include	"../cc/cc.h"
+#include	"../qc/q.out.h"
+
+/*
+ * qc/power
+ * powerpc
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+struct	Adr
+{
+/*	union	*/
+/*	{	*/
+		long	offset;
+		double	dval;
+		char	sval[NSNAME];
+/*	};	*/
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	from3;		/* third argument for fmadd, fmsub, ... */
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	short	as;
+	char	reg;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+/*	union	*/
+/*	{	*/
+		Reg*	log5;
+		long	active;
+/*	};	*/
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	int	hintabsize;
+EXTERN	long	maxargsafe;
+EXTERN	Multab	multab[20];
+EXTERN	int	mnstring;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	Node*	nodretv;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	int	reg[NREG+NREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	5
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+EXTERN	int	suppress;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+#define	R0ISZERO	(debug['0']==0)
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	usedset(Node*, int);
+void	noretval(int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gloadhi(Node*, Node*, int);
+void	gloadlo(Node*, Node*, int);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gins3(int a, Node*, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+int	uconst(Node*);
+long	hi64v(Node*);
+long	lo64v(Node*);
+Node*	hi64(Node*);
+Node*	lo64(Node*);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	swit2(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(Node*, long);
+int	mulcon1(Node*, long, Node*);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+/*
+ * com64.c
+ */
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/qc/list.c
@@ -1,0 +1,243 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('B', Bconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else {
+		s = seprint(str, str+sizeof(str), "	%A	%D", a, &p->from);
+		if(p->reg != NREG)
+			s = seprint(s, str+sizeof(str), ",%c%d", p->from.type==D_FREG? 'F': 'R', p->reg);
+		if(p->from3.type != D_NONE)
+			s = seprint(s, str+sizeof(str), ",%D", &p->from3);
+		seprint(s, s+sizeof(str), ",%D", &p->to);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a <= ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(F%d)(REG)", a, a->reg);
+		break;
+
+	case D_CREG:
+		sprint(str, "C%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(C%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+	int i, l, b, n;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		if(a->offset > 64 || -a->offset > 64) {
+			n = 0;
+			l = a->offset & 1;
+			for(i=0; i<32; i++){
+				b = (a->offset >> i) & 1;
+				if(b != l)
+					n++;
+				l = b;
+			}
+			if(n < 2) {
+				sprint(str, "%#lux", a->offset);
+				goto out;
+			}
+		}
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/qc/machcap.c
@@ -1,0 +1,98 @@
+#include	"gc.h"
+
+int
+machcap(Node *n)
+{
+	if(n == Z)
+		return 1;	/* test */
+	switch(n->op){
+
+	case OADD:
+	case OAND:
+	case OOR:
+	case OSUB:
+	case OXOR:
+		if(typev[n->left->type->etype])
+			return 1;
+		break;
+
+	case OMUL:
+	case OLMUL:
+	case OASMUL:
+	case OASLMUL:
+		return 1;
+
+	case OLSHR:
+	case OASHR:
+	case OASHL:
+	case OASASHL:
+	case OASASHR:
+	case OASLSHR:
+		return 1;
+
+	case OCAST:
+		if(typev[n->type->etype]) {
+			if(!typefd[n->left->type->etype])
+				return 1;
+		} else if(!typefd[n->type->etype]) {
+			if(typev[n->left->type->etype])
+				return 1;
+		}
+		break;
+
+	case OCOMMA:
+	case OCOND:
+	case OLIST:
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OCOM:
+	case ONEG:
+		if(typechl[n->left->type->etype])
+			return 1;
+		if(typev[n->left->type->etype])
+			return 1;
+		return 0;
+
+	case OASADD:
+	case OASSUB:
+	case OASAND:
+	case OASOR:
+	case OASXOR:
+		return 1;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+	case OPREINC:
+	case OPREDEC:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OGT:
+	case OLT:
+	case OGE:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+
+	case ODIV:
+	case OLDIV:
+	case OLMOD:
+	case OMOD:
+		return 0;
+
+	case OASDIV:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMOD:
+		return 0;
+
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/qc/mkenam
@@ -1,0 +1,18 @@
+ed - ../qc/q.out.h <<'!'
+v/^	A/d
+g/^	AEND/s//&,/
+g/^	ALAST/s//&,/
+g/[ 	]*=.*,/s//,/
+v/,/p
+,s/^	A/	"/
+,s/,.*$/",/
+1i
+char	*anames[] =
+{
+.
+,a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/qc/mkfile
@@ -1,0 +1,38 @@
+<../../mkconfig
+
+TARG=qc
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	machcap.$O\
+	mul.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+
+HFILES=\
+	gc.h\
+	q.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	q.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/qc/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(Node *n, long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("%L: multiply table failure %ld\n", n->lineno, v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=6; g++) {
+		if(g >= 6 && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("%L: multiply table failure (g=%d h=%s) %ld\n",
+				n->lineno, g, hint, v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(n, v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/qc/peep.c
@@ -1,0 +1,969 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == AMOVW || p->as == AFMOVS || p->as == AFMOVD)
+		if(regtyp(&p->to)) {
+			if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			if(regzer(&p->from))
+			if(p->to.type == D_REG) {
+				p->from.type = D_REG;
+				p->from.reg = REGZERO;
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AMOVH:
+		case AMOVHZ:
+		case AMOVB:
+		case AMOVBZ:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+
+	if(debug['Q'] > 1)
+		return;	/* allow following code improvement to be suppressed */
+
+	/*
+	 * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R
+	 * when OP can set condition codes correctly
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		case ACMP:
+			if(!regzer(&p->to))
+				continue;
+			r1 = r->s1;
+			if(r1 == R)
+				continue;
+			switch(r1->prog->as) {
+			default:
+				continue;
+			case ABCL:
+			case ABC:
+				/* the conditions can be complex and these are currently little used */
+				continue;
+			case ABEQ:
+			case ABGE:
+			case ABGT:
+			case ABLE:
+			case ABLT:
+			case ABNE:
+			case ABVC:
+			case ABVS:
+				break;
+			}
+			r1 = r;
+			do
+				r1 = uniqp(r1);
+			while (r1 != R && r1->prog->as == ANOP);
+			if(r1 == R)
+				continue;
+			p1 = r1->prog;
+			if(p1->to.type != D_REG || p1->to.reg != p->from.reg)
+				continue;
+			switch(p1->as) {
+			case ASUB:
+			case AADD:
+			case AXOR:
+			case AOR:
+				/* irregular instructions */
+				if(p1->from.type == D_CONST)
+					continue;
+				break;
+			}
+			switch(p1->as) {
+			default:
+				continue;
+			case AMOVW:
+				if(p1->from.type != D_REG)
+					continue;
+				continue;
+			case AANDCC:
+			case AANDNCC:
+			case AORCC:
+			case AORNCC:
+			case AXORCC:
+			case ASUBCC:
+			case ASUBECC:
+			case ASUBMECC:
+			case ASUBZECC:
+			case AADDCC:
+			case AADDCCC:
+			case AADDECC:
+			case AADDMECC:
+			case AADDZECC:
+			case ARLWMICC:
+			case ARLWNMCC:
+				t = p1->as;
+				break;
+			/* don't deal with floating point instructions for now */
+/*
+			case AFABS:	t = AFABSCC; break;
+			case AFADD:	t = AFADDCC; break;
+			case AFADDS:	t = AFADDSCC; break;
+			case AFCTIW:	t = AFCTIWCC; break;
+			case AFCTIWZ:	t = AFCTIWZCC; break;
+			case AFDIV:	t = AFDIVCC; break;
+			case AFDIVS:	t = AFDIVSCC; break;
+			case AFMADD:	t = AFMADDCC; break;
+			case AFMADDS:	t = AFMADDSCC; break;
+			case AFMOVD:	t = AFMOVDCC; break;
+			case AFMSUB:	t = AFMSUBCC; break;
+			case AFMSUBS:	t = AFMSUBSCC; break;
+			case AFMUL:	t = AFMULCC; break;
+			case AFMULS:	t = AFMULSCC; break;
+			case AFNABS:	t = AFNABSCC; break;
+			case AFNEG:	t = AFNEGCC; break;
+			case AFNMADD:	t = AFNMADDCC; break;
+			case AFNMADDS:	t = AFNMADDSCC; break;
+			case AFNMSUB:	t = AFNMSUBCC; break;
+			case AFNMSUBS:	t = AFNMSUBSCC; break;
+			case AFRSP:	t = AFRSPCC; break;
+			case AFSUB:	t = AFSUBCC; break;
+			case AFSUBS:	t = AFSUBSCC; break;
+			case ACNTLZW:	t = ACNTLZWCC; break;
+			case AMTFSB0:	t = AMTFSB0CC; break;
+			case AMTFSB1:	t = AMTFSB1CC; break;
+*/
+			case AADD:	t = AADDCC; break;
+			case AADDV:	t = AADDVCC; break;
+			case AADDC:	t = AADDCCC; break;
+			case AADDCV:	t = AADDCVCC; break;
+			case AADDME:	t = AADDMECC; break;
+			case AADDMEV:	t = AADDMEVCC; break;
+			case AADDE:	t = AADDECC; break;
+			case AADDEV:	t = AADDEVCC; break;
+			case AADDZE:	t = AADDZECC; break;
+			case AADDZEV:	t = AADDZEVCC; break;
+			case AAND:	t = AANDCC; break;
+			case AANDN:	t = AANDNCC; break;
+			case ADIVW:	t = ADIVWCC; break;
+			case ADIVWV:	t = ADIVWVCC; break;
+			case ADIVWU:	t = ADIVWUCC; break;
+			case ADIVWUV:	t = ADIVWUVCC; break;
+			case AEQV:	t = AEQVCC; break;
+			case AEXTSB:	t = AEXTSBCC; break;
+			case AEXTSH:	t = AEXTSHCC; break;
+			case AMULHW:	t = AMULHWCC; break;
+			case AMULHWU:	t = AMULHWUCC; break;
+			case AMULLW:	t = AMULLWCC; break;
+			case AMULLWV:	t = AMULLWVCC; break;
+			case ANAND:	t = ANANDCC; break;
+			case ANEG:	t = ANEGCC; break;
+			case ANEGV:	t = ANEGVCC; break;
+			case ANOR:	t = ANORCC; break;
+			case AOR:	t = AORCC; break;
+			case AORN:	t = AORNCC; break;
+			case AREM:	t = AREMCC; break;
+			case AREMV:	t = AREMVCC; break;
+			case AREMU:	t = AREMUCC; break;
+			case AREMUV:	t = AREMUVCC; break;
+			case ARLWMI:	t = ARLWMICC; break;
+			case ARLWNM:	t = ARLWNMCC; break;
+			case ASLW:	t = ASLWCC; break;
+			case ASRAW:	t = ASRAWCC; break;
+			case ASRW:	t = ASRWCC; break;
+			case ASUB:	t = ASUBCC; break;
+			case ASUBV:	t = ASUBVCC; break;
+			case ASUBC:	t = ASUBCCC; break;
+			case ASUBCV:	t = ASUBCVCC; break;
+			case ASUBME:	t = ASUBMECC; break;
+			case ASUBMEV:	t = ASUBMEVCC; break;
+			case ASUBE:	t = ASUBECC; break;
+			case ASUBEV:	t = ASUBEVCC; break;
+			case ASUBZE:	t = ASUBZECC; break;
+			case ASUBZEV:	t = ASUBZEVCC; break;
+			case AXOR:	t = AXORCC; break;
+				break;
+			}
+			if(debug['Q'])
+				print("cmp %P; %P -> ", p1, p);
+			p1->as = t;
+			if(debug['Q'])
+				print("%P\n", p1);
+			excise(r);
+			continue;
+		}
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->from3 = zprog.from3;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+/*
+ * if the system forces R0 to be zero,
+ * convert references to $0 to references to R0.
+ */
+int
+regzer(Adr *a)
+{
+	if(R0ISZERO) {
+		if(a->type == D_CONST)
+			if(a->sym == S)
+				if(a->offset == 0)
+					return 1;
+		if(a->type == D_REG)
+			if(a->reg == REGZERO)
+				return 1;
+	}
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG) {
+		if(!R0ISZERO || a->reg != REGZERO)
+			return 1;
+		return 0;
+	}
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ABL:
+			return 0;
+
+		case AADD:
+		case AADDC:
+		case AADDCC:
+		case AADDE:
+		case AADDECC:
+		case ASUB:
+		case ASUBCC:
+		case ASUBC:
+		case ASUBCCC:
+		case ASUBE:
+		case ASUBECC:
+		case ASLW:
+		case ASLWCC:
+		case ASRW:
+		case ASRWCC:
+		case ASRAW:
+		case ASRAWCC:
+		case AOR:
+		case AORCC:
+		case AORN:
+		case AORNCC:
+		case AAND:
+		case AANDCC:
+		case AANDN:
+		case AANDNCC:
+		case ANAND:
+		case ANANDCC:
+		case ANOR:
+		case ANORCC:
+		case AXOR:
+		case AXORCC:
+		case AMULHW:
+		case AMULHWU:
+		case AMULLW:
+		case ADIVW:
+		case ADIVWU:
+		case AREM:
+		case AREMU:
+		case ARLWNM:
+		case ARLWNMCC:
+
+		case AFADD:
+		case AFADDS:
+		case AFSUB:
+		case AFSUBS:
+		case AFMUL:
+		case AFMULS:
+		case AFDIV:
+		case AFDIVS:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AADDME:
+		case AADDMECC:
+		case AADDZE:
+		case AADDZECC:
+		case ASUBME:
+		case ASUBMECC:
+		case ASUBZE:
+		case ASUBZECC:
+		case ANEG:
+		case ANEGCC:
+		case AFNEG:
+		case AFNEGCC:
+		case AFMOVS:
+		case AFMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVH:
+	case AMOVHZ:
+	case AMOVB:
+	case AMOVBZ:
+
+	case ANEG:
+	case ANEGCC:
+	case AADDME:
+	case AADDMECC:
+	case AADDZE:
+	case AADDZECC:
+	case ASUBME:
+	case ASUBMECC:
+	case ASUBZE:
+	case ASUBZECC:
+
+	case AFCTIW:
+	case AFCTIWZ:
+	case AFMOVS:
+	case AFMOVD:
+	case AFRSP:
+	case AFNEG:
+	case AFNEGCC:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARLWMI:	/* read read rar */
+	case ARLWMICC:
+		if(copyas(&p->to, v))
+			return 2;
+		/* fall through */
+
+	case AADD:	/* read read write */
+	case AADDC:
+	case AADDE:
+	case ASUB:
+	case ASLW:
+	case ASRW:
+	case ASRAW:
+	case AOR:
+	case AORCC:
+	case AORN:
+	case AORNCC:
+	case AAND:
+	case AANDCC:
+	case AANDN:
+	case AANDNCC:
+	case ANAND:
+	case ANANDCC:
+	case ANOR:
+	case ANORCC:
+	case AXOR:
+	case AMULHW:
+	case AMULHWU:
+	case AMULLW:
+	case ADIVW:
+	case ADIVWU:
+	case AREM:
+	case AREMU:
+	case ARLWNM:
+	case ARLWNMCC:
+
+	case AFADDS:
+	case AFADD:
+	case AFSUBS:
+	case AFSUB:
+	case AFMULS:
+	case AFMUL:
+	case AFDIVS:
+	case AFDIV:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:
+	case ABGT:
+	case ABGE:
+	case ABLT:
+	case ABLE:
+	case ABNE:
+	case ABVC:
+	case ABVS:
+		break;
+
+	case ACMP:	/* read read */
+	case ACMPU:
+	case AFCMPO:
+	case AFCMPU:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub(&p->to, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		break;
+
+	case ABR:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARETURN:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGRET)
+				return 2;
+		if(v->type == D_FREG)
+			if(v->reg == FREGRET)
+				return 2;
+
+	case ABL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG) {
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+		}
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+	return 0;
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+	case AADD:
+	case AADDC:
+	case AADDCC:
+	case AADDCCC:
+	case AADDE:
+	case AADDECC:
+	case AADDME:
+	case AADDMECC:
+	case AADDZE:
+	case AADDZECC:
+	case ASUB:
+	case ASUBC:
+	case ASUBCC:
+	case ASUBCCC:
+	case ASUBE:
+	case ASUBECC:
+	case ASUBME:
+	case ASUBMECC:
+	case ASUBZE:
+	case ASUBZECC:
+	case ASLW:
+	case ASLWCC:
+	case ASRW:
+	case ASRWCC:
+	case ASRAW:
+	case ASRAWCC:
+	case AOR:
+	case AORCC:
+	case AORN:
+	case AORNCC:
+	case AAND:
+	case AANDCC:
+	case AANDN:
+	case AANDNCC:
+	case AXOR:
+	case AXORCC:
+	case ANEG:
+	case ANEGCC:
+	case AMULHW:
+	case AMULHWU:
+	case AMULLW:
+	case AMULLWCC:
+	case ADIVW:
+	case ADIVWCC:
+	case ADIVWU:
+	case ADIVWUCC:
+	case AREM:
+	case AREMCC:
+	case AREMU:
+	case AREMUCC:
+	case ANAND:
+	case ANANDCC:
+	case ANOR:
+	case ANORCC:
+	case ARLWMI:
+	case ARLWMICC:
+	case ARLWNM:
+	case ARLWNMCC:
+		return D_REG;
+
+	case AFADDS:
+	case AFADDSCC:
+	case AFADD:
+	case AFADDCC:
+	case AFSUBS:
+	case AFSUBSCC:
+	case AFSUB:
+	case AFSUBCC:
+	case AFMULS:
+	case AFMULSCC:
+	case AFMUL:
+	case AFMULCC:
+	case AFDIVS:
+	case AFDIVSCC:
+	case AFDIV:
+	case AFDIVCC:
+	case AFNEG:
+	case AFNEGCC:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v))
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG)
+		if(a->type == D_OREG)
+			if(v->reg == a->reg)
+				return 1;
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v))
+		if(p->from.type == v->type || p->to.type == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v))
+		a->reg = s->reg;
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
--- /dev/null
+++ b/utils/qc/q.out.h
@@ -1,0 +1,488 @@
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+enum
+{
+	REGZERO		= 0,	/* set to zero */
+	REGSP		= 1,
+	REGSB		= 2,
+	REGRET		= 3,
+	REGARG		= 3,
+	REGMIN		= 7,	/* register variables allocated from here to REGMAX */
+	REGMAX		= 27,
+	REGEXT		= 30,	/* external registers allocated from here down */
+	REGTMP		= 31,	/* used by the linker */
+
+	FREGRET		= 0,
+	FREGMIN		= 17,	/* first register variable */
+	FREGEXT		= 26,	/* first external register */
+	FREGCVI		= 27, /* floating conversion constant */
+	FREGZERO	= 28,	/* both float and double */
+	FREGHALF	= 29,	/* double */
+	FREGONE		= 30,	/* double */
+	FREGTWO		= 31	/* double */
+/*
+ * GENERAL:
+ *
+ * compiler allocates R3 up as temps
+ * compiler allocates register variables R7-R27
+ * compiler allocates external registers R30 down
+ *
+ * compiler allocates register variables F17-F26
+ * compiler allocates external registers F26 down
+ */
+};
+
+enum	as
+{
+	AXXX	= 0,
+	AADD,
+	AADDCC,
+	AADDV,
+	AADDVCC,
+	AADDC,
+	AADDCCC,
+	AADDCV,
+	AADDCVCC,
+	AADDME,
+	AADDMECC,
+	AADDMEVCC,
+	AADDMEV,
+	AADDE,
+	AADDECC,
+	AADDEVCC,
+	AADDEV,
+	AADDZE,
+	AADDZECC,
+	AADDZEVCC,
+	AADDZEV,
+	AAND,
+	AANDCC,
+	AANDN,
+	AANDNCC,
+	ABC,
+	ABCL,
+	ABEQ,
+	ABGE,
+	ABGT,
+	ABL,
+	ABLE,
+	ABLT,
+	ABNE,
+	ABR,
+	ABVC,
+	ABVS,
+	ACMP,
+	ACMPU,
+	ACNTLZW,
+	ACNTLZWCC,
+	ACRAND,
+	ACRANDN,
+	ACREQV,
+	ACRNAND,
+	ACRNOR,
+	ACROR,
+	ACRORN,
+	ACRXOR,
+	ADIVW,
+	ADIVWCC,
+	ADIVWVCC,
+	ADIVWV,
+	ADIVWU,
+	ADIVWUCC,
+	ADIVWUVCC,
+	ADIVWUV,
+	AEQV,
+	AEQVCC,
+	AEXTSB,
+	AEXTSBCC,
+	AEXTSH,
+	AEXTSHCC,
+	AFABS,
+	AFABSCC,
+	AFADD,
+	AFADDCC,
+	AFADDS,
+	AFADDSCC,
+	AFCMPO,
+	AFCMPU,
+	AFCTIW,
+	AFCTIWCC,
+	AFCTIWZ,
+	AFCTIWZCC,
+	AFDIV,
+	AFDIVCC,
+	AFDIVS,
+	AFDIVSCC,
+	AFMADD,
+	AFMADDCC,
+	AFMADDS,
+	AFMADDSCC,
+	AFMOVD,
+	AFMOVDCC,
+	AFMOVDU,
+	AFMOVS,
+	AFMOVSU,
+	AFMSUB,
+	AFMSUBCC,
+	AFMSUBS,
+	AFMSUBSCC,
+	AFMUL,
+	AFMULCC,
+	AFMULS,
+	AFMULSCC,
+	AFNABS,
+	AFNABSCC,
+	AFNEG,
+	AFNEGCC,
+	AFNMADD,
+	AFNMADDCC,
+	AFNMADDS,
+	AFNMADDSCC,
+	AFNMSUB,
+	AFNMSUBCC,
+	AFNMSUBS,
+	AFNMSUBSCC,
+	AFRSP,
+	AFRSPCC,
+	AFSUB,
+	AFSUBCC,
+	AFSUBS,
+	AFSUBSCC,
+	AMOVMW,
+	ALSW,
+	ALWAR,
+	AMOVWBR,
+	AMOVB,
+	AMOVBU,
+	AMOVBZ,
+	AMOVBZU,
+	AMOVH,
+	AMOVHBR,
+	AMOVHU,
+	AMOVHZ,
+	AMOVHZU,
+	AMOVW,
+	AMOVWU,
+	AMOVFL,
+	AMOVCRFS,
+	AMTFSB0,
+	AMTFSB0CC,
+	AMTFSB1,
+	AMTFSB1CC,
+	AMULHW,
+	AMULHWCC,
+	AMULHWU,
+	AMULHWUCC,
+	AMULLW,
+	AMULLWCC,
+	AMULLWVCC,
+	AMULLWV,
+	ANAND,
+	ANANDCC,
+	ANEG,
+	ANEGCC,
+	ANEGVCC,
+	ANEGV,
+	ANOR,
+	ANORCC,
+	AOR,
+	AORCC,
+	AORN,
+	AORNCC,
+	AREM,
+	AREMCC,
+	AREMV,
+	AREMVCC,
+	AREMU,
+	AREMUCC,
+	AREMUV,
+	AREMUVCC,
+	ARFI,
+	ARLWMI,
+	ARLWMICC,
+	ARLWNM,
+	ARLWNMCC,
+	ASLW,
+	ASLWCC,
+	ASRW,
+	ASRAW,
+	ASRAWCC,
+	ASRWCC,
+	ASTSW,
+	ASTWCCC,
+	ASUB,
+	ASUBCC,
+	ASUBVCC,
+	ASUBC,
+	ASUBCCC,
+	ASUBCV,
+	ASUBCVCC,
+	ASUBME,
+	ASUBMECC,
+	ASUBMEVCC,
+	ASUBMEV,
+	ASUBV,
+	ASUBE,
+	ASUBECC,
+	ASUBEV,
+	ASUBEVCC,
+	ASUBZE,
+	ASUBZECC,
+	ASUBZEVCC,
+	ASUBZEV,
+	ASYNC,
+	AXOR,
+	AXORCC,
+
+	ADCBF,
+	ADCBI,
+	ADCBST,
+	ADCBT,
+	ADCBTST,
+	ADCBZ,
+	AECIWX,
+	AECOWX,
+	AEIEIO,
+	AICBI,
+	AISYNC,
+	ATLBIE,
+	ATW,
+
+	ASYSCALL,
+	ADATA,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	ANAME,
+	ANOP,
+	ARETURN,
+	ATEXT,
+	AWORD,
+	AEND,
+	ADYNT,
+	AINIT,
+	ASIGNAME,
+
+	/* IBM powerpc embedded; not portable */
+	AMACCHW,
+	AMACCHWCC,
+	AMACCHWS,
+	AMACCHWSCC,
+	AMACCHWSU,
+	AMACCHWSUCC,
+	AMACCHWSUV,
+	AMACCHWSUVCC,
+	AMACCHWSV,
+	AMACCHWSVCC,
+	AMACCHWU,
+	AMACCHWUCC,
+	AMACCHWUV,
+	AMACCHWUVCC,
+	AMACCHWV,
+	AMACCHWVCC,
+	AMACHHW,
+	AMACHHWCC,
+	AMACHHWV,
+	AMACHHWVCC,
+	AMACHHWS,
+	AMACHHWSCC,
+	AMACHHWSV,
+	AMACHHWSVCC,
+	AMACHHWSU,
+	AMACHHWSUCC,
+	AMACHHWSUV,
+	AMACHHWSUVCC,
+	AMACHHWU,
+	AMACHHWUCC,
+	AMACHHWUV,
+	AMACHHWUVCC,
+	AMACLHW,
+	AMACLHWCC,
+	AMACLHWS,
+	AMACLHWSCC,
+	AMACLHWSU,
+	AMACLHWSUCC,
+	AMACLHWSUV,
+	AMACLHWSUVCC,
+	AMACLHWSV,
+	AMACLHWSVCC,
+	AMACLHWU,
+	AMACLHWUCC,
+	AMACLHWUV,
+	AMACLHWUVCC,
+	AMACLHWV,
+	AMACLHWVCC,
+	AMULCHW,
+	AMULCHWCC,
+	AMULCHWU,
+	AMULCHWUCC,
+	AMULHHW,
+	AMULHHWCC,
+	AMULHHWU,
+	AMULHHWUCC,
+	AMULLHW,
+	AMULLHWCC,
+	AMULLHWU,
+	AMULLHWUCC,
+	ANMACCHW,
+	ANMACCHWCC,
+	ANMACCHWS,
+	ANMACCHWSCC,
+	ANMACCHWSV,
+	ANMACCHWSVCC,
+	ANMACCHWV,
+	ANMACCHWVCC,
+	ANMACHHW,
+	ANMACHHWCC,
+	ANMACHHWS,
+	ANMACHHWSCC,
+	ANMACHHWSV,
+	ANMACHHWSVCC,
+	ANMACHHWV,
+	ANMACHHWVCC,
+	ANMACLHW,
+	ANMACLHWCC,
+	ANMACLHWS,
+	ANMACLHWSCC,
+	ANMACLHWSV,
+	ANMACLHWSVCC,
+	ANMACLHWV,
+	ANMACLHWVCC,
+
+	ARFCI,
+
+	/* optional on 32-bit */
+	AFRES,
+	AFRESCC,
+	AFRSQRTE,
+	AFRSQRTECC,
+	AFSEL,
+	AFSELCC,
+	AFSQRT,
+	AFSQRTCC,
+	AFSQRTS,
+	AFSQRTSCC,
+
+	/* parallel, cross, and secondary */
+	AFPSEL,
+	AFPMUL,
+	AFXMUL,
+	AFXPMUL,
+	AFXSMUL,
+	AFPADD,
+	AFPSUB,
+	AFPRE,
+	AFPRSQRTE,
+	AFPMADD,
+	AFXMADD,
+	AFXCPMADD,
+	AFXCSMADD,
+	AFPNMADD,
+	AFXNMADD,
+	AFXCPNMADD,
+	AFXCSNMADD,
+	AFPMSUB,
+	AFXMSUB,
+	AFXCPMSUB,
+	AFXCSMSUB,
+	AFPNMSUB,
+	AFXNMSUB,
+	AFXCPNMSUB,
+	AFXCSNMSUB,
+	AFPABS,
+	AFPNEG,
+	AFPRSP,
+	AFPNABS,
+	AFSCMP,
+	AFSABS,
+	AFSNEG,
+	AFSNABS,
+	AFPCTIW,
+	AFPCTIWZ,
+	AFMOVSPD,
+	AFMOVPSD,
+	AFXCPNPMA,
+	AFXCSNPMA,
+	AFXCPNSMA,
+	AFXCSNSMA,
+	AFXCXNPMA,
+	AFXCXNSMA,
+	AFXCXMA,
+	AFXCXNMS,
+
+	/* parallel, cross, and secondary load and store */
+	AFSMOVS,
+	AFSMOVSU,
+	AFSMOVD,
+	AFSMOVDU,
+	AFXMOVS,
+	AFXMOVSU,
+	AFXMOVD,
+	AFXMOVDU,
+	AFPMOVS,
+	AFPMOVSU,
+	AFPMOVD,
+	AFPMOVDU,
+	AFPMOVIW,
+
+	ALAST
+};
+
+/* type/name */
+enum
+{
+	D_GOK	= 0,
+	D_NONE,
+
+/* name */
+	D_EXTERN,
+	D_STATIC,
+	D_AUTO,
+	D_PARAM,
+
+/* type */
+	D_BRANCH,
+	D_OREG,
+	D_CONST,
+	D_FCONST,
+	D_SCONST,
+	D_REG,
+	D_FPSCR,
+	D_MSR,
+	D_FREG,
+	D_CREG,
+	D_SPR,
+	D_SREG,	/* segment register */
+	D_OPT,	/* branch/trap option */
+	D_FILE,
+	D_FILE1,
+	D_DCR,	/* device control register */
+
+/* reg names iff type is D_SPR */
+	D_XER	= 1,
+	D_LR	= 8,
+	D_CTR	= 9
+	/* and many supervisor level registers */
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/qc/reg.c
@@ -1,0 +1,1121 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = a1;
+	p2 = a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARETURN:
+		case ABR:
+		case ARFI:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVBZ:
+		case AMOVBZU:
+		case AMOVH:
+		case AMOVHBR:
+		case AMOVHU:
+		case AMOVHZ:
+		case AMOVHZU:
+		case AMOVW:
+		case AMOVWU:
+		case AFMOVD:
+		case AFMOVDCC:
+		case AFMOVDU:
+		case AFMOVS:
+		case AFMOVSU:
+		case AFRSP:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case ABL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+	if(debug['R'] && debug['v']) {
+		print("\nlooping structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->use1.b[z] |
+					r->use2.b[z] | r->set.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARETURN)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+	if(debug['R'] && debug['v'])
+		print("\nprop structure:\n");
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		if(debug['R'] && debug['v'])
+			print("%P\n	set = %B; rah = %B; cal = %B\n",
+				r->prog, r->set, r->refahead, r->calahead);
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set an not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L$%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L$%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L$%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TFLOAT)
+		p1->as = AFMOVS;
+	if(v->etype == TDOUBLE)
+		p1->as = AFMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBZ;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHZ;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ABL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARETURN:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost > 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(p->to.type == D_FREG && p->as == AMOVW)
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(p->from.type == D_FREG && p->as == AMOVW)
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ * track register variables including external registers:
+ *	bit	reg
+ *	0	R7
+ *	1	R8
+ *	...	...
+ *	21	R28
+ */
+long
+RtoB(int r)
+{
+
+	if(r >= REGMIN && r <= REGMAX)
+		return 1L << (r-REGMIN);
+	return 0;
+}
+
+int
+BtoR(long b)
+{
+	b &= 0x001fffffL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + REGMIN;
+}
+
+/*
+ *	bit	reg
+ *	22	F17
+ *	23	F18
+ *	...	...
+ *	31	F26
+ */
+long
+FtoB(int f)
+{
+	if(f < FREGMIN || f > FREGEXT)
+		return 0;
+	return 1L << (f - FREGMIN + 22);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xffc00000L;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 22 + FREGMIN;
+}
--- /dev/null
+++ b/utils/qc/sgen.c
@@ -1,0 +1,257 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int v, nr;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASASHL;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASHL;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		v = vlog(l);
+		if(v >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = v;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASLSHR;
+			r->vconst = v;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OLSHR;
+			r->vconst = v;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		v = vlog(r);
+		if(v >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		xcom(l);
+		xcom(r);
+		simplifyshift(n);
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		nr = 1;
+		if(r->type != T && typev[r->type->etype] || n->type != T && typev[n->type->etype]) {
+			nr = 2;
+			if(n->op == OMUL || n->op == OLMUL)
+				nr += 3;
+		}
+		if(r->complex == n->complex)
+			n->complex = r->complex+nr;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0){
+		n->complex++;
+		if(n->type != T && typev[n->type->etype])
+			n->complex++;
+	}
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
+
--- /dev/null
+++ b/utils/qc/swt.c
@@ -1,0 +1,647 @@
+#include "gc.h"
+
+static int	doubleflag;
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	Node tn;
+	
+	regalloc(&tn, &regnode, Z);
+	swit2(q, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(sval(q->val)) {
+				gopcode(OEQ, n, Z, nodconst(q->val));
+			} else {
+				gopcode(OSUB, nodconst(q->val), n, tn);
+				gopcode(OEQ, tn, Z, nodconst(0));
+			}
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(sval(r->val)) {
+		gopcode(OGT, n, Z, nodconst(r->val));
+		sp = p;
+	} else {
+		gopcode(OSUB, nodconst(r->val), n, tn);
+		gopcode(OGT, tn, Z, nodconst(0));
+		sp = p;
+	}
+	gbranch(OGOTO);
+	p->as = ABEQ;
+	patch(p, r->label);
+	swit2(q, i, def, n, tn);
+
+	patch(sp, pc);
+	swit2(r+1, nc-i-1, def, n, tn);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(n, v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	if(v < 0)
+		gopcode(ONEG, &nod1, Z, &nod1);
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+	if(a->op == OCONST && typev[a->type->etype]) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+	long l;
+
+	bf[0] = p->as;
+	bf[1] = p->as>>8;
+	bf[2] = p->reg;
+	if(p->from3.type != D_NONE)
+		bf[2] |= 0x40;
+	l = p->lineno;
+	bf[3] = l;
+	bf[4] = l>>8;
+	bf[5] = l>>16;
+	bf[6] = l>>24;
+	bp = zaddr(bf+7, &p->from, sf);
+	if(bf[2] & 0x40)
+		bp = zaddr(bp, &p->from3, 0);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, ANAME>>8);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[8];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = ASIGNAME>>8;
+		bf[2] = sig;
+		bf[3] = sig>>8;
+		bf[4] = sig>>16;
+		bf[5] = sig>>24;
+		bf[6] = t;
+		bf[7] = s->sym;
+		Bwrite(b, bf, 8);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = ANAME>>8;
+		bf[2] = t;	/* type */
+		bf[3] = s->sym;	/* sym */
+		Bwrite(b, bf, 4);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	long l;
+	Ieee e;
+
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	}
+	return bp;
+}
+
+static int
+doubled(Type *t)
+{
+	Type *v;
+
+	if(debug['4'])
+		return 0;
+	if(t->nbits != 0)
+		return 0;
+	switch(t->etype){
+	case TDOUBLE:
+		return 1;
+
+	case TARRAY:
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		return v->etype == TDOUBLE;
+
+	case TSTRUCT:
+	case TUNION:
+		for(v = t->link; v != T; v = v->down)
+			if(doubled(v))
+				return 1;
+		break;
+	}
+	return 0;
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w, pc;
+
+	o = i;
+	w = 1;
+	pc = 0;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = doubled(t)? SZ_DOUBLE: SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial align of struct element (also automatic) */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG){
+			if(doubled(v)){
+				w = SZ_DOUBLE;
+				doubleflag = 1;
+			}else
+				w = SZ_LONG;
+		}
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial align of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			if(doubled(t)){
+				w = SZ_DOUBLE;
+				pc = SZ_LONG;		/* alignment must account for pc */
+				hasdoubled = 1;
+			}else
+				w = SZ_LONG;
+			break;
+		}
+		o += SZ_LONG - w;	/* big endian adjustment */
+		w = 1;
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		if(doubled(t)){
+			pc = SZ_LONG;
+			hasdoubled = 1;
+		}
+		break;
+
+	case Aaut3:	/* total align of automatic */
+		doubleflag = 0;
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		hasdoubled |= doubleflag;
+		break;
+	}
+	o = round(o+pc, w)-pc;
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	int w;
+
+	w = SZ_LONG;
+	if((debug['8'] || hasdoubled) && !debug['4'])
+		w = SZ_DOUBLE;
+	v = round(v, w);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/qc/txt.c
@@ -1,0 +1,1751 @@
+#include "gc.h"
+
+static	int	resvreg[nelem(reg)];
+
+static	void	gopcode64(int, Node*, Node*, Node*);
+static	void	gori64(int, Node*, Node*, Node*);
+static	void	gandi64(int, Node*, Node*, Node*);
+
+void
+ginit(void)
+{
+	Type *t;
+
+	thechar = 'q';
+	thestring = "power";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	newvlongcode = 1;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	typeswitch = typechlv;
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.from3 = zprog.from;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = 0;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	com64init();
+
+	memset(reg, 0, sizeof(reg));
+	reg[REGZERO] = 1;	/* don't use */
+	reg[REGTMP] = 1;
+	reg[FREGCVI+NREG] = 1;
+	reg[FREGZERO+NREG] = 1;
+	reg[FREGHALF+NREG] = 1;
+	reg[FREGONE+NREG] = 1;
+	reg[FREGTWO+NREG] = 1;
+	memmove(resvreg, reg, sizeof(reg));
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesuv[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn2);
+			(*fnxp)++;
+		} else
+			cgen(n, tn2);
+		return;
+	}
+	if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gopcode(OAS, n, Z, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i > 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NREG)
+				goto out;
+		}
+		j = lasti + NREG;
+		for(i=NREG; i<NREG+NREG; i++) {
+			if(j >= NREG+NREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+
+	case TVLONG:
+	case TUVLONG:
+		n->op = OREGPAIR;
+		n->complex = 0;	/* already in registers */
+		n->addable = 11;
+		n->type = tn->type;
+		n->lineno = nearln;
+		n->left = alloc(sizeof(Node));
+		n->right = alloc(sizeof(Node));
+		if(o != Z && o->op == OREGPAIR) {
+			regalloc(n->left, &regnode, o->left);
+			regalloc(n->right, &regnode, o->right);
+		} else {
+			regalloc(n->left, &regnode, Z);
+			regalloc(n->right, &regnode, Z);
+		}
+		n->right->type = types[TULONG];
+		if(tn->type->etype == TUVLONG)
+			n->left->type = types[TULONG];	/* TO DO: is this a bad idea? */
+		return;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	i = 0;
+out:
+	if(i)
+		reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	if(n->op == OREGPAIR) {
+		regfree(n->left);
+		regfree(n->right);
+		return;
+	}
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d [%d]", i, reg[i]);
+	prtree(n, "regfree");
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe+stkoff, nn->type, Aaut3)-stkoff;
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+//	if(nn->type->etype == TDOUBLE || nn->type->etype == TVLONG){
+//		extern int hasdoubled;
+//		fprint(2, "stkoff=%ld cursafe=%ld curarg=%ld %d\n", stkoff, cursafe, curarg, hasdoubled);
+//	}
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_LONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = REGZERO;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		a->offset += n->xoffset;	/* little hack for reglcgenv */
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+gloadhi(Node *f, Node *t, int c)
+{
+	Type *ot;
+
+	if(f->op == OCONST){
+		f = nodconst((long)(f->vconst>>32));
+		if(c==1 && sconst(f) || c==2 && uconst(f)){
+			if(t->op == OREGISTER)
+				regfree(t);
+			*t = *f;
+			return;
+		}
+	}
+	if(f->op == OREGPAIR) {
+		gmove(f->left, t);
+		return;
+	}
+	ot = f->type;
+	f->type = types[TLONG];
+	gmove(f, t);
+	f->type = ot;
+}
+
+void
+gloadlo(Node *f, Node *t, int c)
+{
+	Type *ot;
+
+	if(f->op == OCONST){
+		f = nodconst((long)f->vconst);
+		if(c && uconst(f)){
+			if(t->op == OREGISTER)
+				regfree(t);
+			*t = *f;
+			return;
+		}
+	}
+	if(f->op == OREGPAIR) {
+		gmove(f->right, t);
+		return;
+	}
+	ot = f->type;
+	f->type = types[TLONG];
+	f->xoffset += SZ_LONG;
+	if(0){
+		prtree(f, "gloadlo f"); prtree(t, "gloadlo t");
+	}
+	gmove(f, t);
+	f->xoffset -= SZ_LONG;
+	f->type = ot;
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod, fxc0, fxc1, fxc2, fxrat;
+	Prog *p1;
+	double d;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0.0) {
+			a = FREGZERO;
+			goto ffreg;
+		}
+		if(d == 0.5) {
+			a = FREGHALF;
+			goto ffreg;
+		}
+		if(d == 1.0) {
+			a = FREGONE;
+			goto ffreg;
+		}
+		if(d == 2.0) {
+			a = FREGTWO;
+			goto ffreg;
+		}
+		if(d == -.5) {
+			fop(OSUB, FREGHALF, FREGZERO, t);
+			return;
+		}
+		if(d == -1.0) {
+			fop(OSUB, FREGONE, FREGZERO, t);
+			return;
+		}
+		if(d == -2.0) {
+			fop(OSUB, FREGTWO, FREGZERO, t);
+			return;
+		}
+		if(d == 1.5) {
+			fop(OADD, FREGONE, FREGHALF, t);
+			return;
+		}
+		if(d == 2.5) {
+			fop(OADD, FREGTWO, FREGHALF, t);
+			return;
+		}
+		if(d == 3.0) {
+			fop(OADD, FREGTWO, FREGONE, t);
+			return;
+		}
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0) {
+			a = FREGZERO;
+		ffreg:
+			nodreg(&nod, f, NREG+a);
+			gmove(&nod, t);
+			return;
+		}
+	}
+	if((ft == TVLONG || ft == TUVLONG) && f->op == OCONST && t->op == OREGPAIR) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gmove(nod32const(f->vconst>>32), t->left);
+		else
+			gmove(nod32const(f->vconst), t->left);
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gmove(nod32const(f->vconst), t->right);
+		else
+			gmove(nod32const(f->vconst>>32), t->right);
+		return;
+	}
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			a = AMOVW;
+			break;
+		case TFLOAT:
+			a = AFMOVS;
+			break;
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBZ;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHZ;
+			break;
+		}
+		if(typev[ft]) {
+			if(typev[tt]) {
+				regalloc(&nod, f, t);
+				/* low order first, because its value will be used first */
+				f->xoffset += SZ_LONG;
+				gins(AMOVW, f, nod.right);
+				f->xoffset -= SZ_LONG;
+				gins(AMOVW, f, nod.left);
+			} else {
+				/* assumed not float or double */
+				regalloc(&nod, &regnode, t);
+				f->xoffset += SZ_LONG;
+				gins(AMOVW, f, &nod);
+				f->xoffset -= SZ_LONG;
+			}
+		} else {
+			regalloc(&nod, f, t);
+			gins(a, f, &nod);
+		}
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBZ;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHZ;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AFMOVS;
+			break;
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		}
+		if(R0ISZERO && !typefd[ft] && vconst(f) == 0) {
+			gins(a, f, t);
+			if(typev[tt]) {
+				t->xoffset += SZ_LONG;
+				gins(a, f, t);
+				t->xoffset -= SZ_LONG;
+			}
+			return;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		if(typev[tt]) {
+			t->xoffset += SZ_LONG;
+			gins(a, nod.right, t);
+			t->xoffset -= SZ_LONG;
+			gins(a, nod.left, t);
+		} else
+			gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+			a = AFMOVD;
+			if(ft == TFLOAT)
+				a = AFMOVS;	/* AFMOVSD */
+			break;
+		case TFLOAT:
+			a = AFRSP;
+			if(ft == TFLOAT)
+				a = AFMOVS;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			/* BUG: not right for unsigned long */
+			regalloc(&nod, f, Z);	/* should be type float */
+			regsalloc(&fxrat, &fconstnode);
+			gins(AFCTIWZ, f, &nod);
+			gins(AFMOVD, &nod, &fxrat);
+			regfree(&nod);
+			fxrat.type = nodrat->type;
+			fxrat.etype = nodrat->etype;
+			fxrat.xoffset += 4;
+			gins(AMOVW, &fxrat, t);
+			gmove(t, t);
+			return;
+		}
+		break;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVHZ;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TFLOAT:
+			goto fxtofl;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TFLOAT:
+		fxtofl:
+			/*
+			 * rat[0] = 0x43300000; rat[1] = f^0x80000000;
+			 * t = *(double*)rat - FREGCVI;
+			 * is-unsigned(t) => if(t<0) t += 2^32;
+			 * could be streamlined for int-to-float
+			 */
+			regalloc(&fxc0, f, Z);
+			regalloc(&fxc2, f, Z);
+			regsalloc(&fxrat, &fconstnode);	/* should be type float */
+			gins(AMOVW, nodconst(0x43300000L), &fxc0);
+			gins(AMOVW, f, &fxc2);
+			gins(AMOVW, &fxc0, &fxrat);
+			gins(AXOR, nodconst(0x80000000L), &fxc2);
+			fxc1 = fxrat;
+			fxc1.type = nodrat->type;
+			fxc1.etype = nodrat->etype;
+			fxc1.xoffset += SZ_LONG;
+			gins(AMOVW, &fxc2, &fxc1);
+			regfree(&fxc2);
+			regfree(&fxc0);
+			regalloc(&nod, t, t);	/* should be type float */
+			gins(AFMOVD, &fxrat, &nod);
+			nodreg(&fxc1, t, NREG+FREGCVI);
+			gins(AFSUB, &fxc1, &nod);
+			a = AFMOVD;
+			if(tt == TFLOAT)
+				a = AFRSP;
+			gins(a, &nod, t);
+			regfree(&nod);
+			if(ft == TULONG) {
+				regalloc(&nod, t, Z);
+				if(tt == TFLOAT) {
+					gins(AFCMPU, t, Z);
+					p->to.type = D_FREG;
+					p->to.reg = FREGZERO;
+					gins(ABGE, Z, Z);
+					p1 = p;
+					gins(AFMOVS, nodfconst(4294967296.), &nod);
+					gins(AFADDS, &nod, t);
+				} else {
+					gins(AFCMPU, t, Z);
+					p->to.type = D_FREG;
+					p->to.reg = FREGZERO;
+					gins(ABGE, Z, Z);
+					p1 = p;
+					gins(AFMOVD, nodfconst(4294967296.), &nod);
+					gins(AFADD, &nod, t);
+				}
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBZ;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TVLONG:
+	case TUVLONG:
+		switch(tt) {
+		case TVLONG:
+		case TUVLONG:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AFMOVS || a == AFMOVD)
+	if(samaddr(f, t))
+		return;
+	if(typev[ft]) {
+		if(f->op != OREGPAIR || t->op != OREGPAIR)
+			diag(Z, "bad vlong in gmove (%O->%O)", f->op, t->op);
+		gins(a, f->left, t->left);
+		gins(a, f->right, t->right);
+	} else
+		gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gins3(int a, Node *f1, Node *f2, Node *t)
+{
+	Adr ta;
+
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z && (f2->op != OREGISTER || !samaddr(f2, t))) {
+		ta = zprog.from;	/* TO DO */
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+		if(ta.type == D_CONST && ta.offset == 0) {
+			if(R0ISZERO)
+				p->reg = REGZERO;
+			else
+				diag(Z, "REGZERO in gins3 %A", a);
+		}else if(ta.type == D_CONST)
+			p->from3 = ta;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gins4(int a, Node *f1, Node *f2, Node *f3, Node *t)
+{
+	Adr ta;
+
+	nextpc();
+	p->as = a;
+	naddr(f1, &p->from);
+	if(f2->op != OREGISTER && (f2->op != OCONST || vconst(f2) != 0))
+		diag(f2, "invalid gins4");
+	naddr(f2, &ta);
+	p->reg = ta.reg;
+	if(ta.type == D_CONST && ta.offset == 0)
+		p->reg = REGZERO;
+	naddr(f3, &p->from3);
+	naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et, uns;
+
+	if(o == OAS) {
+		gmove(f1, t);
+		return;
+	}
+	et = TLONG;
+	if(f1 != Z && f1->type != T) {
+		if(f1->op == OCONST && t != Z && t->type != T)
+			et = t->type->etype;
+		else
+			et = f1->type->etype;
+	}
+	if((typev[et] || t->type != T && typev[t->type->etype]) && o != OFUNC) {
+		gopcode64(o, f1, f2, t);
+		return;
+	}
+	uns = 0;
+	a = AGOK;
+	switch(o) {
+
+	case OASADD:
+	case OADD:
+		a = AADD;
+		if(et == TFLOAT)
+			a = AFADDS;
+		else
+		if(et == TDOUBLE)
+			a = AFADD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUB;
+		if(et == TFLOAT)
+			a = AFSUBS;
+		else
+		if(et == TDOUBLE)
+			a = AFSUB;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		if(f1->op == OCONST)
+			a = AANDCC;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRW;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRAW;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLW;
+		break;
+
+	case OFUNC:
+		a = ABL;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT) {
+			a = AFMULS;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = AFMUL;
+			break;
+		}
+		a = AMULLW;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = AFDIVS;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = AFDIV;
+			break;
+		}
+		a = ADIVW;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = AREM;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		a = AREMU;
+		break;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVWU;
+		break;
+
+	case OCOM:
+		a = ANOR;
+		break;
+
+	case ONEG:
+		a = ANEG;
+		if(et == TFLOAT || et == TDOUBLE)
+			a = AFNEG;
+		break;
+
+	case OEQ:
+		a = ABEQ;
+		if(t->op == OCONST && t->vconst >= (1<<15))
+			goto cmpu;
+		goto cmp;
+
+	case ONE:
+		a = ABNE;
+		if(t->op == OCONST && t->vconst >= (1<<15))
+			goto cmpu;
+		goto cmp;
+
+	case OLT:
+		a = ABLT;
+		goto cmp;
+
+	case OLE:
+		a = ABLE;
+		goto cmp;
+
+	case OGE:
+		a = ABGE;
+		goto cmp;
+
+	case OGT:
+		a = ABGT;
+		goto cmp;
+
+	case OLO:
+		a = ABLT;
+		goto cmpu;
+
+	case OLS:
+		a = ABLE;
+		goto cmpu;
+
+	case OHS:
+		a = ABGE;
+		goto cmpu;
+
+	case OHI:
+		a = ABGT;
+		goto cmpu;
+
+	cmpu:
+		uns = 1;
+	cmp:
+		nextpc();
+		p->as = uns? ACMPU: ACMP;
+		if(et == TFLOAT)
+			p->as = AFCMPU;
+		else
+		if(et == TDOUBLE)
+			p->as = AFCMPU;
+		if(f1 != Z)
+			naddr(f1, &p->from);
+		if(t != Z)
+			naddr(t, &p->to);
+		if(f1 == Z || t == Z || f2 != Z)
+			diag(Z, "bad cmp in gopcode %O", o);
+		if(debug['g'])
+			print("%P\n", p);
+		f1 = Z;
+		f2 = Z;
+		t = Z;
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	gins3(a, f1, f2, t);
+}
+
+static void
+gopcode64(int o, Node *f1, Node *f2, Node *t)
+{
+	int a1, a2;
+	Node nod, nod1, nod2, sh;
+	ulong m;
+	Prog *p1;
+
+	if(t->op != OREGPAIR || f2 != Z && f2->op != OREGPAIR) {
+		diag(Z, "bad f2/dest in gopcode64 %O", o);
+		return;
+	}
+	if(f1->op != OCONST &&
+	   (typev[f1->type->etype] && f1->op != OREGPAIR || !typev[f1->type->etype] && f1->op != OREGISTER)) {
+		diag(Z, "bad f1[%O] in gopcode64 %O", f1->op, o);
+		return;
+	}
+	/* a1 for low-order, a2 for high-order */
+	a1 = AGOK;
+	a2 = AGOK;
+	switch(o) {
+	case OASADD:
+	case OADD:
+		if(f1->op == OCONST && sconst(f1)) {
+			if(f2 == Z)
+				f2 = t;
+			gins3(AADDC, f1, f2->right, t->right);
+			if((f1->vconst>>32) == 0)
+				gins(AADDZE, f2->left, t->left);
+			else if((f1->vconst>>32) == -1)
+				gins(AADDME, f2->left, t->left);
+			else
+				diag(t, "odd vlong ADD: %lld", f1->vconst);
+			return;
+		}
+		a1 = AADDC;
+		a2 = AADDE;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a1 = ASUBC;
+		a2 = ASUBE;
+		break;
+
+	case OASOR:
+	case OOR:
+		if(f1->op == OCONST) {
+			gori64(AOR, f1, f2, t);
+			return;
+		}
+		a1 = a2 = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		if(f1->op == OCONST) {
+			gandi64(AANDCC, f1, f2, t);
+			return;
+		}
+		a1 = a2 = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		if(f1->op == OCONST) {
+			gori64(AXOR, f1, f2, t);
+			return;
+		}
+		a1 = a2 = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		if(f2 == Z)
+			f2 = t;
+		if(f1->op == OCONST) {
+			if(f1->vconst >= 32) {
+				if(f1->vconst == 32)
+					gmove(f2->left, t->right);
+				else if(f1->vconst < 64)
+					gins3(ASRW, nodconst(f1->vconst-32), f2->left, t->right);
+				else
+					gmove(nodconst(0), t->right);
+				gmove(nodconst(0), t->left);
+				return;
+			}
+			if(f1->vconst <= 0) {
+				if(f2 != t)
+					gmove(f2, t);
+				return;
+			}
+			sh = *nodconst(32 - f1->vconst);
+			m = 0xFFFFFFFFUL >> f1->vconst;
+			gins4(ARLWNM, &sh, f2->right, nodconst(m), t->right);
+			gins4(ARLWMI, &sh, f2->left, nodconst(~m), t->right);
+			gins4(ARLWNM, &sh, f2->left, nodconst(m), t->left);
+			return;
+		}
+		regalloc(&nod, &regnode, Z);
+		gins3(ASUBC, f1, nodconst(32), &nod);
+		gins3(ASRW, f1, f2->right, t->right);
+		regalloc(&nod1, &regnode, Z);
+		gins3(ASLW, &nod, f2->left, &nod1);
+		gins(AOR, &nod1, t->right);
+		gins3(AADD, nodconst(-32), f1, &nod);
+		gins3(ASRW, &nod, f2->left, &nod1);
+		gins(AOR, &nod1, t->right);
+		gins3(ASRW, f1, f2->left, t->left);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+	case OASASHR:
+	case OASHR:
+		if(f2 == Z)
+			f2 = t;
+		if(f1->op == OCONST) {
+			if(f1->vconst >= 32) {
+				if(f1->vconst == 32)
+					gmove(f2->left, t->right);
+				else if(f1->vconst < 64)
+					gins3(ASRAW, nodconst(f1->vconst-32), f2->left, t->right);
+				gins3(ASRAW, nodconst(31), f2->left, t->left);
+				if(f1->vconst >= 64) {
+					gmove(t->left, t->right);
+					return;
+				}
+				return;
+			}
+			if(f1->vconst <= 0) {
+				if(f2 != t)
+					gmove(f2, t);
+				return;
+			}
+			sh = *nodconst(32 - f1->vconst);
+			m = 0xFFFFFFFFUL >> f1->vconst;
+			gins4(ARLWNM, &sh, f2->right, nodconst(m), t->right);
+			gins4(ARLWMI, &sh, f2->left, nodconst(~m), t->right);
+			gins3(ASRAW, &sh, f2->left, t->left);
+			return;
+		}
+		regalloc(&nod, &regnode, Z);
+		gins3(ASUBC, f1, nodconst(32), &nod);
+		gins3(ASRW, f1, f2->right, t->right);
+		regalloc(&nod1, &regnode, Z);
+		gins3(ASLW, &nod, f2->left, &nod1);
+		gins(AOR, &nod1, t->right);
+		gins3(AADDCCC, nodconst(-32), f1, &nod);
+		gins3(ASRAW, &nod, f2->left, &nod1);
+		gins(ABLE, Z, Z);
+		p1 = p;
+		gins(AMOVW, &nod1, t->right);
+		patch(p1, pc);
+		gins3(ASRAW, f1, f2->left, t->left);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+	case OASASHL:
+	case OASHL:
+		if(f2 == Z)
+			f2 = t;
+		if(f1->op == OCONST) {
+			if(f1->vconst >= 32) {
+				if(f1->vconst == 32)
+					gmove(f2->right, t->left);
+				else if(f1->vconst >= 64)
+					gmove(nodconst(0), t->left);
+				else
+					gins3(ASLW, nodconst(f1->vconst-32), f2->right, t->left);
+				gmove(nodconst(0), t->right);
+				return;
+			}
+			if(f1->vconst <= 0) {
+				if(f2 != t)
+					gmove(f2, t);
+				return;
+			}
+			m = 0xFFFFFFFFUL << f1->vconst;
+			gins4(ARLWNM, f1, f2->left, nodconst(m), t->left);
+			gins4(ARLWMI, f1, f2->right, nodconst(~m), t->left);
+			gins4(ARLWNM, f1, f2->right, nodconst(m), t->right);
+			return;
+		}
+		regalloc(&nod, &regnode, Z);
+		gins3(ASUBC, f1, nodconst(32), &nod);
+		gins3(ASLW, f1, f2->left, t->left);
+		regalloc(&nod1, &regnode, Z);
+		gins3(ASRW, &nod, f2->right, &nod1);
+		gins(AOR, &nod1, t->left);
+		gins3(AADD, nodconst(-32), f1, &nod);
+		gins3(ASLW, &nod, f2->right, &nod1);
+		gins(AOR, &nod1, t->left);
+		gins3(ASLW, f1, f2->right, t->right);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+	case OASLMUL:
+	case OLMUL:
+	case OASMUL:
+	case OMUL:
+		if(f2 == Z)
+			f2 = t;
+		regalloc(&nod, &regnode, Z);
+		gins3(AMULLW, f1->right, f2->right, &nod);	/* lo(f2.low*f1.low) */
+		regalloc(&nod1, &regnode, Z);
+		gins3(AMULHWU, f1->right, f2->right, &nod1);		/* hi(f2.low*f1.low) */
+		regalloc(&nod2, &regnode, Z);
+		gins3(AMULLW, f2->right, f1->left, &nod2);	/* lo(f2.low*f1.high) */
+		gins(AADD, &nod2, &nod1);
+		gins3(AMULLW, f1->right, f2->left, &nod2);	/* lo(f2.high*f1.low) */
+		gins(AADD, &nod2, &nod1);
+		regfree(&nod2);
+		gmove(&nod, t->right);
+		gmove(&nod1, t->left);
+		regfree(&nod);
+		regfree(&nod1);
+		return;
+
+	case OCOM:
+		a1 = a2 = ANOR;
+		break;
+
+	case ONEG:
+		gins3(ASUBC, t->right, nodconst(0), t->right);
+		gins(ASUBZE, t->left, t->left);
+		return;
+	}
+	if(a1 == AGOK || a2 == AGOK)
+		diag(Z, "bad in gopcode64 %O", o);
+	if(f1->op == OCONST) {
+		if(f2 != Z & f2 != t)
+			diag(Z, "bad const in gopcode64 %O", o);
+		gins(a1, nod32const(f1->vconst), t->right);
+		gins(a2, nod32const(f1->vconst>>32), t->left);
+	} else {
+		if(f2 != Z && f2 != t) {
+			gins3(a1, f1->right, f2->right, t->right);
+			gins3(a2, f1->left, f2->left, t->left);
+		} else {
+			gins(a1, f1->right, t->right);
+			gins(a2, f1->left, t->left);
+		}
+	}
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+
+	case OREGPAIR:
+		return samaddr(f->left, t->left) && samaddr(f->right, t->right);
+	}
+	return 0;
+}
+
+static void
+gori64(int a, Node *f1, Node *f2, Node *t)
+{
+	ulong lo, hi;
+
+	if(f2 == Z)
+		f2 = t;
+	lo = f1->vconst & MASK(32);
+	hi = (f1->vconst >> 32) & MASK(32);
+	if(lo & 0xFFFF)
+		gins3(a, nodconst(lo & 0xFFFF), f2->right, t->right);
+	if((lo >> 16) != 0)
+		gins3(a, nodconst(lo & 0xFFFF0000UL), f2->right, t->right);
+	if(hi & 0xFFFF)
+		gins3(a, nodconst(hi & 0xFFFF), f2->left, t->left);
+	if((hi >> 16) != 0)
+		gins3(a, nodconst(hi & 0xFFFF0000UL), f2->left, t->left);
+}
+
+static void
+gandi64(int a, Node *f1, Node *f2, Node *t)
+{
+	ulong lo, hi;
+
+	if(f2 == Z)
+		f2 = t;
+	lo = f1->vconst & MASK(32);
+	hi = (f1->vconst >> 32) & MASK(32);
+	if(lo == 0)
+		gins(AMOVW, nodconst(0), t->right);
+	else
+		gins3(a, nodconst(lo), f2->right, t->right);
+	if(hi == 0)
+		gins(AMOVW, nodconst(0), t->left);
+	else
+		gins3(a, nodconst(hi), f2->left, t->left);
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARETURN;
+		break;
+	case OGOTO:
+		a = ABR;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	p->from.name = D_EXTERN;
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sval(long v)
+{
+
+	if(v >= -(1<<15) && v < (1<<15))
+		return 1;
+	return 0;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= -(((vlong)1)<<15) && vv < (((vlong)1)<<15))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+uconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= 0 && vv < (((vlong)1)<<16))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= 3)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= 16)
+			return 0;
+		o = exfregoffset + NREG;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
--- /dev/null
+++ b/utils/ql/asm.c
@@ -1,0 +1,727 @@
+#include	"l.h"
+
+#define JMPSZ	sizeof(u32int)		/* size of bootstrap jump section */
+
+#define	LPUT(c)\
+	{\
+		cbp[0] = (c)>>24;\
+		cbp[1] = (c)>>16;\
+		cbp[2] = (c)>>8;\
+		cbp[3] = (c);\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	CPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp++;\
+		cbc--;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+void	strnput(char*, int);
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	if(dlm && s->type == SDATA)
+		return s->value+INITDAT;
+	if(s->type != STEXT && s->type != SLEAF)
+		diag("entry not text: %s", s->name);
+	return s->value;
+}
+
+static void
+elf32jmp(Putl putl)
+{
+	/* describe a tiny text section at end with jmp to start; see below */
+	elf32phdr(putl, PT_LOAD, HEADR+textsize-JMPSZ, 0xFFFFFFFC, 0xFFFFFFFC,
+		JMPSZ, JMPSZ, R|X, 0);	/* text */
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t;
+	Optab *o;
+	long prevpc;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+
+	/* emit text segment */
+	seek(cout, HEADR, 0);
+	prevpc = pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if(p->from3.type == D_CONST) {
+				for(; pc < p->pc; pc++)
+					CPUT(0);
+			}
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(asmout(p, o, 0)) {
+			p = p->link;
+			pc += 4;
+		}
+		pc += o->size;
+		if (prevpc & (1<<31) && (pc & (1<<31)) == 0) {
+			char *tn;
+
+			tn = "??none??";
+			if(curtext != P && curtext->from.sym != S)
+				tn = curtext->from.sym->name;
+			Bprint(&bso, "%s: warning: text segment wrapped past 0\n", tn);
+		}
+		prevpc = pc;
+	}
+
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	/* emit data segment */
+	curtext = P;
+	switch(HEADTYPE) {
+	case 6:
+		/*
+		 * but first, for virtex 4, inject a jmp instruction after
+		 * other text: branch to absolute entry address (0xfffe2100).
+		 */
+		lput((18 << 26) | (0x03FFFFFC & entryvalue()) | 2);
+		textsize += JMPSZ;
+		cflush();
+		/* fall through */
+	case 0:
+	case 1:
+	case 2:
+	case 5:
+		seek(cout, HEADR+textsize, 0);
+		break;
+	case 3:
+		seek(cout, rnd(HEADR+textsize, 4), 0);
+		break;
+	case 4:
+		seek(cout, rnd(HEADR+textsize, 4096), 0);
+		break;
+	}
+
+	if(dlm){
+		char buf[8];
+
+		write(cout, buf, INITDAT-textsize);
+		textsize = INITDAT;
+	}
+
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100);
+		else
+			datblk(t, datsize-t);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 1:
+		case 2:
+		case 5:
+		case 6:
+			seek(cout, HEADR+textsize+datsize, 0);
+			break;
+		case 3:
+			seek(cout, rnd(HEADR+textsize, 4)+datsize, 0);
+			break;
+		case 4:
+			seek(cout, rnd(HEADR+textsize, 4096)+datsize, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sp\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		if(dlm)
+			asmdyn();
+		if(HEADTYPE == 0 || HEADTYPE == 1)	/* round up file length for boot image */
+			if((symsize+lcsize) & 1)
+				CPUT(0);
+		cflush();
+	}
+	else if(dlm){
+		asmdyn();
+		cflush();
+	}
+
+	/* back up and write the header */
+	seek(cout, 0L, 0);
+	switch(HEADTYPE) {
+	case 0:
+		lput(0x1030107);		/* magic and sections */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 1:
+		lput(0x4a6f7921);		/* Joy! */
+		lput(0x70656666);		/* peff */
+		lput(0x70777063);		/* pwpc */
+		lput(1);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0x30002);			/*YY*/
+		lput(0);
+		lput(~0);
+		lput(0);
+		lput(textsize+datsize);
+		lput(textsize+datsize);
+		lput(textsize+datsize);
+		lput(0xd0);			/* header size */
+		lput(0x10400);
+		lput(~0);
+		lput(0);
+		lput(0xc);
+		lput(0xc);
+		lput(0xc);
+		lput(0xc0);
+		lput(0x01010400);
+		lput(~0);
+		lput(0);
+		lput(0x38);
+		lput(0x38);
+		lput(0x38);
+		lput(0x80);
+		lput(0x04040400);
+		lput(0);
+		lput(1);
+		lput(0);
+		lput(~0);
+		lput(0);
+		lput(~0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0);
+		lput(0x3100);			/* load address */
+		lput(0);
+		lput(0);
+		lput(0);			/* whew! */
+		break;
+	case 2:
+		if(dlm)
+			lput(0x80000000 | (4*21*21+7));		/* magic */
+		else
+			lput(4*21*21+7);	/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 3:
+		break;
+	case 4:
+		lput((0x1DFL<<16)|3L);		/* magic and sections */
+		lput(time(0));			/* time and date */
+		lput(rnd(HEADR+textsize, 4096)+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x48L<<16)|15L);		/* size of optional hdr and flags */
+
+		lput((0413<<16)|01L);		/* magic and version */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT);			/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT);			/* address of TOC */
+		lput((1L<<16)|1);		/* sn(entry) | sn(text) */
+		lput((2L<<16)|1);		/* sn(data) | sn(toc) */
+		lput((0L<<16)|3);		/* sn(loader) | sn(bss) */
+		lput((3L<<16)|3);		/* maxalign(text) | maxalign(data) */
+		lput(('1'<<24)|('L'<<16)|0);	/* type field, and reserved */
+		lput(0);			/* max stack allowed */
+		lput(0);			/* max data allowed */
+		lput(0); lput(0); lput(0);	/* reserved */
+
+		strnput(".text", 8);		/* text segment */
+		lput(INITTEXT);			/* address */
+		lput(INITTEXT);
+		lput(textsize);
+		lput(HEADR);
+		lput(0L);
+		lput(HEADR+textsize+datsize+symsize);
+		lput(lcsize);			/* line number size */
+		lput(0x20L);			/* flags */
+
+		strnput(".data", 8);		/* data segment */
+		lput(INITDAT);			/* address */
+		lput(INITDAT);
+		lput(datsize);
+		lput(rnd(HEADR+textsize, 4096));/* sizes */
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x40L);			/* flags */
+
+		strnput(".bss", 8);		/* bss segment */
+		lput(INITDAT+datsize);		/* address */
+		lput(INITDAT+datsize);
+		lput(bsssize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x80L);			/* flags */
+		break;
+	case 5:
+		/*
+		 * intended for blue/gene
+		 */
+		elf32(POWER, ELFDATA2MSB, 0, nil);
+		break;
+	case 6:
+		/*
+		 * intended for virtex 4 boot
+		 */
+		debug['S'] = 1;			/* symbol table */
+		elf32(POWER, ELFDATA2MSB, 1, elf32jmp);
+		break;
+	}
+	cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		CPUT(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		CPUT(0);
+}
+
+void
+cput(long l)
+{
+	CPUT(l);
+}
+
+void
+wput(long l)
+{
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+wputl(long l)
+{
+	cbp[0] = l;
+	cbp[1] = l>>8;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)
+{
+	LPUT(l);
+}
+
+void
+lputl(long c)
+{
+	cbp[0] = (c);
+	cbp[1] = (c)>>8;
+	cbp[2] = (c)>>16;
+	cbp[3] = (c)>>24;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->sym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->sym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->sym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->sym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	LPUT(v);
+	if(ver)
+		t += 'a' - 'A';
+	CPUT(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		CPUT(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			CPUT(s[i]);
+			CPUT(s[i+1]);
+		}
+		CPUT(0);
+		CPUT(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			CPUT(s[i]);
+		CPUT(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			CPUT(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			CPUT(0);	/* 0 vv +lc */
+			CPUT(s>>24);
+			CPUT(s>>16);
+			CPUT(s>>8);
+			CPUT(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			CPUT(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			CPUT(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		CPUT(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization\n%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(&p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i+4]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)&p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			if(p->to.sym) {
+				if(p->to.sym->type == SUNDEF){
+					ckoff(p->to.sym, d);
+					d += p->to.sym->value;
+				}
+				if(p->to.sym->type == STEXT ||
+				   p->to.sym->type == SLEAF)
+					d += p->to.sym->value;
+				if(p->to.sym->type == SDATA)
+					d += p->to.sym->value + INITDAT;
+				if(p->to.sym->type == SBSS)
+					d += p->to.sym->value + INITDAT;
+				if(dlm)
+					dynreloc(p->to.sym, l+s+INITDAT, 1, 0, 0);
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
--- /dev/null
+++ b/utils/ql/asmout.c
@@ -1,0 +1,1385 @@
+#include "l.h"
+
+#define	OPVCC(o,xo,oe,rc) (((o)<<26)|((xo)<<1)|((oe)<<10)|((rc)&1))
+#define	OPCC(o,xo,rc) OPVCC((o),(xo),0,(rc))
+#define	OP(o,xo) OPVCC((o),(xo),0,0)
+
+/* the order is dest, a/s, b/imm for both arithmetic and logical operations */
+#define	AOP_RRR(op,d,a,b) ((op)|(((d)&31L)<<21)|(((a)&31L)<<16)|(((b)&31L)<<11))
+#define	AOP_IRR(op,d,a,simm) ((op)|(((d)&31L)<<21)|(((a)&31L)<<16)|((simm)&0xFFFF))
+#define	LOP_RRR(op,a,s,b) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|(((b)&31L)<<11))
+#define	LOP_IRR(op,a,s,uimm) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|((uimm)&0xFFFF))
+#define	OP_BR(op,li,aa) ((op)|((li)&0x03FFFFFC)|((aa)<<1))
+#define	OP_BC(op,bo,bi,bd,aa) ((op)|(((bo)&0x1F)<<21)|(((bi)&0x1F)<<16)|((bd)&0xFFFC)|((aa)<<1))
+#define	OP_BCR(op,bo,bi) ((op)|(((bo)&0x1F)<<21)|(((bi)&0x1F)<<16))
+#define	OP_RLW(op,a,s,sh,mb,me) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|(((sh)&31L)<<11)|\
+					(((mb)&31L)<<6)|(((me)&31L)<<1))
+
+#define	OP_ADD	OPVCC(31,266,0,0)
+#define	OP_ADDI	OPVCC(14,0,0,0)
+#define	OP_ADDIS OPVCC(15,0,0,0)
+#define	OP_ANDI	OPVCC(28,0,0,0)
+#define	OP_EXTSB	OPVCC(31,954,0,0)
+#define	OP_EXTSH OPVCC(31,922,0,0)
+#define	OP_MCRF	OPVCC(19,0,0,0)
+#define	OP_MCRFS OPVCC(63,64,0,0)
+#define	OP_MCRXR OPVCC(31,512,0,0)
+#define	OP_MFCR	OPVCC(31,19,0,0)
+#define	OP_MFFS	OPVCC(63,583,0,0)
+#define	OP_MFMSR OPVCC(31,83,0,0)
+#define	OP_MFSPR OPVCC(31,339,0,0)
+#define	OP_MFSR	OPVCC(31,595,0,0)
+#define	OP_MFSRIN	OPVCC(31,659,0,0)
+#define	OP_MTCRF OPVCC(31,144,0,0)
+#define	OP_MTFSF OPVCC(63,711,0,0)
+#define	OP_MTFSFI OPVCC(63,134,0,0)
+#define	OP_MTMSR OPVCC(31,146,0,0)
+#define	OP_MTSPR OPVCC(31,467,0,0)
+#define	OP_MTSR	OPVCC(31,210,0,0)
+#define	OP_MTSRIN	OPVCC(31,242,0,0)
+#define	OP_MULLW OPVCC(31,235,0,0)
+#define	OP_OR	OPVCC(31,444,0,0)
+#define	OP_ORI	OPVCC(24,0,0,0)
+#define	OP_RLWINM	OPVCC(21,0,0,0)
+#define	OP_SUBF	OPVCC(31,40,0,0)
+
+#define	oclass(v)	((v).class-1)
+
+long	oprrr(int), opirr(int), opload(int), opstore(int), oploadx(int), opstorex(int);
+
+int
+getmask(uchar *m, ulong v)
+{
+	int i;
+
+	m[0] = m[1] = 0;
+	if(v != ~0L && v & (1<<31) && v & 1){	/* MB > ME */
+		if(getmask(m, ~v)){
+			i = m[0]; m[0] = m[1]+1; m[1] = i-1;
+			return 1;
+		}
+		return 0;
+	}
+	for(i=0; i<32; i++)
+		if(v & (1<<(31-i))){
+			m[0] = i;
+			do {
+				m[1] = i;
+			} while(++i<32 && (v & (1<<(31-i))) != 0);
+			for(; i<32; i++)
+				if(v & (1<<(31-i)))
+					return 0;
+			return 1;
+		}
+	return 0;
+}
+
+void
+maskgen(Prog *p, uchar *m, ulong v)
+{
+	if(!getmask(m, v))
+		diag("cannot generate mask #%lux\n%P", v, p);
+}
+
+static void
+reloc(Adr *a, long pc, int sext)
+{
+	if(a->name == D_EXTERN || a->name == D_STATIC)
+		dynreloc(a->sym, pc, 1, 1, sext);
+}
+	
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+	long o1, o2, o3, o4, o5, v, t;
+	Prog *ct;
+	int r, a;
+	uchar mask[2];
+
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	switch(o->type) {
+	default:
+		if(aflag)
+			return 0;
+		diag("unknown type %d", o->type);
+		if(!debug['a'])
+			prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		if(aflag) {
+			if(p->link) {
+				if(p->as == ATEXT) {
+					ct = curtext;
+					o2 = autosize;
+					curtext = p;
+					autosize = p->to.offset + 4;
+					o1 = asmout(p->link, oplook(p->link), aflag);
+					curtext = ct;
+					autosize = o2;
+				} else
+					o1 = asmout(p->link, oplook(p->link), aflag);
+			}
+			return o1;
+		}
+		break;
+
+	case 1:		/* mov r1,r2 ==> OR Rs,Rs,Ra */
+		if(p->to.reg == REGZERO && p->from.type == D_CONST) {
+			v = regoff(&p->from);
+			if(r0iszero && v != 0) {
+				nerrors--;
+				diag("literal operation on R0\n%P", p);
+			}
+			o1 = LOP_IRR(OP_ADDI, REGZERO, REGZERO, v);
+			break;
+		}
+		o1 = LOP_RRR(OP_OR, p->to.reg, p->from.reg, p->from.reg);
+		break;
+
+	case 2:		/* int/cr/fp op Rb,[Ra],Rd */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, r, p->from.reg);
+		break;
+
+	case 3:		/* mov $soreg/addcon/ucon, r ==> addis/addi $i,reg',r */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		a = OP_ADDI;
+		if(o->a1 == C_UCON) {
+			a = OP_ADDIS;
+			v >>= 16;
+		}
+		if(r0iszero && p->to.reg == 0 && (r != 0 || v != 0))
+			diag("literal operation on R0\n%P", p);
+		o1 = AOP_IRR(a, p->to.reg, r, v);
+		break;
+
+	case 4:		/* add/mul $scon,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		if(r0iszero && p->to.reg == 0)
+			diag("literal operation on R0\n%P", p);
+		o1 = AOP_IRR(opirr(p->as), p->to.reg, r, v);
+		break;
+
+	case 5:		/* syscall */
+		if(aflag)
+			return 0;
+		o1 = oprrr(p->as);
+		break;
+
+	case 6:		/* logical op Rb,[Rs,]Ra; no literal */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = LOP_RRR(oprrr(p->as), p->to.reg, r, p->from.reg);
+		break;
+
+	case 7:		/* mov r, soreg ==> stw o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		if(p->to.type == D_OREG && p->reg != NREG) {
+			if(v)
+				diag("illegal indexed instruction\n%P", p);
+			o1 = AOP_RRR(opstorex(p->as), p->from.reg, p->reg, r);
+		} else
+			o1 = AOP_IRR(opstore(p->as), p->from.reg, r, v);
+		break;
+
+	case 8:		/* mov soreg, r ==> lbz/lhz/lwz o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		if(p->from.type == D_OREG && p->reg != NREG) {
+			if(v)
+				diag("illegal indexed instruction\n%P", p);
+			o1 = AOP_RRR(oploadx(p->as), p->to.reg, p->reg, r);
+		} else
+			o1 = AOP_IRR(opload(p->as), p->to.reg, r, v);
+		break;
+
+	case 9:		/* movb soreg, r ==> lbz o(r),r2; extsb r2,r2 */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		if(p->from.type == D_OREG && p->reg != NREG) {
+			if(v)
+				diag("illegal indexed instruction\n%P", p);
+			o1 = AOP_RRR(oploadx(p->as), p->to.reg, p->reg, r);
+		} else
+			o1 = AOP_IRR(opload(p->as), p->to.reg, r, v);
+		o2 = LOP_RRR(OP_EXTSB, p->to.reg, p->to.reg, 0);
+		break;
+
+	case 10:		/* sub Ra,[Rb],Rd => subf Rd,Ra,Rb */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, p->from.reg, r);
+		break;
+
+	case 11:	/* br/bl lbra */
+		if(aflag)
+			return 0;
+		v = 0;
+		if(p->cond == UP){
+			if(p->to.sym->type != SUNDEF)
+				diag("bad branch sym type");
+			v = (ulong)p->to.sym->value >> (Roffset-2);
+			dynreloc(p->to.sym, p->pc, 0, 0, 0);
+		}
+		else if(p->cond)
+			v = p->cond->pc - p->pc;
+		if(v & 03) {
+			diag("odd branch target address\n%P", p);
+			v &= ~03;
+		}
+		if(v < -(1L<<25) || v >= (1L<<25))
+			diag("branch too far\n%P", p);
+		o1 = OP_BR(opirr(p->as), v, 0);
+		break;
+
+	case 12:	/* movb r,r (signed); extsb is on PowerPC but not POWER */
+		o1 = LOP_RRR(OP_EXTSB, p->to.reg, p->from.reg, 0);
+		break;
+
+	case 13:	/* mov[bh]z r,r; uses rlwinm not andi. to avoid changing CC */
+		if(p->as == AMOVBZ)
+			o1 = OP_RLW(OP_RLWINM, p->to.reg, p->from.reg, 0, 24, 31);
+		else if(p->as == AMOVH)
+			o1 = LOP_RRR(OP_EXTSH, p->to.reg, p->from.reg, 0);
+		else if(p->as == AMOVHZ)
+			o1 = OP_RLW(OP_RLWINM, p->to.reg, p->from.reg, 0, 16, 31);
+		else
+			diag("internal: bad mov[bh]z\n%P", p);
+		break;
+
+/*14 */
+
+	case 17:	/* bc bo,bi,lbra (same for now) */
+	case 16:	/* bc bo,bi,sbra */
+		if(aflag)
+			return 0;
+		a = 0;
+		if(p->from.type == D_CONST)
+			a = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		v = 0;
+		if(p->cond)
+			v = p->cond->pc - p->pc;
+		if(v & 03) {
+			diag("odd branch target address\n%P", p);
+			v &= ~03;
+		}
+		if(v < -(1L<<16) || v >= (1L<<16))
+			diag("branch too far\n%P", p);
+		o1 = OP_BC(opirr(p->as), a, r, v, 0);
+		break;
+
+	case 15:	/* br/bl (r) => mov r,lr; br/bl (lr) */
+		if(aflag)
+			return 0;
+		if(p->as == ABC || p->as == ABCL)
+			v = regoff(&p->to)&31L;
+		else
+			v = 20;	/* unconditional */
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		o1 = AOP_RRR(OP_MTSPR, p->to.reg, 0, 0) | ((D_LR&0x1f)<<16) | (((D_LR>>5)&0x1f)<<11);
+		o2 = OPVCC(19, 16, 0, 0);
+		if(p->as == ABL || p->as == ABCL)
+			o2 |= 1;
+		o2 = OP_BCR(o2, v, r);
+		break;
+
+	case 18:	/* br/bl (lr/ctr); bc/bcl bo,bi,(lr/ctr) */
+		if(aflag)
+			return 0;
+		if(p->as == ABC || p->as == ABCL)
+			v = regoff(&p->from)&31L;
+		else
+			v = 20;	/* unconditional */
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		switch(oclass(p->to)) {
+		case C_CTR:
+			o1 = OPVCC(19, 528, 0, 0);
+			break;
+		case C_LR:
+			o1 = OPVCC(19, 16, 0, 0);
+			break;
+		default:
+			diag("bad optab entry (18): %d\n%P", p->to.class, p);
+			v = 0;
+		}
+		if(p->as == ABL || p->as == ABCL)
+			o1 |= 1;
+		o1 = OP_BCR(o1, v, r);
+		break;
+
+	case 19:	/* mov $lcon,r ==> cau+or */
+		v = regoff(&p->from);
+		o1 = AOP_IRR(OP_ADDIS, p->to.reg, REGZERO, v>>16);
+		o2 = LOP_IRR(OP_ORI, p->to.reg, p->to.reg, v);
+		if(dlm)
+			reloc(&p->from, p->pc, 0);
+		break;
+
+	case 20:	/* add $ucon,,r */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		if(p->as == AADD && (!r0iszero && p->reg == 0 || r0iszero && p->to.reg == 0))
+			diag("literal operation on R0\n%P", p);
+		o1 = AOP_IRR(opirr(p->as+AEND), p->to.reg, r, v>>16);
+		break;
+
+	case 22:	/* add $lcon,r1,r2 ==> cau+or+add */	/* could do add/sub more efficiently */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP || p->reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = LOP_IRR(OP_ORI, REGTMP, REGTMP, v);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o3 = AOP_RRR(oprrr(p->as), p->to.reg, REGTMP, r);
+		if(dlm)
+			reloc(&p->from, p->pc, 0);
+		break;
+
+	case 23:	/* and $lcon,r1,r2 ==> cau+or+and */	/* masks could be done using rlnm etc. */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP || p->reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = LOP_IRR(OP_ORI, REGTMP, REGTMP, v);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o3 = LOP_RRR(oprrr(p->as), p->to.reg, REGTMP, r);
+		if(dlm)
+			reloc(&p->from, p->pc, 0);
+		break;
+/*24*/
+
+	case 26:	/* mov $lsext/auto/oreg,,r2 ==> cau+add */
+		v = regoff(&p->from);
+		if(v & 0x8000L)
+			v += 0x10000L;
+		if(p->to.reg == REGTMP)
+			diag("can't synthesize large constant\n%P", p);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+		o2 = AOP_IRR(OP_ADDI, p->to.reg, REGTMP, v);
+		break;
+
+	case 27:		/* subc ra,$simm,rd => subfic rd,ra,$simm */
+		v = regoff(&p->from3);
+		r = p->from.reg;
+		o1 = AOP_IRR(opirr(p->as), p->to.reg, r, v);
+		break;
+
+	case 28:	/* subc r1,$lcon,r2 ==> cau+or+subfc */
+		v = regoff(&p->from3);
+		if(p->to.reg == REGTMP || p->from.reg == REGTMP)
+			diag("can't synthesize large constant\n%P", p);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = LOP_IRR(OP_ORI, REGTMP, REGTMP, v);
+		o3 = AOP_RRR(oprrr(p->as), p->to.reg, p->from.reg, REGTMP);
+		if(dlm)
+			reloc(&p->from3, p->pc, 0);
+		break;
+
+/*29, 30, 31 */
+
+	case 32:	/* fmul frc,fra,frd */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, r, 0)|((p->from.reg&31L)<<6);
+		break;
+
+	case 33:	/* fabs [frb,]frd; fmr. frb,frd */
+		r = p->from.reg;
+		if(oclass(p->from) == C_NONE)
+			r = p->to.reg;
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, 0, r);
+		break;
+
+	case 34:	/* FMADDx fra,frb,frc,frd (d=a*b+c) */
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, p->from.reg, p->reg)|((p->from3.reg&31L)<<6);
+		break;
+
+	case 35:	/* mov r,lext/lauto/loreg ==> cau $(v>>16),sb,r'; store o(r') */
+		v = regoff(&p->to);
+		if(v & 0x8000L)
+			v += 0x10000L;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+		o2 = AOP_IRR(opstore(p->as), p->from.reg, REGTMP, v);
+		break;
+
+	case 36:	/* mov bz/h/hz lext/lauto/lreg,r ==> lbz/lha/lhz etc */
+		v = regoff(&p->from);
+		if(v & 0x8000L)
+			v += 0x10000L;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+		o2 = AOP_IRR(opload(p->as), p->to.reg, REGTMP, v);
+		break;
+
+	case 37:	/* movb lext/lauto/lreg,r ==> lbz o(reg),r; extsb r */
+		v = regoff(&p->from);
+		if(v & 0x8000L)
+			v += 0x10000L;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+		o2 = AOP_IRR(opload(p->as), p->to.reg, REGTMP, v);
+		o3 = LOP_RRR(OP_EXTSB, p->to.reg, p->to.reg, 0);
+		break;
+
+	case 40:	/* word */
+		if(aflag)
+			return 0;
+		o1 = regoff(&p->from);
+		break;
+
+	case 41:	/* stswi */
+		o1 = AOP_RRR(opirr(p->as), p->from.reg, p->to.reg, 0) | ((regoff(&p->from3)&0x7F)<<11);
+		break;
+
+	case 42:	/* lswi */
+		o1 = AOP_RRR(opirr(p->as), p->to.reg, p->from.reg, 0) | ((regoff(&p->from3)&0x7F)<<11);
+		break;
+
+	case 43:	/* unary indexed source: dcbf (b); dcbf (a+b) */
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		o1 = AOP_RRR(oprrr(p->as), 0, r, p->from.reg);
+		break;
+
+	case 44:	/* indexed store */
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		o1 = AOP_RRR(opstorex(p->as), p->from.reg, r, p->to.reg);
+		break;
+	case 45:	/* indexed load */
+		r = p->reg;
+		if(r == NREG)
+			r = 0;
+		o1 = AOP_RRR(oploadx(p->as), p->to.reg, r, p->from.reg);
+		break;
+
+	case 46:	/* plain op */
+		o1 = oprrr(p->as);
+		break;
+
+	case 47:	/* op Ra, Rd; also op [Ra,] Rd */
+		r = p->from.reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = AOP_RRR(oprrr(p->as), p->to.reg, r, 0);
+		break;
+
+	case 48:	/* op Rs, Ra */
+		r = p->from.reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = LOP_RRR(oprrr(p->as), p->to.reg, r, 0);
+		break;
+
+	case 49:	/* op Rb */
+		o1 = AOP_RRR(oprrr(p->as), 0, 0, p->from.reg);
+		break;
+
+/*50*/
+
+	case 51:	/* rem[u] r1[,r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		v = oprrr(p->as);
+		t = v & ((1<<10)|1);	/* OE|Rc */
+		o1 = AOP_RRR(v&~t, REGTMP, r, p->from.reg);
+		o2 = AOP_RRR(OP_MULLW, REGTMP, REGTMP, p->from.reg);
+		o3 = AOP_RRR(OP_SUBF|t, p->to.reg, REGTMP, r);
+		break;
+
+	case 52:	/* mtfsbNx cr(n) */
+		v = regoff(&p->from)&31L;
+		o1 = AOP_RRR(oprrr(p->as), v, 0, 0);
+		break;
+
+	case 53:	/* mffsX ,fr1 */
+		o1 = AOP_RRR(OP_MFFS, p->to.reg, 0, 0);
+		break;
+
+	case 54:	/* mov msr,r1; mov r1, msr*/
+		if(oclass(p->from) == C_REG)
+			o1 = AOP_RRR(OP_MTMSR, p->from.reg, 0, 0);
+		else
+			o1 = AOP_RRR(OP_MFMSR, p->to.reg, 0, 0);
+		break;
+
+	case 55:	/* mov sreg,r1; mov r1,sreg */
+		v = 0;
+		if(p->from.type == D_SREG) {
+			r = p->from.reg;
+			o1 = OP_MFSR;
+			if(r == NREG && p->reg != NREG) {
+				r = 0;
+				v = p->reg;
+				o1 = OP_MFSRIN;
+			}
+			o1 = AOP_RRR(o1, p->to.reg, r&15L, v);
+		} else {
+			r = p->to.reg;
+			o1 = OP_MTSR;
+			if(r == NREG && p->reg != NREG) {
+				r = 0;
+				v = p->reg;
+				o1 = OP_MTSRIN;
+			}
+			o1 = AOP_RRR(o1, p->from.reg, r&15L, v);
+		}
+		if(r == NREG)
+			diag("illegal move indirect to/from segment register\n%P", p);
+		break;
+
+	case 56:	/* sra $sh,[s,]a */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = AOP_RRR(opirr(p->as), r, p->to.reg, v&31L);
+		break;
+
+	case 57:	/* slw $sh,[s,]a -> rlwinm ... */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		/*
+		 * Let user (gs) shoot himself in the foot. 
+		 * qc has already complained.
+		 *
+		if(v < 0 || v > 31)
+			diag("illegal shift %ld\n%P", v, p);
+		 */
+		if(v < 0)
+			v = 0;
+		else if(v > 32)
+			v = 32;
+		if(p->as == ASRW || p->as == ASRWCC) {	/* shift right */
+			mask[0] = v;
+			mask[1] = 31;
+			v = 32-v;
+		} else {
+			mask[0] = 0;
+			mask[1] = 31-v;
+		}
+		o1 = OP_RLW(OP_RLWINM, p->to.reg, r, v, mask[0], mask[1]);
+		if(p->as == ASLWCC || p->as == ASRWCC)
+			o1 |= 1;	/* Rc */
+		break;
+
+	case 58:		/* logical $andcon,[s],a */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = LOP_IRR(opirr(p->as), p->to.reg, r, v);
+		break;
+
+	case 59:	/* or/and $ucon,,r */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = LOP_IRR(opirr(p->as+AEND), p->to.reg, r, v>>16);	/* oris, xoris, andis */
+		break;
+
+	case 60:	/* tw to,a,b */
+		r = regoff(&p->from)&31L;
+		o1 = AOP_RRR(oprrr(p->as), r, p->reg, p->to.reg);
+		break;
+
+	case 61:	/* tw to,a,$simm */
+		r = regoff(&p->from)&31L;
+		v = regoff(&p->to);
+		o1 = AOP_IRR(opirr(p->as), r, p->reg, v);
+		break;
+
+	case 62:	/* rlwmi $sh,s,$mask,a */
+		v = regoff(&p->from);
+		maskgen(p, mask, regoff(&p->from3));
+		o1 = AOP_RRR(opirr(p->as), p->reg, p->to.reg, v);
+		o1 |= ((mask[0]&31L)<<6)|((mask[1]&31L)<<1);
+		break;
+
+	case 63:	/* rlwmi b,s,$mask,a */
+		maskgen(p, mask, regoff(&p->from3));
+		o1 = AOP_RRR(opirr(p->as), p->reg, p->to.reg, p->from.reg);
+		o1 |= ((mask[0]&31L)<<6)|((mask[1]&31L)<<1);
+		break;
+
+	case 64:	/* mtfsf fr[, $m] {,fpcsr} */
+		if(p->from3.type != D_NONE)
+			v = regoff(&p->from3)&255L;
+		else
+			v = 255;
+		o1 = OP_MTFSF | (v<<17) | (p->from.reg<<11);
+		break;
+
+	case 65:	/* MOVFL $imm,FPSCR(n) => mtfsfi crfd,imm */
+		if(p->to.reg == NREG)
+			diag("must specify FPSCR(n)\n%P", p);
+		o1 = OP_MTFSFI | ((p->to.reg&15L)<<23) | ((regoff(&p->from)&31L)<<12);
+		break;
+
+	case 66:	/* mov spr,r1; mov r1,spr, also dcr */
+		if(p->from.type == D_REG) {
+			r = p->from.reg;
+			v = p->to.offset;
+			if(p->to.type == D_DCR) {
+				if(p->to.reg != NREG) {
+					o1 = OPVCC(31,387,0,0);	/* mtdcrx */
+					v = p->to.reg;
+				}else
+					o1 = OPVCC(31,451,0,0);	/* mtdcr */
+			}else
+				o1 = OPVCC(31,467,0,0); /* mtspr */
+		} else {
+			r = p->to.reg;
+			v = p->from.offset;
+			if(p->from.type == D_DCR) {
+				if(p->from.reg != NREG) {
+					o1 = OPVCC(31,259,0,0);	/* mfdcrx */
+					v = p->from.reg;
+				}else
+					o1 = OPVCC(31,323,0,0);	/* mfdcr */
+			}else
+				o1 = OPVCC(31,339,0,0);	/* mfspr */
+		}
+		o1 = AOP_RRR(o1, r, 0, 0) | ((v&0x1f)<<16) | (((v>>5)&0x1f)<<11);
+		break;
+
+	case 67:	/* mcrf crfD,crfS */
+		if(p->from.type != D_CREG || p->from.reg == NREG ||
+		   p->to.type != D_CREG || p->to.reg == NREG)
+			diag("illegal CR field number\n%P", p);
+		o1 = AOP_RRR(OP_MCRF, ((p->to.reg&7L)<<2), ((p->from.reg&7)<<2), 0);
+		break;
+
+	case 68:	/* mfcr rD */
+		if(p->from.type == D_CREG && p->from.reg != NREG)
+			diag("must move whole CR to register\n%P", p);
+		o1 = AOP_RRR(OP_MFCR, p->to.reg, 0, 0);
+		break;
+
+	case 69:	/* mtcrf CRM,rS */
+		if(p->from3.type != D_NONE) {
+			if(p->to.reg != NREG)
+				diag("can't use both mask and CR(n)\n%P", p);
+			v = regoff(&p->from3) & 0xff;
+		} else {
+			if(p->to.reg == NREG)
+				v = 0xff;	/* CR */
+			else
+				v = 1<<(7-(p->to.reg&7));	/* CR(n) */
+		}
+		o1 = AOP_RRR(OP_MTCRF, p->from.reg, 0, 0) | (v<<12);
+		break;
+
+	case 70:	/* [f]cmp r,r,cr*/
+		if(p->reg == NREG)
+			r = 0;
+		else
+			r = (p->reg&7)<<2;
+		o1 = AOP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
+		break;
+
+	case 71:	/* cmp[l] r,i,cr*/
+		if(p->reg == NREG)
+			r = 0;
+		else
+			r = (p->reg&7)<<2;
+		o1 = AOP_RRR(opirr(p->as), r, p->from.reg, 0) | (regoff(&p->to)&0xffff);
+		break;
+
+	case 72:	/* mcrxr crfD */
+		if(p->to.reg == NREG)
+			diag("must move XER to CR(n)\n%P", p);
+		o1 = AOP_RRR(OP_MCRXR, ((p->to.reg&7L)<<2), 0, 0);
+		break;
+
+	case 73:	/* mcrfs crfD,crfS */
+		if(p->from.type != D_FPSCR || p->from.reg == NREG ||
+		   p->to.type != D_CREG || p->to.reg == NREG)
+			diag("illegal FPSCR/CR field number\n%P", p);
+		o1 = AOP_RRR(OP_MCRFS, ((p->to.reg&7L)<<2), ((p->from.reg&7)<<2), 0);
+		break;
+
+	/* relocation operations */
+
+	case 74:
+		v = regoff(&p->to);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = AOP_IRR(opstore(p->as), p->from.reg, REGTMP, v);
+		if(dlm)
+			reloc(&p->to, p->pc, 1);
+		break;
+
+	case 75:
+		v = regoff(&p->from);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = AOP_IRR(opload(p->as), p->to.reg, REGTMP, v);
+		if(dlm)
+			reloc(&p->from, p->pc, 1);
+		break;
+
+	case 76:
+		v = regoff(&p->from);
+		o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, v>>16);
+		o2 = AOP_IRR(opload(p->as), p->to.reg, REGTMP, v);
+		o3 = LOP_RRR(OP_EXTSB, p->to.reg, p->to.reg, 0);
+		if(dlm)
+			reloc(&p->from, p->pc, 1);
+		break;
+
+	}
+	if(aflag)
+		return o1;
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		lput(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		lput(o1);
+		lput(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		lput(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		lput(o1);
+		lput(o2);
+		lput(o3);
+		lput(o4);
+		lput(o5);
+		break;
+	}
+	return 0;
+}
+
+long
+oprrr(int a)
+{
+	switch(a) {
+	case AADD:	return OPVCC(31,266,0,0);
+	case AADDCC:	return OPVCC(31,266,0,1);
+	case AADDV:	return OPVCC(31,266,1,0);
+	case AADDVCC:	return OPVCC(31,266,1,1);
+	case AADDC:	return OPVCC(31,10,0,0);
+	case AADDCCC:	return OPVCC(31,10,0,1);
+	case AADDCV:	return OPVCC(31,10,1,0);
+	case AADDCVCC:	return OPVCC(31,10,1,1);
+	case AADDE:	return OPVCC(31,138,0,0);
+	case AADDECC:	return OPVCC(31,138,0,1);
+	case AADDEV:	return OPVCC(31,138,1,0);
+	case AADDEVCC:	return OPVCC(31,138,1,1);
+	case AADDME:	return OPVCC(31,234,0,0);
+	case AADDMECC:	return OPVCC(31,234,0,1);
+	case AADDMEV:	return OPVCC(31,234,1,0);
+	case AADDMEVCC:	return OPVCC(31,234,1,1);
+	case AADDZE:	return OPVCC(31,202,0,0);
+	case AADDZECC:	return OPVCC(31,202,0,1);
+	case AADDZEV:	return OPVCC(31,202,1,0);
+	case AADDZEVCC:	return OPVCC(31,202,1,1);
+
+	case AAND:	return OPVCC(31,28,0,0);
+	case AANDCC:	return OPVCC(31,28,0,1);
+	case AANDN:	return OPVCC(31,60,0,0);
+	case AANDNCC:	return OPVCC(31,60,0,1);
+
+	case ACMP:	return OPVCC(31,0,0,0);
+	case ACMPU:	return OPVCC(31,32,0,0);
+
+	case ACNTLZW:	return OPVCC(31,26,0,0);
+	case ACNTLZWCC:	return OPVCC(31,26,0,1);
+
+	case ACRAND:	return OPVCC(19,257,0,0);
+	case ACRANDN:	return OPVCC(19,129,0,0);
+	case ACREQV:	return OPVCC(19,289,0,0);
+	case ACRNAND:	return OPVCC(19,225,0,0);
+	case ACRNOR:	return OPVCC(19,33,0,0);
+	case ACROR:	return OPVCC(19,449,0,0);
+	case ACRORN:	return OPVCC(19,417,0,0);
+	case ACRXOR:	return OPVCC(19,193,0,0);
+
+	case ADCBF:	return OPVCC(31,86,0,0);
+	case ADCBI:	return OPVCC(31,470,0,0);
+	case ADCBST:	return OPVCC(31,54,0,0);
+	case ADCBT:	return OPVCC(31,278,0,0);
+	case ADCBTST:	return OPVCC(31,246,0,0);
+	case ADCBZ:	return OPVCC(31,1014,0,0);
+
+	case AREM:
+	case ADIVW:	return OPVCC(31,491,0,0);
+	case AREMCC:
+	case ADIVWCC:	return OPVCC(31,491,0,1);
+	case AREMV:
+	case ADIVWV:	return OPVCC(31,491,1,0);
+	case AREMVCC:
+	case ADIVWVCC:	return OPVCC(31,491,1,1);
+	case AREMU:
+	case ADIVWU:	return OPVCC(31,459,0,0);
+	case AREMUCC:
+	case ADIVWUCC:	return OPVCC(31,459,0,1);
+	case AREMUV:
+	case ADIVWUV:	return OPVCC(31,459,1,0);
+	case AREMUVCC:
+	case ADIVWUVCC:	return OPVCC(31,459,1,1);
+
+	case AEIEIO:	return OPVCC(31,854,0,0);
+
+	case AEQV:	return OPVCC(31,284,0,0);
+	case AEQVCC:	return OPVCC(31,284,0,1);
+
+	case AEXTSB:	return OPVCC(31,954,0,0);
+	case AEXTSBCC:	return OPVCC(31,954,0,1);
+	case AEXTSH:	return OPVCC(31,922,0,0);
+	case AEXTSHCC:	return OPVCC(31,922,0,1);
+
+	case AFABS:	return OPVCC(63,264,0,0);
+	case AFABSCC:	return OPVCC(63,264,0,1);
+	case AFADD:	return OPVCC(63,21,0,0);
+	case AFADDCC:	return OPVCC(63,21,0,1);
+	case AFADDS:	return OPVCC(59,21,0,0);
+	case AFADDSCC:	return OPVCC(59,21,0,1);
+	case AFCMPO:	return OPVCC(63,32,0,0);
+	case AFCMPU:	return OPVCC(63,0,0,0);
+	case AFCTIW:	return OPVCC(63,14,0,0);
+	case AFCTIWCC:	return OPVCC(63,14,0,1);
+	case AFCTIWZ:	return OPVCC(63,15,0,0);
+	case AFCTIWZCC:	return OPVCC(63,15,0,1);
+	case AFDIV:	return OPVCC(63,18,0,0);
+	case AFDIVCC:	return OPVCC(63,18,0,1);
+	case AFDIVS:	return OPVCC(59,18,0,0);
+	case AFDIVSCC:	return OPVCC(59,18,0,1);
+	case AFMADD:	return OPVCC(63,29,0,0);
+	case AFMADDCC:	return OPVCC(63,29,0,1);
+	case AFMADDS:	return OPVCC(59,29,0,0);
+	case AFMADDSCC:	return OPVCC(59,29,0,1);
+	case AFMOVS:
+	case AFMOVD:	return OPVCC(63,72,0,0);	/* load */
+	case AFMOVDCC:	return OPVCC(63,72,0,1);
+	case AFMSUB:	return OPVCC(63,28,0,0);
+	case AFMSUBCC:	return OPVCC(63,28,0,1);
+	case AFMSUBS:	return OPVCC(59,28,0,0);
+	case AFMSUBSCC:	return OPVCC(59,28,0,1);
+	case AFMUL:	return OPVCC(63,25,0,0);
+	case AFMULCC:	return OPVCC(63,25,0,1);
+	case AFMULS:	return OPVCC(59,25,0,0);
+	case AFMULSCC:	return OPVCC(59,25,0,1);
+	case AFNABS:	return OPVCC(63,136,0,0);
+	case AFNABSCC:	return OPVCC(63,136,0,1);
+	case AFNEG:	return OPVCC(63,40,0,0);
+	case AFNEGCC:	return OPVCC(63,40,0,1);
+	case AFNMADD:	return OPVCC(63,31,0,0);
+	case AFNMADDCC:	return OPVCC(63,31,0,1);
+	case AFNMADDS:	return OPVCC(59,31,0,0);
+	case AFNMADDSCC:	return OPVCC(59,31,0,1);
+	case AFNMSUB:	return OPVCC(63,30,0,0);
+	case AFNMSUBCC:	return OPVCC(63,30,0,1);
+	case AFNMSUBS:	return OPVCC(59,30,0,0);
+	case AFNMSUBSCC:	return OPVCC(59,30,0,1);
+	case AFRES:	return OPVCC(59,24,0,0);
+	case AFRESCC:	return OPVCC(59,24,0,1);
+	case AFRSP:	return OPVCC(63,12,0,0);
+	case AFRSPCC:	return OPVCC(63,12,0,1);
+	case AFRSQRTE:	return OPVCC(63,26,0,0);
+	case AFRSQRTECC:	return OPVCC(63,26,0,1);
+	case AFSEL:	return OPVCC(63,23,0,0);
+	case AFSELCC:	return OPVCC(63,23,0,1);
+	case AFSQRT:	return OPVCC(63,22,0,0);
+	case AFSQRTCC:	return OPVCC(63,22,0,1);
+	case AFSQRTS:	return OPVCC(59,22,0,0);
+	case AFSQRTSCC:	return OPVCC(59,22,0,1);
+	case AFSUB:	return OPVCC(63,20,0,0);
+	case AFSUBCC:	return OPVCC(63,20,0,1);
+	case AFSUBS:	return OPVCC(59,20,0,0);
+	case AFSUBSCC:	return OPVCC(59,20,0,1);
+
+	/* fp2 */
+	case AFPMUL:	return OPVCC(0,8,0,0);
+	case AFXMUL:	return OPVCC(0,9,0,0);
+	case AFXPMUL:	return OPVCC(0,10,0,0);
+	case AFXSMUL:	return OPVCC(0,11,0,0);
+	case AFPADD:	return OPVCC(0,12,0,0);
+	case AFPSUB:	return OPVCC(0,13,0,0);
+	case AFPRE:	return OPVCC(0,14,0,0);
+	case AFPRSQRTE:	return OPVCC(0,15,0,0);
+	case AFPMADD:	return OPVCC(0,16,0,0);
+	case AFXMADD:	return OPVCC(0,17,0,0);
+	case AFXCPMADD:	return OPVCC(0,18,0,0);
+	case AFXCSMADD:	return OPVCC(0,19,0,0);
+	case AFPNMADD:	return OPVCC(0,20,0,0);
+	case AFXNMADD:	return OPVCC(0,21,0,0);
+	case AFXCPNMADD:	return OPVCC(0,22,0,0);
+	case AFXCSNMADD:	return OPVCC(0,23,0,0);
+	case AFPMSUB:	return OPVCC(0,24,0,0);
+	case AFXMSUB:	return OPVCC(0,25,0,0);
+	case AFXCPMSUB:	return OPVCC(0,26,0,0);
+	case AFXCSMSUB:	return OPVCC(0,27,0,0);
+	case AFPNMSUB:	return OPVCC(0,28,0,0);
+	case AFXNMSUB:	return OPVCC(0,29,0,0);
+	case AFXCPNMSUB:	return OPVCC(0,30,0,0);
+	case AFXCSNMSUB:	return OPVCC(0,31,0,0);
+	case AFPABS:	return OPVCC(0,96,0,0);
+	case AFPNEG:	return OPVCC(0,160,0,0);
+	case AFPRSP:	return OPVCC(0,192,0,0);
+	case AFPNABS:	return OPVCC(0,224,0,0);
+	case AFSCMP:	return OPVCC(0,320,0,0)|(3<<21);
+	case AFSABS:	return OPVCC(0,352,0,0);
+	case AFSNEG:	return OPVCC(0,416,0,0);
+	case AFSNABS:	return OPVCC(0,480,0,0);
+	case AFPCTIW:	return OPVCC(0,576,0,0);
+	case AFPCTIWZ:	return OPVCC(0,704,0,0);
+
+	case AFPMOVD:	return OPVCC(0,32,0,0);	/* fpmr */
+	case AFSMOVD:	return OPVCC(0,288,0,0);	/* fsmr */
+	case AFXMOVD:	return OPVCC(0,544,0,0);	/* fxmr */
+	case AFMOVSPD:		return OPVCC(0,800,0,0);	/* fsmtp */
+	case AFMOVPSD:		return OPVCC(0,928,0,0);	/* fsmfp */
+
+	case AFXCPNPMA:	return OPVCC(4,24,0,0);
+	case AFXCSNPMA:	return OPVCC(4,25,0,0);
+	case AFXCPNSMA:	return OPVCC(4,26,0,0);
+	case AFXCSNSMA:	return OPVCC(4,27,0,0);
+	case AFXCXNPMA:	return OPVCC(4,29,0,0);
+	case AFXCXNSMA:	return OPVCC(4,30,0,0);
+	case AFXCXMA:	return OPVCC(4,28,0,0);
+	case AFXCXNMS:	return OPVCC(4,31,0,0);
+
+	case AICBI:	return OPVCC(31,982,0,0);
+	case AISYNC:	return OPVCC(19,150,0,0);
+
+	case AMTFSB0:	return OPVCC(63,70,0,0);
+	case AMTFSB0CC:	return OPVCC(63,70,0,1);
+	case AMTFSB1:	return OPVCC(63,38,0,0);
+	case AMTFSB1CC:	return OPVCC(63,38,0,1);
+
+	case AMULHW:	return OPVCC(31,75,0,0);
+	case AMULHWCC:	return OPVCC(31,75,0,1);
+	case AMULHWU:	return OPVCC(31,11,0,0);
+	case AMULHWUCC:	return OPVCC(31,11,0,1);
+	case AMULLW:	return OPVCC(31,235,0,0);
+	case AMULLWCC:	return OPVCC(31,235,0,1);
+	case AMULLWV:	return OPVCC(31,235,1,0);
+	case AMULLWVCC:	return OPVCC(31,235,1,1);
+
+	/* the following group is only available on IBM embedded powerpc */
+	case AMACCHW:	return OPVCC(4,172,0,0);
+	case AMACCHWCC:	return OPVCC(4,172,0,1);
+	case AMACCHWS:	return OPVCC(4,236,0,0);
+	case AMACCHWSCC:	return OPVCC(4,236,0,1);
+	case AMACCHWSU:	return OPVCC(4,204,0,0);
+	case AMACCHWSUCC:	return OPVCC(4,204,0,1);
+	case AMACCHWSUV:	return OPVCC(4,204,1,0);
+	case AMACCHWSUVCC:	return OPVCC(4,204,1,1);
+	case AMACCHWSV:	return OPVCC(4,236,1,0);
+	case AMACCHWSVCC:	return OPVCC(4,236,1,1);
+	case AMACCHWU:	return OPVCC(4,140,0,0);
+	case AMACCHWUCC:	return OPVCC(4,140,0,1);
+	case AMACCHWUV:	return OPVCC(4,140,1,0);
+	case AMACCHWUVCC:	return OPVCC(4,140,1,1);
+	case AMACCHWV:	return OPVCC(4,172,1,0);
+	case AMACCHWVCC:	return OPVCC(4,172,1,1);
+	case AMACHHW:	return OPVCC(4,44,0,0);
+	case AMACHHWCC:	return OPVCC(4,44,0,1);
+	case AMACHHWS:	return OPVCC(4,108,0,0);
+	case AMACHHWSCC:	return OPVCC(4,108,0,1);
+	case AMACHHWSU:	return OPVCC(4,76,0,0);
+	case AMACHHWSUCC:	return OPVCC(4,76,0,1);
+	case AMACHHWSUV:	return OPVCC(4,76,1,0);
+	case AMACHHWSUVCC:	return OPVCC(4,76,1,1);
+	case AMACHHWSV:	return OPVCC(4,108,1,0);
+	case AMACHHWSVCC:	return OPVCC(4,108,1,1);
+	case AMACHHWU:	return OPVCC(4,12,0,0);
+	case AMACHHWUCC:	return OPVCC(4,12,0,1);
+	case AMACHHWUV:	return OPVCC(4,12,1,0);
+	case AMACHHWUVCC:	return OPVCC(4,12,1,1);
+	case AMACHHWV:	return OPVCC(4,44,1,0);
+	case AMACHHWVCC:	return OPVCC(4,44,1,1);
+	case AMACLHW:	return OPVCC(4,428,0,0);
+	case AMACLHWCC:	return OPVCC(4,428,0,1);
+	case AMACLHWS:	return OPVCC(4,492,0,0);
+	case AMACLHWSCC:	return OPVCC(4,492,0,1);
+	case AMACLHWSU:	return OPVCC(4,460,0,0);
+	case AMACLHWSUCC:	return OPVCC(4,460,0,1);
+	case AMACLHWSUV:	return OPVCC(4,460,1,0);
+	case AMACLHWSUVCC:	return OPVCC(4,460,1,1);
+	case AMACLHWSV:	return OPVCC(4,492,1,0);
+	case AMACLHWSVCC:	return OPVCC(4,492,1,1);
+	case AMACLHWU:	return OPVCC(4,396,0,0);
+	case AMACLHWUCC:	return OPVCC(4,396,0,1);
+	case AMACLHWUV:	return OPVCC(4,396,1,0);
+	case AMACLHWUVCC:	return OPVCC(4,396,1,1);
+	case AMACLHWV:	return OPVCC(4,428,1,0);
+	case AMACLHWVCC:	return OPVCC(4,428,1,1);
+	case AMULCHW:	return OPVCC(4,168,0,0);
+	case AMULCHWCC:	return OPVCC(4,168,0,1);
+	case AMULCHWU:	return OPVCC(4,136,0,0);
+	case AMULCHWUCC:	return OPVCC(4,136,0,1);
+	case AMULHHW:	return OPVCC(4,40,0,0);
+	case AMULHHWCC:	return OPVCC(4,40,0,1);
+	case AMULHHWU:	return OPVCC(4,8,0,0);
+	case AMULHHWUCC:	return OPVCC(4,8,0,1);
+	case AMULLHW:	return OPVCC(4,424,0,0);
+	case AMULLHWCC:	return OPVCC(4,424,0,1);
+	case AMULLHWU:	return OPVCC(4,392,0,0);
+	case AMULLHWUCC:	return OPVCC(4,392,0,1);
+	case ANMACCHW:	return OPVCC(4,174,0,0);
+	case ANMACCHWCC:	return OPVCC(4,174,0,1);
+	case ANMACCHWS:	return OPVCC(4,238,0,0);
+	case ANMACCHWSCC:	return OPVCC(4,238,0,1);
+	case ANMACCHWSV:	return OPVCC(4,238,1,0);
+	case ANMACCHWSVCC:	return OPVCC(4,238,1,1);
+	case ANMACCHWV:	return OPVCC(4,174,1,0);
+	case ANMACCHWVCC:	return OPVCC(4,174,1,1);
+	case ANMACHHW:	return OPVCC(4,46,0,0);
+	case ANMACHHWCC:	return OPVCC(4,46,0,1);
+	case ANMACHHWS:	return OPVCC(4,110,0,0);
+	case ANMACHHWSCC:	return OPVCC(4,110,0,1);
+	case ANMACHHWSV:	return OPVCC(4,110,1,0);
+	case ANMACHHWSVCC:	return OPVCC(4,110,1,1);
+	case ANMACHHWV:	return OPVCC(4,46,1,0);
+	case ANMACHHWVCC:	return OPVCC(4,46,1,1);
+	case ANMACLHW:	return OPVCC(4,430,0,0);
+	case ANMACLHWCC:	return OPVCC(4,430,0,1);
+	case ANMACLHWS:	return OPVCC(4,494,0,0);
+	case ANMACLHWSCC:	return OPVCC(4,494,0,1);
+	case ANMACLHWSV:	return OPVCC(4,494,1,0);
+	case ANMACLHWSVCC:	return OPVCC(4,494,1,1);
+	case ANMACLHWV:	return OPVCC(4,430,1,0);
+	case ANMACLHWVCC:	return OPVCC(4,430,1,1);
+
+	case ANAND:	return OPVCC(31,476,0,0);
+	case ANANDCC:	return OPVCC(31,476,0,1);
+	case ANEG:	return OPVCC(31,104,0,0);
+	case ANEGCC:	return OPVCC(31,104,0,1);
+	case ANEGV:	return OPVCC(31,104,1,0);
+	case ANEGVCC:	return OPVCC(31,104,1,1);
+	case ANOR:	return OPVCC(31,124,0,0);
+	case ANORCC:	return OPVCC(31,124,0,1);
+	case AOR:	return OPVCC(31,444,0,0);
+	case AORCC:	return OPVCC(31,444,0,1);
+	case AORN:	return OPVCC(31,412,0,0);
+	case AORNCC:	return OPVCC(31,412,0,1);
+
+	case ARFI:	return OPVCC(19,50,0,0);
+	case ARFCI:	return OPVCC(19,51,0,0);
+
+	case ARLWMI:	return OPVCC(20,0,0,0);
+	case ARLWMICC: return OPVCC(20,0,0,1);
+	case ARLWNM:	return OPVCC(23,0,0,0);
+	case ARLWNMCC:	return OPVCC(23,0,0,1);
+
+	case ASYSCALL:	return OPVCC(17,1,0,0);
+
+	case ASLW:	return OPVCC(31,24,0,0);
+	case ASLWCC:	return OPVCC(31,24,0,1);
+
+	case ASRAW:	return OPVCC(31,792,0,0);
+	case ASRAWCC:	return OPVCC(31,792,0,1);
+
+	case ASRW:	return OPVCC(31,536,0,0);
+	case ASRWCC:	return OPVCC(31,536,0,1);
+
+	case ASUB:	return OPVCC(31,40,0,0);
+	case ASUBCC:	return OPVCC(31,40,0,1);
+	case ASUBV:	return OPVCC(31,40,1,0);
+	case ASUBVCC:	return OPVCC(31,40,1,1);
+	case ASUBC:	return OPVCC(31,8,0,0);
+	case ASUBCCC:	return OPVCC(31,8,0,1);
+	case ASUBCV:	return OPVCC(31,8,1,0);
+	case ASUBCVCC:	return OPVCC(31,8,1,1);
+	case ASUBE:	return OPVCC(31,136,0,0);
+	case ASUBECC:	return OPVCC(31,136,0,1);
+	case ASUBEV:	return OPVCC(31,136,1,0);
+	case ASUBEVCC:	return OPVCC(31,136,1,1);
+	case ASUBME:	return OPVCC(31,232,0,0);
+	case ASUBMECC:	return OPVCC(31,232,0,1);
+	case ASUBMEV:	return OPVCC(31,232,1,0);
+	case ASUBMEVCC:	return OPVCC(31,232,1,1);
+	case ASUBZE:	return OPVCC(31,200,0,0);
+	case ASUBZECC:	return OPVCC(31,200,0,1);
+	case ASUBZEV:	return OPVCC(31,200,1,0);
+	case ASUBZEVCC:	return OPVCC(31,200,1,1);
+
+	case ASYNC:	return OPVCC(31,598,0,0);
+	case ATLBIE:	return OPVCC(31,306,0,0);
+	case ATW:	return OPVCC(31,4,0,0);
+
+	case AXOR:	return OPVCC(31,316,0,0);
+	case AXORCC:	return OPVCC(31,316,0,1);
+	}
+	diag("bad r/r opcode %A", a);
+	return 0;
+}
+
+long
+opirr(int a)
+{
+	switch(a) {
+	case AADD:	return OPVCC(14,0,0,0);
+	case AADDC:	return OPVCC(12,0,0,0);
+	case AADDCCC:	return OPVCC(13,0,0,0);
+	case AADD+AEND:	return OPVCC(15,0,0,0);		/* ADDIS/CAU */
+
+	case AANDCC:	return OPVCC(28,0,0,0);
+	case AANDCC+AEND:	return OPVCC(29,0,0,0);		/* ANDIS./ANDIU. */
+
+	case ABR:	return OPVCC(18,0,0,0);
+	case ABL:	return OPVCC(18,0,0,0) | 1;
+	case ABC:	return OPVCC(16,0,0,0);
+	case ABCL:	return OPVCC(16,0,0,0) | 1;
+
+	case ABEQ:	return AOP_RRR(16<<26,12,2,0);
+	case ABGE:	return AOP_RRR(16<<26,4,0,0);
+	case ABGT:	return AOP_RRR(16<<26,12,1,0);
+	case ABLE:	return AOP_RRR(16<<26,4,1,0);
+	case ABLT:	return AOP_RRR(16<<26,12,0,0);
+	case ABNE:	return AOP_RRR(16<<26,4,2,0);
+	case ABVC:	return AOP_RRR(16<<26,4,3,0);
+	case ABVS:	return AOP_RRR(16<<26,12,3,0);
+
+	case ACMP:	return OPVCC(11,0,0,0);
+	case ACMPU:	return OPVCC(10,0,0,0);
+	case ALSW:	return OPVCC(31,597,0,0);
+
+	case AMULLW:	return OPVCC(7,0,0,0);
+
+	case AOR:	return OPVCC(24,0,0,0);
+	case AOR+AEND:	return OPVCC(25,0,0,0);		/* ORIS/ORIU */
+
+	case ARLWMI:	return OPVCC(20,0,0,0);		/* rlwimi */
+	case ARLWMICC:	return OPVCC(20,0,0,1);
+
+	case ARLWNM:	return OPVCC(21,0,0,0);		/* rlwinm */
+	case ARLWNMCC:	return OPVCC(21,0,0,1);
+
+	case ASRAW:	return OPVCC(31,824,0,0);
+	case ASRAWCC:	return OPVCC(31,824,0,1);
+
+	case ASTSW:	return OPVCC(31,725,0,0);
+
+	case ASUBC:	return OPVCC(8,0,0,0);
+
+	case ATW:	return OPVCC(3,0,0,0);
+
+	case AXOR:	return OPVCC(26,0,0,0);		/* XORIL */
+	case AXOR+AEND:	return OPVCC(27,0,0,0);		/* XORIU */
+	}
+	diag("bad opcode i/r %A", a);
+	return 0;
+}
+
+/*
+ * load o(a),d
+ */
+long
+opload(int a)
+{
+	switch(a) {
+	case AMOVW:	return OPVCC(32,0,0,0);		/* lwz */
+	case AMOVWU:	return OPVCC(33,0,0,0);		/* lwzu */
+	case AMOVB:
+	case AMOVBZ:	return OPVCC(34,0,0,0);		/* load */
+	case AMOVBU:
+	case AMOVBZU:	return OPVCC(35,0,0,0);
+	case AFMOVD:	return OPVCC(50,0,0,0);
+	case AFMOVDU:	return OPVCC(51,0,0,0);
+	case AFMOVS:	return OPVCC(48,0,0,0);
+	case AFMOVSU:	return OPVCC(49,0,0,0);
+	case AMOVH:	return OPVCC(42,0,0,0);
+	case AMOVHU:	return OPVCC(43,0,0,0);
+	case AMOVHZ:	return OPVCC(40,0,0,0);
+	case AMOVHZU:	return OPVCC(41,0,0,0);
+	case AMOVMW:	return OPVCC(46,0,0,0);	/* lmw */
+	}
+	diag("bad load opcode %A", a);
+	return 0;
+}
+
+/*
+ * indexed load a(b),d
+ */
+long
+oploadx(int a)
+{
+	switch(a) {
+	case AMOVW: return OPVCC(31,23,0,0);	/* lwzx */
+	case AMOVWU:	return OPVCC(31,55,0,0); /* lwzux */
+	case AMOVB:
+	case AMOVBZ: return OPVCC(31,87,0,0);	/* lbzx */
+	case AMOVBU:
+	case AMOVBZU: return OPVCC(31,119,0,0);	/* lbzux */
+	case AFMOVD:	return OPVCC(31,599,0,0);	/* lfdx */
+	case AFMOVDU:	return OPVCC(31,631,0,0);	/*  lfdux */
+	case AFMOVS:	return OPVCC(31,535,0,0);	/* lfsx */
+	case AFMOVSU:	return OPVCC(31,567,0,0);	/* lfsux */
+	case AMOVH:	return OPVCC(31,343,0,0);	/* lhax */
+	case AMOVHU:	return OPVCC(31,375,0,0);	/* lhaux */
+	case AMOVHBR:	return OPVCC(31,790,0,0);	/* lhbrx */
+	case AMOVWBR:	return OPVCC(31,534,0,0);	/* lwbrx */
+	case AMOVHZ:	return OPVCC(31,279,0,0);	/* lhzx */
+	case AMOVHZU:	return OPVCC(31,311,0,0);	/* lhzux */
+	case AECIWX:	return OPVCC(31,310,0,0);	/* eciwx */
+	case ALWAR:	return OPVCC(31,20,0,0);	/* lwarx */
+	case ALSW:	return OPVCC(31,533,0,0);	/* lswx */
+	case AFSMOVS:	return OPVCC(31,142,0,0);	/* lfssx */
+	case AFSMOVSU:	return OPVCC(31,174,0,0);	/* lfssux */
+	case AFSMOVD:	return OPVCC(31,206,0,0);	/* lfsdx */
+	case AFSMOVDU:	return OPVCC(31,238,0,0);	/* lfsdux */
+	case AFXMOVS:	return OPVCC(31,270,0,0);	/* lfxsx */
+	case AFXMOVSU:	return OPVCC(31,302,0,0);	/* lfxsux */
+	case AFXMOVD:	return OPVCC(31,334,0,0);	/* lfxdx */
+	case AFXMOVDU:	return OPVCC(31,366,0,0);	/* lfxdux */
+	case AFPMOVS:	return OPVCC(31,398,0,0);	/* lfpsx */
+	case AFPMOVSU:	return OPVCC(31,430,0,0);	/* lfpsux */
+	case AFPMOVD:	return OPVCC(31,462,0,0);	/* lfpdx */
+	case AFPMOVDU:	return OPVCC(31,494,0,0);	/* lfpdux */
+	}
+	diag("bad loadx opcode %A", a);
+	return 0;
+}
+
+/*
+ * store s,o(d)
+ */
+long
+opstore(int a)
+{
+	switch(a) {
+	case AMOVB:
+	case AMOVBZ:	return OPVCC(38,0,0,0);	/* stb */
+	case AMOVBU:
+	case AMOVBZU:	return OPVCC(39,0,0,0);	/* stbu */
+	case AFMOVD:	return OPVCC(54,0,0,0);	/* stfd */
+	case AFMOVDU:	return OPVCC(55,0,0,0);	/* stfdu */
+	case AFMOVS:	return OPVCC(52,0,0,0);	/* stfs */
+	case AFMOVSU:	return OPVCC(53,0,0,0);	/* stfsu */
+	case AMOVHZ:
+	case AMOVH:	return OPVCC(44,0,0,0);	/* sth */
+	case AMOVHZU:
+	case AMOVHU:	return OPVCC(45,0,0,0);	/* sthu */
+	case AMOVMW:	return OPVCC(47,0,0,0);	/* stmw */
+	case ASTSW:	return OPVCC(31,725,0,0);	/* stswi */
+	case AMOVW:	return OPVCC(36,0,0,0);	/* stw */
+	case AMOVWU:	return OPVCC(37,0,0,0);	/* stwu */
+	}
+	diag("unknown store opcode %A", a);
+	return 0;
+}
+
+/*
+ * indexed store s,a(b)
+ */
+long
+opstorex(int a)
+{
+	switch(a) {
+	case AMOVB:
+	case AMOVBZ:	return OPVCC(31,215,0,0);	/* stbx */
+	case AMOVBU:
+	case AMOVBZU:	return OPVCC(31,247,0,0);	/* stbux */
+	case AFMOVD:	return OPVCC(31,727,0,0);	/* stfdx */
+	case AFMOVDU:	return OPVCC(31,759,0,0);	/* stfdux */
+	case AFMOVS:	return OPVCC(31,663,0,0);	/* stfsx */
+	case AFMOVSU:	return OPVCC(31,695,0,0);	/* stfsux */
+	case AMOVHZ:
+	case AMOVH:	return OPVCC(31,407,0,0);	/* sthx */
+	case AMOVHBR:	return OPVCC(31,918,0,0);	/* sthbrx */
+	case AMOVHZU:
+	case AMOVHU:	return OPVCC(31,439,0,0);	/* sthux */
+	case AMOVW:	return OPVCC(31,151,0,0);	/* stwx */
+	case AMOVWU:	return OPVCC(31,183,0,0);	/* stwux */
+	case ASTSW:	return OPVCC(31,661,0,0);	/* stswx */
+	case AMOVWBR:	return OPVCC(31,662,0,0);	/* stwbrx */
+	case ASTWCCC:	return OPVCC(31,150,0,1);	/* stwcx. */
+	case AECOWX:	return OPVCC(31,438,0,0);	/* ecowx */
+	case AFSMOVS:	return OPVCC(31,654,0,0);	/* stfssx */
+/*	case AFSMOVSU:	return OPVCC(31,yy,0,0); */	/* stfssux not known */
+/*	case AFSMOVD:	return OPVCC(31,yy,0,0); */ /* stfsdx not known */
+	case AFSMOVDU:	return OPVCC(31,750,0,0);	/* stfsdux */
+	case AFXMOVS:	return OPVCC(31,782,0,0);	/* stfxsx */
+	case AFXMOVSU:	return OPVCC(31,814,0,0);	/* stfxsux */
+	case AFXMOVD:	return OPVCC(31,846,0,0);	/* stfxdx */
+	case AFXMOVDU:	return OPVCC(31,878,0,0);	/* stfxdux */
+	case AFPMOVS:	return OPVCC(31,910,0,0);	/* stfpsx */
+	case AFPMOVSU:	return OPVCC(31,942,0,0);	/* stfpsux */
+	case AFPMOVD:	return OPVCC(31,974,0,0);	/* stfpdx */
+	case AFPMOVDU:	return OPVCC(31,1006,0,0);	/* stfpdux */
+	case AFPMOVIW:	return OPVCC(31,526,0,0);	/* stfpiwx */
+	}
+	diag("unknown storex opcode %A", a);
+	return 0;
+}
--- /dev/null
+++ b/utils/ql/cnam.c
@@ -1,0 +1,37 @@
+char	*cnames[] =
+{
+	"NONE",
+	"REG",
+	"FREG",
+	"CREG",
+	"SPR",
+	"SREG",
+	"ZCON",
+	"SCON",
+	"UCON",
+	"ADDCON",
+	"ANDCON",
+	"LCON",
+	"SACON",
+	"SECON",
+	"LACON",
+	"LECON",
+	"SBRA",
+	"LBRA",
+	"SAUTO",
+	"LAUTO",
+	"SEXT",
+	"LEXT",
+	"ZOREG",
+	"SOREG",
+	"LOREG",
+	"FPSCR",
+	"MSR",
+	"XER",
+	"LR",
+	"CTR",
+	"ANY",
+	"GOK",
+	"ADDR",
+	"NCLASS",
+};
--- /dev/null
+++ b/utils/ql/l.h
@@ -1,0 +1,351 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../qc/q.out.h"
+#include	"../ld/elf.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	LIBNAMELEN	300
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char	u0sval[NSNAME];
+		Ieee	u0ieee;
+	}u0;
+	Sym	*sym;
+	Auto	*autom;
+	char	type;
+	uchar	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+struct	Prog
+{
+	Adr	from;
+	Adr	from3;	/* fma and rlwm */
+	Adr	to;
+	Prog	*forwd;
+	Prog	*cond;
+	Prog	*link;
+	long	pc;
+	long	regused;
+	short	line;
+	short	mark;
+	short	optab;		/* could be uchar */
+	ushort	as;
+	char	reg;
+};
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	uchar	subtype;
+	ushort	file;
+	long	value;
+	long	sig;
+	Sym	*link;
+};
+struct	Autom
+{
+	Sym	*sym;
+	Auto	*link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	ushort	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	a4;
+	char	type;
+	char	size;
+	char	param;
+};
+struct
+{
+	Optab*	start;
+	Optab*	stop;
+} oprange[ALAST];
+
+enum
+{
+	FPCHIP		= 1,
+	BIG		= 32768-8,
+	STRINGSZ	= 200,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+	DATBLK		= 1024,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	NSCHED		= 20,
+
+/* mark flags */
+	LABEL		= 1<<0,
+	LEAF		= 1<<1,
+	FLOAT		= 1<<2,
+	BRANCH		= 1<<3,
+	LOAD		= 1<<4,
+	FCMP		= 1<<5,
+	SYNC		= 1<<6,
+	LIST		= 1<<7,
+	FOLL		= 1<<8,
+	NOSCHED		= 1<<9,
+
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+	SUNDEF,
+
+	SIMPORT,
+	SEXPORT,
+
+	C_NONE		= 0,
+	C_REG,
+	C_FREG,
+	C_CREG,
+	C_SPR,		/* special processor register */
+	C_SREG,		/* segment register (32 bit implementations only) */
+	C_ZCON,
+	C_SCON,		/* 16 bit signed */
+	C_UCON,		/* low 16 bits 0 */
+	C_ADDCON,	/* -0x8000 <= v < 0 */
+	C_ANDCON,	/* 0 < v <= 0xFFFF */
+	C_LCON,		/* other */
+	C_SACON,
+	C_SECON,
+	C_LACON,
+	C_LECON,
+	C_SBRA,
+	C_LBRA,
+	C_SAUTO,
+	C_LAUTO,
+	C_SEXT,
+	C_LEXT,
+	C_ZOREG,
+	C_SOREG,
+	C_LOREG,
+	C_FPSCR,
+	C_MSR,
+	C_XER,
+	C_LR,
+	C_CTR,
+	C_ANY,
+	C_GOK,
+	C_ADDR,
+
+	C_NCLASS,
+
+	Roffset	= 22,		/* no. bits for offset in relocation address */
+	Rindex	= 10		/* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	long	INITTEXTP;		/* text location (physical) */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	Prog*	prog_movsw;
+EXTERN	Prog*	prog_movdw;
+EXTERN	Prog*	prog_movws;
+EXTERN	Prog*	prog_movwd;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi8[8];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	char*	noname;
+EXTERN	long	instoffset;
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	int	r0iszero;
+EXTERN	long	symsize;
+EXTERN	long	staticgen;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	tothunk;
+EXTERN	char	xcmp[C_NCLASS][C_NCLASS];
+EXTERN	int	version;
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+
+EXTERN	int	doexp, dlm;
+EXTERN	int	imports, nimports;
+EXTERN	int	exports, nexports;
+EXTERN	char*	EXPTAB;
+EXTERN	Prog	undefp;
+
+#define	UP	(&undefp)
+
+extern	Optab	optab[];
+extern	char*	anames[];
+extern	char*	cnames[];
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	Rconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	histtoauto(void);
+void	addlibpath(char*);
+void	addnop(Prog*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmdyn(void);
+void	asmlc(void);
+int	asmout(Prog*, Optab*, int);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	cflush(void);
+void	ckoff(Sym*, long);
+int	cmp(int, int);
+void	cput(long);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long);
+void	diag(char*, ...);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+void	dynreloc(Sym*, long, int, int, int);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+void	export(void);
+int	fileexists(char*);
+int	find1(long, int);
+char*	findlib(char*);
+void	follow(void);
+void	gethunk(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+void	import(void);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+void	initmuldiv(void);
+Sym*	lookup(char*, int);
+void	llput(vlong);
+void	llputl(vlong);
+void	lput(long);
+void	lputl(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	noops(void);
+void	nopout(Prog*);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opcode(int);
+Optab*	oplook(Prog*);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+void	readundefs(char*, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	sched(Prog*, Prog*);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	undefsym(Sym*);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+void	zerosig(char*);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
--- /dev/null
+++ b/utils/ql/list.c
@@ -1,0 +1,314 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('R', Rconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	if(a == ADATA || a == AINIT || a == ADYNT)
+		sprint(str, "(%d)	%A	%D/%d,%D", p->line, a, &p->from, p->reg, &p->to);
+	else {
+		s = str;
+		if(p->mark & NOSCHED)
+			s += sprint(s, "*");
+		if(p->reg == NREG && p->from3.type == D_NONE)
+			sprint(s, "(%d)	%A	%D,%D", p->line, a, &p->from, &p->to);
+		else
+		if(a != ATEXT && p->from.type == D_OREG) {
+			sprint(s, "(%d)	%A	%ld(R%d+R%d),%D", p->line, a,
+				p->from.offset, p->from.reg, p->reg, &p->to);
+		} else
+		if(p->to.type == D_OREG) {
+			sprint(s, "(%d)	%A	%D,%ld(R%d+R%d)", p->line, a,
+					&p->from, p->to.offset, p->to.reg, p->reg);
+		} else {
+			s += sprint(s, "(%d)	%A	%D", p->line, a, &p->from);
+			if(p->reg != NREG)
+				s += sprint(s, ",%c%d", p->from.type==D_FREG?'F':'R', p->reg);
+			if(p->from3.type != D_NONE)
+				s += sprint(s, ",%D", &p->from3);
+			sprint(s, ",%D", &p->to);
+		}
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(F%d)(REG)", a, a->reg);
+		break;
+
+	case D_CREG:
+		if(a->reg == NREG)
+			strcpy(str, "CR");
+		else
+			sprint(str, "CR%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(C%d)(REG)", a, a->reg);
+		break;
+
+	case D_SPR:
+		if(a->name == D_NONE && a->sym == S) {
+			switch(a->offset) {
+			case D_XER: sprint(str, "XER"); break;
+			case D_LR: sprint(str, "LR"); break;
+			case D_CTR: sprint(str, "CTR"); break;
+			default: sprint(str, "SPR(%ld)", a->offset); break;
+			}
+			break;
+		}
+		sprint(str, "SPR-GOK(%d)", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(SPR-GOK%d)(REG)", a, a->reg);
+		break;
+
+	case D_DCR:
+		if(a->name == D_NONE && a->sym == S) {
+			if(a->reg == NREG)
+				sprint(str, "DCR(%ld)", a->offset);
+			else
+				sprint(str, "DCR(R%d)", a->reg);
+			break;
+		}
+		sprint(str, "DCR-GOK(%d)", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(DCR-GOK%d)(REG)", a, a->reg);
+		break;
+
+	case D_OPT:
+		sprint(str, "OPT(%d)", a->reg);
+		break;
+
+	case D_FPSCR:
+		if(a->reg == NREG)
+			strcpy(str, "FPSCR");
+		else
+			sprint(str, "FPSCR(%d)", a->reg);
+		break;
+
+	case D_MSR:
+		sprint(str, "MSR");
+		break;
+
+	case D_SREG:
+		sprint(str, "SREG(%d)", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(SREG%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(v >= INITTEXT)
+				v -= INITTEXT-HEADR;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%lux-%lux", a->ieee.h, a->ieee.l);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "C_??";
+	if(a >= C_NONE && a <= C_NCLASS)
+		s = cnames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/ql/mkcname
@@ -1,0 +1,17 @@
+ed - ../ql/l.h <<'!'
+v/^	C_/d
+g/^	C_NCLASS/s//&,/
+g/[ 	]*=.*,/s//,/
+v/,/p
+,s/^	C_/	"/
+,s/,.*$/",/
+1i
+char	*cnames[] =
+{
+.
+,a
+};
+.
+w cnam.c
+Q
+!
--- /dev/null
+++ b/utils/ql/mkfile
@@ -1,0 +1,42 @@
+<../../mkconfig
+
+TARG=ql
+
+OFILES=\
+	asm.$O\
+	asmout.$O\
+	list.$O\
+	noop.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	cnam.$O\
+	sched.$O\
+	$TARGMODEL.$O\
+	elf.$O\
+
+HFILES=\
+	l.h\
+	../qc/q.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include -I.
+
+enam.$O:	../qc/enam.c
+	$CC $CFLAGS ../qc/enam.c
+
+elf.$O:	../ld/elf.c
+	$CC $CFLAGS ../ld/elf.c
+#cnam.c:	l.h
+#	rc mkcname
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/ql/noop.c
@@ -1,0 +1,513 @@
+#include	"l.h"
+
+void
+noops(void)
+{
+	Prog *p, *p1, *q, *q1;
+	int o, mov, aoffset, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		/* too hard, just leave alone */
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			q = p;
+			p->mark |= LABEL|LEAF|SYNC;
+			if(p->link)
+				p->link->mark |= LABEL;
+			curtext = p;
+			break;
+
+		case ANOR:
+			q = p;
+			if(p->to.type == D_REG)
+				if(p->to.reg == REGZERO)
+					p->mark |= LABEL|SYNC;
+			break;
+
+		case ALWAR:
+		case ASTWCCC:
+		case AECIWX:
+		case AECOWX:
+		case AEIEIO:
+		case AICBI:
+		case AISYNC:
+		case ATLBIE:
+		case ADCBF:
+		case ADCBI:
+		case ADCBST:
+		case ADCBT:
+		case ADCBTST:
+		case ADCBZ:
+		case ASYNC:
+		case ATW:
+		case AWORD:
+		case ARFI:
+		case ARFCI:
+			q = p;
+			p->mark |= LABEL|SYNC;
+			continue;
+
+		case AMOVW:
+			q = p;
+			switch(p->from.type) {
+			case D_MSR:
+			case D_SREG:
+			case D_SPR:
+			case D_FPSCR:
+			case D_CREG:
+			case D_DCR:
+				p->mark |= LABEL|SYNC;
+			}
+			switch(p->to.type) {
+			case D_MSR:
+			case D_SREG:
+			case D_SPR:
+			case D_FPSCR:
+			case D_CREG:
+			case D_DCR:
+				p->mark |= LABEL|SYNC;
+			}
+			continue;
+
+		case AFABS:
+		case AFABSCC:
+		case AFADD:
+		case AFADDCC:
+		case AFCTIW:
+		case AFCTIWCC:
+		case AFCTIWZ:
+		case AFCTIWZCC:
+		case AFDIV:
+		case AFDIVCC:
+		case AFMADD:
+		case AFMADDCC:
+		case AFMOVD:
+		case AFMOVDU:
+		/* case AFMOVDS: */
+		case AFMOVS:
+		case AFMOVSU:
+		/* case AFMOVSD: */
+		case AFMSUB:
+		case AFMSUBCC:
+		case AFMUL:
+		case AFMULCC:
+		case AFNABS:
+		case AFNABSCC:
+		case AFNEG:
+		case AFNEGCC:
+		case AFNMADD:
+		case AFNMADDCC:
+		case AFNMSUB:
+		case AFNMSUBCC:
+		case AFRSP:
+		case AFRSPCC:
+		case AFSUB:
+		case AFSUBCC:
+			q = p;
+			p->mark |= FLOAT;
+			continue;
+
+		case ABL:
+		case ABCL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case ABC:
+		case ABEQ:
+		case ABGE:
+		case ABGT:
+		case ABLE:
+		case ABLT:
+		case ABNE:
+		case ABR:
+		case ABVC:
+		case ABVS:
+
+			p->mark |= BRANCH;
+			q = p;
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+				if(!(q1->mark & LEAF))
+					q1->mark |= LABEL;
+			} else
+				p->mark |= LABEL;
+			q1 = p->link;
+			if(q1 != P)
+				q1->mark |= LABEL;
+			continue;
+
+		case AFCMPO:
+		case AFCMPU:
+			q = p;
+			p->mark |= FCMP|FLOAT;
+			continue;
+
+		case ARETURN:
+			/* special form of RETURN is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+
+			q = p;
+			if(p->link != P)
+				p->link->mark |= LABEL;
+			continue;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		default:
+			q = p;
+			continue;
+		}
+	}
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+
+		case ABL:	/* ABCL? */
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	curtext = P;
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			mov = AMOVW;
+			aoffset = 0;
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if((p->mark & LEAF) && autosize <= 4)
+				autosize = 0;
+			else
+				if(autosize & 4)
+					autosize += 4;
+			p->to.offset = autosize - 4;
+
+			q = p;
+			if(autosize) {
+				/* use MOVWU to adjust R1 when saving R31, if autosize is small */
+				if(!(curtext->mark & LEAF) && autosize >= -BIG && autosize <= BIG) {
+					mov = AMOVWU;
+					aoffset = -autosize;
+				} else {
+					q = prg();
+					q->as = AADD;
+					q->line = p->line;
+					q->from.type = D_CONST;
+					q->from.offset = -autosize;
+					q->to.type = D_REG;
+					q->to.reg = REGSP;
+
+					q->link = p->link;
+					p->link = q;
+				}
+			} else
+			if(!(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+				break;
+			}
+
+			q1 = prg();
+			q1->as = mov;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGTMP;
+			q1->to.type = D_OREG;
+			q1->to.offset = aoffset;
+			q1->to.reg = REGSP;
+
+			q1->link = q->link;
+			q->link = q1;
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->line = p->line;
+			q1->from.type = D_SPR;
+			q1->from.offset = D_LR;
+			q1->to.type = D_REG;
+			q1->to.reg = REGTMP;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		case ARETURN:
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = ABR;
+					p->from = zprg.from;
+					p->to.type = D_SPR;
+					p->to.offset = D_LR;
+					p->mark |= BRANCH;
+					break;
+				}
+
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = ABR;
+				q->line = p->line;
+				q->to.type = D_SPR;
+				q->to.offset = D_LR;
+				q->mark |= BRANCH;
+
+				q->link = p->link;
+				p->link = q;
+				break;
+			}
+
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->as = AMOVW;
+			q->line = p->line;
+			q->from.type = D_REG;
+			q->from.reg = REGTMP;
+			q->to.type = D_SPR;
+			q->to.offset = D_LR;
+
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			if(autosize) {
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			}
+
+			q1 = prg();
+			q1->as = ABR;
+			q1->line = p->line;
+			q1->to.type = D_SPR;
+			q1->to.offset = D_LR;
+			q1->mark |= BRANCH;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				q = prg();
+				q->line = p->line;
+				q->as = ABR;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				q->mark |= BRANCH;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+			}
+			q = prg();
+			q->line = p->line;
+			q->as = ABR;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->mark |= BRANCH;
+			q->link = p->link;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AADD;
+			q->from.type = D_CONST;
+			q->from.offset = autosize;
+			q->to.type = D_REG;
+			q->to.reg = REGSP;
+			q->link = p->link;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AMOVW;
+			q->line = p->line;
+			q->from.type = D_REG;
+			q->from.reg = REGTMP;
+			q->to.type = D_SPR;
+			q->to.offset = D_LR;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVW;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			break;
+		}
+	}
+
+	if(debug['Q'] == 0)
+		return;
+
+	curtext = P;
+	q = P;		/* p - 1 */
+	q1 = firstp;	/* top of block */
+	o = 0;		/* count of instructions */
+	for(p = firstp; p != P; p = p1) {
+		p1 = p->link;
+		o++;
+		if(p->mark & NOSCHED){
+			if(q1 != p){
+				sched(q1, q);
+			}
+			for(; p != P; p = p->link){
+				if(!(p->mark & NOSCHED))
+					break;
+				q = p;
+			}
+			p1 = p;
+			q1 = p;
+			o = 0;
+			continue;
+		}
+		if(p->mark & (LABEL|SYNC)) {
+			if(q1 != p)
+				sched(q1, q);
+			q1 = p;
+			o = 1;
+		}
+		if(p->mark & (BRANCH|SYNC)) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		if(o >= NSCHED) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		q = p;
+	}
+}
+
+void
+addnop(Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = AOR;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGZERO;
+	q->to.type = D_REG;
+	q->to.reg = REGZERO;
+
+	q->link = p->link;
+	p->link = q;
+}
--- /dev/null
+++ b/utils/ql/obj.c
@@ -1,0 +1,1593 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+#define	OANAME	229	/* old ANAME */
+
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= 'q';
+char	*thestring 	= "power";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H0 -T0x200000 -R0		is boot
+ *	-H1 -T0x100000 -R4		is Be boot
+ *	-H2 -T0x100020 -R0x100000	is plan9 format (was -T4128 -R4096)
+ *	-H3 -T0x02010000 -D0x00001000	is raw
+ *	-H4 -T0x1000200 -D0x20000e00 -R4	is aix xcoff executable
+ *	-H5 -T0x80010000 -t0x10000	ELF, phys = 10000, vaddr = 0x8001...
+ *					appropriate for blue gene (bg/l anyway)
+ *	-H6 -T0xfffe2100 -R4		ELF, phys = vaddr = 0xfffe2100
+ *					appropriate for virtex 4 boot
+ */
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+static int
+isobjfile(char *f)
+{
+	int n, v;
+	Biobuf *b;
+	char buf1[5], buf2[SARMAG];
+
+	b = Bopen(f, OREAD);
+	if(b == nil)
+		return 0;
+	n = Bread(b, buf1, 5);
+	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+		v = 1;	/* good enough for our purposes */
+	else{
+		Bseek(b, 0, 0);
+		n = Bread(b, buf2, SARMAG);
+		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+	}
+	Bterm(b);
+	return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+	char name[LIBNAMELEN];
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITTEXTP = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	case 'x':	/* produce export table */
+		doexp = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SEXPORT);
+		break;
+	case 'u':	/* produce dynamically loadable module */
+		dlm = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SIMPORT);
+		break;
+	} ARGEND
+	USED(argc);
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	a = getenv("ccroot");
+	if(a != nil && *a != '\0') {
+		if(!fileexists(a)) {
+			diag("nonexistent $ccroot: %s", a);
+			errorexit();
+		}
+	}else
+		a = "";
+	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+	addlibpath(name);
+	r0iszero = debug['0'] == 0;
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+
+	case 0:	/* boot */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x200000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096L;
+		break;
+	case 1:	/* Be boot format (PEF) */
+		HEADR = 208L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x100000;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x100020;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 0x100000;
+		break;
+	case 3:	/* raw */
+		HEADR = 0;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1) {
+			INITDAT = 0;
+			INITRND = 4;
+		}
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 4:	/* aix unix xcoff executable */
+		HEADR = 20L+72L+3*40L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x1000000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x20000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 5:	/* elf executable */
+		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 6:	/* elf for virtex 4 */
+		HEADR = rnd(Ehdr32sz+4*Phdr32sz, 16); /* extra phdr for JMP */
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%x -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.from3 = zprg.from;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0)
+		outfile = "q.out";
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	nuxiinit();
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	if(doexp || dlm){
+		EXPTAB = "_exporttab";
+		zerosig(EXPTAB);
+		zerosig("etext");
+		zerosig("edata");
+		zerosig("end");
+		if(dlm){
+			import();
+			HEADTYPE = 2;
+			INITTEXT = INITDAT = 0;
+			INITRND = 8;
+			INITENTRY = EXPTAB;
+		}
+		export();
+	}
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", tothunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+	int i;
+	char name[LIBNAMELEN];
+
+	for(i = 0; i < nlibdir; i++) {
+		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+		if(fileexists(name))
+			return libdir[i];
+	}
+	return nil;
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	Bflush(&bso);
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[LIBNAMELEN], pname[LIBNAMELEN];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	if(file[0] == '-' && file[1] == 'l') {
+		snprint(pname, sizeof(pname), "lib%s.a", file+2);
+		e = findlib(pname);
+		if(e == nil) {
+			diag("cannot find library: %s", file);
+			errorexit();
+		}
+		snprint(name, sizeof(name), "%s/%s", e, pname);
+		file = name;
+	}
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open %s: %r", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			l = read(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	int l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = AEND+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = AEND+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = AEND+1;
+		return 0;	/* force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_CREG:
+	case D_FPSCR:
+	case D_MSR:
+	case D_SREG:
+	case D_OPT:
+		break;
+
+	case D_SPR:
+	case D_DCR:
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		a->ieee.l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee.h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		goto out;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		goto out;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->sym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			goto out;
+		}
+
+	u = malloc(sizeof(Auto));
+
+	u->link = curauto;
+	curauto = u;
+	u->sym = s;
+	u->aoffset = l;
+	u->type = i;
+out:
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+
+	cleanname(name);
+
+	if(search){
+		p = findlib(name);
+		if(p != nil){
+			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+			name = fn2;
+		}
+	}
+
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->sym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	Prog *p, *t;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+	long ipc;
+	uchar *bloc, *bsize, *stop;
+	ulong sig;
+	static int files;
+	static char **filen;
+	char **nfilen;
+
+	if((files&15) == 0){
+		nfilen = malloc((files+16)*sizeof(char*));
+		memmove(nfilen, filen, files*sizeof(char*));
+		free(filen);
+		filen = nfilen;
+	}
+	filen[files++] = strdup(pn);
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	histfrogp = 0;
+	version++;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0] | (bloc[1] << 8);		/* as */
+	if(bloc[0] == OANAME && o != OANAME) {
+		diag("%s: probably old .q file\n", pn);
+		errorexit();
+	}
+	if(o <= 0 || o >= ALAST) {
+		diag("%s: opcode out of range %d", pn, o);
+		print("	probably not a .%c file\n", thechar);
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		sig = 0;
+		if(o == ASIGNAME) {
+			sig = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[2];	/* type */
+		o = bloc[3];	/* sym */
+		bloc += 4;
+		c -= 4;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+		if(sig != 0){
+			if(s->sig != 0 && s->sig != sig)
+				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+			s->sig = sig;
+			s->file = files-1;
+		}
+
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->reg = bloc[2] & 0x3f;
+	if(bloc[2] & 0x80)
+		p->mark = NOSCHED;
+	p->line = bloc[3] | (bloc[4]<<8) | (bloc[5]<<16) | (bloc[6]<<24);
+	r = zaddr(bloc+7, &p->from, h) + 7;
+	if(bloc[2] & 0x40)
+		r += zaddr(bloc+r, &p->from3, h);
+	else
+		p->from3 = zprg.from3;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg < 0 || p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case ADATA:
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		curtext = p;
+		autosize = (p->to.offset+3L) & ~3L;
+		p->to.offset = autosize;
+		autosize += 4;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		if(textp != P) {
+			for(t = textp; t->cond != P; t = t->cond)
+				;
+			t->cond = p;
+		} else
+			textp = p;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+
+	case AFMOVS:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(&p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AFMOVD:
+		if(skip)
+			goto casedef;
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee.l, p->from.ieee.h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case ASUBC:
+		if(p->from.type == D_CONST) {
+			p->from.offset = -p->from.offset;
+			p->as = AADDC;
+		}
+		goto casedef;
+
+	case ASUBCCC:
+		if(p->from.type == D_CONST) {
+			p->from.offset = -p->from.offset;
+			p->as = AADDCCC;
+		}
+		goto casedef;
+
+	case ASUB:
+		if(p->from.type == D_CONST) {
+			p->from.offset = -p->from.offset;
+			p->as = AADD;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	h &= 0xffffff;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l + 1);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->sig = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+	int n;
+
+	n = (sizeof(Prog) + 3) & ~3;
+	while(nhunk < n)
+		gethunk();
+
+	p = (Prog*)hunk;
+	nhunk -= n;
+	hunk += n;
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(tothunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(tothunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char *)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+
+	hunk = h;
+	nhunk = nh;
+	tothunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				p->reg = 1;
+				ps2 = p;
+			}
+			if(p->from.sym == s4) {
+				p->reg = 1;
+				ps4 = p;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+
+			if(p->reg & NOPROF) {	/* dont profile */
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * BL	profin
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = ABR;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->cond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = ABL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARETURN) {
+			/*
+			 * RETURN (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+			/*
+			 * RETURN
+			 */
+			q = prg();
+			q->as = ARETURN;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * BL profout
+			 */
+			p->as = ABL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x01020304L, i+1);
+		if(i >= 2)
+			inuxi2[i-2] = c;
+		if(i >= 3)
+			inuxi1[i-3] = c;
+		inuxi4[i] = c;
+
+		fnuxi8[i] = c+4;
+		fnuxi8[i+4] = c;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
+
+void
+undefsym(Sym *s)
+{
+	int n;
+
+	n = imports;
+	if(s->value != 0)
+		diag("value != 0 on SXREF");
+	if(n >= 1<<Rindex)
+		diag("import index %d out of range", n);
+	s->value = n<<Roffset;
+	s->type = SUNDEF;
+	imports++;
+}
+
+void
+zerosig(char *sp)
+{
+	Sym *s;
+
+	s = lookup(sp, 0);
+	s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+	int i, n;
+	Sym *s;
+	Biobuf *b;
+	char *l, buf[256], *fields[64];
+
+	if(f == nil)
+		return;
+	b = Bopen(f, OREAD);
+	if(b == nil){
+		diag("could not open %s: %r", f);
+		errorexit();
+	}
+	while((l = Brdline(b, '\n')) != nil){
+		n = Blinelen(b);
+		if(n >= sizeof(buf)){
+			diag("%s: line too long", f);
+			errorexit();
+		}
+		memmove(buf, l, n);
+		buf[n-1] = '\0';
+		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+		if(n == nelem(fields)){
+			diag("%s: bad format", f);
+			errorexit();
+		}
+		for(i = 0; i < n; i++){
+			s = lookup(fields[i], 0);
+			s->type = SXREF;
+			s->subtype = t;
+			if(t == SIMPORT)
+				nimports++;
+			else
+				nexports++;
+		}
+	}
+	Bterm(b);
+}
--- /dev/null
+++ b/utils/ql/optab.c
@@ -1,0 +1,302 @@
+#include	"l.h"
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE, C_NONE, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG, C_NONE, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_NONE, C_LCON, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG, C_LCON, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_NONE, C_NONE, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_REG, C_NONE, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_NONE, C_LCON, 	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_REG, C_LCON, 	C_LCON, 	 0, 0, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_REG,		 1, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_REG,		12, 4, 0 },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_REG,		13, 4, 0 },
+
+	{ AADD,		C_REG,	C_REG, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_NONE, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AADD,		C_ADDCON,C_REG, C_NONE, 	C_REG,		 4, 4, 0 },
+	{ AADD,		C_ADDCON,C_NONE, C_NONE, C_REG,		 4, 4, 0 },
+	{ AADD,		C_UCON,	C_REG, C_NONE, 	C_REG,		20, 4, 0 },
+	{ AADD,		C_UCON,	C_NONE, C_NONE, 	C_REG,		20, 4, 0 },
+	{ AADD,		C_LCON,	C_REG, C_NONE, 	C_REG,		22, 12, 0 },
+	{ AADD,		C_LCON,	C_NONE, C_NONE, 	C_REG,		22, 12, 0 },
+
+	{ AADDC,	C_REG,	C_REG, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AADDC,	C_REG,	C_NONE, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AADDC,	C_ADDCON,C_REG, C_NONE, 	C_REG,		 4, 4, 0 },
+	{ AADDC,	C_ADDCON,C_NONE, C_NONE, C_REG,		 4, 4, 0 },
+	{ AADDC,	C_LCON,	C_REG, C_NONE, 	C_REG,		22, 12, 0 },
+	{ AADDC,	C_LCON,	C_NONE, C_NONE, 	C_REG,		22, 12, 0 },
+
+	{ AAND,		C_REG,	C_REG, C_NONE, 	C_REG,		6, 4, 0 },	/* logical, no literal */
+	{ AAND,		C_REG,	C_NONE, C_NONE, 	C_REG,		6, 4, 0 },
+	{ AANDCC,	C_REG,	C_REG, C_NONE, 	C_REG,		6, 4, 0 },
+	{ AANDCC,	C_REG,	C_NONE, C_NONE, 	C_REG,		6, 4, 0 },
+
+	{ AANDCC,	C_ANDCON,C_NONE, C_NONE, C_REG,		58, 4, 0 },
+	{ AANDCC,	C_ANDCON,C_REG, C_NONE, 	C_REG,		58, 4, 0 },
+	{ AANDCC,	C_UCON,	C_NONE, C_NONE, 	C_REG,		59, 4, 0 },
+	{ AANDCC,	C_UCON,	C_REG, C_NONE, 	C_REG,		59, 4, 0 },
+	{ AANDCC,	C_LCON,	C_NONE, C_NONE, 	C_REG,		23, 12, 0 },
+	{ AANDCC,	C_LCON,	C_REG, C_NONE, 	C_REG,		23, 12, 0 },
+
+	{ AMULLW,	C_REG,	C_REG, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AMULLW,	C_REG,	C_NONE, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ AMULLW,	C_ADDCON,C_REG, C_NONE, 	C_REG,		 4, 4, 0 },
+	{ AMULLW,	C_ADDCON,C_NONE, C_NONE, C_REG,		 4, 4, 0 },
+	{ AMULLW,	C_ANDCON,C_REG, C_NONE, 	C_REG,		 4, 4, 0 },
+	{ AMULLW,	C_ANDCON,	C_NONE, C_NONE,	C_REG,	 4, 4, 0 },
+	{ AMULLW,	C_LCON,	C_REG,	C_NONE,	C_REG,		22, 12, 0},
+	{ AMULLW,	C_LCON,	C_NONE,	C_NONE,	C_REG,		22, 12, 0},
+
+	{ ASUBC,	C_REG,	C_REG, C_NONE, 	C_REG,		 10, 4, 0 },
+	{ ASUBC,	C_REG,	C_NONE, C_NONE, 	C_REG,		 10, 4, 0 },
+	{ ASUBC,	C_REG,	C_NONE, C_ADDCON, 	C_REG,	 27, 4, 0 },
+	{ ASUBC,	C_REG,	C_NONE,	C_LCON,	C_REG,		28, 12, 0},
+
+	{ AOR,		C_REG,	C_REG, C_NONE, 	C_REG,		6, 4, 0 },	/* logical, literal not cc (or/xor) */
+	{ AOR,		C_REG,	C_NONE, C_NONE, 	C_REG,		6, 4, 0 },
+	{ AOR,		C_ANDCON, C_NONE, C_NONE,  C_REG,	58, 4, 0 },
+	{ AOR,		C_ANDCON, C_REG, C_NONE,  C_REG,		58, 4, 0 },
+	{ AOR,		C_UCON, C_NONE, C_NONE,  C_REG,		59, 4, 0 },
+	{ AOR,		C_UCON, C_REG, C_NONE,  C_REG,		59, 4, 0 },
+	{ AOR,		C_LCON,	C_NONE, C_NONE, 	C_REG,		23, 12, 0 },
+	{ AOR,		C_LCON,	C_REG, C_NONE, 	C_REG,		23, 12, 0 },
+
+	{ ADIVW,	C_REG,	C_REG, C_NONE, 	C_REG,		 2, 4, 0 },	/* op r1[,r2],r3 */
+	{ ADIVW,	C_REG,	C_NONE, C_NONE, 	C_REG,		 2, 4, 0 },
+	{ ASUB,	C_REG,	C_REG, C_NONE, 	C_REG,		 10, 4, 0 },	/* op r2[,r1],r3 */
+	{ ASUB,	C_REG,	C_NONE, C_NONE, 	C_REG,		 10, 4, 0 },
+
+	{ ASLW,	C_REG,	C_NONE, C_NONE, 	C_REG,		 6, 4, 0 },
+	{ ASLW,	C_REG,	C_REG, C_NONE, 	C_REG,		 6, 4, 0 },
+	{ ASLW,	C_SCON,	C_REG, C_NONE, 	C_REG,		57, 4, 0 },
+	{ ASLW,	C_SCON,	C_NONE, C_NONE, 	C_REG,		57, 4, 0 },
+
+	{ ASRAW,	C_REG,	C_NONE, C_NONE, 	C_REG,		 6, 4, 0 },
+	{ ASRAW,	C_REG,	C_REG, C_NONE, 	C_REG,		 6, 4, 0 },
+	{ ASRAW,	C_SCON,	C_REG, C_NONE, 	C_REG,		56, 4, 0 },
+	{ ASRAW,	C_SCON,	C_NONE, C_NONE, 	C_REG,		56, 4, 0 },
+
+	{ ARLWMI,	C_SCON, C_REG, C_LCON, 	C_REG,		62, 4, 0 },
+	{ ARLWMI,	C_REG,	C_REG, C_LCON, 	C_REG,		63, 4, 0 },
+
+	{ AFADD,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		 2, 4, 0 },
+	{ AFADD,	C_FREG,	C_REG, C_NONE, 	C_FREG,		 2, 4, 0 },
+	{ AFABS,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		33, 4, 0 },
+	{ AFABS,	C_NONE,	C_NONE, C_NONE, 	C_FREG,		33, 4, 0 },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		33, 4, 0 },
+
+	{ AFMADD,	C_FREG,	C_REG, C_FREG, 	C_FREG,		 34, 4, 0 },
+	{ AFMUL,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		 32, 4, 0 },
+	{ AFMUL,	C_FREG,	C_REG, C_NONE, 	C_FREG,		 32, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	 7, 4, REGZERO },
+	{ AMOVBZ,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	 7, 4, REGZERO },
+	{ AMOVBZU,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	 7, 4, REGZERO },
+	{ AMOVB,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	 7, 4, REGZERO },
+	{ AMOVBU,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	 7, 4, REGZERO },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_SEXT,		 7, 4, REGSB },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_SEXT,		 7, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_SEXT,		 7, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBZU,	C_REG,	C_NONE, C_NONE, 	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE, C_NONE, 	C_SOREG,	 7, 4, REGZERO },
+
+	{ AMOVW,	C_ZOREG,C_REG, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVBZ,	C_ZOREG,C_REG, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVBZU,	C_ZOREG,C_REG, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVB,	C_ZOREG,C_REG, C_NONE, 	C_REG,		9, 8, REGZERO },
+	{ AMOVBU,	C_ZOREG,C_REG, C_NONE, 	C_REG,		9, 8, REGZERO },
+	{ AMOVW,	C_SEXT,	C_NONE, C_NONE, 	C_REG,		 8, 4, REGSB },
+	{ AMOVBZ,	C_SEXT,	C_NONE, C_NONE, 	C_REG,		 8, 4, REGSB },
+	{ AMOVB,	C_SEXT,	C_NONE, C_NONE, 	C_REG,		9, 8, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE, C_NONE, 	C_REG,		 8, 4, REGSP },
+	{ AMOVBZ,	C_SAUTO,C_NONE, C_NONE, 	C_REG,		 8, 4, REGSP },
+	{ AMOVB,	C_SAUTO,C_NONE, C_NONE, 	C_REG,		9, 8, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVBZ,	C_SOREG,C_NONE, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVBZU,	C_SOREG,C_NONE, C_NONE, 	C_REG,		 8, 4, REGZERO },
+	{ AMOVB,	C_SOREG,C_NONE, C_NONE, 	C_REG,		9, 8, REGZERO },
+	{ AMOVBU,	C_SOREG,C_NONE, C_NONE, 	C_REG,		9, 8, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_LEXT,		35, 8, REGSB },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_LEXT,		35, 8, REGSB },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_LEXT,		35, 8, REGSB },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_LAUTO,	35, 8, REGSP },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_LAUTO,	35, 8, REGSP },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_LAUTO,	35, 8, REGSP },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_LOREG,	35, 8, REGZERO },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_LOREG,	35, 8, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_LOREG,	35, 8, REGZERO },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_ADDR,		74, 8, 0 },
+	{ AMOVBZ,	C_REG,	C_NONE, C_NONE, 	C_ADDR,		74, 8, 0 },
+	{ AMOVB,	C_REG,	C_NONE, C_NONE, 	C_ADDR,		74, 8, 0 },
+
+	{ AMOVW,	C_LEXT,	C_NONE, C_NONE, 	C_REG,		36, 8, REGSB },
+	{ AMOVBZ,	C_LEXT,	C_NONE, C_NONE, 	C_REG,		36, 8, REGSB },
+	{ AMOVB,	C_LEXT,	C_NONE, C_NONE, 	C_REG,		37, 12, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE, C_NONE, 	C_REG,		36, 8, REGSP },
+	{ AMOVBZ,	C_LAUTO,C_NONE, C_NONE, 	C_REG,		36, 8, REGSP },
+	{ AMOVB,	C_LAUTO,C_NONE, C_NONE, 	C_REG,		37, 12, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE, C_NONE, 	C_REG,		36, 8, REGZERO },
+	{ AMOVBZ,	C_LOREG,C_NONE, C_NONE, 	C_REG,		36, 8, REGZERO },
+	{ AMOVB,	C_LOREG,C_NONE, C_NONE, 	C_REG,		37, 12, REGZERO },
+	{ AMOVW,	C_ADDR,	C_NONE, C_NONE, 	C_REG,		75, 8, 0 },
+	{ AMOVBZ,	C_ADDR,	C_NONE, C_NONE, 	C_REG,		75, 8, 0 },
+	{ AMOVB,	C_ADDR,	C_NONE, C_NONE, 	C_REG,		76, 12, 0 },
+
+	{ AMOVW,	C_SECON,C_NONE, C_NONE, 	C_REG,		 3, 4, REGSB },
+	{ AMOVW,	C_SACON,C_NONE, C_NONE, 	C_REG,		 3, 4, REGSP },
+	{ AMOVW,	C_LECON,C_NONE, C_NONE, 	C_REG,		26, 8, REGSB }, 
+	{ AMOVW,	C_LACON,C_NONE, C_NONE, 	C_REG,		26, 8, REGSP },
+	{ AMOVW,	C_ADDCON,C_NONE, C_NONE, C_REG,		 3, 4, REGZERO },
+
+	{ AMOVW,	C_UCON, C_NONE, C_NONE,  C_REG,		3, 4, REGZERO },
+	{ AMOVW,	C_LCON,	C_NONE, C_NONE, 	C_REG,		19, 8, 0 },
+
+	{ AMOVHBR,	C_ZOREG,	C_REG, C_NONE, C_REG,		45, 4, 0 },
+	{ AMOVHBR,	C_ZOREG, C_NONE, C_NONE, C_REG,	45, 4, 0 },
+	{ AMOVHBR,	C_REG,	C_REG, C_NONE,	C_ZOREG,		44, 4, 0 },
+	{ AMOVHBR,	C_REG,	C_NONE, C_NONE,	C_ZOREG,		44, 4, 0 },
+
+	{ ASYSCALL,	C_NONE,	C_NONE, C_NONE, 	C_NONE,		 5, 4, 0 },
+
+	{ ABEQ,		C_NONE,	C_NONE, C_NONE, 	C_SBRA,		16, 4, 0 },
+	{ ABEQ,		C_CREG,	C_NONE, C_NONE, 	C_SBRA,		16, 4, 0 },
+
+	{ ABR,		C_NONE,	C_NONE, C_NONE, 	C_LBRA,		11, 4, 0 },
+
+	{ ABC,		C_SCON,	C_REG, C_NONE, 	C_SBRA,		16, 4, 0 },
+	{ ABC,		C_SCON, C_REG, C_NONE, 	C_LBRA,		17, 4, 0 },
+
+	{ ABR,		C_NONE,	C_NONE, C_NONE, 	C_LR,		18, 4, 0 },
+	{ ABR,		C_NONE,	C_NONE, C_NONE, 	C_CTR,		18, 4, 0 },
+	{ ABR,		C_NONE,	C_NONE, C_NONE, 	C_ZOREG,		15, 8, 0 },
+
+	{ ABC,		C_NONE,	C_REG, C_NONE, 	C_LR,		18, 4, 0 },
+	{ ABC,		C_NONE,	C_REG, C_NONE, 	C_CTR,		18, 4, 0 },
+	{ ABC,		C_SCON,	C_REG, C_NONE, 	C_LR,		18, 4, 0 },
+	{ ABC,		C_SCON,	C_REG, C_NONE, 	C_CTR,		18, 4, 0 },
+	{ ABC,		C_NONE,	C_NONE, C_NONE, 	C_ZOREG,		15, 8, 0 },
+
+	{ AFMOVD,	C_SEXT,	C_NONE, C_NONE, 	C_FREG,		8, 4, REGSB },
+	{ AFMOVD,	C_SAUTO,C_NONE, C_NONE, 	C_FREG,		8, 4, REGSP },
+	{ AFMOVD,	C_SOREG,C_NONE, C_NONE, 	C_FREG,		8, 4, REGZERO },
+
+	{ AFMOVD,	C_LEXT,	C_NONE, C_NONE, 	C_FREG,		8, 4, REGSB },
+	{ AFMOVD,	C_LAUTO,C_NONE, C_NONE, 	C_FREG,		8, 4, REGSP },
+	{ AFMOVD,	C_LOREG,C_NONE, C_NONE, 	C_FREG,		8, 4, REGZERO },
+	{ AFMOVD,	C_ADDR,	C_NONE, C_NONE, 	C_FREG,		75, 8, 0 },
+
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_SEXT,		7, 4, REGSB },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_SAUTO,	7, 4, REGSP },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_SOREG,	7, 4, REGZERO },
+
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_LEXT,		7, 4, REGSB },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_LAUTO,	7, 4, REGSP },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_LOREG,	7, 4, REGZERO },
+	{ AFMOVD,	C_FREG,	C_NONE, C_NONE, 	C_ADDR,		74, 8, 0 },
+
+	{ ASYNC,		C_NONE,	C_NONE, C_NONE, 	C_NONE,		46, 4, 0 },
+	{ AWORD,	C_LCON,	C_NONE, C_NONE, 	C_NONE,		40, 4, 0 },
+
+	{ AADDME,	C_REG,	C_NONE, C_NONE, 	C_REG,		47, 4, 0 },
+
+	{ AEXTSB,	C_REG,	C_NONE, C_NONE, 	C_REG,		48, 4, 0 },
+	{ AEXTSB,	C_NONE,	C_NONE, C_NONE, 	C_REG,		48, 4, 0 },
+
+	{ ANEG,		C_REG,	C_NONE, C_NONE, 	C_REG,		47, 4, 0 },
+	{ ANEG,		C_NONE,	C_NONE, C_NONE, 	C_REG,		47, 4, 0 },
+
+	{ AREM,		C_REG,	C_NONE, C_NONE, 	C_REG,		51, 12, 0 },
+	{ AREM,		C_REG,	C_REG, C_NONE, 	C_REG,		51, 12, 0 },
+
+	{ AMTFSB0,	C_SCON,	C_NONE, C_NONE, 	C_NONE,		52, 4, 0 },
+	{ AMOVFL, C_FPSCR, C_NONE, C_NONE,	C_FREG,		53, 4, 0 },
+	{ AMOVFL, C_FREG, C_NONE, C_NONE,	C_FPSCR,		64, 4, 0 },
+	{ AMOVFL, C_FREG, C_NONE, C_LCON,	C_FPSCR,		64, 4, 0 },
+	{ AMOVFL,	C_LCON, C_NONE, C_NONE,	C_FPSCR,		65, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_MSR,		54, 4, 0 },
+	{ AMOVW,	C_MSR,	C_NONE, C_NONE, 	C_REG,		54, 4, 0 },
+
+	{ AMOVW,	C_SREG,	C_NONE, C_NONE, 	C_REG,		55, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_SREG,		55, 4, 0 },
+	{ AMOVW,	C_SREG, C_REG, C_NONE, 	C_REG,		55, 4, 0 },	/* MOVW SR(Rn), Rm and v.v.*/
+	{ AMOVW,	C_REG,	C_REG, C_NONE, 	C_SREG,		55, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_SPR,		66, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_LR,		66, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_CTR,		66, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE, C_NONE, 	C_XER,		66, 4, 0 },
+	{ AMOVW,	C_SPR,	C_NONE, C_NONE, 	C_REG,		66, 4, 0 },
+	{ AMOVW,	C_LR,	C_NONE, C_NONE, 	C_REG,		66, 4, 0 },
+	{ AMOVW,	C_CTR,	C_NONE, C_NONE, 	C_REG,		66, 4, 0 },
+	{ AMOVW,	C_XER,	C_NONE, C_NONE, 	C_REG,		66, 4, 0 },
+
+	{ AMOVFL,	C_FPSCR, C_NONE, C_NONE, 	C_CREG,		73, 4, 0 },
+	{ AMOVFL,	C_CREG,	C_NONE, C_NONE, 	C_CREG,		67, 4, 0 },
+	{ AMOVW,	C_XER,	C_NONE, C_NONE, 	C_CREG,		72, 4, 0 },
+	{ AMOVW,	C_CREG,	C_NONE,	C_NONE,		C_REG,		68, 4, 0 },
+	{ AMOVFL,	C_REG, C_NONE, C_LCON, C_CREG,		69, 4, 0 },
+	{ AMOVFL,	C_REG, C_NONE, C_NONE, C_CREG,		69, 4, 0 },
+	{ AMOVW,	C_REG, C_NONE, C_NONE, C_CREG,		69, 4, 0 },
+
+	{ ACMP,	C_REG,	C_NONE, C_NONE, 	C_REG,	70, 4, 0 },
+	{ ACMP,	C_REG,	C_REG, C_NONE, 	C_REG,	70, 4, 0 },
+	{ ACMP,	C_REG,	C_NONE, C_NONE,	C_ADDCON,	71, 4, 0 },
+	{ ACMP,	C_REG,	C_REG, C_NONE,	C_ADDCON,	71, 4, 0 },
+
+	{ ACMPU,	C_REG,	C_NONE, C_NONE, 	C_REG,	70, 4, 0 },
+	{ ACMPU,	C_REG,	C_REG, C_NONE, 	C_REG,	70, 4, 0 },
+	{ ACMPU,	C_REG,	C_NONE, C_NONE,	C_ANDCON,	71, 4, 0 },
+	{ ACMPU,	C_REG,	C_REG, C_NONE,	C_ANDCON,	71, 4, 0 },
+
+	{ AFCMPO,	C_FREG,	C_NONE, C_NONE, 	C_FREG,	70, 4, 0 },
+	{ AFCMPO,	C_FREG,	C_REG, C_NONE, 	C_FREG,	70, 4, 0 },
+
+	{ ATW,		C_LCON,	C_REG, C_NONE, 	C_REG,		60, 4, 0 },
+	{ ATW,		C_LCON,	C_REG, C_NONE, 	C_ADDCON,	61, 4, 0 },
+
+	{ ADCBF,	C_ZOREG, C_NONE, C_NONE,  C_NONE,	43, 4, 0 },
+	{ ADCBF,	C_ZOREG, C_REG, C_NONE,  C_NONE,	43, 4, 0 },
+
+	{ AECOWX,	C_REG,	C_REG, C_NONE, 	C_ZOREG,	44, 4, 0 },
+	{ AECIWX,	C_ZOREG, C_REG, C_NONE,  C_REG,		45, 4, 0 },
+	{ AECOWX,	C_REG,	C_NONE, C_NONE, 	C_ZOREG,	44, 4, 0 },
+	{ AECIWX,	C_ZOREG, C_NONE, C_NONE,  C_REG,		45, 4, 0 },
+
+	{ AEIEIO,	C_NONE,	C_NONE, C_NONE, 	C_NONE,		46, 4, 0 },
+	{ ATLBIE,	C_REG, C_NONE, C_NONE,		C_NONE,		49, 4, 0 },
+
+	{ ASTSW,	C_REG,	C_NONE, C_NONE, 	C_ZOREG,	44, 4, 0 },
+	{ ASTSW,	C_REG,	C_NONE, C_LCON, 	C_ZOREG,	41, 4, 0 },
+	{ ALSW,	C_ZOREG, C_NONE, C_NONE,  C_REG,		45, 4, 0 },
+	{ ALSW,	C_ZOREG, C_NONE, C_LCON,  C_REG,		42, 4, 0 },
+
+	{ AMACCHW,	C_REG,	C_REG, C_NONE, 	C_REG,		 2, 4, 0 },	/* op rb,ra,rt */
+
+	{ AFSMOVS,	C_ZOREG,	C_REG, C_NONE, C_FREG,		45, 4, 0 },
+	{ AFSMOVS,	C_ZOREG, C_NONE, C_NONE, C_FREG,	45, 4, 0 },
+	{ AFSMOVS,	C_FREG,	C_REG, C_NONE,	C_ZOREG,		44, 4, 0 },
+	{ AFSMOVS,	C_FREG,	C_NONE, C_NONE,	C_ZOREG,		44, 4, 0 },
+
+	{ AFPMOVD,	C_ZOREG,	C_REG, C_NONE, C_FREG,		45, 4, 0 },
+	{ AFPMOVD,	C_ZOREG, C_NONE, C_NONE, C_FREG,	45, 4, 0 },
+	{ AFPMOVD,	C_FREG,	C_REG, C_NONE,	C_ZOREG,		44, 4, 0 },
+	{ AFPMOVD,	C_FREG,	C_NONE, C_NONE,	C_ZOREG,		44, 4, 0 },
+
+	{ AFPMOVD,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		33, 4, 0 },	/* f[xps]mr */
+	{ AFMOVSPD,	C_FREG,	C_NONE, C_NONE, 	C_FREG,		33, 4, 0 },	/* fsm[tf]p */
+
+	{ AXXX,		C_NONE,	C_NONE, C_NONE, 	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/ql/pass.c
@@ -1,0 +1,667 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p, *p1;
+	long orig, orig1, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on REGSB)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 3)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+	orig1 = orig;
+
+	/*
+	 * pass 2
+	 *	assign 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		if(v >= 8)
+			while(orig & 7)
+				orig++;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	/*
+	 * pass 4
+	 *	add literals to all large values.
+	 *	at this time:
+	 *		small data is allocated DATA
+	 *		large data is allocated DATA1
+	 *		large bss is allocated BSS
+	 *	the new literals are loaded between
+	 *	small data and large data.
+	 */
+	orig = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as != AMOVW)
+			continue;
+		if(p->from.type != D_CONST)
+			continue;
+		if(s = p->from.sym) {
+			t = s->type;
+			if(t != SDATA && t != SDATA1 && t != SBSS)
+				continue;
+			t = p->from.name;
+			if(t != D_EXTERN && t != D_STATIC)
+				continue;
+			v = s->value + p->from.offset;
+			if(v >= 0 && v <= 0xffff)
+				continue;
+			if(!strcmp(s->name, "setSB"))
+				continue;
+			/* size should be 19 max */
+			if(strlen(s->name) >= 10)	/* has loader address */ 
+				sprint(literal, "$%p.%lux", s, p->from.offset);
+			else
+				sprint(literal, "$%s.%d.%lux", s->name, s->version, p->from.offset);
+		} else {
+			if(p->from.name != D_NONE)
+				continue;
+			if(p->from.reg != NREG)
+				continue;
+			v = p->from.offset;
+			if(v >= -0x7fff-1 && v <= 0x7fff)
+				continue;
+			if(!(v & 0xffff))
+				continue;
+			if(v)
+				continue;	/* quicker to build it than load it */
+			/* size should be 9 max */
+			sprint(literal, "$%lux", v);
+		}
+		s = lookup(literal, 0);
+		if(s->type == 0) {
+			s->type = SDATA;
+			s->value = orig1+orig;
+			orig += 4;
+			p1 = prg();
+			p1->as = ADATA;
+			p1->line = p->line;
+			p1->from.type = D_OREG;
+			p1->from.sym = s;
+			p1->from.name = D_EXTERN;
+			p1->reg = 4;
+			p1->to = p->from;
+			p1->link = datap;
+			datap = p1;
+		}
+		if(s->type != SDATA)
+			diag("literal not data: %s", s->name);
+		p->from.type = D_OREG;
+		p->from.sym = s;
+		p->from.name = D_EXTERN;
+		p->from.offset = 0;
+		continue;
+	}
+	while(orig & 7)
+		orig++;
+	/*
+	 * pass 5
+	 *	re-adjust offsets
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t == SBSS) {
+			s->value += orig;
+			continue;
+		}
+		if(t == SDATA1) {
+			s->type = SDATA;
+			s->value += orig;
+			continue;
+		}
+	}
+	datsize += orig;
+	xdefine("setSB", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+int
+relinv(int a)
+{
+
+	switch(a) {
+	case ABEQ:	return ABNE;
+	case ABNE:	return ABEQ;
+
+	case ABGE:	return ABLT;
+	case ABLT:	return ABGE;
+
+	case ABGT:	return ABLE;
+	case ABLE:	return ABGT;
+
+	case ABVC:	return ABVS;
+	case ABVS:	return ABVC;
+	}
+	return 0;
+}
+
+void
+follow(void)
+{
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, b, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == ABR) {
+		q = p->cond;
+		if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
+			p->mark |= FOLL;
+			lastp->link = p;
+			lastp = p;
+			p = p->link;
+			xfol(p);
+			p = q;
+			if(p && !(p->mark & FOLL))
+				goto loop;
+			return;
+		}
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp || (q->mark&NOSCHED))
+				break;
+			b = 0;		/* set */
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			b = relinv(a);
+			if(!b)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI)
+					return;
+				r->as = b;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+
+		a = ABR;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI){
+		if(p->mark & NOSCHED){
+			p = p->link;
+			goto loop;
+		}
+		return;
+	}
+	if(p->cond != P)
+	if(a != ABL && p->link != P) {
+		xfol(p->link);
+		p = p->cond;
+		if(p == P || (p->mark&FOLL))
+			return;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == ABL || a == ARETURN) && p->to.sym != S) {
+			s = p->to.sym;
+			if(s->type != STEXT && s->type != SUNDEF) {
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+			}
+			if(s->type == SUNDEF){
+				p->to.offset = 0;
+				p->cond = UP;
+			}
+			else
+				p->to.offset = s->value;
+			p->to.type = D_BRANCH;
+		}
+		if(p->to.type != D_BRANCH || p->cond == UP)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		p->mark = 0;	/* initialization for follow */
+		if(p->cond != P && p->cond != UP) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != ABR || (p->mark&NOSCHED))
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
+
+void
+import(void)
+{
+	int i;
+	Sym *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+				undefsym(s);
+				Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value);
+			}
+}
+
+void
+ckoff(Sym *s, long v)
+{
+	if(v < 0 || v >= 1<<Roffset)
+		diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+	Prog *p;
+
+	p = prg();
+	p->link = datap;
+	datap = p;
+	p->as = ADATA;
+	p->reg = w;
+	p->from.type = D_OREG;
+	p->from.name = t;
+	p->from.sym = s;
+	p->from.offset = o;
+	p->to.type = D_CONST;
+	p->to.name = D_NONE;
+	return p;
+}
+
+void
+export(void)
+{
+	int i, j, n, off, nb, sv, ne;
+	Sym *s, *et, *str, **esyms;
+	Prog *p;
+	char buf[NSNAME], *t;
+
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				n++;
+	esyms = malloc(n*sizeof(Sym*));
+	ne = n;
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				esyms[n++] = s;
+	for(i = 0; i < ne-1; i++)
+		for(j = i+1; j < ne; j++)
+			if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+				s = esyms[i];
+				esyms[i] = esyms[j];
+				esyms[j] = s;
+			}
+
+	nb = 0;
+	off = 0;
+	et = lookup(EXPTAB, 0);
+	if(et->type != 0 && et->type != SXREF)
+		diag("%s already defined", EXPTAB);
+	et->type = SDATA;
+	str = lookup(".string", 0);
+	if(str->type == 0)
+		str->type = SDATA;
+	sv = str->value;
+	for(i = 0; i < ne; i++){
+		s = esyms[i];
+		Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type);
+
+		/* signature */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.offset = s->sig;
+
+		/* address */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_EXTERN;
+		p->to.sym = s;
+
+		/* string */
+		t = s->name;
+		n = strlen(t)+1;
+		for(;;){
+			buf[nb++] = *t;
+			sv++;
+			if(nb >= NSNAME){
+				p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+				p->to.type = D_SCONST;
+				memmove(p->to.sval, buf, NSNAME);
+				nb = 0;
+			}
+			if(*t++ == 0)
+				break;
+		}
+
+		/* name */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_STATIC;
+		p->to.sym = str;
+		p->to.offset = sv-n;
+	}
+
+	if(nb > 0){
+		p = newdata(str, sv-nb, nb, D_STATIC);
+		p->to.type = D_SCONST;
+		memmove(p->to.sval, buf, nb);
+	}
+
+	for(i = 0; i < 3; i++){
+		newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+	}
+	et->value = off;
+	if(sv == 0)
+		sv = 1;
+	str->value = sv;
+	exports = ne;
+	free(esyms);
+}
--- /dev/null
+++ b/utils/ql/sched.c
@@ -1,0 +1,801 @@
+#include	"l.h"
+
+enum
+{
+	E_ICC	= 1<<0,
+	E_FCC	= 1<<1,
+	E_MEM	= 1<<2,
+	E_MEMSP	= 1<<3,	/* uses offset and size */
+	E_MEMSB	= 1<<4,	/* uses offset and size */
+	E_LR	= 1<<5,
+	E_CR = 1<<6,
+	E_CTR = 1<<7,
+	E_XER = 1<<8,
+
+	E_CR0 = 0xF<<0,
+	E_CR1 = 0xF<<4,
+
+	ANYMEM	= E_MEM|E_MEMSP|E_MEMSB,
+	ALL	= ~0,
+};
+
+typedef	struct	Sch	Sch;
+typedef	struct	Dep	Dep;
+
+struct	Dep
+{
+	ulong	ireg;
+	ulong	freg;
+	ulong	cc;
+	ulong	cr;
+};
+struct	Sch
+{
+	Prog	p;
+	Dep	set;
+	Dep	used;
+	long	soffset;
+	char	size;
+	char	comp;
+};
+
+void	regused(Sch*, Prog*);
+int	depend(Sch*, Sch*);
+int	conflict(Sch*, Sch*);
+int	offoverlap(Sch*, Sch*);
+void	dumpbits(Sch*, Dep*);
+
+void
+sched(Prog *p0, Prog *pe)
+{
+	Prog *p, *q;
+	Sch sch[NSCHED], *s, *t, *u, *se, stmp;
+
+	if(!debug['Q'])
+		return;
+	/*
+	 * build side structure
+	 */
+	s = sch;
+	for(p=p0;; p=p->link) {
+		memset(s, 0, sizeof(*s));
+		s->p = *p;
+		regused(s, p);
+		if(debug['X']) {
+			Bprint(&bso, "%P\tset", &s->p);
+			dumpbits(s, &s->set);
+			Bprint(&bso, "; used");
+			dumpbits(s, &s->used);
+			if(s->comp)
+				Bprint(&bso, "; compound");
+			if(s->p.mark & LOAD)
+				Bprint(&bso, "; load");
+			if(s->p.mark & BRANCH)
+				Bprint(&bso, "; branch");
+			if(s->p.mark & FCMP)
+				Bprint(&bso, "; fcmp");
+			Bprint(&bso, "\n");
+		}
+		s++;
+		if(p == pe)
+			break;
+	}
+	se = s;
+
+	for(s=se-1; s>=sch; s--) {
+
+		/*
+		 * load delay. interlocked.
+		 */
+		if(s->p.mark & LOAD) {
+			if(s >= se-1)
+				continue;
+			if(!conflict(s, (s+1)))
+				continue;
+			/*
+			 * s is load, s+1 is immediate use of result
+			 * t is the trial instruction to insert between s and s+1
+			 */
+			for(t=s-1; t>=sch; t--) {
+				if(t->p.mark & BRANCH)
+					goto no2;
+				if(t->p.mark & FCMP)
+					if((s+1)->p.mark & BRANCH)
+						goto no2;
+				if(t->p.mark & LOAD)
+					if(conflict(t, (s+1)))
+						goto no2;
+				for(u=t+1; u<=s; u++)
+					if(depend(u, t))
+						goto no2;
+				goto out2;
+			no2:;
+			}
+			if(debug['X'])
+				Bprint(&bso, "?l%P\n", &s->p);
+			continue;
+		out2:
+			if(debug['X']) {
+				Bprint(&bso, "!l%P\n", &t->p);
+				Bprint(&bso, "%P\n", &s->p);
+			}
+			stmp = *t;
+			memmove(t, t+1, (uchar*)s - (uchar*)t);
+			*s = stmp;
+			s--;
+			continue;
+		}
+
+		/*
+		 * fop2 delay.
+		 */
+		if(s->p.mark & FCMP) {
+			if(s >= se-1)
+				continue;
+			if(!((s+1)->p.mark & BRANCH))
+				continue;
+			/* t is the trial instruction to use */
+			for(t=s-1; t>=sch; t--) {
+				for(u=t+1; u<=s; u++)
+					if(depend(u, t))
+						goto no3;
+				goto out3;
+			no3:;
+			}
+			if(debug['X'])
+				Bprint(&bso, "?f%P\n", &s->p);
+			continue;
+		out3:
+			if(debug['X']) {
+				Bprint(&bso, "!f%P\n", &t->p);
+				Bprint(&bso, "%P\n", &s->p);
+			}
+			stmp = *t;
+			memmove(t, t+1, (uchar*)s - (uchar*)t);
+			*s = stmp;
+			s--;
+			continue;
+		}
+	}
+
+	/*
+	 * put it all back
+	 */
+	for(s=sch, p=p0; s<se; s++, p=q) {
+		q = p->link;
+		if(q != s->p.link) {
+			*p = s->p;
+			p->link = q;
+		}
+	}
+	if(debug['X'])
+		Bprint(&bso, "\n");
+}
+
+void
+regused(Sch *s, Prog *realp)
+{
+	int c, ar, ad, ld, sz, nr, upd;
+	ulong m;
+	Prog *p;
+
+	p = &s->p;
+	s->comp = compound(p);
+	if(s->comp) {
+		s->set.ireg |= 1<<REGTMP;
+		s->used.ireg |= 1<<REGTMP;
+	}
+	ar = 0;		/* dest is really reference */
+	ad = 0;		/* source/dest is really address */
+	ld = 0;		/* opcode is load instruction */
+	sz = 32*4;		/* size of load/store for overlap computation */
+	nr = 0;	/* source/dest is not really reg */
+	upd = 0;	/* move with update; changes reg */
+
+/*
+ * flags based on opcode
+ */
+	switch(p->as) {
+	case ATEXT:
+		curtext = realp;
+		autosize = p->to.offset + 4;
+		ad = 1;
+		break;
+	case ABL:
+		s->set.cc |= E_LR;
+		ar = 1;
+		ad = 1;
+		break;
+	case ABR:
+		ar = 1;
+		ad = 1;
+		break;
+	case ACMP:
+		s->set.cc |= E_ICC;
+		if(p->reg == 0)
+			s->set.cr |= E_CR0;
+		else
+			s->set.cr |= (0xF<<((p->reg&7)*4));
+		ar = 1;
+		break;
+	case AFCMPO:
+	case AFCMPU:
+		s->set.cc |= E_FCC;
+		if(p->reg == 0)
+			s->set.cr |= E_CR0;
+		else
+			s->set.cr |= (0xF<<((p->reg&7)*4));
+		ar = 1;
+		break;
+	case ACRAND:
+	case ACRANDN:
+	case ACREQV:
+	case ACRNAND:
+	case ACRNOR:
+	case ACROR:
+	case ACRORN:
+	case ACRXOR:
+		s->used.cr |= 1<<p->from.reg;
+		s->set.cr |= 1<<p->to.reg;
+		nr = 1;
+		break;
+	case ABCL:	/* tricky */
+		s->used.cc |= E_FCC|E_ICC;
+		s->used.cr = ALL;
+		s->set.cc |= E_LR;
+		ar = 1;
+		break;
+	case ABC:		/* tricky */
+		s->used.cc |= E_FCC|E_ICC;
+		s->used.cr = ALL;
+		ar = 1;
+		break;
+	case ABEQ:
+	case ABGE:
+	case ABGT:
+	case ABLE:
+	case ABLT:
+	case ABNE:
+	case ABVC:
+	case ABVS:
+		s->used.cc |= E_ICC;
+		s->used.cr |= E_CR0;
+		ar = 1;
+		break;
+	case ALSW:
+	case AMOVMW:
+		/* could do better */
+		sz = 32*4;
+		ld = 1;
+		break;
+	case AMOVBU:
+	case AMOVBZU:
+		upd = 1;
+		sz = 1;
+		ld = 1;
+		break;
+	case AMOVB:
+	case AMOVBZ:
+		sz = 1;
+		ld = 1;
+		break;
+	case AMOVHU:
+	case AMOVHZU:
+		upd = 1;
+		sz = 2;
+		ld = 1;
+		break;
+	case AMOVH:
+	case AMOVHBR:
+	case AMOVHZ:
+		sz = 2;
+		ld = 1;
+		break;
+	case AFMOVSU:
+	case AMOVWU:
+		upd = 1;
+		sz = 4;
+		ld = 1;
+		break;
+	case AFMOVS:
+	case AMOVW:
+	case AMOVWBR:
+	case ALWAR:
+		sz = 4;
+		ld = 1;
+		break;
+	case AFMOVDU:
+		upd = 1;
+		sz = 8;
+		ld = 1;
+		break;
+	case AFMOVD:
+		sz = 8;
+		ld = 1;
+		break;
+	case AFMOVDCC:
+		sz = 8;
+		ld = 1;
+		s->set.cc |= E_FCC;
+		s->set.cr |= E_CR1;
+		break;
+	case AMOVFL:
+	case AMOVCRFS:
+	case AMTFSB0:
+	case AMTFSB0CC:
+	case AMTFSB1:
+	case AMTFSB1CC:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		s->set.cr = ALL;
+		break;
+	case AADDCC:
+	case AADDVCC:
+	case AADDCCC:
+	case AADDCVCC:
+	case AADDMECC:
+	case AADDMEVCC:
+	case AADDECC:
+	case AADDEVCC:
+	case AADDZECC:
+	case AADDZEVCC:
+	case AANDCC:
+	case AANDNCC:
+	case ACNTLZWCC:
+	case ADIVWCC:
+	case ADIVWVCC:
+	case ADIVWUCC:
+	case ADIVWUVCC:
+	case AEQVCC:
+	case AEXTSBCC:
+	case AEXTSHCC:
+	case AMULHWCC:
+	case AMULHWUCC:
+	case AMULLWCC:
+	case AMULLWVCC:
+	case ANANDCC:
+	case ANEGCC:
+	case ANEGVCC:
+	case ANORCC:
+	case AORCC:
+	case AORNCC:
+	case AREMCC:
+	case AREMVCC:
+	case AREMUCC:
+	case AREMUVCC:
+	case ARLWMICC:
+	case ARLWNMCC:
+	case ASLWCC:
+	case ASRAWCC:
+	case ASRWCC:
+	case ASTWCCC:
+	case ASUBCC:
+	case ASUBVCC:
+	case ASUBCCC:
+	case ASUBCVCC:
+	case ASUBMECC:
+	case ASUBMEVCC:
+	case ASUBECC:
+	case ASUBEVCC:
+	case ASUBZECC:
+	case ASUBZEVCC:
+	case AXORCC:
+		s->set.cc |= E_ICC;
+		s->set.cr |= E_CR0;
+		break;
+	case AFABSCC:
+	case AFADDCC:
+	case AFADDSCC:
+	case AFCTIWCC:
+	case AFCTIWZCC:
+	case AFDIVCC:
+	case AFDIVSCC:
+	case AFMADDCC:
+	case AFMADDSCC:
+	case AFMSUBCC:
+	case AFMSUBSCC:
+	case AFMULCC:
+	case AFMULSCC:
+	case AFNABSCC:
+	case AFNEGCC:
+	case AFNMADDCC:
+	case AFNMADDSCC:
+	case AFNMSUBCC:
+	case AFNMSUBSCC:
+	case AFRSPCC:
+	case AFSUBCC:
+	case AFSUBSCC:
+		s->set.cc |= E_FCC;
+		s->set.cr |= E_CR1;
+		break;
+	}
+
+/*
+ * flags based on 'to' field
+ */
+	c = p->to.class;
+	if(c == 0) {
+		c = aclass(&p->to) + 1;
+		p->to.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->to);
+
+	case C_NONE:
+	case C_ZCON:
+	case C_SCON:
+	case C_UCON:
+	case C_LCON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+	case C_CREG:
+		c = p->to.reg;
+		if(c == NREG)
+			s->set.cr = ALL;
+		else
+			s->set.cr |= (0xF << ((p->from.reg&7)*4));
+		s->set.cc = ALL;
+		break;
+	case C_SPR:
+	case C_SREG:
+	case C_FPSCR:
+	case C_MSR:
+	case C_XER:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		s->set.cr = ALL;
+		break;
+	case C_LR:
+		s->set.cc |= E_LR;
+		break;
+	case C_CTR:
+		s->set.cc |= E_CTR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->to.reg;
+		s->used.ireg |= 1<<c;
+		if(upd)
+			s->set.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		if(ar)
+			s->used.cc |= m;
+		else
+			s->set.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		if(upd)
+			s->set.ireg |= 1<<c;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		if(upd)
+			s->set.ireg |= 1<<c;
+		break;
+	case C_REG:
+		if(nr)
+			break;
+		if(ar)
+			s->used.ireg |= 1<<p->to.reg;
+		else
+			s->set.ireg |= 1<<p->to.reg;
+		break;
+	case C_FREG:
+		if(ar)
+			s->used.freg |= 1<<p->to.reg;
+		else
+			s->set.freg |= 1<<p->to.reg;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(upd)
+			s->set.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSP;
+		else
+			s->set.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(upd)
+			s->set.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSB;
+		else
+			s->set.cc |= E_MEMSB;
+		break;
+	}
+
+/*
+ * flags based on 'from' field
+ */
+	c = p->from.class;
+	if(c == 0) {
+		c = aclass(&p->from) + 1;
+		p->from.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->from);
+
+	case C_NONE:
+	case C_ZCON:
+	case C_SCON:
+	case C_UCON:
+	case C_LCON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_SBRA:
+	case C_LBRA:
+		c = p->from.reg;
+		if(c != NREG)
+			s->used.ireg |= 1<<c;
+		break;
+	case C_CREG:
+		c = p->from.reg;
+		if(c == NREG)
+			s->used.cr = ALL;
+		else
+			s->used.cr |= (0xF << ((p->from.reg&7)*4));
+		s->used.cc = ALL;
+		break;
+	case C_SPR:
+	case C_SREG:
+	case C_FPSCR:
+	case C_MSR:
+	case C_XER:
+		s->set.ireg = ALL;
+		s->set.freg = ALL;
+		s->set.cc = ALL;
+		s->set.cr = ALL;
+		break;
+	case C_LR:
+		s->used.cc |= E_LR;
+		break;
+	case C_CTR:
+		s->used.cc |= E_CTR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->from.reg;
+		s->used.ireg |= 1<<c;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		s->used.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		if(nr)
+			break;
+		s->used.ireg |= 1<<p->from.reg;
+		break;
+	case C_FREG:
+		s->used.freg |= 1<<p->from.reg;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSB;
+		break;
+	}
+	
+	c = p->reg;
+	if(c != NREG) {
+		if(p->from.type == D_FREG || p->to.type == D_FREG)
+			s->used.freg |= 1<<c;
+		else
+			s->used.ireg |= 1<<c;
+	}
+}
+
+/*
+ * test to see if 2 instrictions can be
+ * interchanged without changing semantics
+ */
+int
+depend(Sch *sa, Sch *sb)
+{
+	ulong x;
+
+	if(sa->set.ireg & (sb->set.ireg|sb->used.ireg))
+		return 1;
+	if(sb->set.ireg & sa->used.ireg)
+		return 1;
+
+	if(sa->set.freg & (sb->set.freg|sb->used.freg))
+		return 1;
+	if(sb->set.freg & sa->used.freg)
+		return 1;
+
+	if(sa->set.cr & (sb->set.cr|sb->used.cr))
+		return 1;
+	if(sb->set.cr & sa->used.cr)
+		return 1;
+
+
+	x = (sa->set.cc & (sb->set.cc|sb->used.cc)) |
+		(sb->set.cc & sa->used.cc);
+	if(x) {
+		/*
+		 * allow SB and SP to pass each other.
+		 * allow SB to pass SB iff doffsets are ok
+		 * anything else conflicts
+		 */
+		if(x != E_MEMSP && x != E_MEMSB)
+			return 1;
+		x = sa->set.cc | sb->set.cc |
+			sa->used.cc | sb->used.cc;
+		if(x & E_MEM)
+			return 1;
+		if(offoverlap(sa, sb))
+			return 1;
+	}
+
+	return 0; 
+}
+
+int
+offoverlap(Sch *sa, Sch *sb)
+{
+
+	if(sa->soffset < sb->soffset) {
+		if(sa->soffset+sa->size > sb->soffset)
+			return 1;
+		return 0;
+	}
+	if(sb->soffset+sb->size > sa->soffset)
+		return 1;
+	return 0;
+}
+
+/*
+ * test 2 adjacent instructions
+ * and find out if inserted instructions
+ * are desired to prevent stalls.
+ * first instruction is a load instruction.
+ */
+int
+conflict(Sch *sa, Sch *sb)
+{
+
+	if(sa->set.ireg & sb->used.ireg)
+		return 1;
+	if(sa->set.freg & sb->used.freg)
+		return 1;
+	if(sa->set.cr & sb->used.cr)
+		return 1;
+	return 0;
+}
+
+int
+compound(Prog *p)
+{
+	Optab *o;
+
+	o = oplook(p);
+	if(o->size != 4)
+		return 1;
+	if(p->to.type == D_REG && p->to.reg == REGSB)
+		return 1;
+	return 0;
+}
+
+void
+dumpbits(Sch *s, Dep *d)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(d->ireg & (1<<i))
+			Bprint(&bso, " R%d", i);
+	for(i=0; i<32; i++)
+		if(d->freg & (1<<i))
+			Bprint(&bso, " F%d", i);
+	for(i=0; i<32; i++)
+		if(d->cr & (1<<i))
+			Bprint(&bso, " C%d", i);
+	for(i=0; i<32; i++)
+		switch(d->cc & (1<<i)) {
+		default:
+			break;
+		case E_ICC:
+			Bprint(&bso, " ICC");
+			break;
+		case E_FCC:
+			Bprint(&bso, " FCC");
+			break;
+		case E_LR:
+			Bprint(&bso, " LR");
+			break;
+		case E_CR:
+			Bprint(&bso, " CR");
+			break;
+		case E_CTR:
+			Bprint(&bso, " CTR");
+			break;
+		case E_XER:
+			Bprint(&bso, " XER");
+			break;
+		case E_MEM:
+			Bprint(&bso, " MEM%d", s->size);
+			break;
+		case E_MEMSB:
+			Bprint(&bso, " SB%d", s->size);
+			break;
+		case E_MEMSP:
+			Bprint(&bso, " SP%d", s->size);
+			break;
+		}
+}
--- /dev/null
+++ b/utils/ql/span.c
@@ -1,0 +1,1055 @@
+#include	"l.h"
+#define	r0iszero	1
+
+void
+span(void)
+{
+	Prog *p, *q;
+	Sym *setext;
+	Optab *o;
+	int m, bflag;
+	long c, otxt;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	otxt = c;
+	for(p = firstp; p != P; p = p->link) {
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 4;
+				if(p->from3.type == D_CONST) {
+					if(p->from3.offset & 3)
+						diag("illegal origin\n%P", p);
+					if(c > p->from3.offset)
+						diag("passed origin (#%lux)\n%P", c, p);
+					else
+						c = p->from3.offset;
+					p->pc = c;
+				}
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches? */
+				if(c-otxt >= (1L<<15))
+					bflag = c;
+				otxt = c;
+				continue;
+			}
+			if(p->as != ANOP)
+				diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		c += m;
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			p->pc = c;
+			o = oplook(p);
+			if((o->type == 16 || o->type == 17) && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < -(1L<<16)+10 || otxt >= (1L<<15)-10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = ABR;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = ABR;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					addnop(p->link);
+					addnop(p);
+					bflag = 1;
+				}
+			}
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 4;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				if(p->as != ANOP)
+					diag("zero-width instruction\n%P", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+		
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_CREG:
+		return C_CREG;
+
+	case D_SPR:
+		if(a->offset == D_LR)
+			return C_LR;
+		if(a->offset == D_XER)
+			return C_XER;
+		if(a->offset == D_CTR)
+			return C_CTR;
+		return C_SPR;
+
+	case D_DCR:
+		return C_SPR;
+
+	case D_SREG:
+		return C_SREG;
+
+	case D_FPSCR:
+		return C_FPSCR;
+
+	case D_MSR:
+		return C_MSR;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == S)
+				break;
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			if(dlm){
+				instoffset = a->sym->value + a->offset;
+				switch(a->sym->type){
+				case STEXT:
+				case SLEAF:
+				case SCONST:
+				case SUNDEF:
+					break;
+				default:
+					instoffset += INITDAT;
+				}
+				return C_ADDR;
+			}
+			instoffset = a->sym->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SEXT;
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			if(instoffset == 0)
+				return C_ZOREG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SOREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_OPT:
+		instoffset = a->offset & 31L;
+		if(a->name == D_NONE)
+			return C_SCON;
+		return C_GOK;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+		consize:
+			if(instoffset >= 0) {
+				if(r0iszero && instoffset == 0)
+					return C_ZCON;
+				if(instoffset <= 0x7fff)
+					return C_SCON;
+				if(instoffset <= 0xffff)
+					return C_ANDCON;
+				if((instoffset & 0xffff) == 0)
+					return C_UCON;
+				return C_LCON;
+			}
+			if(instoffset >= -0x8000)
+				return C_ADDCON;
+			if((instoffset & 0xffff) == 0)
+				return C_UCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF) {
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			if(s->type == SCONST) {
+				instoffset = s->value + a->offset;
+				if(dlm)
+					return C_LCON;
+				goto consize;
+			}
+			if(!dlm){
+				instoffset = s->value + a->offset - BIG;
+				if(instoffset >= -BIG && instoffset < BIG && instoffset != 0)
+					return C_SECON;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(dlm)
+				return C_LCON;
+			/* not sure why this barfs */
+			return C_LCON;
+		/*
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x8000 && instoffset <= 0xffff)
+				return C_SCON;
+			if((instoffset & 0xffff) == 0)
+				return C_UCON;
+			return C_LCON;
+		*/
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, a4, r;
+	char *c1, *c3, *c4;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->from3.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->from3) + 1;
+		p->from3.class = a3;
+	}
+	a3--;
+	a4 = p->to.class;
+	if(a4 == 0) {
+		a4 = aclass(&p->to) + 1;
+		p->to.class = a4;
+	}
+	a4--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0)
+		o = oprange[r].stop; /* just generate an error */
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	c4 = xcmp[a4];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3])
+		if(c4[o->a4]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %R %R %R %R",
+		p->as, a1, a2, a3, a4);
+	if(1||!debug['a'])
+		prasm(p);
+	if(o == 0)
+		errorexit();
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON)
+			return 1;
+		break;
+	case C_ADDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_ANDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_SPR:
+		if(b == C_LR || b == C_XER || b == C_CTR)
+			return 1;
+		break;
+	case C_UCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_SCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_SACON)
+			return 1;
+		break;
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_LEXT:
+		if(b == C_SEXT)
+			return 1;
+		break;
+	case C_LAUTO:
+		if(b == C_SAUTO)
+			return 1;
+		break;
+	case C_REG:
+		if(r0iszero && b == C_ZCON)
+			return 1;
+		break;
+	case C_LOREG:
+		if(b == C_ZOREG || b == C_SOREG)
+			return 1;
+		break;
+	case C_SOREG:
+		if(b == C_ZOREG)
+			return 1;
+		break;
+
+	case C_ANY:
+		return 1;
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = a1;
+	p2 = a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	n = p1->a4 - p2->a4;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	for(i=0; i<C_NCLASS; i++)
+		for(n=0; n<C_NCLASS; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+		
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case ADCBF:	/* unary indexed: op (b+a); op (b) */
+			oprange[ADCBI] = oprange[r];
+			oprange[ADCBST] = oprange[r];
+			oprange[ADCBT] = oprange[r];
+			oprange[ADCBTST] = oprange[r];
+			oprange[ADCBZ] = oprange[r];
+			oprange[AICBI] = oprange[r];
+			break;
+		case AECOWX:	/* indexed store: op s,(b+a); op s,(b) */
+			oprange[ASTWCCC] = oprange[r];
+			break;
+		case AREM:	/* macro */
+			oprange[AREMCC] = oprange[r];
+			oprange[AREMV] = oprange[r];
+			oprange[AREMVCC] = oprange[r];
+			oprange[AREMU] = oprange[r];
+			oprange[AREMUCC] = oprange[r];
+			oprange[AREMUV] = oprange[r];
+			oprange[AREMUVCC] = oprange[r];
+			break;
+		case ADIVW:	/* op Rb[,Ra],Rd */
+			oprange[AMULHW] = oprange[r];
+			oprange[AMULHWCC] = oprange[r];
+			oprange[AMULHWU] = oprange[r];
+			oprange[AMULHWUCC] = oprange[r];
+			oprange[AMULLWCC] = oprange[r];
+			oprange[AMULLWVCC] = oprange[r];
+			oprange[AMULLWV] = oprange[r];
+			oprange[ADIVWCC] = oprange[r];
+			oprange[ADIVWV] = oprange[r];
+			oprange[ADIVWVCC] = oprange[r];
+			oprange[ADIVWU] = oprange[r];
+			oprange[ADIVWUCC] = oprange[r];
+			oprange[ADIVWUV] = oprange[r];
+			oprange[ADIVWUVCC] = oprange[r];
+			oprange[AADDCC] = oprange[r];
+			oprange[AADDCV] = oprange[r];
+			oprange[AADDCVCC] = oprange[r];
+			oprange[AADDV] = oprange[r];
+			oprange[AADDVCC] = oprange[r];
+			oprange[AADDE] = oprange[r];
+			oprange[AADDECC] = oprange[r];
+			oprange[AADDEV] = oprange[r];
+			oprange[AADDEVCC] = oprange[r];
+			oprange[ACRAND] = oprange[r];
+			oprange[ACRANDN] = oprange[r];
+			oprange[ACREQV] = oprange[r];
+			oprange[ACRNAND] = oprange[r];
+			oprange[ACRNOR] = oprange[r];
+			oprange[ACROR] = oprange[r];
+			oprange[ACRORN] = oprange[r];
+			oprange[ACRXOR] = oprange[r];
+			oprange[AMULCHW] = oprange[r];
+			oprange[AMULCHWCC] = oprange[r];
+			oprange[AMULCHWU] = oprange[r];
+			oprange[AMULCHWUCC] = oprange[r];
+			oprange[AMULHHW] = oprange[r];
+			oprange[AMULHHWCC] = oprange[r];
+			oprange[AMULHHWU] = oprange[r];
+			oprange[AMULHHWUCC] = oprange[r];
+			oprange[AMULLHW] = oprange[r];
+			oprange[AMULLHWCC] = oprange[r];
+			oprange[AMULLHWU] = oprange[r];
+			oprange[AMULLHWUCC] = oprange[r];
+			break;
+		case AMACCHW:	/* strictly 3 registers */
+			oprange[AMACCHWCC] = oprange[r];
+			oprange[AMACCHWS] = oprange[r];
+			oprange[AMACCHWSCC] = oprange[r];
+			oprange[AMACCHWSU] = oprange[r];
+			oprange[AMACCHWSUCC] = oprange[r];
+			oprange[AMACCHWSUV] = oprange[r];
+			oprange[AMACCHWSUVCC] = oprange[r];
+			oprange[AMACCHWSV] = oprange[r];
+			oprange[AMACCHWSVCC] = oprange[r];
+			oprange[AMACCHWU] = oprange[r];
+			oprange[AMACCHWUCC] = oprange[r];
+			oprange[AMACCHWUV] = oprange[r];
+			oprange[AMACCHWUVCC] = oprange[r];
+			oprange[AMACCHWV] = oprange[r];
+			oprange[AMACCHWVCC] = oprange[r];
+			oprange[AMACHHW] = oprange[r];
+			oprange[AMACHHWCC] = oprange[r];
+			oprange[AMACHHWS] = oprange[r];
+			oprange[AMACHHWSCC] = oprange[r];
+			oprange[AMACHHWSU] = oprange[r];
+			oprange[AMACHHWSUCC] = oprange[r];
+			oprange[AMACHHWSUV] = oprange[r];
+			oprange[AMACHHWSUVCC] = oprange[r];
+			oprange[AMACHHWSV] = oprange[r];
+			oprange[AMACHHWSVCC] = oprange[r];
+			oprange[AMACHHWU] = oprange[r];
+			oprange[AMACHHWUCC] = oprange[r];
+			oprange[AMACHHWUV] = oprange[r];
+			oprange[AMACHHWUVCC] = oprange[r];
+			oprange[AMACHHWV] = oprange[r];
+			oprange[AMACHHWVCC] = oprange[r];
+			oprange[AMACLHW] = oprange[r];
+			oprange[AMACLHWCC] = oprange[r];
+			oprange[AMACLHWS] = oprange[r];
+			oprange[AMACLHWSCC] = oprange[r];
+			oprange[AMACLHWSU] = oprange[r];
+			oprange[AMACLHWSUCC] = oprange[r];
+			oprange[AMACLHWSUV] = oprange[r];
+			oprange[AMACLHWSUVCC] = oprange[r];
+			oprange[AMACLHWSV] = oprange[r];
+			oprange[AMACLHWSVCC] = oprange[r];
+			oprange[AMACLHWU] = oprange[r];
+			oprange[AMACLHWUCC] = oprange[r];
+			oprange[AMACLHWUV] = oprange[r];
+			oprange[AMACLHWUVCC] = oprange[r];
+			oprange[AMACLHWV] = oprange[r];
+			oprange[AMACLHWVCC] = oprange[r];
+			oprange[ANMACCHW] = oprange[r];
+			oprange[ANMACCHWCC] = oprange[r];
+			oprange[ANMACCHWS] = oprange[r];
+			oprange[ANMACCHWSCC] = oprange[r];
+			oprange[ANMACCHWSV] = oprange[r];
+			oprange[ANMACCHWSVCC] = oprange[r];
+			oprange[ANMACCHWV] = oprange[r];
+			oprange[ANMACCHWVCC] = oprange[r];
+			oprange[ANMACHHW] = oprange[r];
+			oprange[ANMACHHWCC] = oprange[r];
+			oprange[ANMACHHWS] = oprange[r];
+			oprange[ANMACHHWSCC] = oprange[r];
+			oprange[ANMACHHWSV] = oprange[r];
+			oprange[ANMACHHWSVCC] = oprange[r];
+			oprange[ANMACHHWV] = oprange[r];
+			oprange[ANMACHHWVCC] = oprange[r];
+			oprange[ANMACLHW] = oprange[r];
+			oprange[ANMACLHWCC] = oprange[r];
+			oprange[ANMACLHWS] = oprange[r];
+			oprange[ANMACLHWSCC] = oprange[r];
+			oprange[ANMACLHWSV] = oprange[r];
+			oprange[ANMACLHWSVCC] = oprange[r];
+			oprange[ANMACLHWV] = oprange[r];
+			oprange[ANMACLHWVCC] = oprange[r];
+			break;
+		case AMOVBZ:	/* lbz, stz, rlwm(r/r), lhz, lha, stz, and x variants */
+			oprange[AMOVH] = oprange[r];
+			oprange[AMOVHZ] = oprange[r];
+			break;
+		case AMOVBZU:	/* lbz[x]u, stb[x]u, lhz[x]u, lha[x]u, sth[u]x */
+			oprange[AMOVHU] = oprange[r];
+			oprange[AMOVHZU] = oprange[r];
+			oprange[AMOVWU] = oprange[r];
+			oprange[AMOVMW] = oprange[r];
+			break;
+		case AAND:	/* logical op Rb,Rs,Ra; no literal */
+			oprange[AANDN] = oprange[r];
+			oprange[AANDNCC] = oprange[r];
+			oprange[AEQV] = oprange[r];
+			oprange[AEQVCC] = oprange[r];
+			oprange[ANAND] = oprange[r];
+			oprange[ANANDCC] = oprange[r];
+			oprange[ANOR] = oprange[r];
+			oprange[ANORCC] = oprange[r];
+			oprange[AORCC] = oprange[r];
+			oprange[AORN] = oprange[r];
+			oprange[AORNCC] = oprange[r];
+			oprange[AXORCC] = oprange[r];
+			break;
+		case AADDME:	/* op Ra, Rd */
+			oprange[AADDMECC] = oprange[r];
+			oprange[AADDMEV] = oprange[r];
+			oprange[AADDMEVCC] = oprange[r];
+			oprange[AADDZE] = oprange[r];
+			oprange[AADDZECC] = oprange[r];
+			oprange[AADDZEV] = oprange[r];
+			oprange[AADDZEVCC] = oprange[r];
+			oprange[ASUBME] = oprange[r];
+			oprange[ASUBMECC] = oprange[r];
+			oprange[ASUBMEV] = oprange[r];
+			oprange[ASUBMEVCC] = oprange[r];
+			oprange[ASUBZE] = oprange[r];
+			oprange[ASUBZECC] = oprange[r];
+			oprange[ASUBZEV] = oprange[r];
+			oprange[ASUBZEVCC] = oprange[r];
+			break;
+		case AADDC:
+			oprange[AADDCCC] = oprange[r];
+			break;
+		case ABEQ:
+			oprange[ABGE] = oprange[r];
+			oprange[ABGT] = oprange[r];
+			oprange[ABLE] = oprange[r];
+			oprange[ABLT] = oprange[r];
+			oprange[ABNE] = oprange[r];
+			oprange[ABVC] = oprange[r];
+			oprange[ABVS] = oprange[r];
+			break;
+		case ABR:
+			oprange[ABL] = oprange[r];
+			break;
+		case ABC:
+			oprange[ABCL] = oprange[r];
+			break;
+		case AEXTSB:	/* op Rs, Ra */
+			oprange[AEXTSBCC] = oprange[r];
+			oprange[AEXTSH] = oprange[r];
+			oprange[AEXTSHCC] = oprange[r];
+			oprange[ACNTLZW] = oprange[r];
+			oprange[ACNTLZWCC] = oprange[r];
+			break;
+		case AFABS:	/* fop [s,]d */
+			oprange[AFABSCC] = oprange[r];
+			oprange[AFNABS] = oprange[r];
+			oprange[AFNABSCC] = oprange[r];
+			oprange[AFNEG] = oprange[r];
+			oprange[AFNEGCC] = oprange[r];
+			oprange[AFRSP] = oprange[r];
+			oprange[AFRSPCC] = oprange[r];
+			oprange[AFCTIW] = oprange[r];
+			oprange[AFCTIWCC] = oprange[r];
+			oprange[AFCTIWZ] = oprange[r];
+			oprange[AFCTIWZCC] = oprange[r];
+			oprange[AFRES] = oprange[r];
+			oprange[AFRESCC] = oprange[r];
+			oprange[AFRSQRTE] = oprange[r];
+			oprange[AFRSQRTECC] = oprange[r];
+			oprange[AFSQRT] = oprange[r];
+			oprange[AFSQRTCC] = oprange[r];
+			oprange[AFSQRTS] = oprange[r];
+			oprange[AFSQRTSCC] = oprange[r];
+			oprange[AFPRE] = oprange[r];
+			oprange[AFPRSQRTE] = oprange[r];
+			oprange[AFPABS] = oprange[r];
+			oprange[AFPNEG] = oprange[r];
+			oprange[AFPRSP] = oprange[r];
+			oprange[AFPNABS] = oprange[r];
+			oprange[AFSABS] = oprange[r];
+			oprange[AFSNEG] = oprange[r];
+			oprange[AFSNABS] = oprange[r];
+			oprange[AFPCTIW] = oprange[r];
+			oprange[AFPCTIWZ] = oprange[r];
+			break;
+		case AFADD:
+			oprange[AFADDS] = oprange[r];
+			oprange[AFADDCC] = oprange[r];
+			oprange[AFADDSCC] = oprange[r];
+			oprange[AFDIV] = oprange[r];
+			oprange[AFDIVS] = oprange[r];
+			oprange[AFDIVCC] = oprange[r];
+			oprange[AFDIVSCC] = oprange[r];
+			oprange[AFSUB] = oprange[r];
+			oprange[AFSUBS] = oprange[r];
+			oprange[AFSUBCC] = oprange[r];
+			oprange[AFSUBSCC] = oprange[r];
+			oprange[AFPADD] = oprange[r];
+			oprange[AFPSUB] = oprange[r];
+			break;
+		case AFMADD:
+			oprange[AFMADDCC] = oprange[r];
+			oprange[AFMADDS] = oprange[r];
+			oprange[AFMADDSCC] = oprange[r];
+			oprange[AFMSUB] = oprange[r];
+			oprange[AFMSUBCC] = oprange[r];
+			oprange[AFMSUBS] = oprange[r];
+			oprange[AFMSUBSCC] = oprange[r];
+			oprange[AFNMADD] = oprange[r];
+			oprange[AFNMADDCC] = oprange[r];
+			oprange[AFNMADDS] = oprange[r];
+			oprange[AFNMADDSCC] = oprange[r];
+			oprange[AFNMSUB] = oprange[r];
+			oprange[AFNMSUBCC] = oprange[r];
+			oprange[AFNMSUBS] = oprange[r];
+			oprange[AFNMSUBSCC] = oprange[r];
+			oprange[AFSEL] = oprange[r];
+			oprange[AFSELCC] = oprange[r];
+			oprange[AFPSEL] = oprange[r];
+			oprange[AFPMADD] = oprange[r];
+			oprange[AFXMADD] = oprange[r];
+			oprange[AFXCPMADD] = oprange[r];
+			oprange[AFXCSMADD] = oprange[r];
+			oprange[AFPNMADD] = oprange[r];
+			oprange[AFXNMADD] = oprange[r];
+			oprange[AFXCPNMADD] = oprange[r];
+			oprange[AFXCSNMADD] = oprange[r];
+			oprange[AFPMSUB] = oprange[r];
+			oprange[AFXMSUB] = oprange[r];
+			oprange[AFXCPMSUB] = oprange[r];
+			oprange[AFXCSMSUB] = oprange[r];
+			oprange[AFPNMSUB] = oprange[r];
+			oprange[AFXNMSUB] = oprange[r];
+			oprange[AFXCPNMSUB] = oprange[r];
+			oprange[AFXCSNMSUB] = oprange[r];
+			oprange[AFXCPNPMA] = oprange[r];
+			oprange[AFXCSNPMA] = oprange[r];
+			oprange[AFXCPNSMA] = oprange[r];
+			oprange[AFXCSNSMA] = oprange[r];
+			oprange[AFXCXNPMA] = oprange[r];
+			oprange[AFXCXNSMA] = oprange[r];
+			oprange[AFXCXMA] = oprange[r];
+			oprange[AFXCXNMS] = oprange[r];
+			break;
+		case AFMUL:
+			oprange[AFMULS] = oprange[r];
+			oprange[AFMULCC] = oprange[r];
+			oprange[AFMULSCC] = oprange[r];
+			oprange[AFPMUL] = oprange[r];
+			oprange[AFXMUL] = oprange[r];
+			oprange[AFXPMUL] = oprange[r];
+			oprange[AFXSMUL] = oprange[r];
+			break;
+		case AFCMPO:
+			oprange[AFCMPU] = oprange[r];
+			break;
+		case AMTFSB0:
+			oprange[AMTFSB0CC] = oprange[r];
+			oprange[AMTFSB1] = oprange[r];
+			oprange[AMTFSB1CC] = oprange[r];
+			break;
+		case ANEG:	/* op [Ra,] Rd */
+			oprange[ANEGCC] = oprange[r];
+			oprange[ANEGV] = oprange[r];
+			oprange[ANEGVCC] = oprange[r];
+			break;
+		case AOR:	/* or/xor Rb,Rs,Ra; ori/xori $uimm,Rs,Ra; oris/xoris $uimm,Rs,Ra */
+			oprange[AXOR] = oprange[r];
+			break;
+		case ASLW:
+			oprange[ASLWCC] = oprange[r];
+			oprange[ASRW] = oprange[r];
+			oprange[ASRWCC] = oprange[r];
+			break;
+		case ASRAW:	/* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */
+			oprange[ASRAWCC] = oprange[r];
+			break;
+		case ASUB:	/* SUB Ra,Rb,Rd => subf Rd,ra,rb */
+			oprange[ASUB] = oprange[r];
+			oprange[ASUBCC] = oprange[r];
+			oprange[ASUBV] = oprange[r];
+			oprange[ASUBVCC] = oprange[r];
+			oprange[ASUBCCC] = oprange[r];
+			oprange[ASUBCV] = oprange[r];
+			oprange[ASUBCVCC] = oprange[r];
+			oprange[ASUBE] = oprange[r];
+			oprange[ASUBECC] = oprange[r];
+			oprange[ASUBEV] = oprange[r];
+			oprange[ASUBEVCC] = oprange[r];
+			break;
+		case ASYNC:
+			oprange[AISYNC] = oprange[r];
+			break;
+		case ARLWMI:
+			oprange[ARLWMICC] = oprange[r];
+			oprange[ARLWNM] = oprange[r];
+			oprange[ARLWNMCC] = oprange[r];
+			break;
+		case AFMOVD:
+			oprange[AFMOVDCC] = oprange[r];
+			oprange[AFMOVDU] = oprange[r];
+			oprange[AFMOVS] = oprange[r];
+			oprange[AFMOVSU] = oprange[r];
+			break;
+		case AECIWX:
+			oprange[ALWAR] = oprange[r];
+			break;
+		case ASYSCALL:	/* just the op; flow of control */
+			oprange[ARFI] = oprange[r];
+			oprange[ARFCI] = oprange[r];
+			break;
+		case AMOVHBR:
+			oprange[AMOVWBR] = oprange[r];
+			break;
+		case AFSMOVS:	/* indexed floating loads and stores (fp2) */
+			oprange[AFSMOVSU] = oprange[r];
+			oprange[AFSMOVDU] = oprange[r];
+			oprange[AFXMOVS] = oprange[r];
+			oprange[AFXMOVSU] = oprange[r];
+			oprange[AFXMOVDU] = oprange[r];
+			oprange[AFPMOVS] = oprange[r];
+			oprange[AFPMOVSU] = oprange[r];
+			oprange[AFPMOVDU] = oprange[r];
+			oprange[AFPMOVIW] = oprange[r];
+			break;
+		case AFPMOVD:	/* indexed load/store and moves (fp2) */
+			oprange[AFSMOVD] = oprange[r];
+			oprange[AFXMOVD] = oprange[r];
+			break;
+		case AFMOVSPD:	/* move between fp reg sets (fp2) */
+			oprange[AFMOVPSD] = oprange[r];
+			break;
+		case AADD:
+		case AANDCC:	/* and. Rb,Rs,Ra; andi. $uimm,Rs,Ra; andis. $uimm,Rs,Ra */
+		case ACMP:
+		case ACMPU:
+		case AEIEIO:
+		case ALSW:
+		case AMOVB:	/* macro: move byte with sign extension */
+		case AMOVBU:	/* macro: move byte with sign extension & update */
+		case AMOVW:
+		case AMOVFL:
+		case AMULLW:	/* op $s[,r2],r3; op r1[,r2],r3; no cc/v */
+		case ASUBC:	/* op r1,$s,r3; op r1[,r2],r3 */
+		case ASTSW:
+		case ATLBIE:
+		case ATW:
+		case AWORD:
+		case ANOP:
+		case ATEXT:
+			break;
+		}
+	}
+}
+
+enum{
+	ABSD = 0,
+	ABSU = 1,
+	RELD = 2,
+	RELU = 3,
+};
+
+int modemap[8] = { 0, 1, -1, 2, 3, 4, 5, 6};
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+	int n;
+	int t;
+	uchar *m;
+	ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+	int t;
+	uchar *m, *nm;
+	ulong *a, *na;
+
+	t = r->t;
+	r->t += 64;
+	m = r->m;
+	a = r->a;
+	r->m = nm = malloc(r->t*sizeof(uchar));
+	r->a = na = malloc(r->t*sizeof(ulong));
+	memmove(nm, m, t*sizeof(uchar));
+	memmove(na, a, t*sizeof(ulong));
+	free(m);
+	free(a);
+}
+
+void
+dynreloc(Sym *s, long v, int abs, int split, int sext)
+{
+	int i, k, n;
+	uchar *m;
+	ulong *a;
+	Reloc *r;
+
+	if(v&3)
+		diag("bad relocation address");
+	v >>= 2;
+	if(s->type == SUNDEF)
+		k = abs ? ABSU : RELU;
+	else
+		k = abs ? ABSD : RELD;
+	if(split)
+		k += 4;
+	if(sext)
+		k += 2;
+	/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
+	k = modemap[k];
+	r = &rels;
+	n = r->n;
+	if(n >= r->t)
+		grow(r);
+	m = r->m;
+	a = r->a;
+	for(i = n; i > 0; i--){
+		if(v < a[i-1]){	/* happens occasionally for data */
+			m[i] = m[i-1];
+			a[i] = a[i-1];
+		}
+		else
+			break;
+	}
+	m[i] = k;
+	a[i] = v;
+	r->n++;
+}
+
+static int
+sput(char *s)
+{
+	char *p;
+
+	p = s;
+	while(*s)
+		cput(*s++);
+	cput(0);
+	return s-p+1;
+}
+
+void
+asmdyn()
+{
+	int i, n, t, c;
+	Sym *s;
+	ulong la, ra, *a;
+	vlong off;
+	uchar *m;
+	Reloc *r;
+
+	cflush();
+	off = seek(cout, 0, 1);
+	lput(0);
+	t = 0;
+	lput(imports);
+	t += 4;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type == SUNDEF){
+				lput(s->sig);
+				t += 4;
+				t += sput(s->name);
+			}
+	
+	la = 0;
+	r = &rels;
+	n = r->n;
+	m = r->m;
+	a = r->a;
+	lput(n);
+	t += 4;
+	for(i = 0; i < n; i++){
+		ra = *a-la;
+		if(*a < la)
+			diag("bad relocation order");
+		if(ra < 256)
+			c = 0;
+		else if(ra < 65536)
+			c = 1;
+		else
+			c = 2;
+		cput((c<<6)|*m++);
+		t++;
+		if(c == 0){
+			cput(ra);
+			t++;
+		}
+		else if(c == 1){
+			wput(ra);
+			t += 2;
+		}
+		else{
+			lput(ra);
+			t += 4;
+		}
+		la = *a++;
+	}
+
+	cflush();
+	seek(cout, off, 0);
+	lput(t);
+
+	if(debug['v']){
+		Bprint(&bso, "import table entries = %d\n", imports);
+		Bprint(&bso, "export table entries = %d\n", exports);
+	}
+}
--- /dev/null
+++ b/utils/rcsh/Nt.c
@@ -1,0 +1,549 @@
+#include "rc.h"
+#include <windows.h>
+
+enum {
+	Nchild	= 100,
+};
+
+typedef struct Child	Child;
+
+struct Child {
+	int	pid;
+	HANDLE	handle;
+};
+
+static Child child[Nchild];
+
+static void
+winerror(void)
+{
+	int e, r;
+	char buf[100], *p, *q;
+
+	e = GetLastError();
+	
+	r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+		0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		buf, sizeof(buf), 0);
+
+	if(r == 0)
+		snprint(buf, sizeof(buf), "windows error %d", e);
+
+	for(p=q=buf; *p; p++) {
+		if(*p == '\r')
+			continue;
+		if(*p == '\n')
+			*q++ = ' ';
+		else
+			*q++ = *p;
+	}
+	*q = 0;
+	errstr(buf, sizeof buf);
+}
+
+static int
+badentry(char *filename)
+{
+	if(*filename == 0)
+		return 1;
+	if(filename[0] == '.'){
+		if(filename[1] == 0)
+			return 1;
+		if(filename[1] == '.' && filename[2] == 0)
+			return 1;
+	}
+	return 0;
+}
+
+Direntry*
+readdirect(char *path)
+{
+	long n;
+	HANDLE h;
+	Direntry *d;
+	char fullpath[MAX_PATH];
+	WIN32_FIND_DATA data;
+
+	snprint(fullpath, MAX_PATH, "%s\\*.*", path);
+
+	h = FindFirstFile(fullpath, &data);
+	if(h == INVALID_HANDLE_VALUE)
+		return 0;
+
+	n = 0;
+	d = 0;
+	for(;;){
+		if(!badentry(data.cFileName)){
+			d = realloc(d, (n+2)*sizeof(Direntry));
+			if(d == 0){
+				werrstr("memory allocation");
+				return 0;
+			}
+			d[n].name = malloc(strlen(data.cFileName)+1);
+			if(d[n].name == 0){
+				werrstr("memory allocation");
+				return 0;
+			}
+			strcpy(d[n].name, data.cFileName);
+			if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+				d[n].isdir = 1;
+			else
+				d[n].isdir = 0;
+			n++;
+		}
+		if(FindNextFile(h, &data) == 0)
+			break;
+	}
+	FindClose(h);
+	if(d){
+		d[n].name = 0;
+		d[n].isdir = 0;
+	}
+	return d;
+}
+
+void
+fatal(char *fmt, ...)
+{
+	char buf[512];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+
+	fprint(2, "rc: %s\n", buf);
+	_exits(buf);
+}
+
+static int
+tas(int *p)
+{	
+	int v;
+	
+	_asm {
+		mov	eax, p
+		mov	ebx, 1
+		xchg	ebx, [eax]
+		mov	v, ebx
+	}
+
+	return v;
+}
+
+static void
+lock(Lock *lk)
+{
+	int i;
+
+	/* easy case */
+	if(!tas(&lk->val))
+		return;
+
+	/* for muli processor machines */
+	for(i=0; i<100; i++)
+		if(!tas(&lk->val))
+			return;
+
+	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+	for(;;) {
+		for(i=0; i<10000; i++) {
+			Sleep(0);
+			if(!tas(&lk->val)) {
+				SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+				return;
+			}
+		}
+	}
+}
+
+static void
+unlock(Lock *lk)
+{
+	lk->val = 0;
+}
+
+int
+refinc(Ref *r)
+{
+	int i;
+
+	lock(&r->lk);
+	i = r->ref;
+	r->ref++;
+	unlock(&r->lk);
+	return i;
+}
+
+int	
+refdec(Ref *r)
+{
+	int i;
+
+	lock(&r->lk);
+	r->ref--;
+	i = r->ref;
+	unlock(&r->lk);
+
+	return i;
+}
+
+/*
+ * windows quoting rules - I think
+ * Words are seperated by space or tab
+ * Words containing a space or tab can be quoted using "
+ * 2N backslashes + " ==> N backslashes and end quote
+ * 2N+1 backslashes + " ==> N backslashes + literal "
+ * N backslashes not followed by " ==> N backslashes
+ */
+static char *
+dblquote(char *cmd, char *s)
+{
+	int nb;
+	char *p;
+
+	for(p=s; *p; p++)
+		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
+			break;
+
+	if(*p == 0){				/* easy case */
+		strcpy(cmd, s);
+		return cmd+(p-s);
+	}
+
+	*cmd++ = '"';
+	for(;;) {
+		for(nb=0; *s=='\\'; nb++)
+			*cmd++ = *s++;
+
+		if(*s == 0) {			/* trailing backslashes -> 2N */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			break;
+		}
+
+		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */
+			while(nb-- > 0)
+				*cmd++ = '\\';
+			*cmd++ = '\\';		/* escape the quote */
+		}
+		*cmd++ = *s++;
+	}
+
+	*cmd++ = '"';
+	*cmd = 0;
+
+	return cmd;
+}
+
+static char *
+proccmd(char **argv)
+{
+	int i, n;
+	char *cmd, *p;
+
+		/* conservatively calculate length of command;
+		 * backslash expansion can cause growth in dblquote().
+		 */
+	for(i=0,n=0; argv[i]; i++) {
+		n += 2*strlen(argv[i]);
+	}
+	n++;
+	
+	cmd = malloc(n);
+	for(i=0,p=cmd; argv[i]; i++) {
+		p = dblquote(p, argv[i]);
+		*p++ = ' ';
+	}
+	if(p != cmd)
+		p--;
+	*p = 0;
+
+	return cmd;
+}
+
+static char *
+exportenv(char **e)
+{
+	int i, j, n;
+	char *buf;
+
+	if(e == 0 || *e == 0)
+		return 0;
+
+	buf = 0;
+	n = 0;
+	for(i = 0; *e; e++, i++) {
+		j = strlen(*e)+1;
+		buf = realloc(buf, n+j);
+		strcpy(buf+n, *e);
+		n += j;
+	}
+	/* final null */
+	buf = realloc(buf, n+1);
+	buf[n] = 0;
+
+	return buf;
+}
+
+static int
+setpath(char *path, char *file)
+{
+	char *p, *last, tmp[MAX_PATH+1];
+	int n;
+
+	if(strlen(file) >= MAX_PATH){
+		werrstr("file name too long");
+		return -1;
+	}
+	strcpy(tmp, file);
+
+	for(p=tmp; *p; p++) {
+		if(*p == '/')
+			*p = '\\';
+	}
+
+	if(tmp[0] != 0 && tmp[1] == ':') {
+		if(tmp[2] == 0) {
+			tmp[2] = '\\';
+			tmp[3] = 0;
+		} else if(tmp[2] != '\\') {
+			/* don't allow c:foo - only c:\foo */
+			werrstr("illegal file name");
+			return -1;
+		}
+	}
+
+	path[0] = 0;
+	n = GetFullPathName(tmp, MAX_PATH, path, &last);
+	if(n >= MAX_PATH) {
+		werrstr("file name too long");
+		return -1;
+	}
+	if(n == 0 && tmp[0] == '\\' && tmp[1] == '\\' && tmp[2] != 0) {
+		strcpy(path, tmp);
+		return -1;
+	}
+
+	if(n == 0) {
+		werrstr("bad file name");
+		return -1;
+	}
+
+	for(p=path; *p; p++) {
+		if(*p < 32 || *p == '*' || *p == '?') {
+			werrstr("file not found");
+			return -1;
+		}
+	}
+
+	/* get rid of trailling \ */
+	if(path[n-1] == '\\') {
+		if(n <= 2) {
+			werrstr("illegal file name");
+			return -1;
+		}
+		path[n-1] = 0;
+		n--;
+	}
+
+	if(path[1] == ':' && path[2] == 0) {
+		path[2] = '\\';
+		path[3] = '.';
+		path[4] = 0;
+		return -1;
+	}
+
+	if(path[0] != '\\' || path[1] != '\\')
+		return 0;
+
+	for(p=path+2,n=0; *p; p++)
+		if(*p == '\\')
+			n++;
+	if(n == 0)
+		return -1;
+	if(n == 1)
+		return -1;
+	return 0;
+}
+
+
+static int
+execpath(char *path, char *file)
+{
+	int n;
+
+	if(setpath(path, file) < 0)
+		return 0;
+
+	n = strlen(path)-4;
+	if(path[n] == '.') {
+		if(GetFileAttributes(path) != -1)
+			return 1;
+	}
+	strncat(path, ".exe", MAX_PATH);
+	path[MAX_PATH-1] = 0;
+	if(GetFileAttributes(path) != -1)
+		return 1;
+	return 0;
+}
+
+static HANDLE
+fdexport(int fd)
+{
+	HANDLE h, r;
+
+	if(fd < 0)
+		return INVALID_HANDLE_VALUE;
+
+	h = (HANDLE)_get_osfhandle(fd);
+	if(h < 0)
+		return INVALID_HANDLE_VALUE;
+
+	if(!DuplicateHandle(GetCurrentProcess(), h,
+				GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS,
+				1, DUPLICATE_SAME_ACCESS))
+		return INVALID_HANDLE_VALUE;
+	return r;
+}
+
+static int
+addchild(int pid, HANDLE handle)
+{
+	int i;
+	
+	for(i=0; i<Nchild; i++) {
+		if(child[i].handle == 0) {
+			child[i].handle = handle;
+			child[i].pid = pid;
+			return 1;
+		}
+	}
+	werrstr("child table full");
+	return 0;
+}
+
+int
+procwait(uint pid)
+{
+	HANDLE h;
+	int i, exit;
+	
+	if(pid == 0)
+		return 0;
+
+	h = 0;
+	for(i = 0; i < Nchild; i++){
+		if(child[i].pid == pid){
+			h = child[i].handle;
+			child[i].pid = 0;
+			child[i].handle = 0;
+			break;
+		}
+	}
+
+	if(h == 0){	/* we don't know about this one - let the system try to find it */
+		h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+		if(h == 0)
+			return 0;		/* can't find it */
+	}
+
+	if(WaitForSingleObject(h, INFINITE) == WAIT_FAILED) {
+		winerror();
+		fatal("procwait: ");
+	}
+
+	if(!GetExitCodeProcess(h, &exit)) {
+		winerror();
+		exit = 1;
+	}
+
+	CloseHandle(h);
+	return exit;
+}
+
+uint
+proc(char **argv, int stdin, int stdout, int stderr)
+{
+	char *p, *arg0, *q, buf[MAX_PATH], path[MAX_PATH], *cmd, *eb;
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	int r, found, full;
+	extern char **_environ;
+	Word *w;
+
+	arg0 = argv[0];
+	if(arg0 == 0) {
+		werrstr("null argv[0]");
+		return 0;
+	}
+
+	full = arg0[0] == '\\' || arg0[0] == '/' || arg0[0] == '.';
+	found = execpath(path, arg0);
+
+	if(!found && !full) {
+		w = vlook("path")->val;
+		if(w)
+			p = w->word;
+		else
+			p = getenv("path");
+		for(; p && *p; p = q){
+			q = strchr(p, ';');
+			if(q)
+				*q = 0;
+			snprint(buf, sizeof(buf), "%s/%s", p, arg0);
+			if(q)
+				*q++ = ';';
+			found = execpath(path, buf);
+			if(found)
+				break;
+		}
+	}
+
+	if(!found) {
+		werrstr("file not found");
+		return 0;
+	}
+
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+	si.wShowWindow = SW_SHOW;
+	si.hStdInput = fdexport(stdin);
+	si.hStdOutput = fdexport(stdout);
+	si.hStdError = fdexport(stderr);
+
+	eb = exportenv(_environ);
+
+	cmd = proccmd(argv);
+
+	r = CreateProcess(path, cmd, 0, 0, 1, 0, eb, 0, &si, &pi);
+
+	/* allow child to run */
+	Sleep(0);
+
+	free(cmd);
+	free(eb);
+
+	CloseHandle(si.hStdInput);
+	CloseHandle(si.hStdOutput);
+	CloseHandle(si.hStdError);
+
+	if(!r) {
+		winerror();
+		return 0;
+	}
+
+	CloseHandle(pi.hThread);
+
+	if(addchild(pi.dwProcessId, pi.hProcess) == 0)
+		return 0;
+
+	return pi.dwProcessId;
+}
+
+int
+pipe(int *fd)
+{
+	return _pipe(fd, 0, _O_BINARY);
+}
--- /dev/null
+++ b/utils/rcsh/code.c
@@ -1,0 +1,486 @@
+#include "rc.h"
+#include "y.tab.h"
+
+#define	c0	t->child[0]
+#define	c1	t->child[1]
+#define	c2	t->child[2]
+#define	emitf(x) ((codep!=ncode || morecode()), codebuf[codep].f=(x), codep++)
+#define	emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i=(x), codep++)
+#define	emits(x) ((codep!=ncode || morecode()), codebuf[codep].s=(x), codep++)
+
+void	stuffdot(int);
+char	*fnstr(Tree*);
+void	outcode(Tree*, int);
+void	codeswitch(Tree*, int);
+int	iscase(Tree*);
+Code	*codecopy(Code*);
+void	codefree(Code*);
+
+int	codep, ncode;
+Code	*codebuf;
+
+int
+morecode(void)
+{
+	ncode+=100;
+	codebuf=realloc(codebuf, ncode*sizeof codebuf[0]);
+	if(codebuf==0)
+		panic("Can't realloc %d bytes in morecode!",
+				ncode*sizeof codebuf[0]);
+	return 0;	/* not used */
+}
+
+void
+stuffdot(int a)
+{
+	if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a);
+	codebuf[a].i=codep;
+}
+
+int
+compile(Tree *t)
+{
+	ncode=100;
+	codebuf=malloc(ncode*sizeof codebuf[0]);
+	codep=0;
+	emiti(0);			/* reference count */
+	outcode(t, flag['e']?1:0);
+	if(nerror){
+		free(codebuf);
+		return 0;
+	}
+/*	readhere(); */
+	emitf(Xreturn);
+	emitf(0);
+	return 1;
+}
+
+void
+cleanhere(char *f)
+{
+	emitf(Xdelhere);
+	emits(strdup(f));
+}
+
+char *
+fnstr(Tree *t)
+{
+	Io *f=openstr();
+	char *v;
+	extern char nl;
+	char svnl=nl;
+
+	nl=';';
+	pfmt(f, "%t", t);
+	nl=svnl;
+	v=f->strp;
+	f->strp=0;
+	closeio(f);
+	return v;
+}
+
+void
+outcode(Tree *t, int eflag)
+{
+	int p, q;
+	Tree *tt;
+
+	if(t==0)
+		return;
+	if(t->type != NOT && t->type != ';')
+		runq->iflast=0;
+	switch(t->type){
+	default:
+		pfmt(err, "bad type %d in outcode\n", t->type);
+		break;
+	case '$':
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xdol);
+		break;
+	case '"':
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xqdol);
+		break;
+	case SUB:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xsub);
+		break;
+	case '&':
+		emitf(Xasync);
+		emits(fnstr(c0));
+/*
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+*/
+		break;
+	case ';':
+		outcode(c0, eflag);
+		outcode(c1, eflag);
+		break;
+	case '^':
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xconc);
+		break;
+	case '`':
+		emitf(Xbackq);
+		emits(fnstr(c0));
+/*
+		p=emiti(0);
+		outcode(c0, 0);
+		emitf(Xexit);
+		stuffdot(p);
+*/
+		break;
+	case ANDAND:
+		outcode(c0, 0);
+		emitf(Xtrue);
+		p=emiti(0);
+		outcode(c1, eflag);
+		stuffdot(p);
+		break;
+	case ARGLIST:
+		outcode(c1, eflag);
+		outcode(c0, eflag);
+		break;
+	case BANG:
+		outcode(c0, eflag);
+		emitf(Xbang);
+		break;
+	case PCMD:
+	case BRACE:
+		outcode(c0, eflag);
+		break;
+	case COUNT:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xcount);
+		break;
+	case FN:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		if(c1){
+			emitf(Xfn);
+			p=emiti(0);
+			emits(fnstr(c1));
+			outcode(c1, eflag);
+			emitf(Xunlocal);	/* get rid of $* */
+			emitf(Xreturn);
+			stuffdot(p);
+		}
+		else
+			emitf(Xdelfn);
+		break;
+	case IF:
+		outcode(c0, 0);
+		emitf(Xif);
+		p=emiti(0);
+		outcode(c1, eflag);
+		emitf(Xwastrue);
+		stuffdot(p);
+		break;
+	case NOT:
+		if(!runq->iflast) yyerror("`if not' does not follow `if(...)'");
+		emitf(Xifnot);
+		p=emiti(0);
+		outcode(c0, eflag);
+		stuffdot(p);
+		break;
+	case OROR:
+		outcode(c0, 0);
+		emitf(Xfalse);
+		p=emiti(0);
+		outcode(c1, eflag);
+		stuffdot(p);
+		break;
+	case PAREN:
+		outcode(c0, eflag);
+		break;
+	case SIMPLE:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xsimple);
+		if(eflag) emitf(Xeflag);
+		break;
+	case SUBSHELL:
+		emitf(Xsubshell);
+		emits(fnstr(c0));
+/*
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+*/
+		if(eflag) emitf(Xeflag);
+		break;
+	case SWITCH:
+		codeswitch(t, eflag);
+		break;
+	case TWIDDLE:
+		emitf(Xmark);
+		outcode(c1, eflag);
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xmatch);
+		if(eflag) emitf(Xeflag);
+		break;
+	case WHILE:
+		q=codep;
+		emitf(Xsettrue);
+		outcode(c0, 0);
+		emitf(Xtrue);
+		p=emiti(0);
+		outcode(c1, eflag);
+		emitf(Xjump);
+		emiti(q);
+		stuffdot(p);
+		break;
+	case WORDS:
+		outcode(c1, eflag);
+		outcode(c0, eflag);
+		break;
+	case FOR:
+		emitf(Xmark);
+		if(c1){
+			outcode(c1, eflag);
+			emitf(Xglob);
+		}
+		else{
+			emitf(Xmark);
+			emitf(Xword);
+			emits(strdup("*"));
+			emitf(Xdol);
+		}
+		emitf(Xmark);		/* dummy value for Xlocal */
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xlocal);
+		p=emitf(Xfor);
+		q=emiti(0);
+		outcode(c2, eflag);
+		emitf(Xjump);
+		emiti(p);
+		stuffdot(q);
+		emitf(Xunlocal);
+		break;
+	case WORD:
+		emitf(Xword);
+		emits(strdup(t->str));
+		break;
+	case DUP:
+		if(t->rtype==DUPFD) {
+			emitf(Xdup);
+			emiti(t->fd0);
+			emiti(t->fd1);
+		} else { /* t->rtype == CLOSE */
+			emitf(Xclose);
+			emiti(t->fd0);
+		}
+		outcode(c1, eflag);
+		emitf(Xpopredir);
+		break;
+/*
+	case PIPEFD:
+		emitf(Xpipefd);
+		emiti(t->rtype);
+		p=emiti(0);
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+		break;
+*/
+	case REDIR:
+		emitf(Xmark);
+		outcode(c0, eflag);
+		emitf(Xglob);
+		switch(t->rtype){
+		case APPEND:
+			emitf(Xappend);
+			break;
+		case WRITE:
+			emitf(Xwrite);
+			break;
+		case READ:
+		case HERE:
+			emitf(Xread);
+			break;
+		}
+		emiti(t->fd0);
+		outcode(c1, eflag);
+		emitf(Xpopredir);
+		break;
+	case '=':
+		tt=t;
+		for(;t && t->type=='=';t=c2)
+			;
+		if(t){
+			for(t=tt;t->type=='=';t=c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				outcode(c0, eflag);
+				emitf(Xlocal);
+			}
+			t=tt;
+			outcode(c2, eflag);
+			for(;t->type=='=';t=c2)
+				emitf(Xunlocal);
+		}
+		else{
+			for(t=tt;t;t=c2){
+				emitf(Xmark);
+				outcode(c1, eflag);
+				emitf(Xmark);
+				outcode(c0, eflag);
+				emitf(Xassign);
+			}
+		}
+		t=tt;	/* so tests below will work */
+		break;
+	case PIPE:
+		emitf(Xpipe);
+		emiti(t->fd0);
+		emiti(t->fd1);
+		emits(fnstr(c0));
+		q=emiti(0);
+/*
+		outcode(c0, eflag);
+		emitf(Xexit);
+		stuffdot(p);
+*/
+		outcode(c1, eflag);
+		emitf(Xreturn);
+		stuffdot(q);
+		emitf(Xpipewait);
+		break;
+	}
+	if(t->type!=NOT && t->type!=';')
+		runq->iflast=t->type==IF;
+	else if(c0) runq->iflast=c0->type==IF;
+}
+
+/*
+ * switch code looks like this:
+ *	Xmark
+ *	(get switch value)
+ *	Xjump	1f
+ * out:	Xjump	leave
+ * 1:	Xmark
+ *	(get case values)
+ *	Xcase	1f
+ *	(commands)
+ *	Xjump	out
+ * 1:	Xmark
+ *	(get case values)
+ *	Xcase	1f
+ *	(commands)
+ *	Xjump	out
+ * 1:
+ * leave:
+ *	Xpopm
+ */
+void
+codeswitch(Tree *t, int eflag)
+{
+	int leave;		/* patch jump address to leave switch */
+	int out;		/* jump here to leave switch */
+	int nextcase;		/* patch jump address to next case */
+	Tree *tt;
+	if(c1->child[0]->type!=';'
+	|| !iscase(c1->child[0]->child[0])){
+		yyerror("case missing in switch");
+		return;
+	}
+	emitf(Xmark);
+	outcode(c0, eflag);
+	emitf(Xjump);
+	nextcase=emiti(0);
+	out=emitf(Xjump);
+	leave=emiti(0);
+	stuffdot(nextcase);
+	t=c1->child[0];
+	while(t->type==';'){
+		tt=c1;
+		emitf(Xmark);
+		for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag);
+		emitf(Xcase);
+		nextcase=emiti(0);
+		t=tt;
+		for(;;){
+			if(t->type==';'){
+				if(iscase(c0)) break;
+				outcode(c0, eflag);
+				t=c1;
+			}
+			else{
+				outcode(t, eflag);
+				break;
+			}
+		}
+		emitf(Xjump);
+		emiti(out);
+		stuffdot(nextcase);
+	}
+	stuffdot(leave);
+	emitf(Xpopm);
+}
+
+int
+iscase(Tree *t)
+{
+	if(t->type!=SIMPLE)
+		return 0;
+	do
+		t=c0;
+	while(t->type==ARGLIST);
+
+	return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0;
+}
+
+Code *
+codecopy(Code *cp)
+{
+	cp[0].i++;
+	return cp;
+}
+
+void
+codefree(Code *cp)
+{
+	Code *p;
+
+	if(--cp[0].i!=0)
+		return;
+
+	for(p=cp+1;p->f;){
+		if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
+		|| p->f==Xasync || p->f==Xcase || p->f==Xfalse
+		|| p->f==Xfor || p->f==Xjump
+		|| p->f==Xsubshell || p->f==Xtrue)
+			p+=2;
+		else if(p->f==Xdup || p->f==Xpipefd)
+			p+=3;
+		else if(p->f==Xpipe) {
+			free(p[3].s);
+			p+=5;
+		} else if(p->f==Xword || p->f==Xdelhere || p->f==Xbackq) {
+			free(p[1].s);
+			p+=2;
+		} else if(p->f==Xfn){
+			free(p[2].s);
+			p+=3;
+		} else
+			p++;
+	}
+
+	free(cp);
+}
--- /dev/null
+++ b/utils/rcsh/exec.c
@@ -1,0 +1,802 @@
+#include "rc.h"
+
+extern	char	*argv0;
+
+int	ifnot;
+int	eflagok;
+
+/*
+ * Opcode routines
+ * Arguments on stack (...)
+ * Arguments in line [...]
+ * Code in line with jump around {...}
+ *
+ * Xappend(file)[fd]			open file to append
+ * Xassign(name, val)			assign val to name
+ * Xasync{... Xexit}			make thread for {}, no wait
+ * Xbackq{... Xreturn}			make thread for {}, push stdout
+ * Xbang				complement condition
+ * Xcase(pat, value){...}		exec code on match, leave (value) on
+ * 					stack
+ * Xclose[i]				close file descriptor
+ * Xconc(left, right)			concatenate, push results
+ * Xcount(name)				push var count
+ * Xdelfn(name)				delete function definition
+ * Xdeltraps(names)			delete named traps
+ * Xdol(name)				get variable value
+ * Xqdol(name)				concatenate variable components
+ * Xdup[i j]				dup file descriptor
+ * Xexit				rc exits with status
+ * Xfalse{...}				execute {} if false
+ * Xfn(name){... Xreturn}			define function
+ * Xfor(var, list){... Xreturn}		for loop
+ * Xjump[addr]				goto
+ * Xlocal(name, val)			create local variable, assign value
+ * Xmark				mark stack
+ * Xmatch(pat, str)			match pattern, set status
+ * Xpipe[i j]{... Xreturn}{... Xreturn}	construct a pipe between 2 new threads,
+ * 					wait for both
+ * Xpipefd[type]{... Xreturn}		connect {} to pipe (input or output,
+ * 					depending on type), push /dev/fd/??
+ * Xpopm(value)				pop value from stack
+ * Xread(file)[fd]			open file to read
+ * Xsettraps(names){... Xreturn}		define trap functions
+ * Xshowtraps				print trap list
+ * Xsimple(args)			run command and wait
+ * Xreturn				kill thread
+ * Xsubshell{... Xexit}			execute {} in a subshell and wait
+ * Xtrue{...}				execute {} if true
+ * Xunlocal				delete local variable
+ * Xword[string]			push string
+ * Xwrite(file)[fd]			open file to write
+ */
+
+static char **
+rcargv(char *s)
+{
+	char *flags;
+
+	if(flag['e'])
+		flags = "-Se";
+	else
+		flags = "-S";
+	return procargv(argv0, flags, "-c", s, vlook("*")->val);
+}
+
+void
+Xappend(void)
+{
+	char *file;
+	int f;
+
+	switch(count(runq->argv->words)){
+	default: Xerror(">> requires singleton"); return;
+	case 0: Xerror(">> requires file"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f=open(file, 1))<0 && (f=create(file, 1, 0666))<0){
+		Xperror(file);
+		return;
+	}
+	seek(f, 0L, 2);
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+
+void
+Xassign(void)
+{
+	Var *v;
+
+	if(count(runq->argv->words)!=1){
+		Xerror("variable name not singleton!");
+		return;
+	}
+	deglob(runq->argv->words->word);
+	v=vlook(runq->argv->words->word);
+	poplist();
+	globlist();
+	freewords(v->val);
+	v->val=runq->argv->words;
+	v->changed=1;
+	runq->argv->words=0;
+	poplist();
+}
+
+void
+Xasync(void)
+{
+	uint pid;
+	char buf[20], **argv;
+
+	updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = proc(argv, -1, 1, 2);
+	free(argv);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		return;
+	}
+
+	runq->pc++;
+	sprint(buf, "%d", pid);
+	setvar("apid", newword(buf, (Word *)0));
+}
+
+void
+Xbackq(void)
+{
+	char wd[8193], **argv;
+	int c;
+	char *s, *ewd=&wd[8192], *stop;
+	Io *f;
+	Var *ifs=vlook("ifs");
+	Word *v, *nextv;
+	int pfd[2];
+	int pid;
+
+	stop = ifs->val?ifs->val->word:"";
+	if(pipe(pfd)<0){
+		Xerror("can't make pipe");
+		return;
+	}
+
+	updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = proc(argv, -1, pfd[1], 2);
+	free(argv);
+
+	close(pfd[1]);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		close(pfd[0]);
+		return;
+	}
+
+	f = openfd(pfd[0]);
+	s = wd;
+	v = 0;
+	while((c=rchr(f))!=EOF){
+		if(strchr(stop, c) || s==ewd){
+			if(s!=wd){
+				*s='\0';
+				v=newword(wd, v);
+				s=wd;
+			}
+		}
+		else *s++=c;
+	}
+	if(s!=wd){
+		*s='\0';
+		v=newword(wd, v);
+	}
+	closeio(f);
+	waitfor(pid);
+	/* v points to reversed arglist -- reverse it onto argv */
+	while(v){
+		nextv=v->next;
+		v->next=runq->argv->words;
+		runq->argv->words=v;
+		v=nextv;
+	}
+	runq->pc++;
+}
+
+void
+Xbang(void)
+{
+	setstatus(truestatus()?"false":"");
+}
+
+void
+Xcase(void)
+{
+	Word *p;
+	char *s;
+	int ok=0;
+
+	s=list2str(runq->argv->next->words);
+	for(p=runq->argv->words;p;p=p->next){
+		if(match(s, p->word, '\0')){
+			ok=1;
+			break;
+		}
+	}
+	free(s);
+	if(ok)
+		runq->pc++;
+	else
+		runq->pc=runq->code[runq->pc].i;
+	poplist();
+}
+
+void
+Xclose(void)
+{
+	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
+	runq->pc++;
+}
+
+void
+Xconc(void)
+{
+	Word *lp=runq->argv->words;
+	Word *rp=runq->argv->next->words;
+	Word *vp=runq->argv->next->next->words;
+	int lc=count(lp), rc=count(rp);
+
+	if(lc!=0 || rc!=0){
+		if(lc==0 || rc==0){
+			Xerror("null list in concatenation");
+			return;
+		}
+		if(lc!=1 && rc!=1 && lc!=rc){
+			Xerror("mismatched list lengths in concatenation");
+			return;
+		}
+		vp=conclist(lp, rp, vp);
+	}
+	poplist();
+	poplist();
+	runq->argv->words=vp;
+}
+
+void
+Xcount(void)
+{
+	Word *a;
+	char *s, *t;
+	int n;
+	char num[12];
+
+	if(count(runq->argv->words)!=1){
+		Xerror("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	n=0;
+	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+	if(n==0 || *t){
+		a=vlook(s)->val;
+		sprint(num, "%d", count(a));
+	}
+	else{
+		a=vlook("*")->val;
+		sprint(num, "%d", a && 1<=n && n<=count(a)?1:0);
+	}
+	poplist();
+	pushword(num);
+}
+
+void
+Xdelfn(void)
+{
+	Var *v;
+	Word *a;
+
+	for(a=runq->argv->words;a;a=a->next){
+		v=gvlook(a->word);
+		if(v->fn)
+			codefree(v->fn);
+		v->fn=0;
+		v->fnchanged=1;
+	}
+	poplist();
+}
+
+void
+Xdelhere(void)
+{
+	Var *v;
+	Word *a;
+
+	for(a=runq->argv->words;a;a=a->next){
+		v=gvlook(a->word);
+		if(v->fn) codefree(v->fn);
+		v->fn=0;
+		v->fnchanged=1;
+	}
+	poplist();
+}
+
+void
+Xdol(void)
+{
+	Word *a, *star;
+	char *s, *t;
+	int n;
+
+	if(count(runq->argv->words)!=1){
+		Xerror("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	n=0;
+	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+	a=runq->argv->next->words;
+	if(n==0 || *t)
+		a=copywords(vlook(s)->val, a);
+	else{
+		star=vlook("*")->val;
+		if(star && 1<=n && n<=count(star)){
+			while(--n) star=star->next;
+			a=newword(star->word, a);
+		}
+	}
+	poplist();
+	runq->argv->words=a;
+}
+
+void
+Xdup(void)
+{
+	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
+	runq->pc+=2;
+}
+
+void
+Xeflag(void)
+{
+	if(eflagok && !truestatus())
+		Xexit();
+}
+
+void
+Xexit(void)
+{
+	Var *trapreq;
+	Word *starval;
+	char *c;
+	static int beenhere=0;
+
+	if(truestatus())
+		c = "";
+	else
+		c = getstatus();
+
+	if(flag['S'] || beenhere)
+		exits(c);
+
+	trapreq=vlook("sigexit");
+	if(trapreq->fn){
+		beenhere=1;
+		--runq->pc;
+		starval=vlook("*")->val;
+		start(trapreq->fn, trapreq->pc, (Var*)0);
+		runq->local=newvar(strdup("*"), runq->local);
+		runq->local->val=copywords(starval, (Word*)0);
+		runq->local->changed=1;
+		runq->redir=runq->startredir=0;
+	}
+
+	exits(c);
+}
+
+void
+Xfalse(void)
+{
+	if(truestatus())
+		runq->pc=runq->code[runq->pc].i;
+	else
+		runq->pc++;
+}
+
+void
+Xfor(void)
+{
+	if(runq->argv->words==0) {
+		poplist();
+		runq->pc=runq->code[runq->pc].i;
+	} else {
+		freelist(runq->local->val);
+		runq->local->val=runq->argv->words;
+		runq->local->changed=1;
+		runq->argv->words=runq->argv->words->next;
+		runq->local->val->next=0;
+		runq->pc++;
+	}
+}
+
+void
+Xfn(void)
+{
+	Var *v;
+	Word *a;
+	int end;
+
+	end=runq->code[runq->pc].i;
+	for(a=runq->argv->words;a;a=a->next){
+		v=gvlook(a->word);
+		if(v->fn)
+			codefree(v->fn);
+		v->fn=codecopy(runq->code);
+		v->pc=runq->pc+2;
+		v->fnchanged=1;
+	}
+	runq->pc=end;
+	poplist();
+}
+
+void
+Xglob(void)
+{
+	globlist();
+}
+
+void
+Xif(void)
+{
+	ifnot=1;
+	if(truestatus()) runq->pc++;
+	else runq->pc=runq->code[runq->pc].i;
+}
+
+void
+Xifnot(void)
+{
+	if(ifnot)
+		runq->pc++;
+	else
+		runq->pc=runq->code[runq->pc].i;
+}
+
+void
+Xjump(void)
+{
+	runq->pc=runq->code[runq->pc].i;
+}
+
+
+void
+Xlocal(void)
+{
+	if(count(runq->argv->words)!=1){
+		Xerror("variable name must be singleton\n");
+		return;
+	}
+	deglob(runq->argv->words->word);
+	runq->local=newvar(strdup(runq->argv->words->word), runq->local);
+	runq->local->val=copywords(runq->argv->next->words, 0);
+	runq->local->changed=1;
+	poplist();
+	poplist();
+}
+
+
+void 
+Xmark(void)
+{
+	pushlist();
+}
+
+void
+Xmatch(void)
+{
+	Word *p;
+	char *subject;
+
+	subject=list2str(runq->argv->words);
+	setstatus("no match");
+	for(p=runq->argv->next->words;p;p=p->next) {
+		if(match(subject, p->word, '\0')){
+			setstatus("");
+			break;
+		}
+	}
+	free(subject);
+	poplist();
+	poplist();
+}
+
+void
+Xpipe(void)
+{
+	Thread *p=runq;
+	int pc=p->pc, pid;
+	int lfd=p->code[pc].i;
+	int rfd=p->code[pc+1].i;
+	int pfd[2];
+	char **argv;
+
+	if(pipe(pfd)<0){
+		Xperror("can't get pipe");
+		return;
+	}
+
+	updenv();
+
+	argv = rcargv(runq->code[pc+2].s);
+	pid = proc(argv, 0, pfd[1], 2);
+	free(argv);
+	close(pfd[1]);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		close(pfd[0]);
+		return;
+	}
+
+	start(p->code, pc+4, runq->local);
+	pushredir(ROPEN, pfd[0], rfd);
+	p->pc=p->code[pc+3].i;
+	p->pid=pid;
+}
+
+void
+Xpipefd(void)
+{
+	fatal("Xpipefd");
+}
+
+void
+Xpipewait(void)
+{
+	char status[NSTATUS+1];
+	if(runq->pid==-1)
+		setstatus(concstatus(runq->status, getstatus()));
+	else{
+		strncpy(status, getstatus(), NSTATUS);
+		status[NSTATUS]='\0';
+		waitfor(runq->pid);
+		runq->pid=-1;
+		setstatus(concstatus(getstatus(), status));
+	}
+}
+
+void
+Xpopm(void)
+{
+	poplist();
+}
+
+void
+Xpopredir(void)
+{
+	Redir *rp=runq->redir;
+
+	if(rp==0)
+		panic("turfredir null!", 0);
+	runq->redir=rp->next;
+	if(rp->type==ROPEN)
+		close(rp->from);
+	free((char *)rp);
+}
+
+void
+Xqdol(void)
+{
+	Word *a, *p;
+	char *s;
+	int n;
+
+	if(count(runq->argv->words)!=1){
+		Xerror("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->words->word;
+	deglob(s);
+	a=vlook(s)->val;
+	poplist();
+	n=count(a);
+	if(n==0){
+		pushword("");
+		return;
+	}
+	for(p=a;p;p=p->next) n+=strlen(p->word);
+	s=malloc(n);
+	if(a){
+		strcpy(s, a->word);
+		for(p=a->next;p;p=p->next){
+			strcat(s, " ");
+			strcat(s, p->word);
+		}
+	}
+	else
+		s[0]='\0';
+	pushword(s);
+	free(s);
+}
+
+void
+Xrdcmds(void)
+{
+	Thread *p=runq;
+	Word *prompt;
+
+	flush(err);
+	nerror=0;
+	if(flag['s'] && !truestatus())
+		pfmt(err, "status=%v\n", vlook("status")->val);
+	if(runq->iflag){
+		prompt=vlook("prompt")->val;
+		if(prompt)
+			promptstr=prompt->word;
+		else
+			promptstr="% ";
+	}
+	interrupted=0;
+	if(yyparse()) {
+		if(!p->iflag || p->eof /* && !Eintr() */) {
+			if(p->cmdfile)
+				free(p->cmdfile);
+			closeio(p->cmdfd);
+			Xreturn();	/* should this be omitted? */
+		} else {
+			if(interrupted){
+				pchr(err, '\n');
+				p->eof=0;
+			}
+			--p->pc;	/* go back for next command */
+		}
+	} else {
+		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
+		start(codebuf, 1, runq->local);
+	}
+	freenodes();
+}
+
+void
+Xread(void)
+{
+	char *file;
+	int f;
+
+	switch(count(runq->argv->words)){
+	default: Xerror("< requires singleton\n"); return;
+	case 0: Xerror("< requires file\n"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f=open(file, 0))<0){
+		Xperror(file);
+		return;
+	}
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+
+void
+Xreturn(void)
+{
+	Thread *p=runq;
+
+	turfredir();
+	while(p->argv)
+		poplist();
+	codefree(p->code);
+	runq=p->ret;
+	free(p);
+	if(runq==0)
+		exits(truestatus()?"":getstatus());
+}
+
+void
+Xsettrue(void)
+{
+	setstatus("");
+}
+
+
+void
+Xsub(void)
+{
+	Word *a, *v;
+	char *s;
+	if(count(runq->argv->next->words)!=1){
+		Xerror("variable name not singleton!");
+		return;
+	}
+	s=runq->argv->next->words->word;
+	deglob(s);
+	a=runq->argv->next->next->words;
+	v=vlook(s)->val;
+	a=subwords(v, count(v), runq->argv->words, a);
+	poplist();
+	poplist();
+	runq->argv->words=a;
+}
+
+void
+Xsubshell(void)
+{
+	char **argv;
+	uint pid;
+
+	updenv();
+
+	argv = rcargv(runq->code[runq->pc].s);
+	pid = proc(argv, -1, 1, 2);
+	free(argv);
+
+	if(pid == 0) {
+		Xerror("proc failed");
+		return;
+	}
+
+	waitfor(pid);
+	runq->pc++;
+}
+
+void
+Xtrue(void)
+{
+	if(truestatus())
+		runq->pc++;
+	else	
+		runq->pc=runq->code[runq->pc].i;
+}
+
+void
+Xunlocal(void)
+{
+	Var *v=runq->local, *hid;
+
+	if(v==0)
+		panic("Xunlocal: no locals!", 0);
+	runq->local=v->next;
+	hid=vlook(v->name);
+	hid->changed=1;
+	free(v->name);
+	freewords(v->val);
+	free(v);
+}
+
+void
+Xwastrue(void)
+{
+	ifnot=0;
+}
+
+void
+Xwrite(void)
+{
+	char *file;
+	int f;
+
+	switch(count(runq->argv->words)){
+	default: Xerror("> requires singleton\n"); return;
+	case 0: Xerror("> requires file\n"); return;
+	case 1: break;
+	}
+	file=runq->argv->words->word;
+	if((f = create(file, 1, 0666))<0){
+		Xperror(file);
+		return;
+	}
+	pushredir(ROPEN, f, runq->code[runq->pc].i);
+	runq->pc++;
+	poplist();
+}
+
+void
+Xword(void)
+{
+	pushword(runq->code[runq->pc++].s);
+}
+
+void
+Xerror(char *s)
+{
+	pfmt(err, "rcsh: %s\n", s);
+	flush(err);
+	while(!runq->iflag)
+		Xreturn();
+}
+
+void
+Xperror(char *s)
+{
+	pfmt(err, "rcsh: %s: %r\n", s);
+	flush(err);
+	while(!runq->iflag)
+		Xreturn();
+}
--- /dev/null
+++ b/utils/rcsh/glob.c
@@ -1,0 +1,286 @@
+#include "rc.h"
+
+char	*globname;
+Word	*globv;
+
+int	matchfn(char *s, char *p);
+int	globsize(char *p);
+
+/*
+ * delete all the GLOB marks from s, in place
+ */
+void
+deglob(char *s)
+{
+	char *t=s;
+	do{
+		if(*t==GLOB) t++;
+		*s++=*t;
+	}while(*t++);
+}
+
+int
+globcmp(void *s, void *t)
+{
+	return strcmp(*(char**)s, *(char**)t);
+}
+
+void
+globsort(Word *left, Word *right)
+{
+	char **list;
+	Word *a;
+	int n=0;
+	for(a=left;a!=right;a=a->next) n++;
+	list=(char **)malloc(n*sizeof(char *));
+	for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word;
+	qsort((void*)list, (size_t)n, sizeof(char*), globcmp);
+	for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n];
+	free(list);
+}
+
+/*
+ * Push names prefixed by globname and suffixed by a match of p onto the astack.
+ * namep points to the end of the prefix in globname.
+ */
+void
+globdir(char *p, char *namep)
+{
+	char *t, *q, *newp;
+	Direntry *dp, *dq;
+	Dir *dir;
+
+	/* scan the pattern looking for a component with a metacharacter in it */
+	if(*p=='\0'){
+		globv=newword(globname, globv);
+		return;
+	}
+	t=namep;
+	newp=p;
+	while(*newp){
+		if(*newp==GLOB)
+			break;
+		*t=*newp++;
+		if(*t++=='/'){
+			namep=t;
+			p=newp;
+		}
+	}
+	/* If we ran out of pattern, append the name if accessible */
+	if(*newp=='\0'){
+		*t='\0';
+		if(access(globname, 0)==0)
+			globv=newword(globname, globv);
+		return;
+	}
+	/* read the directory and recur for any entry that matches */
+	*namep='\0';
+	t = globname;
+	if(*t == 0)
+		t = ".";
+	q = strdup(t);
+	if (q[strlen(q)-1] == '/')
+		q[strlen(q)-1] = 0;
+	if((dir=dirstat(q)) == nil || !(dir->mode&0x80000000)){
+		free(dir);
+		return;
+	}
+	free(dir);
+	dq = readdirect(q);
+	if(dq == 0){
+		fprint(2, "could not open %s: %r\n", q);
+		return;
+	}
+	while(*newp!='/' && *newp!='\0')
+		newp++;
+	for(dp = dq;dp->name; dp++){
+		strcpy(namep, dp->name);
+		if(matchfn(namep, p))
+			globdir(newp, namep+strlen(namep));
+		free(dp->name);
+	}
+	free(dq);
+}
+
+/*
+ * Push all file names matched by p on the current thread's stack.
+ * If there are no matches, the list consists of p.
+ */
+void
+glob(char *p)
+{
+	Word *svglobv=globv;
+	int globlen=globsize(p);
+
+	if(globlen == 0){
+		deglob(p);
+		globv=newword(p, globv);
+		return;
+	}
+	globname=malloc(globlen);
+	globname[0]='\0';
+	globdir(p, globname);
+	free(globname);
+	if(svglobv==globv){
+		deglob(p);
+		globv=newword(p, globv);
+	}
+	else
+		globsort(globv, svglobv);
+}
+
+
+/*
+ * Do p and q point at equal utf codes
+ */
+int
+equtf(char *p, char *q)
+{
+	if(*p!=*q)
+		return 0;
+	if(twobyte(*p)) return p[1]==q[1];
+	if(threebyte(*p)){
+		if(p[1]!=q[1]) return 0;
+		if(p[1]=='\0') return 1;	/* broken code at end of string! */
+		return p[2]==q[2];
+	}
+	return 1;
+}
+
+/*
+ * Return a pointer to the next utf code in the string,
+ * not jumping past nuls in broken utf codes!
+ */
+char *
+nextutf(char *p)
+{
+	if(twobyte(*p))
+		return p[1]=='\0'?p+1:p+2;
+	if(threebyte(*p))
+		return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
+	return p+1;
+}
+
+/*
+ * Convert the utf code at *p to a unicode value
+ */
+int
+unicode(char *p)
+{
+	int u=*p&0xff;
+	if(twobyte(u))
+		return ((u&0x1f)<<6)|(p[1]&0x3f);
+	if(threebyte(u))
+		return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
+	return u;
+}
+
+/*
+ * Does the string s match the pattern p
+ * . and .. are only matched by patterns starting with .
+ * * matches any sequence of characters
+ * ? matches any single character
+ * [...] matches the enclosed list of characters
+ */
+int
+matchfn(char *s, char *p)
+{
+	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
+		return 0;
+	return match(s, p, '/');
+}
+
+int
+match(char *s, char *p, int stop)
+{
+	int compl, hit, lo, hi, t, c;
+
+	for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)) {
+		if(*p!=GLOB){
+			if(!equtf(p, s)) return 0;
+		}
+		else switch(*++p){
+		case GLOB:
+			if(*s!=GLOB) return 0;
+			break;
+		case '*':
+			for(;;){
+				if(match(s, nextutf(p), stop)) return 1;
+				if(!*s) break;
+				s=nextutf(s);
+			}
+			return 0;
+		case '?':
+			if(*s=='\0') return 0;
+			break;
+		case '[':
+			if(*s=='\0') return 0;
+			c=unicode(s);
+			p++;
+			compl=*p=='~';
+			if(compl) p++;
+			hit=0;
+			while(*p!=']'){
+				if(*p=='\0') return 0;		/* syntax error */
+				lo=unicode(p);
+				p=nextutf(p);
+				if(*p!='-') hi=lo;
+				else{
+					p++;
+					if(*p=='\0') return 0;	/* syntax error */
+					hi=unicode(p);
+					p=nextutf(p);
+					if(hi<lo){ t=lo; lo=hi; hi=t; }
+				}
+				if(lo<=c && c<=hi) hit=1;
+			}
+			if(compl) hit=!hit;
+			if(!hit) return 0;
+			break;
+		}
+	}
+	return *s=='\0';
+}
+
+void
+globlist1(Word *gl)
+{
+	if(gl){
+		globlist1(gl->next);
+		glob(gl->word);
+	}
+}
+
+void
+globlist(void)
+{
+	Word *a;
+
+	globv=0;
+	globlist1(runq->argv->words);
+	poplist();
+	pushlist();
+	if(globv){
+		for(a=globv;a->next;a=a->next);
+		a->next=runq->argv->words;
+		runq->argv->words=globv;
+	}
+}
+
+#define	NDIR	128
+int
+globsize(char *p)
+{
+	ulong isglob=0, globlen=NDIR+1;
+
+	for(;*p;p++){
+		if(*p==GLOB){
+			p++;
+			if(*p!=GLOB) isglob++;
+			globlen+=*p=='*'?NDIR:1;
+		}
+		else
+			globlen++;
+	}
+	return isglob?globlen:0;
+}
--- /dev/null
+++ b/utils/rcsh/here.c
@@ -1,0 +1,145 @@
+#include "rc.h"
+#include "y.tab.h"
+
+Here	*here, **ehere;
+int	ser;
+
+char	tmp[]="/tmp/here0000.0000";
+char	hex[]="0123456789abcdef";
+
+void psubst(Io*, char*);
+void pstrs(Io*, Word*);
+
+void hexnum(char *p, int n)
+{
+	*p++=hex[(n>>12)&0xF];
+	*p++=hex[(n>>8)&0xF];
+	*p++=hex[(n>>4)&0xF];
+	*p=hex[n&0xF];
+}
+
+Tree *
+heredoc(Tree *tag)
+{
+	Here *h=new(Here);
+
+	if(tag->type!=WORD)
+		yyerror("Bad here tag");
+	h->next=0;
+	if(here)
+		*ehere=h;
+	else
+		here=h;
+	ehere=&h->next;
+	h->tag=tag;
+	hexnum(&tmp[9], getpid());
+	hexnum(&tmp[14], ser++);
+	h->name=strdup(tmp);
+	return token(tmp, WORD);
+}
+/*
+ * bug: lines longer than NLINE get split -- this can cause spurious
+ * missubstitution, or a misrecognized EOF marker.
+ */
+#define	NLINE	4096
+void
+readhere(void)
+{
+	Here *h, *nexth;
+	Io *f;
+	char *s, *tag;
+	int c, subst;
+	char line[NLINE+1];
+
+	for(h=here;h;h=nexth){
+		subst=!h->tag->quoted;
+		tag=h->tag->str;
+		c=create(h->name, 1, 0666);
+		if(c<0) yyerror("can't create here document");
+		f=openfd(c);
+		s=line;
+		pprompt();
+		while((c=rchr(runq->cmdfd))!=EOF){
+			if(c=='\n' || s==&line[NLINE]){
+				*s='\0';
+				if(strcmp(line, tag)==0) break;
+				if(subst) psubst(f, line);
+				else pstr(f, line);
+				s=line;
+				if(c=='\n'){
+					pprompt();
+					pchr(f, c);
+				}
+				else *s++=c;
+			}
+			else *s++=c;
+		}
+		flush(f);
+		closeio(f);
+		cleanhere(h->name);
+		nexth=h->next;
+		free(h);
+	}
+	here=0;
+	doprompt=1;
+}
+
+void
+psubst(Io *f, char *s)
+{
+	char *t, *u;
+	int savec, n;
+	Word *star;
+
+	while(*s){
+		if(*s!='$'){
+			if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){
+				pchr(f, *s++);
+				if(*s=='\0') break;
+			}
+			else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){
+				pchr(f, *s++);
+				if(*s=='\0') break;
+				pchr(f, *s++);
+				if(*s=='\0') break;
+			}
+			pchr(f, *s++);
+		}
+		else{
+			t=++s;
+			if(*t=='$') pchr(f, *t++);
+			else{
+				while(*t && idchr(*t)) t++;
+				savec=*t;
+				*t='\0';
+				n=0;
+				for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0';
+				if(n && *u=='\0'){
+					star=vlook("*")->val;
+					if(star && 1<=n && n<=count(star)){
+						while(--n) star=star->next;
+						pstr(f, star->word);
+					}
+				}
+				else
+					pstrs(f, vlook(s)->val);
+				*t=savec;
+				if(savec=='^') t++;
+			}
+			s=t;
+		}
+	}
+}
+
+void
+pstrs(Io *f, Word *a)
+{
+	if(a){
+		while(a->next && a->next->word){
+			pstr(f, a->word);
+			pchr(f, ' ');
+			a=a->next;
+		}
+		pstr(f, a->word);
+	}
+}
--- /dev/null
+++ b/utils/rcsh/io.c
@@ -1,0 +1,238 @@
+#include "rc.h"
+
+int	pfmtnest=0;
+
+void	pdec(Io*, long);
+void	poct(Io*, ulong);
+void	phex(Io*, long);
+void	pquo(Io*, char*);
+void	pwrd(Io*, char*);
+void	pcmd(Io*, Tree*);
+void	pval(Io*, Word*);
+
+void
+pfmt(Io *f, char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	pfmtnest++;
+	for(;*fmt;fmt++) {
+		if(*fmt!='%') pchr(f, *fmt);
+		else switch(*++fmt){
+		case '\0': va_end(ap); return;
+		case 'c': pchr(f, va_arg(ap, int)); break;
+		case 'd': pdec(f, va_arg(ap, int)); break;
+		case 'o': poct(f, va_arg(ap, unsigned)); break;
+		case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/
+		case 'Q': pquo(f, va_arg(ap, char *)); break;
+		case 'q': pwrd(f, va_arg(ap, char *)); break;
+		case 'r': perr(f); break;
+		case 's': pstr(f, va_arg(ap, char *)); break;
+		case 't': pcmd(f, va_arg(ap, Tree *)); break;
+		case 'v': pval(f, va_arg(ap, Word *)); break;
+		default: pchr(f, *fmt); break;
+		}
+	}
+	va_end(ap);
+	if(--pfmtnest==0) flush(f);
+}
+
+void
+perr(Io *f)
+{
+	char err[ERRMAX];
+	
+	err[0] = 0;
+	errstr(err, sizeof err);
+	pstr(f, err);
+	errstr(err, sizeof err);
+}
+
+void
+pquo(Io *f, char *s)
+{
+	pchr(f, '\'');
+	for(;*s;s++)
+		if(*s=='\'') pfmt(f, "''");
+		else pchr(f, *s);
+	pchr(f, '\'');
+}
+
+void
+pwrd(Io *f, char *s)
+{
+	char *t;
+	for(t=s;*t;t++)
+		if(!wordchr(*t))
+			break;
+	if(t==s || *t)
+		pquo(f, s);
+	else
+		pstr(f, s);
+}
+
+void
+phex(Io *f, long p)
+{
+	int n;
+	for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+}
+
+void
+pstr(Io *f, char *s)
+{
+	if(s==0)
+		s="(null)";
+	while(*s)
+		pchr(f, *s++);
+}
+
+void
+pdec(Io *f, long n)
+{
+	if(n<0){
+		n=-n;
+		if(n>=0){
+			pchr(f, '-');
+			pdec(f, n);
+			return;
+		}
+		/* n is two's complement minimum integer */
+		n=1-n;
+		pchr(f, '-');
+		pdec(f, n/10);
+		pchr(f, n%10+'1');
+		return;
+	}
+	if(n>9) pdec(f, n/10);
+	pchr(f, n%10+'0');
+}
+
+void
+poct(Io *f, ulong n)
+{
+	if(n>7) poct(f, n>>3);
+	pchr(f, (n&7)+'0');
+}
+
+void
+pval(Io *f, Word *a)
+{
+	if(a){
+		while(a->next && a->next->word){
+			pwrd(f, a->word);
+			pchr(f, ' ');
+			a=a->next;
+		}
+		pwrd(f, a->word);
+	}
+}
+
+int
+fullbuf(Io *f, int c)
+{
+	flush(f);
+	return *f->bufp++=c;
+}
+
+void
+flush(Io *f)
+{
+	int n;
+	char *s;
+	if(f->strp){
+		n=f->ebuf-f->strp;
+		f->strp=realloc(f->strp, n+101);
+		if(f->strp==0)
+			panic("Can't realloc %d bytes in flush!", n+101);
+		f->bufp=f->strp+n;
+		f->ebuf=f->bufp+100;
+		for(s=f->bufp;s<=f->ebuf;s++) *s='\0';
+	}
+	else{
+		n=f->bufp-f->buf;
+		if(n && write(f->fd, f->buf, n) < 0){
+/*			write(3, "Write error\n", 12); 
+			if(ntrap.ref)
+				dotrap();
+*/
+		}
+		f->bufp=f->buf;
+		f->ebuf=f->buf+NBUF;
+	}
+}
+
+Io *
+openfd(int fd)
+{
+	Io *f = new(Io);
+	f->fd = fd;
+	f->bufp = f->ebuf = f->buf;
+	f->strp = 0;
+	return f;
+}
+
+Io *
+openstr(void)
+{
+	Io *f=new(struct Io);
+	char *s;
+	f->fd=-1;
+	f->bufp=f->strp=malloc(101);
+	f->ebuf=f->bufp+100;
+	for(s=f->bufp;s<=f->ebuf;s++)
+		*s='\0';
+	return f;
+}
+
+/*
+ * Open a corebuffer to read.  EOF occurs after reading len
+ * characters from buf.
+ */
+Io *
+opencore(char *s, int len)
+{
+	Io *f;
+	char *buf;
+
+	f = new(Io);
+	buf = malloc(len);
+	f->fd = -1;
+	f->bufp = f->strp=buf;
+	f->ebuf = buf+len;
+	memmove(buf, s, len);
+
+	return f;
+}
+
+void
+rewind(Io *io)
+{
+	if(io->fd==-1) {
+		io->bufp = io->strp;
+	} else {
+		io->bufp = io->ebuf = io->buf;
+		seek(io->fd, 0L, 0);
+	}
+}
+
+void
+closeio(Io *io)
+{
+	if(io->fd>=0)
+		close(io->fd);
+	if(io->strp)
+		free(io->strp);
+	free(io);
+}
+
+int 
+emptybuf(Io *f)
+{
+	int n;
+	if(f->fd==-1 || (n=read(f->fd, f->buf, NBUF))<=0) return EOF;
+	f->bufp=f->buf;
+	f->ebuf=f->buf+n;
+	return *f->bufp++&0xff;
+}
--- /dev/null
+++ b/utils/rcsh/lex.c
@@ -1,0 +1,398 @@
+#include "rc.h"
+#include "y.tab.h"
+
+#define	NTOK	8192
+
+int getnext(void);
+
+int	future=EOF;
+int	doprompt=1;
+int	inquote;
+int	nerror;
+char	*promptstr;
+
+char	tok[NTOK];
+
+int	lastdol;	/* was the last token read '$' or '$#' or '"'? */
+int	lastword;	/* was the last token read a word or compound word terminator? */
+int	lastc;
+
+void
+kinit(void)
+{
+	kenter(FOR, "for");
+	kenter(IN, "in");
+	kenter(WHILE, "while");
+	kenter(IF, "if");
+	kenter(NOT, "not");
+	kenter(TWIDDLE, "~");
+	kenter(BANG, "!");
+	kenter(SUBSHELL, "@");
+	kenter(SWITCH, "switch");
+	kenter(FN, "fn");
+}
+
+int
+wordchr(int c)
+{
+	return !strchr("\n \t\r#;&|^$=`'{}()<>", c) && c!=EOF;
+}
+
+int
+idchr(int c)
+{
+	/*
+	 * Formerly:
+	 * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
+	 *	|| c=='_' || c=='*';
+	 */
+	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
+}
+
+/*
+ * Look ahead in the input stream
+ */
+int
+nextc(void)
+{
+	if(future==EOF)
+		future=getnext();
+	return future;
+}
+
+/*
+ * Consume the lookahead character.
+ */
+int
+advance(void)
+{
+	int c=nextc();
+	lastc=future;
+	future=EOF;
+	return c;
+}
+
+/*
+ * read a character from the input stream
+ */	
+int
+getnext(void)
+{
+	register int c;
+	static peekc=EOF;
+	if(peekc!=EOF){
+		c=peekc;
+		peekc=EOF;
+		return c;
+	}
+	if(runq->eof) return EOF;
+	if(doprompt)
+		pprompt();
+	c=rchr(runq->cmdfd);
+	if(!inquote && c=='\\'){
+		c=rchr(runq->cmdfd);
+		if(c=='\n'){
+			doprompt=1;
+			c=' ';
+		}
+		else{
+			peekc=c;
+			c='\\';
+		}
+	}
+	doprompt=doprompt || c=='\n' || c==EOF;
+	if(c==EOF) runq->eof++;
+	else if(flag['V'] || ndot>=2 && flag['v'])
+		pchr(err, c);
+	return c;
+}
+
+void
+pprompt(void)
+{
+	Var *prompt;
+
+	if(runq->iflag){
+		pstr(err, promptstr);
+		flush(err);
+		prompt=vlook("prompt");
+		if(prompt->val && prompt->val->next)
+			promptstr=prompt->val->next->word;
+		else
+			promptstr="\t";
+	}
+	runq->lineno++;
+	doprompt=0;
+}
+
+void
+skipwhite(void)
+{
+	int c;
+	for(;;){
+		c=nextc();
+		if(c=='#'){	/* Why did this used to be  if(!inquote && c=='#') ?? */
+			for(;;){
+				c=nextc();
+				if(c=='\n' || c==EOF) break;
+				advance();
+			}
+		}
+		if(c==' ' || c=='\t' || c=='\r') advance();
+		else return;
+	}
+}
+
+void
+skipnl(void)
+{
+	int c;
+	for(;;){
+		skipwhite();
+		c=nextc();
+		if(c!='\n') return;
+		advance();
+	}
+}
+
+int
+nextis(int c)
+{
+	if(nextc()==c){
+		advance();
+		return 1;
+	}
+	return 0;
+}
+
+char *
+addtok(char *p, int val)
+{
+	if(p==0) return 0;
+	if(p==&tok[NTOK]){
+		*p=0;
+		yyerror("token buffer too short");
+		return 0;
+	}
+	*p++=val;
+	return p;
+}
+
+char *
+addutf(char *p, int c)
+{
+	p=addtok(p, c);
+	if(twobyte(c))	 /* 2-byte escape */
+		return addtok(p, advance());
+	if(threebyte(c)){	/* 3-byte escape */
+		p=addtok(p, advance());
+		return addtok(p, advance());
+	}
+	return p;
+}
+
+int
+yylex(void)
+{
+	int c, d=nextc();
+	char *w=tok;
+	Tree *t;
+
+	yylval.tree=0;
+	/*
+	 * Embarassing sneakiness:  if the last token read was a quoted or unquoted
+	 * WORD then we alter the meaning of what follows.  If the next character
+	 * is `(', we return SUB (a subscript paren) and consume the `('.  Otherwise,
+	 * if the next character is the first character of a simple or compound word,
+	 * we insert a `^' before it.
+	 */
+	if(lastword){
+		lastword=0;
+		if(d=='('){
+			advance();
+			strcpy(tok, "( [SUB]");
+			return SUB;
+		}
+		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
+			strcpy(tok, "^");
+			return '^';
+		}
+	}
+	inquote=0;
+	skipwhite();
+	switch(c=advance()){
+	case EOF:
+		lastdol=0;
+		strcpy(tok, "EOF");
+		return EOF;
+	case '$':
+		lastdol=1;
+		if(nextis('#')){
+			strcpy(tok, "$#");
+			return COUNT;
+		}
+		if(nextis('"')){
+			strcpy(tok, "$\"");
+			return '"';
+		}
+		strcpy(tok, "$");
+		return '$';
+	case '&':
+		lastdol=0;
+		if(nextis('&')){
+			skipnl();
+			strcpy(tok, "&&");
+			return ANDAND;
+		}
+		strcpy(tok, "&");
+		return '&';
+	case '|':
+		lastdol=0;
+		if(nextis(c)){
+			skipnl();
+			strcpy(tok, "||");
+			return OROR;
+		}
+	case '<':
+	case '>':
+		lastdol=0;
+		/*
+		 * funny redirection tokens:
+		 *	redir:	arrow | arrow '[' fd ']'
+		 *	arrow:	'<' | '<<' | '>' | '>>' | '|'
+		 *	fd:	digit | digit '=' | digit '=' digit
+		 *	digit:	'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
+		 * some possibilities are nonsensical and get a message.
+		 */
+		*w++=c;
+		t=newtree();
+		switch(c){
+		case '|':
+			t->type=PIPE;
+			t->fd0=1;
+			t->fd1=0;
+			break;
+		case '>':
+			t->type=REDIR;
+			if(nextis(c)){
+				t->rtype=APPEND;
+				*w++=c;
+			}
+			else t->rtype=WRITE;
+			t->fd0=1;
+			break;
+		case '<':
+			t->type=REDIR;
+			if(nextis(c)){
+				t->rtype=HERE;
+				*w++=c;
+			}
+			else t->rtype=READ;
+			t->fd0=0;
+			break;
+		}
+		if(nextis('[')){
+			*w++='[';
+			c=advance();
+			*w++=c;
+			if(c<'0' || '9'<c){
+			RedirErr:
+				*w=0;
+				yyerror(t->type==PIPE?"pipe syntax"
+						:"redirection syntax");
+				return EOF;
+			}
+			t->fd0=0;
+			do{
+				t->fd0=t->fd0*10+c-'0';
+				*w++=c;
+				c=advance();
+			}while('0'<=c && c<='9');
+			if(c=='='){
+				*w++='=';
+				if(t->type==REDIR)
+					t->type=DUP;
+				c=advance();
+				if('0'<=c && c<='9'){
+					t->rtype=DUPFD;
+					t->fd1=t->fd0;
+					t->fd0=0;
+					do{
+						t->fd0=t->fd0*10+c-'0';
+						*w++=c;
+						c=advance();
+					}while('0'<=c && c<='9');
+				}
+				else{
+					if(t->type==PIPE) goto RedirErr;
+					t->rtype=CLOSE;
+				}
+			}
+			if(c!=']' || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
+				goto RedirErr;
+			*w++=']';
+		}
+		*w='\0';
+		yylval.tree=t;
+		if(t->type==PIPE) skipnl();
+		return t->type;
+	case '\'':
+		lastdol=0;
+		lastword=1;
+		inquote=1;
+		for(;;){
+			c=advance();
+			if(c==EOF) break;
+			if(c=='\''){
+				if(nextc()!='\'')
+					break;
+				advance();
+			}
+			w=addutf(w, c);
+		}
+		if(w!=0) *w='\0';
+		t=token(tok, WORD);
+		t->quoted=1;
+		yylval.tree=t;
+		return t->type;
+	}
+	if(!wordchr(c)){
+		lastdol=0;
+		tok[0]=c;
+		tok[1]='\0';
+		return c;
+	}
+	for(;;){
+		/* next line should have (char)c==GLOB, but ken's compiler is broken */
+		if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
+			w=addtok(w, GLOB);
+		w=addutf(w, c);
+		c=nextc();
+		if(lastdol?!idchr(c):!wordchr(c)) break;
+		advance();
+	}
+Out:
+	lastword=1;
+	lastdol=0;
+	if(w!=0) *w='\0';
+	t=klook(tok);
+	if(t->type!=WORD) lastword=0;
+	t->quoted=0;
+	yylval.tree=t;
+	return t->type;
+}
+
+void 
+yyerror(char *m)
+{
+	pfmt(err, "rc: ");
+	if(runq->cmdfile) pfmt(err, "file %s: ", runq->cmdfile);
+	if(!runq->iflag) pfmt(err, "line %d: ", runq->lineno);
+	if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok);
+	pfmt(err, "%s\n", m);
+	flush(err);
+	lastword=0;
+	lastdol=0;
+	while(lastc!='\n' && lastc!=EOF) advance();
+	nerror++;
+}
--- /dev/null
+++ b/utils/rcsh/main.c
@@ -1,0 +1,228 @@
+#include "rc.h"
+
+int	flag[256];
+Io	*err;
+char	*argv0;
+
+Thread	*runq;
+int	ndot;
+
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	Code bootstrap[17];
+	char *cflag, *cp;
+	char rcmain[200];
+	Var *infroot;
+	char **p;
+
+	cflag = 0;
+
+	/* default to interactive mode */
+	flag['i']++;
+
+	/* hack for DOS-style options, when rcsh started from MS-land */
+	for (p = argv+1; *p && **p == '/'; p++)
+		**p = '-';
+
+	argv0 = *argv;
+	ARGBEGIN{
+	default:
+		fprint(2, "usage: %s: [-seiIlvxr] [-c string] [file [args]]\n", argv0);
+		exits("usage");
+	case 'e': flag['e']++; break;
+	case 'c': cflag = ARGF(); break;
+	case 'i': flag['i']++; break;
+	case 'I': flag['i'] = 0; break;
+	case 'l': flag['l']++; break;
+	case 'r': flag['r']++; break;
+	case 's': flag['s']++; break;
+	case 'S': flag['S']++; break;		/* sub shell */
+	case 'v': flag['v']++; break;
+	case 'V': flag['V']++; break;
+	case 'x': flag['x']++; break;
+	}ARGEND
+
+	err = openfd(2);
+
+	kinit();
+	vinit();
+
+	cp = ROOT;
+	if(0 && strlen(argv0))
+		sprint(rcmain, "%s/../lib/rcmain", argv0);
+	else{
+		infroot = vlook("ROOT");
+		if(infroot->val)
+			cp = infroot->val->word;
+	}
+	sprint(rcmain, "%s/utils/lib/rcmain", cp);
+
+	setvar("rcname", newword(argv0, 0));
+	if(cflag)
+		setvar("cflag", newword(cflag, 0));
+	else
+		setvar("cflag", 0);
+
+	/* bootstrap == . rcmain $* */
+	i=0;
+	bootstrap[i++].i=1;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s="*";
+	bootstrap[i++].f=Xassign;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xmark;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s="*";
+	bootstrap[i++].f=Xdol;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s=rcmain;
+	bootstrap[i++].f=Xword;
+	bootstrap[i++].s=".";
+	bootstrap[i++].f=Xsimple;
+	bootstrap[i++].f=Xexit;
+	bootstrap[i].i=0;
+	start(bootstrap, 1, 0);
+	pushlist();
+	for(i=argc-1;i>=0;i--)
+		pushword(argv[i]);
+
+	for(;;){
+		if(flag['r']) pfnc(err, runq);
+		runq->pc++;
+		(*runq->code[runq->pc-1].f)();
+		if(ntrap.ref)
+			dotrap();
+	}
+}
+
+void
+panic(char *s, int n)
+{
+	pfmt(err, "rc: ");
+	pfmt(err, s, n);
+	pchr(err, '\n');
+	flush(err);
+	pfmt(err, "aborting\n");
+	flush(err);
+	exits("aborting");
+}
+
+void
+setstatus(char *s)
+{
+	setvar("status", newword(s, 0));
+}
+
+char *
+getstatus(void)
+{
+	Var *status=vlook("status");
+
+	return status->val?status->val->word:"";
+}
+
+int
+truestatus(void)
+{
+	char *s;
+	for(s=getstatus();*s;s++)
+		if(*s!='|' && *s!='0') return 0;
+	return 1;
+}
+
+char *
+concstatus(char *s, char *t)
+{
+	static char v[NSTATUS+1];
+	int n=strlen(s);
+	strncpy(v, s, NSTATUS);
+	if(n<NSTATUS){
+		v[n]='|';
+		strncpy(v+n+1, t, NSTATUS-n-1);
+	}
+	v[NSTATUS]='\0';
+	return v;
+}
+
+/*
+ * Start executing the given code at the given pc with the given redirection
+ */
+void
+start(Code *c, int pc, Var *local)
+{
+	Thread *p = new(Thread);
+
+	memset(p, 0, sizeof(Thread));
+	p->code = codecopy(c);
+	p->pc = pc;
+	if(runq) {
+		p->redir = runq->redir;
+		p->startredir = runq->redir;
+	}
+	p->local = local;
+	p->lineno = 1;
+	p->ret = runq;
+	runq=p;
+}
+
+void
+execcmds(Io *f)
+{
+	static Code rdcmds[4];
+	static int first=1;
+
+	if(first){
+		rdcmds[0].i=1;
+		rdcmds[1].f=Xrdcmds;
+		rdcmds[2].f=Xreturn;
+		first=0;
+	}
+	start(rdcmds, 1, runq->local);
+	runq->cmdfd=f;
+	runq->iflast=0;
+}
+
+void
+waitfor(uint pid)
+{
+	int e;
+	char estr[64];
+
+	e = procwait(pid);
+	if(e != 0) {
+		sprint(estr, "error code %d", e);
+		setstatus(estr);
+	} else
+		setstatus("");
+}
+
+char **
+procargv(char *s0, char *s1, char *s2, char *s3, Word *w)
+{
+	int n, i;
+	Word *p;
+	char **argv;
+
+	for(p=w,n=5; p; p=p->next,n++);
+		;
+	
+	argv = malloc(n*sizeof(char*));
+	i = 0;
+	if(s0)
+		argv[i++] = s0;
+	if(s1)
+		argv[i++] = s1;
+	if(s2)
+		argv[i++] = s2;
+	if(s3)
+		argv[i++] = s3;
+	for(p=w; p; p=p->next)
+		argv[i++] = p->word;
+	argv[i] = 0;
+	return argv;
+}
+
--- /dev/null
+++ b/utils/rcsh/mkfile
@@ -1,0 +1,53 @@
+<../../mkconfig
+
+#
+#	this directory contains a stripped-down version of rc
+#	it is only build for Windows Nt and Windows 95
+
+TARG=rcsh
+
+OFILES=	code.$O\
+	exec.$O\
+	glob.$O\
+	here.$O\
+	io.$O\
+	lex.$O\
+	main.$O\
+	$TARGMODEL.$O\
+	pcmd.$O\
+	pfnc.$O\
+	simple.$O\
+	trap.$O\
+	tree.$O\
+	var.$O\
+	word.$O\
+	y.tab.$O\
+
+HFILES=	rc.h\
+	y.tab.h\
+
+YFILES=	syn.y
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS '-DROOT="'$ROOT'"'
+
+$BIN/%:Q:	$O.out
+	echo $TARG must be installed manually on Windows systems
+	echo use: cp $O.out $target
+	cp $O.out $target
+
+install:V:	$ROOT/utils/lib/rcmain
+
+$ROOT/utils/lib/rcmain:Q:	rcmain
+	echo $prereq must be installed manually on Windows systems
+	echo use: cp $prereq $target
+	cp $prereq $target
+
+Posix.c Inferno.c:QV:
+	echo $TARG is only built on Windows NT or Windows 95
+	exit 1
--- /dev/null
+++ b/utils/rcsh/pcmd.c
@@ -1,0 +1,110 @@
+#include "rc.h"
+#include "y.tab.h"
+
+char nl='\n';		/* change to semicolon for bourne-proofing */
+
+#define	c0	t->child[0]
+#define	c1	t->child[1]
+#define	c2	t->child[2]
+
+void
+pdeglob(Io *f, char *s)
+{
+	while(*s){
+		if(*s==GLOB) s++;
+		pchr(f, *s++);
+	}
+}
+
+void
+pcmd(Io *f, Tree *t)
+{
+	if(t==0)
+		return;
+
+	switch(t->type){
+	default:	pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break;
+	case '$':	pfmt(f, "$%t", c0); break;
+	case '"':	pfmt(f, "$\"%t", c0); break;
+	case '&':	pfmt(f, "%t&", c0); break;
+	case '^':	pfmt(f, "%t^%t", c0, c1); break;
+	case '`':	pfmt(f, "`%t", c0); break;
+	case ANDAND:	pfmt(f, "%t && %t", c0, c1); break;
+	case ARGLIST:	pfmt(f, "%t %t", c0, c1); break;
+	case BANG:	pfmt(f, "! %t", c0); break;
+	case BRACE:	pfmt(f, "{%t}", c0); break;
+	case COUNT:	pfmt(f, "$#%t", c0); break;
+	case FN:	pfmt(f, "fn %t %t", c0, c1); break;
+	case IF:	pfmt(f, "if%t%t", c0, c1); break;
+	case NOT:	pfmt(f, "if not %t", c0); break;
+	case OROR:	pfmt(f, "%t || %t", c0, c1); break;
+	case PCMD:
+	case PAREN:	pfmt(f, "(%t)", c0); break;
+	case SUB:	pfmt(f, "$%t(%t)", c0, c1); break;
+	case SIMPLE:	pfmt(f, "%t", c0); break;
+	case SUBSHELL:	pfmt(f, "@ %t", c0); break;
+	case SWITCH:	pfmt(f, "switch %t %t", c0, c1); break;
+	case TWIDDLE:	pfmt(f, "~ %t %t", c0, c1); break;
+	case WHILE:	pfmt(f, "while %t%t", c0, c1); break;
+	case ';':
+		if(c0){
+			if(c1) pfmt(f, "%t%c%t", c0, nl, c1);
+			else pfmt(f, "%t", c0);
+		}
+		else pfmt(f, "%t", c1);
+		break;
+	case WORDS:
+		if(c0) pfmt(f, "%t ", c0);
+		pfmt(f, "%t", c1);
+		break;
+	case FOR:
+		pfmt(f, "for(%t", c0);
+		if(c1) pfmt(f, " in %t", c1);
+		pfmt(f, ")%t", c2);
+		break;
+	case WORD:
+		if(t->quoted) pfmt(f, "%Q", t->str);
+		else pdeglob(f, t->str);
+		break;
+	case DUP:
+		pfmt(f, ">[%d=", t->fd0);
+		if(t->rtype==DUPFD)
+			pfmt(f, "%d", t->fd1);
+		/* else t->rtype == CLOSE */
+		pchr(f, ']');
+		break;
+	case PIPEFD:
+	case REDIR:
+		switch(t->rtype){
+		case HERE:
+			pchr(f, '<');
+		case READ:
+			pchr(f, '<');
+			if(t->fd0!=0)
+				pfmt(f, "[%d]", t->fd0);
+			break;
+		case APPEND:
+			pchr(f, '>');
+		case WRITE:
+			pchr(f, '>');
+			if(t->fd0!=1)
+				pfmt(f, "[%d]", t->fd0);
+			break;
+		}
+		pfmt(f, "%t", c0);
+		if(c1) pfmt(f, " %t", c1);
+		break;
+	case '=':
+		pfmt(f, "%t=%t", c0, c1);
+		if(c2) pfmt(f, " %t", c2);
+		break;
+	case PIPE:
+		pfmt(f, "%t|", c0);
+		if(t->fd1==0){
+			if(t->fd0!=1) pfmt(f, "[%d]", t->fd0);
+		}
+		else pfmt(f, "[%d=%d]", t->fd0, t->fd1);
+		pfmt(f, "%t", c1);
+		break;
+	}
+}
--- /dev/null
+++ b/utils/rcsh/pfnc.c
@@ -1,0 +1,70 @@
+#include "rc.h"
+
+struct{
+	void (*f)(void);
+	char *name;
+}fname[]={
+	Xappend,	"Xappend",
+	Xasync,		"Xasync",
+	Xbang,		"Xbang",
+	Xclose,		"Xclose",
+	Xdup,		"Xdup",
+	Xeflag,		"Xeflag",
+	Xexit,		"Xexit",
+	Xfalse,		"Xfalse",
+	Xifnot,		"Xifnot",
+	Xjump,		"Xjump",
+	Xmark,		"Xmark",
+	Xpopm,		"Xpopm",
+	Xread,		"Xread",
+	Xreturn,	"Xreturn",
+	Xtrue,		"Xtrue",
+	Xif, 		"Xif",
+	Xwastrue, 	"Xwastrue",
+	Xword, 		"Xword",
+	Xwrite, 	"Xwrite",
+	Xmatch, 	"Xmatch",
+	Xcase, 		"Xcase",
+	Xconc, 		"Xconc",
+	Xassign, 	"Xassign",
+	Xdol, 		"Xdol",
+	Xcount, 	"Xcount",
+	Xlocal, 	"Xlocal",
+	Xunlocal, 	"Xunlocal",
+	Xfn, 		"Xfn",
+	Xdelfn, 	"Xdelfn",
+	Xpipe, 		"Xpipe",
+	Xpipewait, 	"Xpipewait",
+	Xrdcmds, 	"Xrdcmds",
+	Xbackq, 	"Xbackq",
+	Xpipefd, 	"Xpipefd",
+	Xsubshell, 	"Xsubshell",
+	Xdelhere, 	"Xdelhere",
+	Xfor, 		"Xfor",
+	Xglob, 		"Xglob",
+	Xsimple, 	"Xsimple",
+	Xqdol, 		"Xqdol",
+	0
+};
+
+void
+pfnc(Io *fd, Thread *t)
+{
+	int i;
+	void (*fn)(void)=t->code[t->pc].f;
+	List *a;
+
+	pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc);
+	for(i=0;fname[i].f;i++) {
+		if(fname[i].f==fn) {
+			pstr(fd, fname[i].name);
+			break;
+		}
+	}
+	if(!fname[i].f)
+		pfmt(fd, "%p", fn);
+	for(a=t->argv;a;a=a->next)
+		pfmt(fd, " (%v)", a->words);
+	pchr(fd, '\n');
+	flush(fd);
+}
--- /dev/null
+++ b/utils/rcsh/rc.h
@@ -1,0 +1,296 @@
+#define INFERNO_KEEPENVIRON
+#include	<lib9.h>
+
+#define	Lock	Rclock
+#define	Ref		Rcref
+
+typedef union Code	Code;
+typedef struct Tree	Tree;
+typedef struct Thread	Thread;
+typedef struct Word	Word;
+typedef struct Var	Var;
+typedef struct List	List;
+typedef struct Redir	Redir;
+typedef struct Io	Io;
+typedef struct Here	Here;
+typedef struct Ref	Ref;
+typedef struct Lock	Lock;
+typedef	struct Direntry Direntry;
+
+#define	EOF	(-1)
+#define	NBUF	512
+
+/* values for Tree->rtype */
+#define	APPEND	1
+#define	WRITE	2
+#define	READ	3
+#define	HERE	4
+#define	DUPFD	5
+#define	CLOSE	6
+
+/*
+ * redir types
+ */
+#define	ROPEN	1			/* dup2(from, to); close(from); */
+#define	RDUP	2			/* dup2(from, to); */
+#define	RCLOSE	3			/* close(from); */
+
+#define	NSTATUS	64			/* length of status (from plan 9) */
+
+#define	IWS	0x01	/* inter word seperator when word lists are stored in env variables */
+
+/*
+ * Glob character escape in strings:
+ *	In a string, GLOB must be followed by *?[ or GLOB.
+ *	GLOB* matches any string
+ *	GLOB? matches any single character
+ *	GLOB[...] matches anything in the brackets
+ *	GLOBGLOB matches GLOB
+ */
+#define	GLOB	((char)0x02)
+
+/*
+ * The first word of any code vector is a reference count.
+ * Always create a new reference to a code vector by calling codecopy(.).
+ * Always call codefree(.) when deleting a reference.
+ */
+union Code {
+	void	(*f)(void);
+	int	i;
+	char	*s;
+};
+
+
+struct Tree
+{
+	int	type;
+	int	rtype, fd0, fd1;		/* details of REDIR PIPE DUP tokens */
+	char	*str;
+	int	quoted;
+	int	iskw;
+	Tree	*child[3];
+	Tree	*next;
+};
+
+struct Thread
+{
+	Code	*code;			/* code for this thread */
+	int	pc;			/* code[pc] is the next instruction */
+	List	*argv;			/* argument stack */
+	Redir	*redir;			/* redirection stack */
+	Redir	*startredir;		/* redir inheritance point */
+	Var	*local;			/* list of local variables */
+	char	*cmdfile;		/* file name in Xrdcmd */
+	Io	*cmdfd;			/* file descriptor for Xrdcmd */
+	int	iflast;			/* static `if not' checking */
+	int	eof;			/* is cmdfd at eof? */
+	int	iflag;			/* interactive? */
+	int	lineno;			/* linenumber */
+	int	pid;			/* process for Xpipewait to wait for */
+	char	status[NSTATUS];	/* status for Xpipewait */
+	Tree	*treenodes;		/* tree nodes created by this process */
+	Thread	*ret;			/* who continues when this finishes */
+};
+
+struct Io
+{
+	int	fd;
+	char	*bufp;
+	char	*ebuf;
+	char	*strp;
+	char	buf[NBUF];
+};
+
+struct Var
+{
+	char	*name;		/* ascii name */
+	Word	*val;		/* value */
+	int	changed;
+	Code	*fn;		/* pointer to function's code vector */
+	int	fnchanged;
+	int	pc;		/* pc of start of function */
+	Var	*next;		/* next on hash or local list */
+};
+
+struct Word
+{
+	char	*word;
+	Word	*next;
+};
+
+struct List
+{
+	Word	*words;
+	List	*next;
+};
+
+struct Redir
+{
+	char	type;		/* what to do */
+	short	from, to;	/* what to do it to */
+	Redir	*next;		/* what else to do (reverse order) */
+};
+
+struct Here{
+	Tree	*tag;
+	char	*name;
+	Here	*next;
+};
+
+struct Lock {
+	int	val;
+};
+
+struct Ref
+{
+	Lock	lk;
+	int	ref;
+};
+
+struct	Direntry
+{
+	int	isdir;
+	char	*name;
+};
+
+/* main.c */
+void	start(Code *c, int pc, Var *local);
+
+/* lex.c */
+void	yyerror(char*);
+int	yylex(void);
+int	yyparse(void);
+int	wordchr(int);
+int	idchr(int);
+
+/* code.c */
+int	compile(Tree*);
+Code	*codecopy(Code*);
+void	codefree(Code*);
+void	cleanhere(char *f);
+
+void	skipnl(void);
+
+void	panic(char*, int);
+
+/* var.c */
+void	kinit(void);
+void	vinit(void);
+Var	*vlook(char*);
+Var	*gvlook(char*);
+Var	*newvar(char*, Var*);
+void	setvar(char*, Word*);
+void	updenv(void);
+void	kenter(int type, char *name);
+
+/* glob.c */
+void	deglob(char*);
+void	globlist(void);
+int	match(char *s, char *p, int stop);
+
+/* main.c */
+void	setstatus(char *s);
+char	*getstatus(void);
+int	truestatus(void);
+void	execcmds(Io*);
+char	*concstatus(char *s, char *t);
+char	**procargv(char*, char*, char*, char*, Word *w);
+
+void	freewords(Word*);
+
+/* tree.c */
+Tree	*newtree(void);
+Tree	*token(char*, int), *klook(char*), *tree1(int, Tree*);
+Tree	*tree2(int, Tree*, Tree*), *tree3(int, Tree*, Tree*, Tree*);
+Tree	*mung1(Tree*, Tree*), *mung2(Tree*, Tree*, Tree*);
+Tree	*mung3(Tree*, Tree*, Tree*, Tree*), *epimung(Tree*, Tree*);
+Tree	*simplemung(Tree*), *heredoc(Tree*);
+void	freetree(Tree*);
+void	freenodes(void);
+
+/* here.c */
+Tree	*heredoc(Tree *tag);
+
+/* exec.c */
+extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void);
+extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void);
+extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void);
+extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void);
+extern void Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void);
+extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void);
+extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void);
+extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
+extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
+extern void Xerror(char*), Xperror(char*);
+
+/* word.c */
+Word	*newword(char*, Word*);
+void	pushlist(void);
+void	poplist(void);
+void	pushword(char*);
+void	popword(void);
+int	count(Word*);
+Word	*copywords(Word*, Word*);
+void	pushredir(int, int, int);
+void	turfredir(void);
+char	*list2str(Word*);
+void	freelist(Word*);
+Word	*conclist(Word*, Word*, Word*);
+Word  	*subwords(Word*, int, Word*, Word*);
+
+/* io.c */
+#define	pchr(b, c) if((b)->bufp==(b)->ebuf)fullbuf((b), (c));else (*(b)->bufp++=(c))
+#define	rchr(b) ((b)->bufp==(b)->ebuf?emptybuf(b):(*(b)->bufp++&0xff))
+
+Io	*openfd(int), *openstr(void), *opencore(char*, int);
+int	emptybuf(Io*);
+void	closeio(Io*);
+void	flush(Io*);
+int	fullbuf(Io*, int);
+
+void	pfmt(Io*, char*, ...);
+void	perr(Io*);
+void	pstr(Io*, char*);
+void	pfnc(Io*, Thread*);
+
+void	pprompt(void);
+
+/* trap.c */
+void	dotrap(void);
+void	dointr(void);
+
+void	waitfor(uint);
+
+/* nt.c */
+
+Direntry* readdirect(char*);
+void	fatal(char*, ...);
+uint	proc(char**, int, int, int);
+int	procwait(uint);
+int	refinc(Ref*);
+int	refdec(Ref*);
+int	pipe(int*);
+
+/*
+ * onebyte(c), twobyte(c), threebyte(c)
+ * Is c the first character of a one- two- or three-byte utf sequence?
+ */
+#define	onebyte(c)	((c&0x80)==0x00)
+#define	twobyte(c)	((c&0xe0)==0xc0)
+#define	threebyte(c)	((c&0xf0)==0xe0)
+
+#define	new(type)	((type *)malloc(sizeof(type)))
+
+
+extern Tree	*cmdtree;
+extern Thread	*runq;
+extern Io	*err;
+extern int	flag[256];
+extern int	doprompt;
+extern char	*promptstr;
+extern int	ndot;
+extern int	nerror;
+extern Code	*codebuf;
+extern int	eflagok;
+extern int	interrupted;
+extern Ref	ntrap;
--- /dev/null
+++ b/utils/rcsh/rcmain
@@ -1,0 +1,29 @@
+# rcmain: 9pm version
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs=' 	
+'
+switch($#prompt){
+case 0
+case 1
+	prompt=('% ' '	')
+}
+if(~ $rcname v.out) prompt=('broken! ' '	')
+if(! ~ $#cflag 0){
+	if(flag l && test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l && test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	if(! ~ $#* 0) . $*
+	if not . -i 'stdin$'
+}
+if not {
+	if(~ $#* 0) . 'stdin$'
+	if not{
+		status=''
+		. $*
+	}
+}
+exit $status
--- /dev/null
+++ b/utils/rcsh/rcpath
@@ -1,0 +1,13 @@
+# rcmain: 9pm version
+PF='Program Files'
+MVS='Microsoft Visual Studio'
+PATH=D:/Users/Inferno/Nt/386/bin
+PATH=$PATH';'D:/apps/mks/mksnt
+PATH=$PATH';'C:/WINNT/system32
+PATH=$PATH';'C:/WINNT
+PATH=$PATH';'D:/$PF/$MVS/Common/Tools/WinNT
+PATH=$PATH';'D:/$PF/$MVS/Common/MSDev98/Bin
+PATH=$PATH';'D:/$PF/$MVS/Common/Tools
+PATH=$PATH';'D:/$PF/$MVS/VC98/bin
+PATH=$PATH';'D:/MSSQL7/BINN
+PATH=$PATH';'D:/$PF/Mts
--- /dev/null
+++ b/utils/rcsh/simple.c
@@ -1,0 +1,528 @@
+#include "rc.h"
+
+typedef struct Builtin	Builtin;
+
+struct Builtin
+{
+	char	*name;
+	void	(*fnc)(void);
+};
+
+int	exitnext(void);
+void	execexec(void);
+void	execfunc(Var *func);
+void	execcd(void);
+void	execwhatis(void);
+void	execeval(void);
+void	execexit(void);
+void	execshift(void);
+void	execwait(void);
+void	execdot(void);
+void	execflag(void);
+
+Builtin builtin[]={
+	"cd",		execcd,
+	"whatis",	execwhatis,
+	"eval",		execeval,
+	"exec",		execexec,	/* but with popword first */
+	"exit",		execexit,
+	"shift",	execshift,
+	"wait",		execwait,
+	".",		execdot,
+	"flag",		execflag,
+	0
+};
+
+int	mapfd(int fd);
+
+void
+Xsimple(void)
+{
+	Word *a;
+	Thread *p=runq;
+	Var *v;
+	Builtin *bp;
+	uint pid;
+	char **argv;
+
+	globlist();
+	a=runq->argv->words;
+	if(a==0){
+		Xerror("empty argument list");
+		return;
+	}
+	if(flag['x'])
+		pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */
+
+	v=gvlook(a->word);
+	if(v->fn)
+		execfunc(v);
+	else{
+		if(strcmp(a->word, "builtin")==0){
+			if(count(a)==1){
+				pfmt(err, "builtin: empty argument list\n");
+				setstatus("empty arg list");
+				poplist();
+				return;
+			}
+			a=a->next;
+			popword();
+		}
+		for(bp=builtin;bp->name;bp++) {
+			if(strcmp(a->word, bp->name)==0){
+				(*bp->fnc)();
+				return;
+			}
+		}
+
+		updenv();
+		argv = procargv(0, 0, 0, 0, runq->argv->words);
+		pid = proc(argv, mapfd(0), mapfd(1), mapfd(2));
+		free(argv);
+
+		if(pid == 0)
+			pfmt(err, "%s: %r\n", runq->argv->words->word);
+		else
+			waitfor(pid);
+		poplist();
+#ifdef XXX
+		if(exitnext()){
+			/* fork and wait is redundant */
+			pushword("exec");
+			execexec();
+			Xexit();
+		}
+		else{
+			flush(err);
+			updenv();
+			switch(pid=fork()){
+			case -1:
+				Xperror("try again");
+				return;
+			case 0:
+				pushword("exec");
+				execexec();
+				Exit("can't exec");
+			default:
+				poplist();
+				/* interrupts don't get us out */
+				while(Waitfor(pid, 1) < 0)
+					;
+			}
+		}
+#endif
+	}
+}
+
+Word nullpath={ "", 0};
+
+Word *
+searchpath(char *w)
+{
+	Word *path;
+
+	return &nullpath;
+
+	if(strncmp(w, "/", 1) == 0
+	|| strncmp(w, "#", 1) == 0
+	|| *w && w[1] == ':'
+	|| strncmp(w, "./", 2) == 0
+	|| strncmp(w, "../", 3) == 0
+	|| (path=vlook("path")->val) == 0)
+		path=&nullpath;
+	return path;
+}
+
+/*
+ * Search through the following code to see if we're just going to exit.
+ */
+int
+exitnext(void)
+{
+	Code *c=&runq->code[runq->pc];
+
+	while(c->f==Xpopredir)
+		c++;
+	return c->f==Xexit;
+}
+
+#ifdef XXX
+
+void
+doredir(Redir *rp)
+{
+	if(rp){
+		doredir(rp->next);
+		switch(rp->type){
+		case ROPEN:
+			if(rp->from!=rp->to){
+				Dup(rp->from, rp->to);
+				close(rp->from);
+			}
+			break;
+		case RDUP: Dup(rp->from, rp->to); break;
+		case RCLOSE: close(rp->from); break;
+		}
+	}
+}
+
+
+#endif
+
+void
+execexec(void)
+{
+	popword();	/* "exec" */
+	if(runq->argv->words==0){
+		Xerror("empty argument list");
+		return;
+	}
+	fatal("execexec not done yet");
+/*
+	doredir(runq->redir);
+	Execute(runq->argv->words, searchpath(runq->argv->words->word));
+*/	
+
+	poplist();
+}
+
+void
+execfunc(Var *func)
+{
+	Word *starval;
+
+	popword();
+	starval=runq->argv->words;
+	runq->argv->words=0;
+	poplist();
+	start(func->fn, func->pc, runq->local);
+	runq->local=newvar(strdup("*"), runq->local);
+	runq->local->val=starval;
+	runq->local->changed=1;
+}
+
+void
+execcd(void)
+{
+	Word *a=runq->argv->words;
+	Word *cdpath;
+	char dir[512];
+
+	setstatus("can't cd");
+	cdpath=vlook("cdpath")->val;
+	switch(count(a)){
+	default:
+		pfmt(err, "Usage: cd [directory]\n");
+		break;
+	case 2:
+		if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath;
+		for(;cdpath;cdpath=cdpath->next){
+			strcpy(dir, cdpath->word);
+			if(dir[0]) strcat(dir, "/");
+			strcat(dir, a->next->word);
+			if(chdir(dir)>=0){
+				if(strlen(cdpath->word)
+				&& strcmp(cdpath->word, ".")!=0)
+					pfmt(err, "%s\n", dir);
+				setstatus("");
+				break;
+			}
+		}
+		if(cdpath==0) pfmt(err, "Can't cd %s\n", a->next->word);
+		break;
+	case 1:
+		a=vlook("home")->val;
+		if(count(a)>=1){
+			if(chdir(a->word)>=0)
+				setstatus("");
+			else
+				pfmt(err, "Can't cd %s\n", a->word);
+		}
+		else
+			pfmt(err, "Can't cd -- $home empty\n");
+		break;
+	}
+	poplist();
+}
+
+void
+execexit(void)
+{
+	switch(count(runq->argv->words)){
+	default: pfmt(err, "Usage: exit [status]\nExiting anyway\n");
+	case 2: setstatus(runq->argv->words->next->word);
+	case 1:	Xexit();
+	}
+}
+
+void
+execflag(void)
+{
+	char *letter, *val;
+	switch(count(runq->argv->words)){
+	case 2:
+		setstatus(flag[runq->argv->words->next->word[0]]?"":"flag not set");
+		break;
+	case 3:
+		letter=runq->argv->words->next->word;
+		val=runq->argv->words->next->next->word;
+		if(strlen(letter)==1){
+			if(strcmp(val, "+")==0){
+				flag[letter[0]]=1;
+				break;
+			}
+			if(strcmp(val, "-")==0){
+				flag[letter[0]]=0;
+				break;
+			}
+		}
+	default:
+		Xerror("Usage: flag [letter] [+-]");
+		return;
+	}
+	poplist();
+}
+
+void
+execshift(void)
+{
+	int n;
+	Word *a;
+	Var *star;
+	switch(count(runq->argv->words)){
+	default:
+		pfmt(err, "Usage: shift [n]\n");
+		setstatus("shift usage");
+		poplist();
+		return;
+	case 2: n=atoi(runq->argv->words->next->word); break;
+	case 1: n=1; break;
+	}
+	star=vlook("*");
+	for(;n && star->val;--n){
+		a=star->val->next;
+		free(star->val->word);
+		free(star->val);
+		star->val=a;
+		star->changed=1;
+	}
+	setstatus("");
+	poplist();
+}
+
+int
+octal(char *s)
+{
+	int n=0;
+	while(*s==' ' || *s=='\t' || *s=='\n') s++;
+	while('0'<=*s && *s<='7') n=n*8+*s++-'0';
+	return n;
+}
+
+void
+execeval(void)
+{
+	char *cmdline, *s, *t;
+	int len=0;
+	Word *ap;
+
+	if(count(runq->argv->words)<=1){
+		Xerror("Usage: eval cmd ...");
+		return;
+	}
+	eflagok=1;
+	for(ap=runq->argv->words->next;ap;ap=ap->next)
+		len+=1+strlen(ap->word);
+	cmdline=malloc(len);
+	s=cmdline;
+	for(ap=runq->argv->words->next;ap;ap=ap->next){
+		for(t=ap->word;*t;) *s++=*t++;
+		*s++=' ';
+	}
+	s[-1]='\n';
+	poplist();
+	execcmds(opencore(cmdline, len));
+	free(cmdline);
+}
+
+void
+execdot(void)
+{
+	int iflag=0;
+	int fd;
+	List *av;
+	Thread *p=runq;
+	char *zero;
+	char file[512];
+	Word *path;
+	static int first=1;
+	static Code dotcmds[14];
+
+	if(first) {
+		dotcmds[0].i=1;
+		dotcmds[1].f=Xmark;
+		dotcmds[2].f=Xword;
+		dotcmds[3].s="0";
+		dotcmds[4].f=Xlocal;
+		dotcmds[5].f=Xmark;
+		dotcmds[6].f=Xword;
+		dotcmds[7].s="*";
+		dotcmds[8].f=Xlocal;
+		dotcmds[9].f=Xrdcmds;
+		dotcmds[10].f=Xunlocal;
+		dotcmds[11].f=Xunlocal;
+		dotcmds[12].f=Xreturn;
+		first=0;
+	} else
+		eflagok=1;
+	popword();
+	if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
+		iflag=1;
+		popword();
+	}
+	/* get input file */
+	if(p->argv->words==0){
+		Xerror("Usage: . [-i] file [arg ...]");
+		return;
+	}
+	zero=strdup(p->argv->words->word);
+	popword();
+	strcpy(file, "**No file name**");
+	fd = -1;
+	if(strcmp(zero, "stdin$") == 0)
+		fd = dup(0);
+	else{
+		for(path=searchpath(zero);path;path=path->next){
+			strcpy(file, path->word);
+			if(file[0])
+				strcat(file, "/");
+			strcat(file, zero);
+			if((fd=open(file, 0))>=0)
+				break;
+		}
+	}
+	if(fd<0){
+		Xperror(file);
+		return;
+	}
+	/* set up for a new command loop */
+	start(dotcmds, 1, 0);
+	pushredir(RCLOSE, fd, 0);
+	runq->cmdfile=zero;
+	runq->cmdfd=openfd(fd);
+	runq->iflag=iflag;
+	runq->iflast=0;
+	/* push $* value */
+	pushlist();
+	runq->argv->words=p->argv->words;
+	/* free caller's copy of $* */
+	av=p->argv;
+	p->argv=av->next;
+	free(av);
+	/* push $0 value */
+	pushlist();
+	pushword(zero);
+	ndot++;
+}
+
+void
+execwhatis(void)
+{	/* mildly wrong -- should fork before writing */
+	Word *a, *b, *path;
+	Var *v;
+	Builtin *bp;
+	char file[512];
+	Io out[1];
+	int found, sep;
+
+	a=runq->argv->words->next;
+	if(a==0){
+		Xerror("Usage: whatis name ...");
+		return;
+	}
+	setstatus("");
+	out->fd=mapfd(1);
+	out->bufp=out->buf;
+	out->ebuf=&out->buf[NBUF];
+	out->strp=0;
+	for(;a;a=a->next){
+		v=vlook(a->word);
+		if(v->val){
+			pfmt(out, "%s=", a->word);
+			if(v->val->next==0)
+				pfmt(out, "%q\n", v->val->word);
+			else{
+				sep='(';
+				for(b=v->val;b && b->word;b=b->next){
+					pfmt(out, "%c%q", sep, b->word);
+					sep=' ';
+				}
+				pfmt(out, ")\n");
+			}
+			found=1;
+		}
+		else
+			found=0;
+		v=gvlook(a->word);
+		if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+		else{
+			for(bp=builtin;bp->name;bp++)
+				if(strcmp(a->word, bp->name)==0){
+					pfmt(out, "builtin %s\n", a->word);
+					break;
+				}
+			if(!bp->name){
+				for(path=searchpath(a->word);path;path=path->next){
+					strcpy(file, path->word);
+					if(file[0]) strcat(file, "/");
+					strcat(file, a->word);
+#ifdef XXX
+					if(Executable(file)){
+						pfmt(out, "%s\n", file);
+						break;
+					}
+#endif
+				}
+				if(!path && !found){
+					pfmt(err, "%s: not found\n", a->word);
+					setstatus("not found");
+				}
+			}
+		}
+	}
+	poplist();
+	flush(err);
+}
+
+void
+execwait(void)
+{
+	fprint(2, "wait: not done yet");
+
+#ifdef XXX
+	switch(count(runq->argv->words)){
+	default: Xerror("Usage: wait [pid]"); return;
+	case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break;
+	case 1: Waitfor(-1, 0); break;
+	}
+	poplist();
+#endif
+}
+
+int
+mapfd(int fd)
+{
+	Redir *rp;
+	for(rp=runq->redir;rp;rp=rp->next){
+		switch(rp->type){
+		case RCLOSE:
+			if(rp->from==fd) fd=-1;
+			break;
+		case RDUP:
+		case ROPEN:
+			if(rp->to==fd) fd=rp->from;
+			break;
+		}
+	}
+	return fd;
+}
--- /dev/null
+++ b/utils/rcsh/syn.y
@@ -1,0 +1,90 @@
+%{
+#include "rc.h"
+%}
+
+/* operator priorities -- lowest first */
+%left IF WHILE FOR SWITCH ')' NOT
+%left ANDAND OROR
+%left BANG SUBSHELL
+%left PIPE
+%left '^'
+%right '$' COUNT '"'
+%left SUB
+
+%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN
+%term WORD REDIR DUP PIPE SUB
+%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */
+
+
+%union{
+	Tree *tree;
+};
+
+%type<tree> line paren brace body cmdsa cmdsan assign epilog redir
+%type<tree> cmd simple first word comword keyword words
+%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN
+%type<tree> WORD REDIR DUP PIPE
+
+%%
+
+rc:				{ return 1;}
+|	line '\n'		{return !compile($1);}
+line:	cmd
+|	cmdsa line		{$$=tree2(';', $1, $2);}
+body:	cmd
+|	cmdsan body		{$$=tree2(';', $1, $2);}
+cmdsa:	cmd ';'
+|	cmd '&'			{$$=tree1('&', $1);}
+cmdsan:	cmdsa
+|	cmd '\n'
+brace:	'{' body '}'		{$$=tree1(BRACE, $2);}
+paren:	'(' body ')'		{$$=tree1(PCMD, $2);}
+assign:	first '=' word		{$$=tree2('=', $1, $3);}
+epilog:				{$$=0;}
+|	redir epilog		{$$=mung2($1, $1->child[0], $2);}
+redir:	REDIR word		{$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
+|	DUP
+cmd:				{$$=0;}
+|	brace epilog		{$$=epimung($1, $2);}
+|	IF paren {skipnl();} cmd
+				{$$=mung2($1, $2, $4);}
+|	IF NOT {skipnl();} cmd	{$$=mung1($2, $4);}
+|	FOR '(' word IN words ')' {skipnl();} cmd
+				{$$=mung3($1, $3, tree1(PAREN, $5), $8);}
+|	FOR '(' word ')' {skipnl();} cmd
+				{$$=mung3($1, $3, 0, $6);}
+|	WHILE paren {skipnl();} cmd
+				{$$=mung2($1, $2, $4);}
+|	SWITCH word {skipnl();} brace
+				{$$=tree2(SWITCH, $2, $4);}
+|	simple			{$$=simplemung($1);}
+|	TWIDDLE word words	{$$=mung2($1, $2, $3);}
+|	cmd ANDAND cmd		{$$=tree2(ANDAND, $1, $3);}
+|	cmd OROR cmd		{$$=tree2(OROR, $1, $3);}
+|	cmd PIPE cmd		{$$=mung2($2, $1, $3);}
+|	redir cmd  %prec BANG	{$$=mung2($1, $1->child[0], $2);}
+|	assign cmd %prec BANG	{$$=mung3($1, $1->child[0], $1->child[1], $2);}
+|	BANG cmd		{$$=mung1($1, $2);}
+|	SUBSHELL cmd		{$$=mung1($1, $2);}
+|	FN words brace		{$$=tree2(FN, $2, $3);}
+|	FN words		{$$=tree1(FN, $2);}
+simple:	first
+|	simple word		{$$=tree2(ARGLIST, $1, $2);}
+|	simple redir		{$$=tree2(ARGLIST, $1, $2);}
+first:	comword	
+|	first '^' word		{$$=tree2('^', $1, $3);}
+word:	keyword			{$1->type=WORD;}
+|	comword
+|	word '^' word		{$$=tree2('^', $1, $3);}
+comword: '$' word		{$$=tree1('$', $2);}
+|	'$' word SUB words ')'	{$$=tree2(SUB, $2, $4);}
+|	'"' word		{$$=tree1('"', $2);}
+|	COUNT word		{$$=tree1(COUNT, $2);}
+|	WORD
+|	'`' brace		{$$=tree1('`', $2);}
+|	'(' words ')'		{$$=tree1(PAREN, $2);}
+/* |	REDIR brace		{$$=mung1($1, $2); $$->type=PIPEFD;} */
+
+keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+words:				{$$=0;}
+|	words word		{$$=tree2(WORDS, $1, $2);}
--- /dev/null
+++ b/utils/rcsh/trap.c
@@ -1,0 +1,42 @@
+#include "rc.h"
+
+int	interrupted;
+Ref	ntrap;
+
+/* runs in a different thread */
+void
+dointr(void)
+{
+	refinc(&ntrap);
+	interrupted = 1;
+}
+
+void
+dotrap(void)
+{
+	Var *trapreq;
+	Word *starval;
+
+	while(refdec(&ntrap) >= 0) {
+		if(flag['S'])
+			exits(truestatus()?"":getstatus());
+		starval=vlook("*")->val;
+		trapreq=vlook("sysint");
+		if(trapreq->fn){
+			start(trapreq->fn, trapreq->pc, (Var*)0);
+			runq->local=newvar(strdup("*"), runq->local);
+			runq->local->val=copywords(starval, (Word*)0);
+			runq->local->changed=1;
+			runq->redir=runq->startredir=0;
+		} else {
+			/*
+			 * run the stack down until we uncover the
+			 * command reading loop.  Xreturn will exit
+			 * if there is none (i.e. if this is not
+			 * an interactive rc.)
+			 */
+			while(!runq->iflag)
+				Xreturn();
+		}
+	}
+}
--- /dev/null
+++ b/utils/rcsh/tree.c
@@ -1,0 +1,140 @@
+#include "rc.h"
+#include "y.tab.h"
+
+Tree *Treenodes;
+
+/*
+ * create and clear a new Tree node, and add it
+ * to the node list.
+ */
+Tree *
+newtree(void)
+{
+	Tree *t=new(Tree);
+	t->iskw=0;
+	t->str=0;
+	t->child[0]=t->child[1]=t->child[2]=0;
+	t->next=Treenodes;
+	Treenodes=t;
+	return t;
+}
+
+void
+freenodes(void)
+{
+	Tree *t, *u;
+	for(t=Treenodes;t;t=u){
+		u=t->next;
+		if(t->str)
+			free(t->str);
+		free(t);
+	}
+	Treenodes=0;
+}
+
+Tree *
+tree1(int type, Tree *c0)
+{
+	return tree3(type, c0, 0, 0);
+}
+
+Tree *
+tree2(int type, Tree *c0, Tree *c1)
+{
+	return tree3(type, c0, c1, 0);
+}
+
+Tree *
+tree3(int type, Tree *c0, Tree *c1, Tree *c2)
+{
+	Tree *t;
+	if(type==';'){
+		if(c0==0) return c1;
+		if(c1==0) return c0;
+	}
+	t=newtree();
+	t->type=type;
+	t->child[0]=c0;
+	t->child[1]=c1;
+	t->child[2]=c2;
+	return t;
+}
+
+Tree *
+mung1(Tree *t, Tree *c0)
+{
+	t->child[0]=c0;
+	return t;
+}
+
+Tree *
+mung2(Tree *t, Tree *c0, Tree *c1)
+{
+	t->child[0]=c0;
+	t->child[1]=c1;
+	return t;
+}
+
+Tree *
+mung3(Tree *t, Tree *c0, Tree *c1, Tree *c2)
+{
+	t->child[0]=c0;
+	t->child[1]=c1;
+	t->child[2]=c2;
+	return t;
+}
+
+Tree *
+epimung(Tree *comp, Tree *epi)
+{
+	Tree *p;
+	if(epi==0) return comp;
+	for(p=epi;p->child[1];p=p->child[1]);
+	p->child[1]=comp;
+	return epi;
+}
+
+/*
+ * Add a SIMPLE node at the root of t and percolate all the redirections
+ * up to the root.
+ */
+Tree *
+simplemung(Tree *t)
+{
+	Tree *u;
+	Io *s;
+	t=tree1(SIMPLE, t);
+	s=openstr();
+	pfmt(s, "%t", t);
+	t->str=strdup(s->strp);
+	closeio(s);
+	for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){
+		if(u->child[1]->type==DUP
+		|| u->child[1]->type==REDIR){
+			u->child[1]->child[1]=t;
+			t=u->child[1];
+			u->child[1]=0;
+		}
+	}
+	return t;
+}
+
+Tree *
+token(char *str, int type)
+{
+	Tree *t=newtree();
+	t->type=type;
+	t->str=strdup(str);
+	return t;
+}
+
+void
+freetree(Tree *p)
+{
+	if(p==0) return;	
+	freetree(p->child[0]);
+	freetree(p->child[1]);
+	freetree(p->child[2]);
+	if(p->str) free(p->str);
+	free((char *)p);
+}
--- /dev/null
+++ b/utils/rcsh/var.c
@@ -1,0 +1,241 @@
+#include "rc.h"
+#include "y.tab.h"
+
+extern char **_environ;
+extern char **environ;
+
+typedef struct Kw	Kw;
+
+#define	NKW	30
+#define NVAR	521
+
+struct Kw{
+	char	*name;
+	int	type;
+	Kw	*next;
+};
+
+void	updenvlocal(Var *v);
+void	addenv(Var *v);
+
+Kw	*kw[NKW];
+Var	*gvar[NVAR];
+
+int
+hash(char *s, int n)
+{
+	int h=0, i=1;
+
+	while(*s)
+		h+=*s++*i++;
+	h%=n;
+	return h<0?h+n:h;
+}
+
+void
+kenter(int type, char *name)
+{
+	int h=hash(name, NKW);
+	Kw *p=new(Kw);
+	p->type=type;
+	p->name=name;
+	p->next=kw[h];
+	kw[h]=p;
+}
+
+void
+vinit(void)
+{
+	char **env, *name, *val, *p;
+	int i;
+	Word *w;
+	Io *f;
+	int n;
+	Var *v;
+
+	env = _environ;
+	for(i=0; env[i]; free(name), i++) {
+		name = strdup(env[i]);
+		p = strchr(name, '=');
+		if(p == 0 || p == name)
+			continue;
+		*p = 0;
+		val = p+1;
+		n = strlen(val);
+		if(n == 0)
+			continue;
+
+		if(strncmp(name, "fn#", 3)!=0) {
+			/* variable */
+			w = 0;
+			p = val+n-1;
+			while(*p) {
+				if(*p == IWS)
+					*p-- = 0;
+				for(; *p && *p != IWS; p--)
+					;
+				w=newword(p+1, w);
+			}
+			setvar(name, w);
+			vlook(name)->changed=0;
+		} else {
+			/* function */
+			f = opencore(val, n);
+			execcmds(f);
+		}
+	}
+	v = vlook("path");
+	p = getenv("path");
+	if(v->val == 0 && p)
+		v->val = newword(p, 0);
+}
+
+
+Tree *
+klook(char *name)
+{
+	Kw *p;
+	Tree *t=token(name, WORD);
+	for(p=kw[hash(name, NKW)];p;p=p->next) {
+		if(strcmp(p->name, name)==0){
+			t->type=p->type;
+			t->iskw=1;
+			break;
+		}
+	}
+	return t;
+}
+
+Var *
+gvlook(char *name)
+{
+	int h=hash(name, NVAR);
+	Var *v;
+	for(v=gvar[h]; v; v=v->next)
+		if(strcmp(v->name, name)==0)
+			return v;
+
+	return gvar[h]=newvar(strdup(name), gvar[h]);
+}
+
+Var *
+vlook(char *name)
+{
+	Var *v;
+	if(runq)
+		for(v=runq->local; v; v=v->next)
+			if(strcmp(v->name, name)==0)
+				return v;
+	return gvlook(name);
+}
+
+void
+setvar(char *name, Word *val)
+{
+	Var *v=vlook(name);
+	freewords(v->val);
+	v->val=val;
+	v->changed=1;
+}
+
+Var *
+newvar(char *name, Var *next)
+{
+	Var *v=new(Var);
+	v->name=name;
+	v->val=0;
+	v->fn=0;
+	v->changed=0;
+	v->fnchanged=0;
+	v->next=next;
+	return v;
+}
+
+
+void
+execfinit(void)
+{
+}
+
+void 
+updenv(void)
+{
+	Var *v, **h;
+
+	for(h=gvar;h!=&gvar[NVAR];h++)
+		for(v=*h;v;v=v->next)
+			addenv(v);
+
+	if(runq)
+		updenvlocal(runq->local);
+}
+
+static void
+envput(char *var, char  *val)
+{
+	int i, n;
+	char *e;
+	char buf[256];
+
+	snprint(buf, sizeof(buf), "%s=%s", var, val);
+	n = strlen(var);
+	for(i = 0;;i++){
+		e = environ[i];
+		if(e == 0)
+			break;
+		if(strncmp(e, var, n) == 0){
+			free(e);
+			environ[i] = strdup(buf);
+			return;
+		}
+	}
+	environ = realloc(environ, (i+2)*sizeof(char*));
+	environ[i++] = strdup(buf);
+	environ[i] = 0;
+}
+
+void
+addenv(Var *v)
+{
+	char buf[100], *p;
+	Io *f;
+	Word *w;
+	int i, n;
+
+	if(v->changed){
+		v->changed=0;
+		p = 0;
+		n = 0;
+		if(v->val) {
+			for(w=v->val; w; w=w->next) {
+				i = strlen(w->word);
+				p = realloc(p, n+i+1);
+				memmove(p+n, w->word, i);
+				n+=i;
+				p[n++] = IWS;
+			}
+			p[n-1] = 0;
+			envput(v->name, p);
+		} else
+			envput(v->name, "");
+		free(p);
+	}
+
+	if(v->fnchanged){
+		v->fnchanged=0;
+		snprint(buf, sizeof(buf), "fn#%s", v->name);
+		f = openstr();
+		pfmt(f, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+		envput(buf, f->strp);
+		closeio(f);
+	}
+}
+
+void
+updenvlocal(Var *v)
+{
+	if(v){
+		updenvlocal(v->next);
+		addenv(v);
+	}
+}
--- /dev/null
+++ b/utils/rcsh/word.c
@@ -1,0 +1,173 @@
+#include "rc.h"
+
+Word *
+newword(char *wd, Word *next)
+{
+	Word *p=new(Word);
+	p->word=strdup(wd);
+	p->next=next;
+	return p;
+}
+
+void
+pushword(char *wd)
+{
+	if(runq->argv==0)
+		panic("pushword but no argv!", 0);
+	runq->argv->words=newword(wd, runq->argv->words);
+}
+
+void
+popword(void)
+{
+	Word *p;
+
+	if(runq->argv==0)
+		panic("popword but no argv!", 0);
+	p=runq->argv->words;
+	if(p==0)
+		panic("popword but no word!", 0);
+	runq->argv->words=p->next;
+	free(p->word);
+	free(p);
+}
+
+void
+freewords(Word *w)
+{
+	Word *nw;
+	while(w){
+		free(w->word);
+		nw=w->next;
+		free(w);
+		w=nw;
+	}
+}
+
+void
+freelist(Word *w)
+{
+	Word *nw;
+	while(w){
+		nw=w->next;
+		free(w->word);
+		free(w);
+		w=nw;
+	}
+}
+
+void
+pushlist(void)
+{
+	List *p=new(List);
+	p->next=runq->argv;
+	p->words=0;
+	runq->argv=p;
+}
+
+void
+poplist(void)
+{
+	List *p=runq->argv;
+	if(p==0)
+		panic("poplist but no argv", 0);
+	freelist(p->words);
+	runq->argv=p->next;
+	free(p);
+}
+
+int
+count(Word *w)
+{
+	int n;
+	for(n=0;w;n++)
+		w=w->next;
+	return n;
+}
+
+/*
+ * copy arglist a, adding the copy to the front of tail
+ */
+Word *
+copywords(Word *a, Word *tail)
+{
+	Word *v=0, **end;
+	for(end=&v;a;a=a->next,end=&(*end)->next)
+		*end=newword(a->word, 0);
+	*end=tail;
+	return v;
+}
+
+char *
+list2str(Word *words)
+{
+	char *value, *s, *t;
+	int len=0;
+	Word *ap;
+
+	for(ap=words;ap;ap=ap->next)
+		len+=1+strlen(ap->word);
+	value=malloc(len+1);
+	s=value;
+	for(ap=words;ap;ap=ap->next){
+		for(t=ap->word;*t;) *s++=*t++;
+		*s++=' ';
+	}
+	if(s==value)
+		*s='\0';
+	else
+		s[-1]='\0';
+	return value;
+}
+
+Word *
+subwords(Word *val, int len, Word *sub, Word *a)
+{
+	int n;
+	char *s;
+
+	if(!sub) return a;
+	a=subwords(val, len, sub->next, a);
+	s=sub->word;
+	deglob(s);
+	n=0;
+	while('0'<=*s && *s<='9') n=n*10+ *s++ -'0';
+	if(n<1 || len<n) return a;
+	for(;n!=1;--n) val=val->next;
+	return newword(val->word, a);
+}
+
+
+void
+pushredir(int type, int from, int to)
+{
+	Redir *rp=new(Redir);
+	rp->type=type;
+	rp->from=from;
+	rp->to=to;
+	rp->next=runq->redir;
+	runq->redir=rp;
+}
+
+void
+turfredir(void)
+{
+	while(runq->redir!=runq->startredir)
+		Xpopredir();
+}
+
+Word*
+conclist(Word *lp, Word *rp, Word *tail)
+{
+	char *buf;
+	Word *v;
+	if(lp->next || rp->next)
+		tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
+			tail);
+	buf=malloc(strlen(lp->word)+strlen(rp->word)+1);
+	strcpy(buf, lp->word);
+	strcat(buf, rp->word);
+	v=newword(buf, tail);
+	free(buf);
+	return v;
+}
--- /dev/null
+++ b/utils/rm/mkfile
@@ -1,0 +1,24 @@
+<../../mkconfig
+
+#
+#	the rm command is only needed on Windows NT and Windows 95
+#
+
+TARG=rm
+
+OFILES=	rm-$TARGMODEL.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+rm-Posix.c rm-Inferno.c:QV:
+	echo 'rm is only built on Windows NT or Windows 95'
+	exit 1
+
+$BIN/rm.exe:	$O.out
+	rm -f $target && cp $prereq $target
--- /dev/null
+++ b/utils/rm/rm-Nt.c
@@ -1,0 +1,151 @@
+#include <lib9.h>
+#include <windows.h>
+
+typedef struct	Direntry
+{
+	int	isdir;
+	char	*name;
+} Direntry;
+
+char	errbuf[ERRMAX];
+long	ndirbuf = 0;
+int	ignerr = 0;
+
+void
+err(char *f)
+{
+	if(!ignerr){
+		errstr(errbuf, sizeof errbuf);
+		fprint(2, "rm: %s: %s\n", f, errbuf);
+	}
+}
+
+int
+badentry(char *filename)
+{
+	if(*filename == 0)
+		return 1;
+	if(filename[0] == '.'){
+		if(filename[1] == 0)
+			return 1;
+		if(filename[1] == '.' && filename[2] == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Read a whole directory before removing anything as the holes formed
+ * by removing affect the read offset.
+ */
+Direntry*
+readdirect(char *path)
+{
+	long n;
+	HANDLE h;
+	Direntry *d;
+	char fullpath[MAX_PATH];
+	WIN32_FIND_DATA data;
+
+	snprint(fullpath, MAX_PATH, "%s\\*.*", path);
+	h = FindFirstFile(fullpath, &data);
+	if(h == INVALID_HANDLE_VALUE)
+		err(path);
+
+	n = 0;
+	d = 0;
+	for(;;){
+		if(!badentry(data.cFileName)){
+			d = realloc(d, (n+2)*sizeof(Direntry));
+			if(d == 0){
+				err("memory allocation");
+				exits(errbuf);
+			}
+			d[n].name = malloc(strlen(data.cFileName)+1);
+			if(d[n].name == 0){
+				err("memory allocation");
+				exits(errbuf);
+			}
+			strcpy(d[n].name, data.cFileName);
+			if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+				d[n].isdir = 1;
+			else
+				d[n].isdir = 0;
+			n++;
+		}
+		if(FindNextFile(h, &data) == 0)
+			break;
+	}
+	FindClose(h);
+	if(d){
+		d[n].name = 0;
+		d[n].isdir = 0;
+	}
+	return d;
+}
+
+/*
+ * f is a non-empty directory. Remove its contents and then it.
+ */
+void
+Ntrmdir(char *f)
+{
+	Direntry *dp, *dq;
+	char name[MAX_PATH];
+
+	dq = readdirect(f);
+
+	if(dq == 0)
+		return;
+
+	for(dp = dq; dp->name; dp++){
+		snprint(name, MAX_PATH, "%s/%s", f, dp->name);
+		if(remove(name) == -1){
+			if(dp->isdir == 0)
+				err(name);
+			else
+			if(RemoveDirectory(name) == 0)
+				Ntrmdir(name);
+		}
+		free(dp->name);
+	}
+	if(RemoveDirectory(f) == 0)
+		err(f);
+	free(dq);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	int recurse;
+	char *f;
+	Dir *db;
+
+	ignerr = 0;
+	recurse = 0;
+	ARGBEGIN{
+	case 'r':
+		recurse = 1;
+		break;
+	case 'f':
+		ignerr = 1;
+		break;
+	default:
+		fprint(2, "usage: rm [-fr] file ...\n");
+		exits("usage");
+	}ARGEND
+	for(i=0; i<argc; i++){
+		f = argv[i];
+		if(remove(f) != -1)
+			continue;
+		if((db = dirstat(f)) == nil || (db->qid.type&QTDIR) ==0)
+			err(f);
+		else if(RemoveDirectory(f) == 0)
+			if(recurse)
+				Ntrmdir(f);
+			else
+				err(f);
+	}
+	exits(errbuf);
+}
--- /dev/null
+++ b/utils/sed/mkfile
@@ -1,0 +1,16 @@
+<../../mkconfig
+
+TARG=sed
+
+OFILES=	sed.$O\
+
+HFILES= $ROOT/include/bio.h\
+	../include/regexp.h\
+
+LIBS=regexp bio 9		# libbio.a uses lib9.a so order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/sed/sed.c
@@ -1,0 +1,1459 @@
+/*
+ * sed -- stream editor
+ */
+#include <lib9.h>
+#include <bio.h>
+#include <regexp.h>
+
+enum {
+	DEPTH		= 20,		/* max nesting depth of {} */
+	MAXCMDS		= 512,		/* max sed commands */
+	ADDSIZE		= 10000,	/* size of add & read buffer */
+	MAXADDS		= 20,		/* max pending adds and reads */
+	LBSIZE		= 8192,		/* input line size */
+	LABSIZE		= 50,		/* max number of labels */
+	MAXSUB		= 10,		/* max number of sub reg exp */
+	MAXFILES	= 120		/* max output files */
+};
+
+/*
+ * An address is a line #, a R.E., "$", a reference to the last
+ * R.E., or nothing.
+ */
+typedef struct {
+	enum {
+		A_NONE,
+		A_DOL,
+		A_LINE,
+		A_RE,
+		A_LAST
+	}type;
+/*	union { */
+		long	line;		/* Line # */
+		Reprog	*rp;		/* Compiled R.E. */
+/*	}; */
+} Addr;
+
+typedef struct	SEDCOM {
+	Addr	ad1;			/* optional start address */
+	Addr	ad2;			/* optional end address */
+/*	union { */
+		Reprog	*re1;		/* compiled R.E. */
+		Rune	*text;		/* added text or file name */
+		struct	SEDCOM	*lb1;	/* destination command of branch */
+/*	}; */
+	Rune	*rhs;			/* Right-hand side of substitution */
+	Biobuf*	fcode;			/* File ID for read and write */
+	char	command;		/* command code -see below */
+	char	gfl;			/* 'Global' flag for substitutions */
+	char	pfl;			/* 'print' flag for substitutions */
+	char	active;			/* 1 => data between start and end */
+	char	negfl;			/* negation flag */
+} SedCom;
+
+/* Command Codes for field SedCom.command */
+#define ACOM	01
+#define BCOM	020
+#define CCOM	02
+#define	CDCOM	025
+#define	CNCOM	022
+#define COCOM	017
+#define	CPCOM	023
+#define DCOM	03
+#define ECOM	015
+#define EQCOM	013
+#define FCOM	016
+#define GCOM	027
+#define CGCOM	030
+#define HCOM	031
+#define CHCOM	032
+#define ICOM	04
+#define LCOM	05
+#define NCOM	012
+#define PCOM	010
+#define QCOM	011
+#define RCOM	06
+#define SCOM	07
+#define TCOM	021
+#define WCOM	014
+#define	CWCOM	024
+#define	YCOM	026
+#define XCOM	033
+
+typedef struct label {			/* Label symbol table */
+	Rune	uninm[9];		/* Label name */
+	SedCom	*chain;
+	SedCom	*address;		/* Command associated with label */
+} Label;
+
+typedef	struct	FILE_CACHE {		/* Data file control block */
+	struct FILE_CACHE *next;	/* Forward Link */
+	char	*name;			/* Name of file */
+} FileCache;
+
+SedCom pspace[MAXCMDS];			/* Command storage */
+SedCom *pend = pspace+MAXCMDS;		/* End of command storage */
+SedCom *rep = pspace;			/* Current fill point */
+
+Reprog	*lastre = 0;			/* Last regular expression */
+Resub	subexp[MAXSUB];			/* sub-patterns of pattern match*/
+
+Rune	addspace[ADDSIZE];		/* Buffer for a, c, & i commands */
+Rune	*addend = addspace+ADDSIZE;
+
+SedCom	*abuf[MAXADDS];			/* Queue of pending adds & reads */
+SedCom	**aptr = abuf;
+
+struct {				/* Sed program input control block */
+	enum PTYPE { 			/* Either on command line or in file */
+		P_ARG,
+		P_FILE
+	} type;
+/*	union PCTL { */			/* Pointer to data */
+		Biobuf	*bp;
+		char	*curr;
+/*	}; */
+} prog;
+
+Rune	genbuf[LBSIZE];			/* Miscellaneous buffer */
+
+FileCache	*fhead = 0;		/* Head of File Cache Chain */
+FileCache	*ftail = 0;		/* Tail of File Cache Chain */
+
+Rune	*loc1;				/* Start of pattern match */
+Rune	*loc2;				/* End of pattern match */
+Rune	seof;				/* Pattern delimiter char */
+
+Rune	linebuf[LBSIZE+1];		/* Input data buffer */
+Rune	*lbend = linebuf+LBSIZE;	/* End of buffer */
+Rune	*spend = linebuf;		/* End of input data */
+Rune	*cp;				/* Current scan point in linebuf */
+
+Rune	holdsp[LBSIZE+1];		/* Hold buffer */
+Rune	*hend = holdsp+LBSIZE;		/* End of hold buffer */
+Rune	*hspend = holdsp;		/* End of hold data */
+
+int	nflag;				/* Command line flags */
+int	gflag;
+
+int	dolflag;			/* Set when at true EOF */
+int	sflag;				/* Set when substitution done */
+int	jflag;				/* Set when jump required */
+int	delflag;			/* Delete current line when set */
+
+long	lnum = 0;			/* Input line count */
+
+char	fname[MAXFILES][40];		/* File name cache */
+Biobuf	*fcode[MAXFILES];		/* File ID cache */
+int	nfiles = 0;			/* Cache fill point */
+
+Biobuf	fout;				/* Output stream */
+Biobuf	bstdin;				/* Default input */
+Biobuf*	f = 0;				/* Input data */
+
+Label	ltab[LABSIZE];			/* Label name symbol table */
+Label	*labend = ltab+LABSIZE;		/* End of label table */
+Label	*lab = ltab+1;			/* Current Fill point */
+
+int	depth = 0;			/* {} stack pointer */
+
+Rune	bad;				/* Dummy err ptr reference */
+Rune	*badp = &bad;
+
+
+char	CGMES[]	 = 	"command garbled: %S";
+char	TMMES[]	 = 	"Too much text: %S";
+char	LTL[]	 = 	"Label too long: %S";
+char	AD0MES[] =	"No addresses allowed: %S";
+char	AD1MES[] =	"Only one address allowed: %S";
+
+void	address(Addr *);
+void	arout(void);
+int	cmp(char *, char *);
+int	rcmp(Rune *, Rune *);
+void	command(SedCom *);
+Reprog	*compile(void);
+Rune	*compsub(Rune *, Rune *);
+void	dechain(void);
+void	dosub(Rune *);
+int	ecmp(Rune *, Rune *, int);
+void	enroll(char *);
+void	errexit(void);
+int	executable(SedCom *);
+void	execute(void);
+void	fcomp(void);
+long	getrune(void);
+Rune	*gline(Rune *);
+int	match(Reprog *, Rune *);
+void	newfile(enum PTYPE, char *);
+int 	opendata(void);
+Biobuf	*open_file(char *);
+Rune	*place(Rune *, Rune *, Rune *);
+void	quit(char *, ...);
+int	rline(Rune *, Rune *);
+Label	*search(Label *);
+int	substitute(SedCom *);
+char	*text(char *);
+Rune	*stext(Rune *, Rune *);
+int	ycomp(SedCom *);
+char *	trans(int c);
+void	putline(Biobuf *bp, Rune *buf, int n);
+
+void
+main(int argc, char **argv)
+{
+	int compfl;
+
+	lnum = 0;
+	Binit(&fout, 1, OWRITE);
+	fcode[nfiles++] = &fout;
+	compfl = 0;
+
+	if(argc == 1)
+		exits(0);
+	ARGBEGIN{
+	case 'e':
+		if (argc <= 1)
+			quit("missing pattern");
+		newfile(P_ARG, ARGF());
+		fcomp();
+		compfl = 1;
+		continue;
+	case 'f':
+		if(argc <= 1)
+			quit("no pattern-file");
+		newfile(P_FILE, ARGF());
+		fcomp();
+		compfl = 1;
+		continue;
+	case 'g':
+		gflag++;
+		continue;
+	case 'n':
+		nflag++;
+		continue;
+	default:
+		fprint(2, "sed: Unknown flag: %c\n", ARGC());
+		continue;
+	} ARGEND
+
+	if(compfl == 0) {
+		if (--argc < 0)
+			quit("missing pattern");
+		newfile(P_ARG, *argv++);
+		fcomp();
+	}
+
+	if(depth)
+		quit("Too many {'s");
+
+	ltab[0].address = rep;
+
+	dechain();
+
+	if(argc <= 0)
+		enroll(0);		/* Add stdin to cache */
+	else
+		while(--argc >= 0)
+			enroll(*argv++);
+	execute();
+	exits(0);
+}
+
+void
+fcomp(void)
+{
+	int	i;
+	Label	*lpt;
+	Rune	*tp;
+	SedCom	*pt, *pt1;
+	static Rune	*p = addspace;
+	static SedCom	**cmpend[DEPTH];	/* stack of {} operations */
+
+	while (rline(linebuf, lbend) >= 0) {
+		cp = linebuf;
+comploop:
+		while(*cp == ' ' || *cp == '\t')
+			cp++;
+		if(*cp == '\0' || *cp == '#')
+			continue;
+		if(*cp == ';') {
+			cp++;
+			goto comploop;
+		}
+
+		address(&rep->ad1);
+		if (rep->ad1.type != A_NONE) {
+			if (rep->ad1.type == A_LAST) {
+				if (!lastre)
+					quit("First RE may not be null");
+				rep->ad1.type = A_RE;
+				rep->ad1.rp = lastre;
+			}
+			if(*cp == ',' || *cp == ';') {
+				cp++;
+				address(&rep->ad2);
+				if (rep->ad2.type == A_LAST) {
+					rep->ad2.type = A_RE;
+					rep->ad2.rp = lastre;
+				}
+			} else
+				rep->ad2.type = A_NONE;
+		}
+		while(*cp == ' ' || *cp == '\t')
+			cp++;
+
+swit:
+		switch(*cp++) {
+		default:
+			quit("Unrecognized command: %S", linebuf);
+
+		case '!':
+			rep->negfl = 1;
+			goto swit;
+
+		case '{':
+			rep->command = BCOM;
+			rep->negfl = !rep->negfl;
+			cmpend[depth++] = &rep->lb1;
+			if(++rep >= pend)
+				quit("Too many commands: %S", linebuf);
+			if(*cp == '\0')
+				continue;
+			goto comploop;
+
+		case '}':
+			if(rep->ad1.type != A_NONE)
+				quit(AD0MES, linebuf);
+			if(--depth < 0)
+				quit("Too many }'s");
+			*cmpend[depth] = rep;
+			if(*cp == 0)
+				continue;
+			goto comploop;
+
+		case '=':
+			rep->command = EQCOM;
+			if(rep->ad2.type != A_NONE)
+				quit(AD1MES, linebuf);
+			break;
+
+		case ':':
+			if(rep->ad1.type != A_NONE)
+				quit(AD0MES, linebuf);
+
+			while(*cp == ' ')
+				cp++;
+			tp = lab->uninm;
+			while (*cp && *cp != ';' && *cp != ' ' &&
+			    *cp != '\t' && *cp != '#') {
+				*tp++ = *cp++;
+				if(tp >= &lab->uninm[8])
+					quit(LTL, linebuf);
+			}
+			*tp = '\0';
+
+			if (*lab->uninm == '\0')		/* no label? */
+				quit(CGMES, linebuf);
+			if(lpt = search(lab)) {
+				if(lpt->address)
+					quit("Duplicate labels: %S", linebuf);
+			} else {
+				lab->chain = 0;
+				lpt = lab;
+				if(++lab >= labend)
+					quit("Too many labels: %S", linebuf);
+			}
+			lpt->address = rep;
+			if (*cp == '#')
+				continue;
+			rep--;			/* reuse this slot */
+			break;
+
+		case 'a':
+			rep->command = ACOM;
+			if(rep->ad2.type != A_NONE)
+				quit(AD1MES, linebuf);
+			if(*cp == '\\')
+				cp++;
+			if(*cp++ != '\n')
+				quit(CGMES, linebuf);
+			rep->text = p;
+			p = stext(p, addend);
+			break;
+		case 'c':
+			rep->command = CCOM;
+			if(*cp == '\\')
+				cp++;
+			if(*cp++ != '\n')
+				quit(CGMES, linebuf);
+			rep->text = p;
+			p = stext(p, addend);
+			break;
+		case 'i':
+			rep->command = ICOM;
+			if(rep->ad2.type != A_NONE)
+				quit(AD1MES, linebuf);
+			if(*cp == '\\')
+				cp++;
+			if(*cp++ != '\n')
+				quit(CGMES, linebuf);
+			rep->text = p;
+			p = stext(p, addend);
+			break;
+
+		case 'g':
+			rep->command = GCOM;
+			break;
+
+		case 'G':
+			rep->command = CGCOM;
+			break;
+
+		case 'h':
+			rep->command = HCOM;
+			break;
+
+		case 'H':
+			rep->command = CHCOM;
+			break;
+
+		case 't':
+			rep->command = TCOM;
+			goto jtcommon;
+
+		case 'b':
+			rep->command = BCOM;
+jtcommon:
+			while(*cp == ' ')
+				cp++;
+			if(*cp == '\0' || *cp == ';') {
+				/* no label; jump to end */
+				if(pt = ltab[0].chain) {
+					while((pt1 = pt->lb1) != nil)
+						pt = pt1;
+					pt->lb1 = rep;
+				} else
+					ltab[0].chain = rep;
+				break;
+			}
+
+			/* copy label into lab->uninm */
+			tp = lab->uninm;
+			while((*tp = *cp++) != '\0' && *tp != ';')
+				if(++tp >= &lab->uninm[8])
+					quit(LTL, linebuf);
+			cp--;
+			*tp = '\0';
+
+			if (*lab->uninm == '\0')
+				/* shouldn't get here */
+				quit(CGMES, linebuf);
+			if((lpt = search(lab)) != nil) {
+				if(lpt->address)
+					rep->lb1 = lpt->address;
+				else {
+					for(pt = lpt->chain; pt != nil &&
+					    (pt1 = pt->lb1) != nil; pt = pt1)
+						;
+					if (pt)
+						pt->lb1 = rep;
+				}
+			} else {			/* add new label */
+				lab->chain = rep;
+				lab->address = 0;
+				if(++lab >= labend)
+					quit("Too many labels: %S", linebuf);
+			}
+			break;
+
+		case 'n':
+			rep->command = NCOM;
+			break;
+
+		case 'N':
+			rep->command = CNCOM;
+			break;
+
+		case 'p':
+			rep->command = PCOM;
+			break;
+
+		case 'P':
+			rep->command = CPCOM;
+			break;
+
+		case 'r':
+			rep->command = RCOM;
+			if(rep->ad2.type != A_NONE)
+				quit(AD1MES, linebuf);
+			if(*cp++ != ' ')
+				quit(CGMES, linebuf);
+			rep->text = p;
+			p = stext(p, addend);
+			break;
+
+		case 'd':
+			rep->command = DCOM;
+			break;
+
+		case 'D':
+			rep->command = CDCOM;
+			rep->lb1 = pspace;
+			break;
+
+		case 'q':
+			rep->command = QCOM;
+			if(rep->ad2.type != A_NONE)
+				quit(AD1MES, linebuf);
+			break;
+
+		case 'l':
+			rep->command = LCOM;
+			break;
+
+		case 's':
+			rep->command = SCOM;
+			seof = *cp++;
+			if ((rep->re1 = compile()) == 0) {
+				if(!lastre)
+					quit("First RE may not be null.");
+				rep->re1 = lastre;
+			}
+			rep->rhs = p;
+			if((p = compsub(p, addend)) == 0)
+				quit(CGMES, linebuf);
+			if(*cp == 'g') {
+				cp++;
+				rep->gfl++;
+			} else if(gflag)
+				rep->gfl++;
+
+			if(*cp == 'p') {
+				cp++;
+				rep->pfl = 1;
+			}
+
+			if(*cp == 'P') {
+				cp++;
+				rep->pfl = 2;
+			}
+
+			if(*cp == 'w') {
+				cp++;
+				if(*cp++ !=  ' ')
+					quit(CGMES, linebuf);
+				text(fname[nfiles]);
+				for(i = nfiles - 1; i >= 0; i--)
+					if(cmp(fname[nfiles], fname[i]) == 0) {
+						rep->fcode = fcode[i];
+						goto done;
+					}
+				if(nfiles >= MAXFILES)
+					quit("Too many files in w commands 1");
+				rep->fcode = open_file(fname[nfiles]);
+			}
+			break;
+
+		case 'w':
+			rep->command = WCOM;
+			if(*cp++ != ' ')
+				quit(CGMES, linebuf);
+			text(fname[nfiles]);
+			for(i = nfiles - 1; i >= 0; i--)
+				if(cmp(fname[nfiles], fname[i]) == 0) {
+					rep->fcode = fcode[i];
+					goto done;
+				}
+			if(nfiles >= MAXFILES){
+				fprint(2, "sed: Too many files in w commands 2 \n");
+				fprint(2, "nfiles = %d; MAXF = %d\n",
+					nfiles, MAXFILES);
+				errexit();
+			}
+			rep->fcode = open_file(fname[nfiles]);
+			break;
+
+		case 'x':
+			rep->command = XCOM;
+			break;
+
+		case 'y':
+			rep->command = YCOM;
+			seof = *cp++;
+			if (ycomp(rep) == 0)
+				quit(CGMES, linebuf);
+			break;
+
+		}
+done:
+		if(++rep >= pend)
+			quit("Too many commands, last: %S", linebuf);
+		if(*cp++ != '\0') {
+			if(cp[-1] == ';')
+				goto comploop;
+			quit(CGMES, linebuf);
+		}
+	}
+}
+
+Biobuf *
+open_file(char *name)
+{
+	int fd;
+	Biobuf *bp;
+
+	if ((bp = malloc(sizeof(Biobuf))) == 0)
+		quit("Out of memory");
+	if ((fd = open(name, OWRITE)) < 0 &&
+	    (fd = create(name, OWRITE, 0666)) < 0)
+		quit("Cannot create %s", name);
+	Binit(bp, fd, OWRITE);
+	Bseek(bp, 0, 2);
+	fcode[nfiles++] = bp;
+	return bp;
+}
+
+Rune *
+compsub(Rune *rhs, Rune *end)
+{
+	Rune r;
+
+	while ((r = *cp++) != '\0') {
+		if(r == '\\') {
+			if (rhs < end)
+				*rhs++ = 0xFFFF;
+			else
+				return 0;
+			r = *cp++;
+			if(r == 'n')
+				r = '\n';
+		} else {
+			if(r == seof) {
+				if (rhs < end)
+					*rhs++ = '\0';
+				else
+					return 0;
+				return rhs;
+			}
+		}
+		if (rhs < end)
+			*rhs++ = r;
+		else
+			return 0;
+	}
+	return 0;
+}
+
+Reprog *
+compile(void)
+{
+	Rune c;
+	char *ep;
+	char expbuf[512];
+
+	if((c = *cp++) == seof)		/* '//' */
+		return 0;
+	ep = expbuf;
+	do {
+		if (c == '\0' || c == '\n')
+			quit(TMMES, linebuf);
+		if (c == '\\') {
+			if (ep >= expbuf+sizeof(expbuf))
+				quit(TMMES, linebuf);
+			ep += runetochar(ep, &c);
+			if ((c = *cp++) == 'n')
+				c = '\n';
+		}
+		if (ep >= expbuf + sizeof(expbuf))
+			quit(TMMES, linebuf);
+		ep += runetochar(ep, &c);
+	} while ((c = *cp++) != seof);
+	*ep = 0;
+	return lastre = regcomp(expbuf);
+}
+
+void
+regerror(char *s)
+{
+	USED(s);
+	quit(CGMES, linebuf);
+}
+
+void
+newfile(enum PTYPE type, char *name)
+{
+	if (type == P_ARG)
+		prog.curr = name;
+	else if ((prog.bp = Bopen(name, OREAD)) == 0)
+		quit("Cannot open pattern-file: %s\n", name);
+	prog.type = type;
+}
+
+int
+rline(Rune *buf, Rune *end)
+{
+	long c;
+	Rune r;
+
+	while ((c = getrune()) >= 0) {
+		if(c == '\r')			/* consume CRs on win95 */
+			continue;
+		r = c;
+		if (r == '\\') {
+			if (buf <= end)
+				*buf++ = r;
+			c = getrune();
+			if (c == '\r')
+				c = getrune();
+			if (c < 0)
+				break;
+			r = c;
+		} else if (r == '\n') {
+			*buf = '\0';
+			return 1;
+		}
+		if (buf <= end)
+			*buf++ = r;
+	}
+	*buf = '\0';
+	return -1;
+}
+
+long
+getrune(void)
+{
+	long c;
+	Rune r;
+	char *p;
+
+	if (prog.type == P_ARG) {
+		if ((p = prog.curr) != 0) {
+			if (*p) {
+				prog.curr += chartorune(&r, p);
+				c = r;
+			} else {
+				c = '\n';	/* fake an end-of-line */
+				prog.curr = 0;
+			}
+		} else
+			c = -1;
+	} else if ((c = Bgetrune(prog.bp)) < 0)
+		Bterm(prog.bp);
+	return c;
+}
+
+void
+address(Addr *ap)
+{
+	int c;
+	long lno;
+
+	if((c = *cp++) == '$')
+		ap->type = A_DOL;
+	else if(c == '/') {
+		seof = c;
+		if (ap->rp = compile())
+			ap->type = A_RE;
+		else
+			ap->type = A_LAST;
+	}
+	else if (c >= '0' && c <= '9') {
+		lno = c - '0';
+		while ((c = *cp) >= '0' && c <= '9')
+			lno = lno*10 + *cp++ - '0';
+		if(!lno)
+			quit("line number 0 is illegal",0);
+		ap->type = A_LINE;
+		ap->line = lno;
+	}
+	else {
+		cp--;
+		ap->type = A_NONE;
+	}
+}
+
+cmp(char *a, char *b)		/* compare characters */
+{
+	while(*a == *b++)
+		if (*a == '\0')
+			return 0;
+		else
+			a++;
+	return 1;
+}
+rcmp(Rune *a, Rune *b)		/* compare runes */
+{
+	while(*a == *b++)
+		if (*a == '\0')
+			return 0;
+		else
+			a++;
+	return 1;
+}
+
+char *
+text(char *p)		/* extract character string */
+{
+	Rune r;
+
+	while(*cp == ' ' || *cp == '\t')
+		cp++;
+	while (*cp) {
+		if ((r = *cp++) == '\\' && (r = *cp++) == '\0')
+			break;
+		if (r == '\n')
+			while (*cp == ' ' || *cp == '\t')
+				cp++;
+		p += runetochar(p, &r);
+	}
+	*p++ = '\0';
+	return p;
+}
+
+Rune *
+stext(Rune *p, Rune *end)		/* extract rune string */
+{
+	while(*cp == ' ' || *cp == '\t')
+		cp++;
+	while (*cp) {
+		if (*cp == '\\' && *++cp == '\0')
+			break;
+		if (p >= end-1)
+			quit(TMMES, linebuf);
+		if ((*p++ = *cp++) == '\n')
+			while(*cp == ' ' || *cp == '\t')
+				cp++;
+	}
+	*p++ = 0;
+	return p;
+}
+
+
+Label *
+search(Label *ptr)
+{
+	Label	*rp;
+
+	for (rp = ltab; rp < ptr; rp++)
+		if(rcmp(rp->uninm, ptr->uninm) == 0)
+			return(rp);
+	return(0);
+}
+
+void
+dechain(void)
+{
+	Label	*lptr;
+	SedCom	*rptr, *trptr;
+
+	for(lptr = ltab; lptr < lab; lptr++) {
+		if(lptr->address == 0)
+			quit("Undefined label: %S", lptr->uninm);
+		if(lptr->chain) {
+			rptr = lptr->chain;
+			while((trptr = rptr->lb1) != nil) {
+				rptr->lb1 = lptr->address;
+				rptr = trptr;
+			}
+			rptr->lb1 = lptr->address;
+		}
+	}
+}
+
+int
+ycomp(SedCom *r)
+{
+	int i;
+	Rune *rp, *sp, *tsp;
+	Rune c, highc;
+
+	highc = 0;
+	for(tsp = cp; *tsp != seof; tsp++) {
+		if(*tsp == '\\')
+			tsp++;
+		if(*tsp == '\n' || *tsp == '\0')
+			return 0;
+		if (*tsp > highc)
+			highc = *tsp;
+	}
+	tsp++;
+	if ((rp = r->text = (Rune *)malloc(sizeof(Rune) * (highc+2))) == nil)
+		quit("Out of memory");
+	*rp++ = highc;				/* save upper bound */
+	for (i = 0; i <= highc; i++)
+		rp[i] = i;
+	sp = cp;
+	while((c = *sp++) != seof) {
+		if(c == '\\' && *sp == 'n') {
+			sp++;
+			c = '\n';
+		}
+		if((rp[c] = *tsp++) == '\\' && *tsp == 'n') {
+			rp[c] = '\n';
+			tsp++;
+		}
+		if(rp[c] == seof || rp[c] == '\0') {
+			free(r->text);
+			r->text = nil;
+			return 0;
+		}
+	}
+	if(*tsp != seof) {
+		free(r->text);
+		r->text = nil;
+		return 0;
+	}
+	cp = tsp+1;
+	return 1;
+}
+
+void
+execute(void)
+{
+	SedCom	*ipc;
+
+	while (spend = gline(linebuf)){
+		for(ipc = pspace; ipc->command; ) {
+			if (!executable(ipc)) {
+				ipc++;
+				continue;
+			}
+			command(ipc);
+
+			if(delflag)
+				break;
+			if(jflag) {
+				jflag = 0;
+				if((ipc = ipc->lb1) == 0)
+					break;
+			} else
+				ipc++;
+		}
+		if(!nflag && !delflag)
+			putline(&fout, linebuf, spend - linebuf);
+		if(aptr > abuf)
+			arout();
+		delflag = 0;
+	}
+}
+
+/* determine if a statement should be applied to an input line */
+int
+executable(SedCom *ipc)
+{
+	if (ipc->active) {	/* Addr1 satisfied - accept until Addr2 */
+		if (ipc->active == 1)		/* Second line */
+			ipc->active = 2;
+		switch(ipc->ad2.type) {
+		case A_NONE:		/* No second addr; use first */
+			ipc->active = 0;
+			break;
+		case A_DOL:		/* Accept everything */
+			return !ipc->negfl;
+		case A_LINE:		/* Line at end of range? */
+			if (lnum <= ipc->ad2.line) {
+				if (ipc->ad2.line == lnum)
+					ipc->active = 0;
+				return !ipc->negfl;
+			}
+			ipc->active = 0;	/* out of range */
+			return ipc->negfl;
+		case A_RE:		/* Check for matching R.E. */
+			if (match(ipc->ad2.rp, linebuf))
+				ipc->active = 0;
+			return !ipc->negfl;
+		default:
+			quit("Internal error");
+		}
+	}
+	switch (ipc->ad1.type) {	/* Check first address */
+	case A_NONE:			/* Everything matches */
+		return !ipc->negfl;
+	case A_DOL:			/* Only last line */
+		if (dolflag)
+			return !ipc->negfl;
+		break;
+	case A_LINE:			/* Check line number */
+		if (ipc->ad1.line == lnum) {
+			ipc->active = 1;	/* In range */
+			return !ipc->negfl;
+		}
+		break;
+	case A_RE:			/* Check R.E. */
+		if (match(ipc->ad1.rp, linebuf)) {
+			ipc->active = 1;	/* In range */
+			return !ipc->negfl;
+		}
+		break;
+	default:
+		quit("Internal error");
+	}
+	return ipc->negfl;
+}
+
+int
+match(Reprog *pattern, Rune *buf)
+{
+	if (!pattern)
+		return 0;
+	subexp[0].s.rsp = buf; 
+	subexp[0].e.ep = 0;
+	if (rregexec(pattern, linebuf, subexp, MAXSUB)) {
+		loc1 = subexp[0].s.rsp;
+		loc2 = subexp[0].e.rep;
+		return 1;
+	}
+	loc1 = loc2 = 0;
+	return 0;
+}
+
+int
+substitute(SedCom *ipc)
+{
+	int len;
+
+	if(!match(ipc->re1, linebuf))
+		return 0;
+
+	/*
+	 * we have at least one match.  some patterns, e.g. '$' or '^', can
+	 * produce 0-length matches, so during a global substitute we must
+	 * bump to the character after a 0-length match to keep from looping.
+	 */
+	sflag = 1;
+	if(ipc->gfl == 0)			/* single substitution */
+		dosub(ipc->rhs);
+	else
+		do{				/* global substitution */
+			len = loc2 - loc1;	/* length of match */
+			dosub(ipc->rhs);	/* dosub moves loc2 */
+			if(*loc2 == 0)		/* end of string */
+				break;
+			if(len == 0)		/* zero-length R.E. match */
+				loc2++;		/* bump over 0-length match */
+			if(*loc2 == 0)		/* end of string */
+				break;
+		} while(match(ipc->re1, loc2));
+	return 1;
+}
+
+void
+dosub(Rune *rhsbuf)
+{
+	int c, n;
+	Rune *lp, *sp, *rp;
+
+	lp = linebuf;
+	sp = genbuf;
+	rp = rhsbuf;
+	while (lp < loc1)
+		*sp++ = *lp++;
+	while(c = *rp++) {
+		if (c == '&') {
+			sp = place(sp, loc1, loc2);
+			continue;
+		}
+		if (c == 0xFFFF && (c = *rp++) >= '1' && c < MAXSUB + '0') {
+			n = c-'0';
+			if (subexp[n].s.rsp && subexp[n].e.rep) {
+				sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
+				continue;
+			}
+			else {
+				fprint(2, "sed: Invalid back reference \\%d\n",n);
+				errexit();
+			}
+		}
+		*sp++ = c;
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	}
+	lp = loc2;
+	loc2 = sp - genbuf + linebuf;
+	while (*sp++ = *lp++)
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	lp = linebuf;
+	sp = genbuf;
+	while (*lp++ = *sp++)
+		;
+	spend = lp - 1;
+}
+
+Rune *
+place(Rune *sp, Rune *l1, Rune *l2)
+{
+	while (l1 < l2) {
+		*sp++ = *l1++;
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	}
+	return sp;
+}
+
+char *
+trans(int c)
+{
+	static char buf[] = "\\x0000";
+	static char hex[] = "0123456789abcdef";
+
+	switch(c) {
+	case '\b':
+		return "\\b";
+	case '\n':
+		return "\\n";
+	case '\r':
+		return "\\r";
+	case '\t':
+		return "\\t";
+	case '\\':
+		return "\\\\";
+	}
+	buf[2] = hex[(c>>12)&0xF];
+	buf[3] = hex[(c>>8)&0xF];
+	buf[4] = hex[(c>>4)&0xF];
+	buf[5] = hex[c&0xF];
+	return buf;
+}
+
+void
+command(SedCom *ipc)
+{
+	int i, c;
+	char *ucp;
+	Rune *execp, *p1, *p2, *rp;
+
+	switch(ipc->command) {
+	case ACOM:
+		*aptr++ = ipc;
+		if(aptr >= abuf+MAXADDS)
+			quit("sed: Too many appends after line %ld\n",
+				(char *)lnum);
+		*aptr = 0;
+		break;
+	case CCOM:
+		delflag = 1;
+		if(ipc->active == 1) {
+			for(rp = ipc->text; *rp; rp++)
+				Bputrune(&fout, *rp);
+			Bputc(&fout, '\n');
+		}
+		break;
+	case DCOM:
+		delflag++;
+		break;
+	case CDCOM:
+		p1 = p2 = linebuf;
+		while(*p1 != '\n') {
+			if(*p1++ == 0) {
+				delflag++;
+				return;
+			}
+		}
+		p1++;
+		while(*p2++ = *p1++)
+			;
+		spend = p2 - 1;
+		jflag++;
+		break;
+	case EQCOM:
+		Bprint(&fout, "%ld\n", lnum);
+		break;
+	case GCOM:
+		p1 = linebuf;
+		p2 = holdsp;
+		while(*p1++ = *p2++)
+			;
+		spend = p1 - 1;
+		break;
+	case CGCOM:
+		*spend++ = '\n';
+		p1 = spend;
+		p2 = holdsp;
+		while(*p1++ = *p2++)
+			if(p1 >= lbend)
+				break;
+		spend = p1 - 1;
+		break;
+	case HCOM:
+		p1 = holdsp;
+		p2 = linebuf;
+		while(*p1++ = *p2++);
+		hspend = p1 - 1;
+		break;
+	case CHCOM:
+		*hspend++ = '\n';
+		p1 = hspend;
+		p2 = linebuf;
+		while(*p1++ = *p2++)
+			if(p1 >= hend)
+				break;
+		hspend = p1 - 1;
+		break;
+	case ICOM:
+		for(rp = ipc->text; *rp; rp++)
+			Bputrune(&fout, *rp);
+		Bputc(&fout, '\n');
+		break;
+	case BCOM:
+		jflag = 1;
+		break;
+	case LCOM:
+		c = 0;
+		for (i = 0, rp = linebuf; *rp; rp++) {
+			c = *rp;
+			if(c >= 0x20 && c < 0x7F && c != '\\') {
+				Bputc(&fout, c);
+				if(i++ > 71) {
+					Bprint(&fout, "\\\n");
+					i = 0;
+				}
+			} else {
+				for (ucp = trans(*rp); *ucp; ucp++){
+					c = *ucp;
+					Bputc(&fout, c);
+					if(i++ > 71) {
+						Bprint(&fout, "\\\n");
+						i = 0;
+					}
+				}
+			}
+		}
+		if(c == ' ')
+			Bprint(&fout, "\\n");
+		Bputc(&fout, '\n');
+		break;
+	case NCOM:
+		if(!nflag)
+			putline(&fout, linebuf, spend-linebuf);
+
+		if(aptr > abuf)
+			arout();
+		if((execp = gline(linebuf)) == 0) {
+			delflag = 1;
+			break;
+		}
+		spend = execp;
+		break;
+	case CNCOM:
+		if(aptr > abuf)
+			arout();
+		*spend++ = '\n';
+		if((execp = gline(spend)) == 0) {
+			delflag = 1;
+			break;
+		}
+		spend = execp;
+		break;
+	case PCOM:
+		putline(&fout, linebuf, spend-linebuf);
+		break;
+	case CPCOM:
+cpcom:
+		for(rp = linebuf; *rp && *rp != '\n'; rp++)
+			Bputc(&fout, *rp);
+		Bputc(&fout, '\n');
+		break;
+	case QCOM:
+		if(!nflag)
+			putline(&fout, linebuf, spend-linebuf);
+		if(aptr > abuf)
+			arout();
+		exits(0);
+	case RCOM:
+		*aptr++ = ipc;
+		if(aptr >= &abuf[MAXADDS])
+			quit("sed: Too many reads after line %ld\n",
+				(char *)lnum);
+		*aptr = 0;
+		break;
+	case SCOM:
+		i = substitute(ipc);
+		if(i && ipc->pfl)
+			if(ipc->pfl == 1)
+				putline(&fout, linebuf, spend-linebuf);
+			else
+				goto cpcom;
+		if(i && ipc->fcode)
+			goto wcom;
+		break;
+
+	case TCOM:
+		if(sflag) {
+			sflag = 0;
+			jflag = 1;
+		}
+		break;
+
+	case WCOM:
+wcom:
+		putline(ipc->fcode,linebuf, spend - linebuf);
+		break;
+	case XCOM:
+		p1 = linebuf;
+		p2 = genbuf;
+		while(*p2++ = *p1++)
+			;
+		p1 = holdsp;
+		p2 = linebuf;
+		while(*p2++ = *p1++)
+			;
+		spend = p2 - 1;
+		p1 = genbuf;
+		p2 = holdsp;
+		while(*p2++ = *p1++)
+			;
+		hspend = p2 - 1;
+		break;
+	case YCOM:
+		p1 = linebuf;
+		p2 = ipc->text;
+		for (i = *p2++;	*p1; p1++)
+			if (*p1 <= i)
+				*p1 = p2[*p1];
+		break;
+	}
+}
+
+void
+putline(Biobuf *bp, Rune *buf, int n)
+{
+	while (n--)
+		Bputrune(bp, *buf++);
+	Bputc(bp, '\n');
+}
+ecmp(Rune *a, Rune *b, int count)
+{
+	while(count--)
+		if(*a++ != *b++)
+			return 0;
+	return 1;
+}
+
+void
+arout(void)
+{
+	int	c;
+	char	*s;
+	char	buf[128];
+	Rune	*p1;
+	Biobuf	*fi;
+
+	for (aptr = abuf; *aptr; aptr++) {
+		if((*aptr)->command == ACOM) {
+			for(p1 = (*aptr)->text; *p1; p1++ )
+				Bputrune(&fout, *p1);
+			Bputc(&fout, '\n');
+		} else {
+			for(s = buf, p1 = (*aptr)->text; *p1; p1++)
+				s += runetochar(s, p1);
+			*s = '\0';
+			if((fi = Bopen(buf, OREAD)) == 0)
+				continue;
+			while((c = Bgetc(fi)) >= 0)
+				Bputc(&fout, c);
+			Bterm(fi);
+		}
+	}
+	aptr = abuf;
+	*aptr = 0;
+}
+
+void
+errexit(void)
+{
+	exits("error");
+}
+
+void
+quit(char *fmt, ...)
+{
+	char *p, *ep;
+	char msg[256];
+	va_list arg;
+
+	ep = msg + sizeof msg;
+	p = seprint(msg, ep, "sed: ");
+	va_start(arg, fmt);
+	p = vseprint(p, ep, fmt, arg);
+	va_end(arg);
+	p = seprint(p, ep, "\n");
+	write(2, msg, p - msg);
+	errexit();
+}
+
+Rune *
+gline(Rune *addr)
+{
+	long c;
+	Rune *p;
+	static long peekc = 0;
+
+	if (f == 0 && opendata() < 0)
+		return 0;
+	sflag = 0;
+	lnum++;
+/*	Bflush(&fout);********* dumped 4/30/92 - bobf****/
+	do {
+		p = addr;
+		for (c = (peekc? peekc: Bgetrune(f)); c >= 0; c = Bgetrune(f)) {
+			if (c == '\n') {
+				if ((peekc = Bgetrune(f)) < 0 && fhead == 0)
+					dolflag = 1;
+				*p = '\0';
+				return p;
+			}
+			if (c && p < lbend)
+				*p++ = c;
+		}
+		/* return partial final line, adding implicit newline */
+		if(p != addr) {
+			*p = '\0';
+			peekc = -1;
+			if (fhead == 0)
+				dolflag = 1;
+			return p;
+		}
+		peekc = 0;
+		Bterm(f);
+	} while (opendata() > 0);		/* Switch to next stream */
+	f = 0;
+	return 0;
+}
+
+/*
+ * Data file input section - the intent is to transparently
+ *	catenate all data input streams.
+ */
+void
+enroll(char *filename)		/* Add a file to the input file cache */
+{
+	FileCache *fp;
+
+	if ((fp = (FileCache *)malloc(sizeof (FileCache))) == nil)
+		quit("Out of memory");
+	if (ftail == nil)
+		fhead = fp;
+	else
+		ftail->next = fp;
+	ftail = fp;
+	fp->next = nil;
+	fp->name = filename;		/* 0 => stdin */
+}
+
+int
+opendata(void)
+{
+	if (fhead == nil)
+		return -1;
+	if (fhead->name) {
+		if ((f = Bopen(fhead->name, OREAD)) == nil)
+			quit("Can't open %s", fhead->name);
+	} else {
+		Binit(&bstdin, 0, OREAD);
+		f = &bstdin;
+	}
+	fhead = fhead->next;
+	return 1;
+}
--- /dev/null
+++ b/utils/sqz/NOTICE
@@ -1,0 +1,27 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below.  It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+	Copyright © 1998 C H Forsyth (forsyth@terzarima.net).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/utils/sqz/mkfile
@@ -1,0 +1,19 @@
+<../../mkconfig
+CFLAGS=$CFLAGS -I../include
+
+TARG=sqz
+
+OFILES=	sqz.$O\
+
+HFILES=	\
+	squeeze.h\
+	../include/a.out.h\
+	../include/mach.h\
+
+LIBS=	mach bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
--- /dev/null
+++ b/utils/sqz/squeeze.h
@@ -1,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ *	Sqhdr
+ *	original Exec header
+ *	two Squeeze tables
+ *	squeezed segment
+ *	unsqueezed segment, if any
+ */
+#define	SQMAGIC	(ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+	uchar	magic[4];	/* SQMAGIC */
+	uchar	text[4];	/* squeezed length of text (excluding tables) */
+	uchar	data[4];	/* squeezed length of data (excluding tables) */
+	uchar	asis[4];	/* length of unsqueezed segment */
+	uchar	toptxt[4];	/* value for 0 encoding in text */
+	uchar	topdat[4];	/* value for 0 encoding in data */
+	uchar	sum[4];	/* simple checksum of unsqueezed data */
+	uchar	flags[4];
+};
+#define	SQHDRLEN	(8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits.  note that the mapping is its own inverse.
+ */
+#define	QREMAP(X)\
+	switch((X)>>26){\
+	case 19: case 31: case 59: case 63:\
+		(X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+	}
--- /dev/null
+++ b/utils/sqz/sqz.c
@@ -1,0 +1,526 @@
+#include <lib9.h>
+#include <a.out.h>
+#include "squeeze.h"
+
+/*
+ * forsyth@vitanuova.com
+ */
+
+typedef struct Word Word;
+struct Word {
+	ulong	v;
+	ushort	freq;
+	ushort	code;
+	Word*	next;
+};
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+	int	n;
+	/*union {*/
+		ulong	tab[7*256];
+		Word*	rep[7*256];
+	/*};*/
+};
+
+enum {
+	HMASK = 0xFFFF,
+	HSIZE = HMASK+1,
+
+	Codebufsize = 3*1024*1024
+};
+
+#define	GET4(p)	(((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+#define	GET4L(p)	(((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
+#define	PUT4(p,v)	(((p)[0]=(v)>>24),((p)[1]=(v)>>16),((p)[2]=(v)>>8),((p)[3]=(v)))
+
+static	uchar	prog[Codebufsize];
+static	uchar	outbuf[Codebufsize];
+static	Word*	hash1[HSIZE];
+static	Word*	hash2[HSIZE];
+static	Sqhdr	sqhdr;
+static	ulong	chksum;
+
+static	int	dflag;	/* squeeze data, not text */
+static	int	tflag;		/* squeeze text, leave data as-is */
+static	int	qflag = 1;	/* enable powerpc option */
+static	int	wflag;	/* write output */
+static	int	islittle;		/* object code uses little-endian byte order */
+static	int	debug;
+static	char*	fname;
+
+static	void	analyse(ulong*, int, Squeeze*, Squeeze*, Word**);
+static	Word**	collate(Word**, int);
+static	void	dumpsq(Squeeze*, int);
+static	void	freehash(Word**);
+static	long	Read(int, void*, long);
+static	void	remap(Squeeze*);
+static	int	squeeze(ulong*, int, uchar*, ulong);
+static	int	squeezetab(int, int, Squeeze*, Word**, int);
+static	void	squirt(int, Squeeze*);
+static	void	Write(int, void*, long);
+
+static void
+usage(void)
+{
+	fprint(2, "Usage: sqz [-w] [-t] [-d] [-q] q.out\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int fd, n, ns, nst, nsd;
+	long txtlen, datlen, asis;
+	ulong topdat, toptxt;
+	Exec ex;
+	Squeeze sq3, sq4, sq5, sq6;
+	Word *top;
+
+	setbinmode();
+/*	fmtinstall('f', gfltconv); */
+	ARGBEGIN{
+	case 'D':
+		debug++;
+		break;
+	case 'd':
+		dflag++;
+		break;
+	case 'q':
+		qflag = 0;
+		break;
+	case 't':
+		tflag++;
+		break;
+	case 'w':
+		wflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+	fname = *argv;
+	if(fname == nil)
+		usage();
+	fd = open(fname, OREAD);
+	if(fd < 0){
+		fprint(2, "sqz: can't open %s: %r\n", fname);
+		exits("open");
+	}
+	Read(fd, &ex, sizeof(Exec));
+	txtlen = GET4((uchar*)&ex.text);
+	datlen = GET4((uchar*)&ex.data);
+	switch(GET4((uchar*)&ex.magic)){
+	case Q_MAGIC:	/* powerpc */
+		islittle = 0;
+		break;
+	case E_MAGIC:	/* arm */
+		islittle = 1;
+		qflag = 0;
+		break;
+	case 0xA0E1:	/* arm AIF */
+		islittle = 1;
+		qflag = 0;
+		txtlen = GET4L((uchar*)&ex+(5*4))-sizeof(Exec);
+		datlen = GET4L((uchar*)&ex+(6*4));
+		break;
+	default:
+		fprint(2, "sqz: unknown magic for sqz: %8.8ux\n", GET4((uchar*)&ex.magic));
+		exits("bad magic");
+	}
+	if(qflag)
+		fprint(2, "PowerPC rules\n");
+	if(islittle)
+		fprint(2, "Little endian\n");
+	if(txtlen > sizeof(prog) || datlen > sizeof(prog) || txtlen+datlen > sizeof(prog)){
+		fprint(2, "sqz: executable too big: %lud+%lud; increase Codebufsize in sqz.c\n", txtlen, datlen);
+		exits("size");
+	}
+	if(dflag){
+		seek(fd, txtlen, 1);
+		Read(fd, prog, datlen);
+	}else{
+		Read(fd, prog, txtlen);
+		Read(fd, prog+txtlen, datlen);
+	}
+	close(fd);
+	asis = 0;
+	if(dflag)
+		n = datlen;
+	else if(tflag){
+		n = txtlen;
+		asis = datlen;
+	}else
+		n = txtlen+datlen;
+	if(dflag || tflag){
+		analyse((ulong*)prog, n/4, &sq3, &sq4, &top);
+		nst = squeeze((ulong*)prog, n/4, outbuf, top->v);
+		if(nst < 0)
+			exits("sqz");
+		nsd = 0;
+		remap(&sq3);
+		remap(&sq4);
+		toptxt = topdat = top->v;
+	}else{
+		analyse((ulong*)prog, txtlen/4, &sq3, &sq4, &top);
+		nst = squeeze((ulong*)prog, txtlen/4, outbuf, top->v);
+		if(nst < 0)
+			exits("sqz");
+		toptxt = top->v;
+		remap(&sq3);
+		remap(&sq4);
+		if(datlen/4){
+			freehash(hash1);
+			freehash(hash2);
+			analyse((ulong*)(prog+txtlen), datlen/4, &sq5, &sq6, &top);
+			nsd = squeeze((ulong*)(prog+txtlen), datlen/4, outbuf+nst, top->v);
+			if(nsd < 0)
+				exits("sqz");
+			topdat = top->v;
+			remap(&sq5);
+			remap(&sq6);
+		}else{
+			nsd = 0;
+			topdat = 0;
+		}
+	}
+	ns = nst+nsd;
+	fprint(2, "%d/%d bytes\n", ns, n);
+	fprint(2, "%8.8lux csum\n", chksum);
+	if(!wflag)
+		exits(0);
+	PUT4(sqhdr.magic, SQMAGIC);
+	PUT4(sqhdr.toptxt, toptxt);
+	PUT4(sqhdr.sum, chksum);
+	PUT4(sqhdr.text, nst);
+	PUT4(sqhdr.topdat, topdat);
+	PUT4(sqhdr.data, nsd);
+	PUT4(sqhdr.asis, asis);
+	PUT4(sqhdr.flags, 0);
+	Write(1, &sqhdr, SQHDRLEN);
+	Write(1, &ex, sizeof(Exec));
+	squirt(1, &sq3);
+	squirt(1, &sq4);
+	Write(1, outbuf, nst);
+	if(nsd){
+		squirt(1, &sq5);
+		squirt(1, &sq6);
+		Write(1, outbuf+nst, nsd);
+	}
+	if(asis)
+		Write(1, prog+txtlen, asis);
+	exits(0);
+}
+
+static void
+analyse(ulong *prog, int nw, Squeeze *sq3, Squeeze *sq4, Word **top)
+{
+	Word *w, **hp, **sorts, **resorts;
+	ulong *rp, *ep;
+	ulong v;
+	int i, nv1, nv2, nv, nz;
+
+	rp = prog;
+	ep = prog+nw;
+	nv = 0;
+	nz = 0;
+	while(rp < ep){
+		if(islittle){
+			v = GET4L((uchar*)rp);
+		}else{
+			v = GET4((uchar*)rp);
+		}
+		rp++;
+		chksum += v;
+		if(v == 0){
+			nz++;
+			if(0)
+				continue;
+		}
+		if(qflag){
+			QREMAP(v);
+		}
+		for(hp = &hash1[v&HMASK]; (w = *hp) != nil; hp = &w->next)
+			if(w->v == v)
+				break;
+		if(w == nil){
+			w = (Word*)malloc(sizeof(*w));
+			w->v = v;
+			w->freq = 0;
+			w->code = 0;
+			w->next = nil;
+			*hp = w;
+			nv++;
+		}
+		w->freq++;
+	}
+	sorts = collate(hash1, nv);
+	fprint(2, "phase 1: %d/%d words (%d zero), %d top (%8.8lux)\n", nv, nw, nz, sorts[0]->freq, sorts[0]->v);
+	*top = sorts[0];
+	nv1 = squeezetab(1, 0x900, sq3, sorts+1, nv-1)+1;
+	nv2 = 0;
+	for(i=nv1; i<nv; i++){
+		v = sorts[i]->v >> 8;
+		for(hp = &hash2[v&HMASK]; (w = *hp) != nil; hp = &w->next)
+			if(w->v == v)
+				break;
+		if(w == nil){
+			w = (Word*)malloc(sizeof(*w));
+			w->v = v;
+			w->freq = 0;
+			w->code = 0;
+			w->next = nil;
+			*hp = w;
+			nv2++;
+		}
+		w->freq++;
+	}
+	free(sorts);
+	resorts = collate(hash2, nv2);
+	fprint(2, "phase 2: %d/%d\n", nv2, nv-nv1);
+	squeezetab(2, 0x200, sq4, resorts, nv2);
+	free(resorts);
+	fprint(2, "phase 3: 1 4-code, %d 12-codes, %d 20-codes, %d uncoded\n",
+		sq3->n, sq4->n, nv-(sq3->n+sq4->n+1));
+}
+
+static int
+wdcmp(void *a, void *b)
+{
+	return (*(Word**)b)->freq - (*(Word**)a)->freq;
+}
+
+static Word **
+collate(Word **tab, int nv)
+{
+	Word *w, **hp, **sorts;
+	int i;
+
+	sorts = (Word**)malloc(nv*sizeof(Word**));
+	i = 0;
+	for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
+		for(w = *hp; w != nil; w = w->next)
+			sorts[i++] = w;
+	qsort(sorts, nv, sizeof(*sorts), wdcmp);
+	if(debug > 1)
+		for(i=0; i<nv; i++)
+			fprint(2, "%d\t%d\t%8.8lux\n", i, sorts[i]->freq, sorts[i]->v);
+	return sorts;
+}
+
+static int
+tabcmp(void *a, void *b)
+{
+	ulong av, bv;
+
+	av = (*(Word**)a)->v;
+	bv = (*(Word**)b)->v;
+	if(av > bv)
+		return 1;
+	if(av < bv)
+		return -1;
+	return 0;
+}
+
+static int
+squeezetab(int tabno, int base, Squeeze *sq, Word **sorts, int nv)
+{
+	int i;
+
+	if(nv >= 7*256)
+		nv = 7*256;
+	memset(sq, 0, sizeof(*sq));
+	for(i=0; i<nv; i++)
+		sq->rep[sq->n++] = sorts[i];
+	qsort(sq->rep, sq->n, sizeof(*sq->rep), tabcmp);
+	for(i=0; i<sq->n; i++)
+		sq->rep[i]->code = base + i;
+	if(debug)
+		dumpsq(sq, tabno);
+	return sq->n;
+}
+
+static void
+dumpsq(Squeeze *sq, int n)
+{
+	int i;
+
+	fprint(2, "table %d: %d entries\n", n, sq->n);
+	for(i=0; i<sq->n; i++)
+		fprint(2, "%.3x\t%8.8lux\t%lux\n", sq->rep[i]->code, sq->rep[i]->v, i? sq->rep[i]->v - sq->rep[i-1]->v: 0);
+}
+
+static void
+remap(Squeeze *sq)
+{
+	int i;
+	ulong v;
+
+	if(sq->n){
+		v = 0;
+		for(i=0; i<sq->n; i++){
+			sq->tab[i] = sq->rep[i]->v - v;
+			v += sq->tab[i];
+		}
+	}
+}
+
+static Word *
+squash(Word **tab, ulong v)
+{
+	Word *w, **hp;
+
+	for(hp = &tab[v&0xFFFF]; (w = *hp) != nil; hp = &w->next)
+		if(w->v == v)
+			return w;
+	return nil;
+}
+
+static void
+freehash(Word **tab)
+{
+	Word *w, **hp;
+
+	for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
+		while((w = *hp) != nil){
+			*hp = w->next;
+			free(w);
+		}
+}
+
+static int
+squeeze(ulong *prog, int nw, uchar *out, ulong top)
+{
+	ulong *rp, *ep;
+	ulong v, bits;
+	ulong e1, e2, e3, e4;
+	Word *w;
+	uchar bytes[8], *bp, *wp;
+	int ctl, n;
+
+	rp = prog;
+	ep = prog+nw;
+	bits = 0;
+	e1 = e2 = e3 = e4 = 0;
+	wp = out;
+	n = 0;
+	ctl = 0;
+	bp = bytes;
+	for(;;){
+		if(n == 2){
+			*wp++ = ctl;
+			if(0)
+				fprint(2, "%x\n", ctl);
+			memmove(wp, bytes, bp-bytes);
+			wp += bp-bytes;
+			bp = bytes;
+			ctl = 0;
+			n = 0;
+		}
+		ctl <<= 4;
+		n++;
+		if(rp >= ep){
+			if(n == 1)
+				break;
+			continue;
+		}
+		if(islittle){
+			v = GET4L((uchar*)rp);
+		}else{
+			v = GET4((uchar*)rp);
+		}
+		rp++;
+		if(qflag){
+			QREMAP(v);
+		}
+		if(v == top){
+			e1++;
+			bits += 4;
+			ctl |= 0;
+			continue;
+		}
+		w = squash(hash1, v);
+		if(w && w->code){
+			e2++;
+			bits += 4+8;
+			ctl |= w->code>>8;
+			*bp++ = w->code;
+			continue;
+		}
+		w = squash(hash2, v>>8);
+		if(w && w->code){
+			e3++;
+			bits += 4+8+8;
+			ctl |= w->code>>8;
+			*bp++ = w->code;
+			*bp++ = v & 0xFF;
+			if(debug > 2)
+				fprint(2, "%x %8.8lux %8.8lux\n", w->code, w->v, v);
+			continue;
+		}
+		e4++;
+		bits += 4+32;
+		ctl |= 0x1;
+		bp[0] = v;
+		bp[1] = v>>8;
+		bp[2] = v>>16;
+		bp[3] = v>>24;
+		bp += 4;
+	}
+	fprint(2, "enc: %lud 4-bits, %lud 12-bits %lud 20-bits %lud 36-bits -- %ld bytes\n",
+		e1, e2, e3, e4, wp-out);
+	return wp-out;
+}
+
+static void
+squirt(int fd, Squeeze *sq)
+{
+	uchar b[7*256*5 + 2], rep[5], *p, *q;
+	ulong v;
+	int i;
+
+	p = b+2;
+	for(i=0; i<sq->n; i++){
+		v = sq->tab[i];
+		q = rep;
+		do {
+			*q++ = v & 0x7F;
+		}while((v >>= 7) != 0);
+		do {
+			*p++ = *--q | 0x80;
+		}while(q != rep);
+		p[-1] &= ~0x80;
+	}
+	if(p > b+sizeof(b))
+		abort();
+	i = p-b;
+	b[0] = i>>8;
+	b[1] = i;
+	Write(fd, b, i);
+	fprint(2, "table: %d/%d\n", i, (sq->n+1)*4);
+}
+
+static long
+Read(int fd, void *buf, long nb)
+{
+	long n;
+
+	n = read(fd, buf, nb);
+	if(n < 0){
+		fprint(2, "sqz: %s: read error: %r\n", fname);
+		exits("read");
+	}
+	if(n < nb){
+		fprint(2, "sqz: %s: unexpected end-of-file\n", fname);
+		exits("read");
+	}
+	return n;
+}
+
+static void
+Write(int fd, void *buf, long nb)
+{
+	if(write(fd, buf, nb) != nb){
+		fprint(2, "sqz: write error: %r\n");
+		exits("write err");
+	}
+}
--- /dev/null
+++ b/utils/sqz/zqs.c
@@ -1,0 +1,243 @@
+#include <lib9.h>
+#include <a.out.h>
+#include "squeeze.h"
+
+/*
+ * forsyth@vitanuova.com
+ */
+
+/*
+ * for details of `unsqueeze' see:
+ *
+ * %A Mark Taunton
+ * %T Compressed Executables: An Exercise in Thinking Small
+ * %P 385-404
+ * %I USENIX
+ * %B USENIX Conference Proceedings
+ * %D Summer 1991
+ * %C Nashville, TN
+ *
+ * several of the unimplemented improvements described in the paper
+ * have been implemented here
+ *
+ * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits
+ * in certain instructions so as to push the fixed bits to the top of the word.
+ */
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+	int	n;
+	ulong	tab[7*256];
+};
+
+enum {
+	CHECK = 1	/* check precise bounds in Squeeze array */
+};
+
+#define	GET4(p)	(((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+static	uchar	out[3*1024*1024];
+static	uchar	bigb[1024*1024];
+static	ulong	top;
+static	int	qflag = 1;
+static	int	islittle = 0;
+static	ulong	chksum, oldsum;
+static	int	rdtab(int, Squeeze*, int);
+static	long	unsqueezefd(int, void*);
+static	uchar*	unsqueeze(uchar*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static	uchar*	unsqzseg(int, uchar*, long, ulong);
+
+void
+main(int argc, char **argv)
+{
+	int fd;
+	long n;
+
+	if(argc < 2)
+		exits("args");
+	fd = open(argv[1], OREAD);
+	if(fd < 0)
+		exits("open");
+	n = unsqueezefd(fd, out);
+	if(n < 0){
+		fprint(2, "zqs: can't unsqueeze\n");
+		exits("err");
+	}
+	if(write(1, out, n) != n){
+		fprint(2, "zqs: write error: %r\n");
+		exits("err");
+	}
+	fprint(2, "%ld bytes, %8.8lux csum\n", n, chksum);
+	exits(0);
+}
+
+static long
+unsqueezefd(int fd, void *v)
+{
+	uchar *wp, *out;
+	ulong toptxt, topdat;
+	long asis, nst, nsd;
+	Sqhdr sqh;
+	Exec ex;
+
+	out = (uchar*)v;
+	if(read(fd, &sqh, SQHDRLEN) != SQHDRLEN)
+		return -1;
+	if(GET4(sqh.magic) != SQMAGIC)
+		return -1;
+	if(read(fd, &ex, sizeof(Exec)) != sizeof(Exec))
+		return -1;
+	toptxt = GET4(sqh.toptxt);
+	topdat = GET4(sqh.topdat);
+	oldsum = GET4(sqh.sum);
+	asis = GET4(sqh.asis);
+	if(asis < 0)
+		asis = 0;
+	nst = GET4(sqh.text);
+	nsd = GET4(sqh.data);
+	switch(GET4((uchar*)&ex.magic)){
+	case Q_MAGIC:
+		if(qflag)
+			fprint(2, "PowerPC mode\n");
+		islittle = 0;
+		break;
+	case E_MAGIC:
+	case 0xA0E1:	/* arm AIF */
+		islittle = 1;
+		qflag = 0;
+		break;
+	default:
+		fprint(2, "Unknown magic: %8.8ux\n", GET4((uchar*)&ex.magic));
+		qflag = 0;
+		break;
+	}
+	memmove(out, &ex, sizeof(ex));
+	wp = unsqzseg(fd, out + sizeof(ex), nst, toptxt);
+	if(wp == nil)
+		return -1;
+	wp = unsqzseg(fd, wp, nsd, topdat);
+	if(wp == nil)
+		return -1;
+	if(asis){
+		if(read(fd, wp, asis) != asis)
+			return -1;
+		wp += asis;
+	}
+	return wp-out;
+}
+
+static uchar*
+unsqzseg(int fd, uchar *wp, long ns, ulong top)
+{
+	Squeeze sq3, sq4;
+
+	if(ns == 0)
+		return wp;
+	if(rdtab(fd, &sq3, 0) < 0)
+		return nil;
+	if(rdtab(fd, &sq4, 8) < 0)
+		return nil;
+	fprint(2, "tables: %d %d\n", sq3.n, sq4.n);
+	if(read(fd, bigb, ns) != ns)
+		return nil;
+	return unsqueeze(wp, bigb, bigb+ns, &sq3, &sq4, top);
+}
+
+static uchar*
+unsqueeze(uchar *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+	ulong nx;
+	int code, n;
+
+	if(qflag){
+		QREMAP(top);	/* adjust top just once, outside the loop */
+	}
+	while(rp < ep){
+		code = *rp++;
+		n = 0;
+		nx = code>>4;
+		do{
+			if(nx == 0){
+				nx = top;
+			}else{
+				if(nx==1){
+					if(rp+3 >= ep)
+						return nil;
+					nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+					rp += 4;
+				}else if(nx <= 8){	/* 2 to 8 */
+					if(rp+1 >= ep)
+						return nil;
+					nx = ((nx-2)<<8) | rp[0];
+					if(CHECK && nx >= sq4->n)
+						return nil;	/* corrupted file */
+					nx = sq4->tab[nx] | rp[1];
+					rp += 2;
+				}else{	/* 9 to 15 */
+					if(rp >= ep)
+						return nil;	/* corrupted file */
+					nx = ((nx-9)<<8) | rp[0];
+					if(CHECK && nx >= sq3->n)
+						return nil;	/* corrupted file */
+					nx = sq3->tab[nx];
+					rp++;
+				}
+				if(rp > ep)
+					return nil;	/* corrupted file */
+				if(qflag){
+					QREMAP(nx);
+				}
+			}
+			if(islittle){
+				wp[0] = nx;
+				wp[1] = nx>>8;
+				wp[2] = nx>>16;
+				wp[3] = nx>>24;
+			}else{
+				wp[0] = nx>>24;
+				wp[1] = nx>>16;
+				wp[2] = nx>>8;
+				wp[3] = nx;
+			}
+			wp += 4;
+			chksum += nx;
+			nx = code & 0xF;
+		}while(++n == 1);
+	}
+	return wp;
+}
+
+static int
+rdtab(int fd, Squeeze *sq, int shift)
+{
+	uchar b[7*256*5], *p, *ep;
+	ulong v, w;
+	int i;
+
+	if(read(fd, b, 2) != 2)
+		return -1;
+	i = (b[0]<<8) | b[1];
+	if(1)
+		fprint(2, "table: %d\n", i);
+	if((i -= 2) > 0){
+		if(read(fd, b, i) != i)
+			return -1;
+	}
+	sq->n = 0;
+	p = b;
+	ep = b+i;
+	v = 0;
+	while(p < ep){
+		w = 0;
+		do{
+			if(p >= ep)
+				return -1;
+			w = (w<<7) | (*p & 0x7F);
+		}while(*p++ & 0x80);
+		v += w;
+		if(0)
+			fprint(2, "%d %8.8lux %8.8lux\n", sq->n, v, w);
+		sq->tab[sq->n++] = v << shift;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/srclist/Nt.c
@@ -1,0 +1,8 @@
+#include	<windows.h>  
+#include	"lib9.h"
+
+char*
+mygetwd(char *path, int len)
+{
+	return getcwd(path, len);
+}
--- /dev/null
+++ b/utils/srclist/Plan9.c
@@ -1,0 +1,7 @@
+#include	"lib9.h"
+
+char*
+mygetwd(char *path, int len)
+{
+	return getwd(path, len);
+}
--- /dev/null
+++ b/utils/srclist/Posix.c
@@ -1,0 +1,10 @@
+#include	"lib9.h"
+#undef getwd
+#undef getwd
+#include	<unistd.h>
+
+char*
+mygetwd(char *path, int len)
+{
+	return getcwd(path, len);
+}
--- /dev/null
+++ b/utils/srclist/mkfile
@@ -1,0 +1,19 @@
+<../../mkconfig
+
+TARG=srclist
+
+OFILES=	srclist.$O\
+	$TARGMODEL.$O\
+
+HFILES=\
+	a.out.h\
+	bio.h\
+	mach.h\
+
+LIBS=mach bio 9	# order matters.
+
+CFLAGS=$CFLAGS -I../include
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/srclist/srclist.c
@@ -1,0 +1,144 @@
+#include <lib9.h>
+#include <bio.h>
+#include <mach.h>
+
+int	conly;
+int	exists;
+
+enum {
+	Maxroot = 10,
+};
+
+int	nroot;
+char	*root[Maxroot];
+int	rootlen[Maxroot];
+
+void	usage(void);
+void	error(char *);
+void	addroot(char *);
+void	addroots(char *);
+void	chomp(char *);
+
+extern char	*mygetwd(char*, int);
+
+void
+main(int argc, char **argv)
+{
+	char buf[1024], *cwd;
+
+	cwd = mygetwd(buf, sizeof(buf));
+	ARGBEGIN {
+	case 'c':
+		conly = 1;
+		break;
+	case 'e':
+		exists = 1;
+		break;
+	case 'r':
+		addroots(EARGF(usage()));
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if(argc != 1)
+		usage();
+
+	if(cwd != nil)
+		chdir(cwd);
+	setbinmode();
+	chomp(argv[0]);
+
+	exits(0);
+}
+
+void
+addroot(char *x)
+{
+	if(nroot >= Maxroot){
+		fprint(2, "srclist: too many root directories\n");
+		exits("usage");
+	}
+	root[nroot] = x;
+	rootlen[nroot] = strlen(x);
+	nroot++;
+}
+
+void
+addrootnt(char *r)
+{
+	addroot(r);
+	if(r[1] != ':')
+		return;	/* phew! */
+	if(*r >= 'a' && *r <= 'z' || *r >= 'A' && *r <= 'Z')
+		addroot(r+2);
+}
+
+void
+addroots(char *r)
+{
+	char buf[1024], *r2;
+
+	addrootnt(r);
+	if(chdir(r) < 0)
+		return;
+	r2 = mygetwd(buf, sizeof(buf));
+	if(r2 && strcmp(r2, r) != 0)
+		addrootnt(r2);
+}
+
+void
+chomp(char *file)
+{
+	int fd, i, j, len;
+	Fhdr fhdr;
+	Dir *td;
+	char fname[1024];
+
+	fd = open(file, OREAD);
+	if(fd < 0)
+		error("open");
+
+	if(crackhdr(fd, &fhdr) == 0)
+		error("crackhdr");
+
+	if(syminit(fd, &fhdr) < 0)
+		error("syminit");
+
+	for(i = 0; i < 1000; i++)
+		if(filesym(i, fname, sizeof(fname)-1)){
+			cleanname(fname);
+			if(conly){
+				len = strlen(fname);
+				if(len < 2 || strcmp(fname+len-2, ".c") != 0)
+					continue;
+			}
+			if(exists){
+				if((td = dirstat(fname)) == nil)
+					continue;
+				free(td);
+			}
+			if(nroot){
+				for(j = 0; j < nroot; j++)
+					if(strncmp(fname, root[j], rootlen[j]) == 0)
+						break;
+				if(j == nroot)
+					continue;
+			}
+			print("%s\n", fname);
+		}
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: srclist [-ce] [-r root] <objfile>\n");
+	exits("usage");
+}
+
+void
+error(char *s)
+{
+	fprint(2, "srclist: %s: %r\n", s);
+	exits(s);
+}
--- /dev/null
+++ b/utils/tc/5.out.h
@@ -1,0 +1,192 @@
+#define	NSNAME		8
+#define	NSYM		50
+#define	NREG		16
+
+#define NOPROF		(1<<0)
+#define DUPOK		(1<<1)
+#define	ALLTHUMBS	(1<<2)
+
+#define	REGRET		0
+#define	REGARG		0
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R3 up */
+#define	REGEXT		6
+/* compiler allocates external registers R5 down */
+#define	REGTMPT		7	/* used by the loader - thumb */
+#define	REGTMP		11	/* used by the loader - arm */
+#define	REGSB		12
+#define	REGSP		13
+#define	REGLINK		14
+#define	REGPC		15
+	
+#define	NFREG		8
+#define	FREGRET		0
+#define	FREGEXT		7
+/* compiler allocates register variables F0 up */
+/* compiler allocates external registers F7 down */
+
+enum	as
+{
+
+	AXXX,
+
+	AAND,
+	AEOR,
+	ASUB,
+	ARSB,	// not used
+	AADD,
+	AADC,
+	ASBC,
+	ARSC,	// not used
+	ATST,
+	ATEQ,	// not used
+	ACMP,
+	ACMN,
+	AORR,
+	ABIC,
+
+	AMVN,
+
+	AB,
+	ABL,
+
+	/* 
+	  * Do not reorder or fragment the conditional branch 
+	  * opcodes, or the predication code will break 
+	  */ 
+
+	ABEQ,
+	ABNE,
+	ABCS,
+	ABHS,
+	ABCC,
+	ABLO,
+	ABMI,
+	ABPL,
+	ABVS,
+	ABVC,
+	ABHI,
+	ABLS,
+	ABGE,
+	ABLT,
+	ABGT,
+	ABLE,
+
+	AMOVWD,
+	AMOVWF,
+	AMOVDW,
+	AMOVFW,
+	AMOVFD,
+	AMOVDF,
+	AMOVF,
+	AMOVD,
+
+	ACMPF,
+	ACMPD,
+	AADDF,
+	AADDD,
+	ASUBF,
+	ASUBD,
+	AMULF,
+	AMULD,
+	ADIVF,
+	ADIVD,
+
+	ASRL,	// right logical
+	ASRA,	// right arithmetic
+	ASLL,	// left logical = left arithmetic
+	AMULU,
+	ADIVU,
+	AMUL,
+	ADIV,
+	AMOD,
+	AMODU,
+
+	AMOVB,
+	AMOVBU,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVM,
+	ASWPBU,	// not used
+	ASWPW,	// not used
+
+	ANOP,
+	ARFE,
+	ASWI,
+	AMULA,	// not used
+
+	ADATA,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	ANAME,
+	ARET,	// fn return
+	ATEXT,	// fn start
+	AWORD,
+	ADYNT,	// not used
+	AINIT,	// not used
+	ABCASE,	// not used
+	ACASE,	// not used
+
+	AEND,
+
+	AMULL,
+	AMULAL,
+	AMULLU,
+	AMULALU,
+
+	ABX,
+	ABXRET,
+
+	ADWORD,
+
+	ASIGNAME,
+
+	ALAST,
+
+};
+
+/* type/name */
+#define	D_GOK	0
+#define	D_NONE	1
+
+/* type */
+#define	D_BRANCH	(D_NONE+1)
+#define	D_OREG		(D_NONE+2)
+#define	D_CONST		(D_NONE+7)
+#define	D_FCONST	(D_NONE+8)
+#define	D_SCONST	(D_NONE+9)
+#define	D_PSR		(D_NONE+10)
+#define	D_REG		(D_NONE+12)
+#define	D_FREG		(D_NONE+13)
+#define	D_FILE		(D_NONE+16)
+#define	D_OCONST	(D_NONE+17)
+#define	D_FILE1		(D_NONE+18)
+
+#define	D_SHIFT		(D_NONE+19)	/* not used */
+#define	D_FPCR		(D_NONE+20)
+#define 	D_REGREG	(D_NONE+21)
+
+/* name */
+#define	D_EXTERN	(D_NONE+3)
+#define	D_STATIC		(D_NONE+4)
+#define	D_AUTO		(D_NONE+5)
+#define	D_PARAM		(D_NONE+6)
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/tc/cgen.c
@@ -1,0 +1,1234 @@
+#include "gc.h"
+
+static int
+commutes(int o)
+{
+	switch(o){
+		case OAND:
+		case OOR:
+		case OXOR:
+		case OLMUL:
+		case OMUL:
+		case OADD:
+			return 1;
+	}
+	return 0;
+}
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o, t;
+	long v, curs;
+
+	if(debug['g'] && nn == Z) {
+		// prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesuv[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case ODIV:
+	case OMOD:
+		if(nn != Z)
+		if((t = vlog(r)) >= 0 && t <= 8) {
+			/* signed div/mod by constant power of 2 */
+			cgen(l, nn);
+			gopcode(OGE, nodconst(0), nn, Z);
+			p1 = p;
+			if(o == ODIV) {
+				gopcode(OADD, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+				gopcode(OASHR, nodconst(t), Z, nn);
+			} else {
+				gopcode(OSUB, nn, nodconst(0), nn);
+				regalloc(&nod, l, Z);
+				gopcode(OAS, nodconst((1<<t)-1), Z, &nod);
+				gopcode(OAND, &nod, Z, nn);
+				regfree(&nod);
+				// gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				gopcode(OSUB, nn, nodconst(0), nn);
+				gbranch(OGOTO);
+				patch(p1, pc);
+				p1 = p;
+				regalloc(&nod, l, Z);
+				gopcode(OAS, nodconst((1<<t)-1), Z, &nod);
+				gopcode(OAND, &nod, Z, nn);
+				regfree(&nod);
+				// gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+			}
+			break;
+		}
+		goto muldiv;
+
+	case OADD:
+	case OSUB:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(sconst(r))
+		if(o == OADD || o == OSUB || r->vconst < (vlong)32) {
+		// if(r->op == OCONST)
+		// if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+				break;
+			gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case OMUL:
+	muldiv:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			if(o == OADD || o == OSUB) {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+				gopcode(o, &nod, &nod1, &nod);
+			}
+			else {
+				if(commutes(o)){
+					regalloc(&nod, r, nn);
+					cgen(r, &nod);
+					regalloc(&nod1, l, Z);
+					cgen(l, &nod1);
+					gopcode(o, &nod1, Z, &nod);
+				}
+				else{
+					regalloc(&nod1, r, Z);
+					cgen(r, &nod1);
+					regalloc(&nod, l, nn);
+					cgen(l, &nod);
+					gopcode(o, &nod1, Z, &nod);
+				}
+			}
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+/*
+	case ONEG:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		gopcode(o, &nod, Z, &nod);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+*/
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASADD:
+	case OASSUB:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(sconst(r))
+		if(o == OASADD || o == OASSUB || r->vconst < (vlong)32) {
+		// if(r->op == OCONST)
+		// if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, r, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASAND:
+	case OASXOR:
+	case OASOR:
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+		} else {
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+
+		regalloc(&nod, n, nn);
+		gmove(&nod2, &nod);
+		gopcode(o, &nod1, Z, &nod);
+		gmove(&nod, &nod2);
+		if(nn != Z)
+			gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(o, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		if(REGARG >= 0)
+			o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG >= 0)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+nod.xoffset) >= 0 && v < 32*r->type->width/SZ_LONG) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type)) {
+			if(nocast(n->type, nn->type)) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else {
+			if (v > 0 && v < 8)
+				gopcode(OADD, nodconst(v), &nod, &nod1);
+			else if (v < 0 && v > -8)
+				gopcode(OSUB, nodconst(-v), &nod, &nod1);
+			else {
+				regalloc(&nod3, l, Z);
+				gopcode(OAS, nodconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+				regfree(&nod3);
+			}
+		}
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else {
+			if (v > 0 && v < 256)
+				gopcode(OADD, nodconst(v), Z, &nod);
+			else if (v < 0 && v > -256)
+				gopcode(OSUB, nodconst(-v), Z, &nod);
+			else {
+				regalloc(&nod3, l, Z);
+				gopcode(OAS, nodconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod);
+				regfree(&nod3);
+			}
+		}
+		gopcode(OAS, &nod, Z, &nod2);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			if (v > 0 && v < 256)
+				gopcode(OADD, nodconst(v), Z, &nod);
+			else if (v < 0 && v > -256)
+				gopcode(OSUB, nodconst(-v), Z, &nod);
+			else {
+				regalloc(&nod3, l, Z);
+				gopcode(OAS, nodconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod);
+				regfree(&nod3);
+			}
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		if (v > 0 && v < 256)
+			gopcode(OADD, nodconst(v), Z, &nod);
+		else if (v < 0 && v > -256)
+			gopcode(OSUB, nodconst(-v), Z, &nod);
+		else {
+			regalloc(&nod3, l, Z);
+			gopcode(OAS, nodconst(v), Z, &nod3);
+			gopcode(OADD, &nod3, &nod, &nod);
+			regfree(&nod3);
+		}
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+	return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+t->xoffset) >= 0 && v < 32*n->type->width/SZ_LONG) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	} else if(n->op == OINDREG) {
+		if((v = n->xoffset) >= 0 && v < (n->reg == REGSP ? 256 : 32)*n->type->width/SZ_LONG) {
+			n->op = OREGISTER;
+			cgen(n, t);
+			t->xoffset += v;
+			n->op = OINDREG;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+reglpcgen(Node *n, Node *nn, int f)
+{
+	Type *t;
+
+	t = nn->type;
+	nn->type = types[TLONG];
+	if(f)
+		reglcgen(n, nn, Z);
+	else {
+		regialloc(n, nn, Z);
+		lcgen(nn, n);
+		regind(n, nn);
+	}
+	nn->type = t;
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		o = ONE;
+		if(true)
+			o = comrel[relindex(o)];
+		if(typefd[n->type->etype]) {
+			gopcode(o, nodfconst(0), &nod, Z);
+		} else
+			gopcode(o, nodconst(0), &nod, Z);
+		regfree(&nod);
+		goto com;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(sconst(l)) {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			o = invrel[relindex(o)];
+			gopcode(o, l, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(sconst(r)) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			gopcode(o, r, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod, &nod1, Z);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+			nod1.xoffset += SZ_LONG;
+			gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+
+			regfree(&nod1);
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && nn->complex >= FNX) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 2) {
+		if(n->complex > nn->complex) {
+			reglpcgen(&nod1, n, 1);
+			reglpcgen(&nod2, nn, 1);
+		} else {
+			reglpcgen(&nod2, nn, 1);
+			reglpcgen(&nod1, n, 1);
+		}
+		regalloc(&nod3, &regnode, Z);
+		regalloc(&nod4, &regnode, Z);
+		nod0 = *nodconst((1<<nod3.reg)|(1<<nod4.reg));
+		if(w == 2 && nod1.xoffset == 0)
+			gmovm(&nod1, &nod0);
+		else {
+			gmove(&nod1, &nod3);
+			if(w == 2) {
+				nod1.xoffset += SZ_LONG;
+				gmove(&nod1, &nod4);
+			}
+		}
+		if(w == 2 && nod2.xoffset == 0)
+			gmovm(&nod0, &nod2);
+		else {
+			gmove(&nod3, &nod2);
+			if(w == 2) {
+				nod2.xoffset += SZ_LONG;
+				gmove(&nod4, &nod2);
+			}
+		}
+		regfree(&nod1);
+		regfree(&nod2);
+		regfree(&nod3);
+		regfree(&nod4);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		reglpcgen(&nod1, n, 0);
+		reglpcgen(&nod2, nn, 0);
+	} else {
+		reglpcgen(&nod2, nn, 0);
+		reglpcgen(&nod1, n, 0);
+	}
+
+	m = 0;
+	for(c = 0; c < w && c < 3; c++) {
+		i = tmpreg();
+		if (i == 0)
+			break;
+		reg[i]++;
+		m |= 1<<i;
+	}
+	nod4 = *(nodconst(m));
+	if(w < 3*c) {
+		for (; w>c; w-=c) {
+			gmovm(&nod1, &nod4);
+			gmovm(&nod4, &nod2);
+		}
+		goto out;
+	}
+
+	regalloc(&nod3, &regnode, Z);
+	gopcode(OAS, nodconst(w/c), Z, &nod3);
+	w %= c;
+	
+	pc1 = pc;
+	gmovm(&nod1, &nod4);
+	gmovm(&nod4, &nod2);
+
+	gopcode(OSUB, nodconst(1), Z, &nod3);
+	gopcode(OEQ, nodconst(0), &nod3, Z);
+	p->as = ABGT;
+	patch(p, pc1);
+	regfree(&nod3);
+
+out:
+	if (w) {
+		i = 0;
+		while (c>w) {
+			while ((m&(1<<i)) == 0)
+				i++;
+			m &= ~(1<<i);
+			reg[i] = 0;
+			c--;
+			i++;
+		}
+		nod4.vconst = m;
+		gmovm(&nod1, &nod4);
+		gmovm(&nod4, &nod2);
+	}
+	i = 0;
+	do {
+		while ((m&(1<<i)) == 0)
+			i++;
+		reg[i] = 0;
+		c--;
+		i++;
+	} while (c>0);
+	regfree(&nod1);
+	regfree(&nod2);
+}
--- /dev/null
+++ b/utils/tc/enam.c
@@ -1,0 +1,98 @@
+char*	anames[] =
+{
+	"XXX",
+	"AND",
+	"EOR",
+	"SUB",
+	"RSB",
+	"ADD",
+	"ADC",
+	"SBC",
+	"RSC",
+	"TST",
+	"TEQ",
+	"CMP",
+	"CMN",
+	"ORR",
+	"BIC",
+	"MVN",
+	"B",
+	"BL",
+	"BEQ",
+	"BNE",
+	"BCS",
+	"BHS",
+	"BCC",
+	"BLO",
+	"BMI",
+	"BPL",
+	"BVS",
+	"BVC",
+	"BHI",
+	"BLS",
+	"BGE",
+	"BLT",
+	"BGT",
+	"BLE",
+	"MOVWD",
+	"MOVWF",
+	"MOVDW",
+	"MOVFW",
+	"MOVFD",
+	"MOVDF",
+	"MOVF",
+	"MOVD",
+	"CMPF",
+	"CMPD",
+	"ADDF",
+	"ADDD",
+	"SUBF",
+	"SUBD",
+	"MULF",
+	"MULD",
+	"DIVF",
+	"DIVD",
+	"SRL",
+	"SRA",
+	"SLL",
+	"MULU",
+	"DIVU",
+	"MUL",
+	"DIV",
+	"MOD",
+	"MODU",
+	"MOVB",
+	"MOVBU",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVM",
+	"SWPBU",
+	"SWPW",
+	"NOP",
+	"RFE",
+	"SWI",
+	"MULA",
+	"DATA",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"NAME",
+	"RET",
+	"TEXT",
+	"WORD",
+	"DYNT",
+	"INIT",
+	"ABCASE",
+	"ACASE",
+	"END",
+	"MULL",
+	"MULAL",
+	"MULLU",
+	"MULALU",
+	"BX",
+	"BX",
+	"DWORD",
+	"SIGNAME",
+	"LAST",
+};
--- /dev/null
+++ b/utils/tc/gc.h
@@ -1,0 +1,349 @@
+#include	"../cc/cc.h"
+#include	"../tc/5.out.h"
+
+/*
+ * 5ct/Thumb
+ * Arm 7500
+ * Thumb
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+
+#define	R0ISZERO	0
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	char	sval[NSNAME];
+	Ieee	ieee;
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	char	as;
+	char	reg;
+	uchar	scond;	/* not used in 5ct */
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	long	val;
+	long	label;
+	char	def;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	long	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Multab	multab[20];
+EXTERN	int	retok;
+EXTERN	int	hintabsize;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	char	reg[NREG+NFREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+EXTERN	int	suppress;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	4
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	usedset(Node*, int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+int	tmpreg(void);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmovm(Node*, Node*);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+void	gopcode2(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(long);
+void	nullwarn(Node*, Node*);
+void	sextern(Sym*, Node*, long, long);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+int	Rconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+void	constprop(Adr*, Adr*, Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+void predicate(void); 
+int	isbranch(Prog *); 
+int	predicable(Prog *p); 
+int	modifiescpsr(Prog *p); 
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"R"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/tc/list.c
@@ -1,0 +1,276 @@
+#define	EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == AMOVM) {
+		if(p->from.type == D_CONST)
+			sprint(str, "	%A	%R,%D", a, &p->from, &p->to);
+		else
+		if(p->to.type == D_CONST)
+			sprint(str, "	%A	%D,%R", a, &p->from, &p->to);
+		else
+			sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	} else
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		sprint(str, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_EXTERN:
+	case D_STATIC:
+		sprint(str, "%N", a);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_PSR:
+		sprint(str, "PSR");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(PSR)(REG)", a);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	int i, v;
+
+	a = va_arg(fp->args, Adr*);
+	sprint(str, "GOK-reglist");
+	switch(a->type) {
+	case D_CONST:
+		if(a->reg != NREG)
+			break;
+		if(a->sym != S)
+			break;
+		v = a->offset;
+		strcpy(str, "");
+		for(i=0; i<NREG; i++) {
+			if(v & (1<<i)) {
+				if(str[0] == 0)
+					strcat(str, "[R");
+				else
+					strcat(str, ",R");
+				sprint(strchr(str, 0), "%d", i);
+			}
+		}
+		strcat(str, "]");
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/tc/mkenam
@@ -1,0 +1,15 @@
+ed - ../5ct/5.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/tc/mkfile
@@ -1,0 +1,28 @@
+<../../mkconfig
+
+TARG=tc
+
+OFILES=	cgen.$O\
+	enam.$O\
+	list.$O\
+	mul.$O\
+	peep.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+
+HFILES=	gc.h\
+	5.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/tc/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static  int	maxmulops = 3;	/* max # of ops to replace mul with */
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("multiply table failure %ld\n", v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=maxmulops; g++) {
+		if(g >= maxmulops && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("multiply table failure %ld\n", v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/tc/peep.c
@@ -1,0 +1,759 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+		if(regtyp(&p->to)) {
+			if(p->from.type == D_CONST)
+				constprop(&p->from, &p->to, r->s1);
+			else if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AEOR:
+			/*
+			 * EOR -1,x,y => MVN x,y
+			 */
+			if(p->from.type == D_CONST && p->from.offset == -1) {
+				p->as = AMVN;
+				p->from.type = D_REG;
+				if(p->reg != NREG)
+					p->from.reg = p->reg;
+				else
+					p->from.reg = p->to.reg;
+				p->reg = NREG;
+			}
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG)
+		return 1;
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ABL:
+		case ABX:
+			return 0;
+
+		// case ACMP:
+		// case ACMN:
+		case AADD:
+		case ASUB:
+		// case ASLL:
+		// case ASRL:
+		// case ASRA:
+		// case AORR:
+		// case AAND:
+		// case AEOR:
+		// case AMUL:
+		// case ADIV:
+		// case ADIVU:
+
+		// case ACMPF:
+		// case ACMPD:
+		// case AADDD:
+		// case AADDF:
+		// case ASUBD:
+		// case ASUBF:
+		// case AMULD:
+		// case AMULF:
+		// case ADIVD:
+		// case ADIVF:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AMOVF:
+		case AMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+
+		case AMOVM:
+			t = 1<<v2->reg;
+			if((p->from.type == D_CONST && (p->from.offset&t)) ||
+			   (p->to.type == D_CONST && (p->to.offset&t)))
+				return 0;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant constants.
+ *	$c1->v1
+ *	($c1->v2 s/$c1/v1)*
+ *	set v1  return
+ * The v1->v2 should be eliminated by copy propagation.
+ */
+void
+constprop(Adr *c1, Adr *v1, Reg *r)
+{
+	Prog *p;
+
+	if(debug['C'])
+		print("constprop %D->%D\n", c1, v1);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['C'])
+			print("%P", p);
+		if(uniqp(r) == R) {
+			if(debug['C'])
+				print("; merge; return\n");
+			return;
+		}
+		if(p->as == AMOVW && copyas(&p->from, c1)) {
+				if(debug['C'])
+					print("; sub%D/%D", &p->from, v1);
+				p->from = *v1;
+		} else if(copyu(p, v1, A) > 1) {
+			if(debug['C'])
+				print("; %Dset; return\n", v1);
+			return;
+		}
+		if(debug['C'])
+			print("\n");
+		if(r->s2)
+			constprop(c1, v1, r->s2);
+	}
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+	case AMOVM:
+		if(v->type != D_REG)
+			return 0;
+		if(p->from.type == D_CONST) {	/* read reglist, read/rar */
+			if(s != A) {
+				if(p->from.offset&(1<<v->reg))
+					return 1;
+				if(copysub(&p->to, v, s, 1))
+					diag(Z, "movm dst being replaced");	// was return 1;
+				return 0;
+			}
+			if(copyau(&p->to, v))
+				return 2;		// register updated in thumb // was return 1;
+			if(p->from.offset&(1<<v->reg))
+				return 1;
+		} else {			/* read/rar, write reglist */
+			if(s != A) {
+				if(p->to.offset&(1<<v->reg))
+					return 1;
+				if(copysub(&p->from, v, s, 1))
+					diag(Z, "movm src being replaced");	// was return 1;
+				return 0;
+			}
+			if(copyau(&p->from, v)) {
+				// if(p->to.offset&(1<<v->reg))
+					// return 4;
+				return 2;		// register updated in thumb // was return 1;
+			}
+			if(p->to.offset&(1<<v->reg))
+				return 3;
+		}
+		return 0;
+		
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVF:
+	case AMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVDW:
+	case AMOVWD:
+	case AMOVFD:
+	case AMOVDF:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AORR:
+	case AAND:
+	case AEOR:
+	case AMUL:
+	case ADIV:
+	case ADIVU:
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+	case ACMPF:
+	case ACMPD:
+	case ACMP:
+	case ACMN:
+		if(copyas(&p->to, v))
+			return 2;
+		/*FALLTHROUGH*/
+
+	case AADD:	/* read, read, write */
+	case ASUB:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:	/* read, read */
+	case ABNE:
+	case ABCS:
+	case ABHS:
+	case ABCC:
+	case ABLO:
+	case ABMI:
+	case ABPL:
+	case ABVS:
+	case ABVC:
+	case ABHI:
+	case ABLS:
+	case ABGE:
+	case ABLT:
+	case ABGT:
+	case ABLE:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub1(p, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		return 0;
+
+	case AB:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case ABL:	/* funny */
+	case ABX:
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG)
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+	/* not reached */
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+
+	case ACMP:
+	case ACMN:
+
+	case AADD:
+	case ASUB:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AORR:
+	case AAND:
+	case AEOR:
+	case AMUL:
+	case ADIV:
+	case ADIVU:
+		return D_REG;
+
+	case ACMPF:
+	case ACMPD:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	} else if(v->type == D_CONST) {		/* for constprop */
+		if(a->type == v->type)
+		if(a->name == v->name)
+		if(a->sym == v->sym)
+		if(a->reg == v->reg)
+		if(a->offset == v->offset)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG) {
+		if(a->type == D_OREG) {
+			if(v->reg == a->reg)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a2type(p) == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v)) {
+		a->reg = s->reg;
+	}
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
--- /dev/null
+++ b/utils/tc/reg.c
@@ -1,0 +1,1161 @@
+#include "gc.h"
+
+void	addsplits(void);
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AB:
+		case ARFE:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVF:
+		case AMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case ABL:
+		case ABX:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+
+		if(p->as == AMOVM) {
+			if(p->from.type == D_CONST)
+				z = p->from.offset;
+			else
+				z = p->to.offset;
+			for(i=0; z; i++) {
+				if(z&1)
+					regbits |= RtoB(i);
+				z >>= 1;
+			}
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+	addsplits();
+
+	if(debug['R'] && debug['v']) {
+		print("\nprop structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->set.b[z] |
+					r->refahead.b[z] | r->calahead.b[z] |
+					r->refbehind.b[z] | r->calbehind.b[z] |
+					r->use1.b[z] | r->use2.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+				if(bany(&r->refahead))
+					print(" ra=%B", r->refahead);
+				if(bany(&r->calahead))
+					print(" ca=%B", r->calahead);
+				if(bany(&r->refbehind))
+					print(" rb=%B", r->refbehind);
+				if(bany(&r->calbehind))
+					print(" cb=%B", r->calbehind);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L $%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L $%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L $%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+void
+addsplits(void)
+{
+	Reg *r, *r1;
+	int z, i;
+	Bits bit;
+
+	for(r = firstr; r != R; r = r->link) {
+		if(r->loop > 1)
+			continue;
+		if(r->prog->as == ABL || r->prog->as == ABX)
+			continue;
+		for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+			if(r1->loop <= 1)
+				continue;
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r1->calbehind.b[z] &
+					(r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+					~(r->calahead.b[z] & addrs.b[z]);
+			while(bany(&bit)) {
+				i = bnum(bit);
+				bit.b[i/32] &= ~(1L << (i%32));
+			}
+		}
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ABL:
+		case ABX:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+	
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TVLONG:
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R0
+ *	1	R1
+ *	...	...
+ *	10	R10
+ */
+long
+RtoB(int r)
+{
+
+	if(r < 2 || r >= REGTMPT)
+		return 0;
+	return 1L << r;
+}
+
+int
+BtoR(long b)
+{
+
+	b &= 0x7cL;	// r2-r6
+	if(b == 0)
+		return 0;
+	return bitno(b);
+}
+
+/*
+ *	bit	reg
+ *	18	F2
+ *	19	F3
+ *	...	...
+ *	23	F7
+ */
+long
+FtoB(int f)
+{
+
+	if(f < 2 || f > NFREG-1)
+		return 0;
+	return 1L << (f + 16);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xfc0000L;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 16;
+}
--- /dev/null
+++ b/utils/tc/sgen.c
@@ -1,0 +1,667 @@
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+	Prog *sp;
+	Node *n1, nod, nod1;
+
+	cursafe = 0;
+	curarg = 0;
+	maxargsafe = 0;
+
+	/*
+	 * isolate name
+	 */
+	for(n1 = nn;; n1 = n1->left) {
+		if(n1 == Z) {
+			diag(nn, "cant find function name");
+			return;
+		}
+		if(n1->op == ONAME)
+			break;
+	}
+	nearln = nn->lineno;
+	gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+	sp = p;
+	sp->reg |= ALLTHUMBS;	/* denotes thumb code */
+
+	/*
+	 * isolate first argument
+	 */
+	if(REGARG >= 0) {
+		if(typesuv[thisfn->link->etype]) {
+			nod1 = *nodret->left;
+			nodreg(&nod, &nod1, REGARG);
+			gopcode(OAS, &nod, Z, &nod1);
+		} else
+		if(firstarg && typechlp[firstargtype->etype]) {
+			nod1 = *nodret->left;
+			nod1.sym = firstarg;
+			nod1.type = firstargtype;
+			nod1.xoffset = align(0, firstargtype, Aarg1);
+			nod1.etype = firstargtype->etype;
+			nodreg(&nod, &nod1, REGARG);
+			gopcode(OAS, &nod, Z, &nod1);
+		}
+	}
+
+	retok = 0;
+	gen(n);
+	if(!retok)
+		if(thisfn->link->etype != TVOID)
+			warn(Z, "no return at end of function: %s", n1->sym->name);
+	noretval(3);
+	gbranch(ORETURN);
+
+	if(!debug['N'] || debug['R'] || debug['P'])
+		regopt(sp);
+
+	sp->to.offset += maxargsafe;
+}
+
+void
+supgen(Node *n)
+{
+	long spc;
+	Prog *sp;
+
+	if(n == Z)
+		return;
+	suppress++;
+	spc = pc;
+	sp = lastp;
+	gen(n);
+	lastp = sp;
+	pc = spc;
+	sp->link = nil;
+	suppress--;
+}
+
+void
+gen(Node *n)
+{
+	Node *l, nod;
+	Prog *sp, *spc, *spb;
+	Case *cn;
+	long sbc, scc;
+	int o, f;
+
+loop:
+	if(n == Z)
+		return;
+	nearln = n->lineno;
+	o = n->op;
+	if(debug['G'])
+		if(o != OLIST)
+			print("%L %O\n", nearln, o);
+
+	retok = 0;
+	switch(o) {
+
+	default:
+		complex(n);
+		cgen(n, Z);
+		break;
+
+	case OLIST:
+		gen(n->left);
+
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		retok = 1;
+		complex(n);
+		if(n->type == T)
+			break;
+		l = n->left;
+		if(l == Z) {
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(typesuv[n->type->etype]) {
+			sugen(l, nodret, n->type->width);
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		regret(&nod, n);
+		cgen(l, &nod);
+		regfree(&nod);
+		if(typefd[n->type->etype])
+			noretval(1);
+		else
+			noretval(2);
+		gbranch(ORETURN);
+		break;
+
+	case OLABEL:
+		l = n->left;
+		if(l) {
+			l->pc = pc;
+			if(l->label)
+				patch(l->label, pc);
+		}
+		gbranch(OGOTO);	/* prevent self reference in reg */
+		patch(p, pc);
+		goto rloop;
+
+	case OGOTO:
+		retok = 1;
+		n = n->left;
+		if(n == Z)
+			return;
+		if(n->complex == 0) {
+			diag(Z, "label undefined: %s", n->sym->name);
+			return;
+		}
+		if(suppress)
+			return;
+		gbranch(OGOTO);
+		if(n->pc) {
+			patch(p, n->pc);
+			return;
+		}
+		if(n->label)
+			patch(n->label, pc-1);
+		n->label = p;
+		return;
+
+	case OCASE:
+		l = n->left;
+		if(cases == C)
+			diag(n, "case/default outside a switch");
+		if(l == Z) {
+			casf();
+			cases->val = 0;
+			cases->def = 1;
+			cases->label = pc;
+			goto rloop;
+		}
+		complex(l);
+		if(l->type == T)
+			goto rloop;
+		if(l->op == OCONST)
+		if(typechl[l->type->etype]) {
+			casf();
+			cases->val = l->vconst;
+			cases->def = 0;
+			cases->label = pc;
+			goto rloop;
+		}
+		diag(n, "case expression must be integer constant");
+		goto rloop;
+
+	case OSWITCH:
+		l = n->left;
+		complex(l);
+		if(l->type == T)
+			break;
+		if(!typechl[l->type->etype]) {
+			diag(n, "switch expression must be integer");
+			break;
+		}
+
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		cn = cases;
+		cases = C;
+		casf();
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		gen(n->right);
+		gbranch(OGOTO);
+		patch(p, breakpc);
+
+		patch(sp, pc);
+		regalloc(&nod, l, Z);
+		nod.type = types[TLONG];
+		cgen(l, &nod);
+		doswit(&nod);
+		regfree(&nod);
+		patch(spb, pc);
+
+		cases = cn;
+		breakpc = sbc;
+		break;
+
+	case OWHILE:
+	case ODWHILE:
+		l = n->left;
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		if(n->op == OWHILE)
+			patch(sp, pc);
+		bcomplex(l, Z);		/* test */
+		patch(p, breakpc);
+
+		if(n->op == ODWHILE)
+			patch(sp, pc);
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		break;
+
+	case OFOR:
+		l = n->left;
+		gen(l->right->left);	/* init */
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		gen(l->right->right);	/* inc */
+		patch(sp, pc);	
+		if(l->left != Z) {	/* test */
+			bcomplex(l->left, Z);
+			patch(p, breakpc);
+		}
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		break;
+
+	case OCONTINUE:
+		if(continpc < 0) {
+			diag(n, "continue not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, continpc);
+		break;
+
+	case OBREAK:
+		if(breakpc < 0) {
+			diag(n, "break not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, breakpc);
+		break;
+
+	case OIF:
+		l = n->left;
+		if(bcomplex(l, n->right)) {
+			if(typefd[l->type->etype])
+				f = !l->fconst;
+			else
+				f = !l->vconst;
+			if(debug['c'])
+				print("%L const if %s\n", nearln, f ? "false" : "true");
+			if(f) {
+				supgen(n->right->left);
+				gen(n->right->right);
+			}
+			else {
+				gen(n->right->left);
+				supgen(n->right->right);
+			}
+		}
+		else {
+			sp = p;
+			if(n->right->left != Z)
+				gen(n->right->left);
+			if(n->right->right != Z) {
+				gbranch(OGOTO);
+				patch(sp, pc);
+				sp = p;
+				gen(n->right->right);
+			}
+			patch(sp, pc);
+		}
+		break;
+
+	case OSET:
+	case OUSED:
+		usedset(n->left, o);
+		break;
+	}
+}
+
+void
+usedset(Node *n, int o)
+{
+	if(n->op == OLIST) {
+		usedset(n->left, o);
+		usedset(n->right, o);
+		return;
+	}
+	complex(n);
+	switch(n->op) {
+	case OADDR:	/* volatile */
+		gins(ANOP, n, Z);
+		break;
+	case ONAME:
+		if(o == OSET)
+			gins(ANOP, Z, n);
+		else
+			gins(ANOP, n, Z);
+		break;
+	}
+}
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+/*
+	case OSUB:
+		xcom(l);
+		xcom(r);
+		if(typefd[n->type->etype] || typev[n->type->etype])
+			break;
+		if(vconst(l) == 0)
+			n->op = ONEG;
+			n->left = l;
+			n->right = Z;
+		}
+		break;
+*/
+
+	case OASHL:
+	case OASHR:
+	case OLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASLSHR:
+		xcom(l);
+		xcom(r);
+		if(sconst(r) && r->vconst < 0){
+			r->vconst = -r->vconst;
+			switch(n->op){
+			case OASHL:	n->op = OASHR; break;
+			case OASHR:	n->op = OASHL; break;
+			case OLSHR:	n->op = OASHL; break;
+			case OASASHL:	n->op = OASASHR; break;
+			case OASASHR:	n->op = OASASHL; break;
+			case OASLSHR:	n->op = OASASHL; break;
+			}
+		}
+		break;
+	
+	case OASLMUL:
+	case OASMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		t = vlog(l);
+		if(t >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+	case OEQ:
+	case ONE:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
+
+int
+bcomplex(Node *n, Node *c)
+{
+
+	complex(n);
+	if(n->type != T)
+	if(tcompat(n, T, n->type, tnot))
+		n->type = T;
+	if(n->type != T) {
+		if(c != Z && n->op == OCONST && deadheads(c))
+			return 1;
+		bool64(n);
+		boolgen(n, 1, Z);
+	} else
+		gbranch(OGOTO);
+	return 0;
+}
--- /dev/null
+++ b/utils/tc/swt.c
@@ -1,0 +1,717 @@
+#include "gc.h"
+
+int
+swcmp(void *a1, void *a2)
+{
+	C1 *p1, *p2;
+
+	p1 = (C1*)a1;
+	p2 = (C1*)a2;
+	if(p1->val < p2->val)
+		return -1;
+	return  p1->val > p2->val;
+}
+
+void
+doswit(Node *n)
+{
+	Case *c;
+	C1 *q, *iq;
+	long def, nc, i;
+
+	def = 0;
+	nc = 0;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def) {
+			if(def)
+				diag(n, "more than one default in switch");
+			def = c->label;
+			continue;
+		}
+		nc++;
+	}
+
+	iq = alloc(nc*sizeof(C1));
+	q = iq;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def)
+			continue;
+		q->label = c->label;
+		q->val = c->val;
+		q++;
+	}
+	qsort(iq, nc, sizeof(C1), swcmp);
+	if(debug['W'])
+		for(i=0; i<nc; i++)
+			print("case %2ld: = %.8lux\n", i, iq[i].val);
+	if(def == 0)
+		def = breakpc;
+	for(i=0; i<nc-1; i++)
+		if(iq[i].val == iq[i+1].val)
+			diag(n, "duplicate cases in switch %ld", iq[i].val);
+	swit1(iq, nc, def, n);
+}
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['W'])
+				print("case = %.8lux\n", q->val);
+			gopcode(OEQ, nodconst(q->val), n, Z);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+
+	i = nc / 2;
+	r = q+i;
+	if(debug['W'])
+		print("case > %.8lux\n", r->val);
+	gopcode(OGT, nodconst(r->val), n, Z);
+	sp = p;
+	gopcode(OEQ, nodconst(r->val), n, Z);	/* just gen the B.EQ */
+	patch(p, r->label);
+	swit1(q, i, def, n);
+
+	if(debug['W'])
+		print("case < %.8lux\n", r->val);
+	patch(sp, pc);
+	swit1(r+1, nc-i-1, def, n);
+}
+
+void
+casf(void)
+{
+	Case *c;
+
+	c = alloc(sizeof(*c));
+	c->link = cases;
+	cases = c;
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode2(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode2(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode2(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+long
+outlstring(ushort *s, long n)
+{
+	char buf[2];
+	int c;
+	long r;
+
+	if(suppress)
+		return nstring;
+	while(nstring & 1)
+		outstring("", 1);
+	r = nstring;
+	while(n > 0) {
+		c = *s++;
+		if(align(0, types[TCHAR], Aarg1)) {
+			buf[0] = c>>8;
+			buf[1] = c;
+		} else {
+			buf[0] = c;
+			buf[1] = c>>8;
+		}
+		outstring(buf, 2);
+		n -= sizeof(ushort);
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v, vs;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	if(debug['M'] && debug['v'])
+		print("%L multiply: %ld\n", n->lineno, v);
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	vs = v;
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		if(vs < 0) {
+			gopcode(OAS, &nod1, Z, &nod1);
+			gopcode(OSUB, &nod1, nodconst(0), nn);
+		} else 
+			gopcode(OAS, &nod1, Z, nn);
+/*
+		if(vs < 0)
+			gopcode(ONEG, &nod1, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+*/
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+nullwarn(Node *l, Node *r)
+{
+	warn(Z, "result of operation not used");
+	if(l != Z)
+		cgen(l, Z);
+	if(r != Z)
+		cgen(r, Z);
+}
+
+void
+sextern(Sym *s, Node *a, long o, long w)
+{
+	long e, lw;
+
+	for(e=0; e<w; e+=NSNAME) {
+		lw = NSNAME;
+		if(w-e < lw)
+			lw = w-e;
+		gpseudo(ADATA, s, nodconst(0));
+		p->from.offset += o+e;
+		p->reg = lw;
+		p->to.type = D_SCONST;
+		memmove(p->to.sval, a->cstring+e, lw);
+	}
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+	if(a->op == OCONST && typev[a->type->etype]) {
+		gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o;
+		p->reg = 4;
+		gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+
+	bf[0] = p->as;
+	bf[1] = 14;
+	bf[2] = p->reg;
+	bf[3] = p->lineno;
+	bf[4] = p->lineno>>8;
+	bf[5] = p->lineno>>16;
+	bf[6] = p->lineno>>24;
+	bp = zaddr(bf+7, &p->from, sf);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[7];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = sig;
+		bf[2] = sig>>8;
+		bf[3] = sig>>16;
+		bf[4] = sig>>24;
+		bf[5] = t;
+		bf[6] = s->sym;
+		Bwrite(b, bf, 7);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = t;	/* type */
+		bf[2] = s->sym;	/* sym */
+		Bwrite(b, bf, 3);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	long l;
+	Ieee e;
+
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	}
+	return bp;
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael2);
+		o = align(o, t, Ael1);
+		w = SZ_LONG;	/* because of a pun in cc/dcl.c:contig() */
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_LONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/tc/txt.c
@@ -1,0 +1,1200 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+	Type *t;
+
+	thechar = 't';
+	thestring = "arm";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMPT;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	memset(reg, 0, sizeof(reg));
+	// reg[REGTMPT] = 1;
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(reg[i])
+			diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NFREG; i++)
+		if(reg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesuv[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG >= 0 && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+	int i;
+
+	for(i=REGRET+1; i<NREG; i++)
+		if(reg[i] == 0)
+			return i;
+	diag(Z, "out of fixed tmp registers");
+	return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<REGTMPT; i++) {
+			if(j >= REGTMPT)
+				j = REGRET+1;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+	case TVLONG:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NFREG)
+				goto out;
+		}
+		j = 0*2 + NREG;
+		for(i=NREG; i<NREG+NFREG; i++) {
+			if(j >= NREG+NFREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	nodreg(n, tn, 0);
+	return;
+out:
+	reg[i]++;
+/* 	lasti++;	*** StrongARM does register forwarding */
+/*	
+	if(lasti >= 5)
+		lasti = 0;
+*/
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_LONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = 0;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+gmovm(Node *f, Node *t)
+{
+	gins(AMOVM, f, t);	// always sets base register now
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod;
+
+// prtree(f, "gmove src");
+// prtree(t, "gmove dst");
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+	}
+
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			a = AMOVW;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		}
+		if(typechlp[ft] && typeilp[tt])
+			regalloc(&nod, t, t);
+		else
+			regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TVLONG:
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TVLONG:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			a = AMOVD;
+			if(ft == TFLOAT)
+				a = AMOVFD;
+			break;
+		case TFLOAT:
+			a = AMOVDF;
+			if(ft == TFLOAT)
+				a = AMOVF;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVDW;
+			if(ft == TFLOAT)
+				a = AMOVFW;
+			break;
+		}
+		break;
+	case TUINT:
+	case TINT:
+	case TULONG:
+	case TLONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			gins(AMOVWD, f, t);
+			if(ft == TULONG) {
+			}
+			return;
+		case TFLOAT:
+			gins(AMOVWF, f, t);
+			if(ft == TULONG) {
+			}
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TUINT:
+		case TINT:
+		case TULONG:
+		case TLONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVWF, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AMOVF || a == AMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et;
+	Adr ta;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		et = f1->type->etype;
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADD;
+		if(et == TFLOAT)
+			a = AADDF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUB;
+		if(et == TFLOAT)
+			a = ASUBF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ASUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AORR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AEOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		break;
+
+	case OFUNC:
+		a = ABL;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		a = AMUL;
+		if(et == TFLOAT)
+			a = AMULF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AMULD;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		a = ADIV;
+		if(et == TFLOAT)
+			a = ADIVF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ADIVD;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = AMOD;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULU;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		a = AMODU;
+		break;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVU;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		a = ACMP;
+		if(et == TFLOAT)
+			a = ACMPF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ACMPD;
+		nextpc();
+		p->as = a;
+		naddr(f1, &p->from);
+/*
+		if(a == ACMP && f1->op == OCONST && p->from.offset < 0) {
+			p->as = ACMN;
+			p->from.offset = -p->from.offset;
+		}
+*/
+		raddr(f2, p);	// expects it to be a register
+		switch(o) {
+		case OEQ:
+			a = ABEQ;
+			break;
+		case ONE:
+			a = ABNE;
+			break;
+		case OLT:
+			a = ABLT;
+			break;
+		case OLE:
+			a = ABLE;
+			break;
+		case OGE:
+			a = ABGE;
+			break;
+		case OGT:
+			a = ABGT;
+			break;
+		case OLO:
+			a = ABLO;
+			break;
+		case OLS:
+			a = ABLS;
+			break;
+		case OHS:
+			a = ABHS;
+			break;
+		case OHI:
+			a = ABHI;
+			break;
+		}
+		f1 = Z;
+		f2 = Z;
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+/* put f1 in a register first */
+void
+gopcode2(int o, Node *f1, Node *f2, Node *t)
+{
+	Node nod;
+
+	if(f2 != Z)
+		diag(Z, "bad parameter in gopcode2");
+	// regalloc(&nod, t, Z);
+	nodreg(&nod, t, REGTMPT);
+	gopcode(OAS, f1, Z, &nod);
+	gopcode(o, &nod, Z, t);
+	// regfree(&nod);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AB;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	p->from.name = D_EXTERN;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= (vlong)0 && vv < (vlong)256)
+				return 1;
+			/*
+			 * should be specialised for constant values which will
+			 * fit in different instructionsl; for now, let 5l
+			 * sort it out
+			 */
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int
+sval(long v)
+{
+	if(v >= -32768 && v < 32768)
+		return 1;
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= NREG-1)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= NFREG-1)
+			return 0;
+		o = exfregoffset + NREG;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
--- /dev/null
+++ b/utils/test/mkfile
@@ -1,0 +1,21 @@
+<../../mkconfig
+
+#
+#	the test command is only needed on Windows NT and Windows 95
+#
+
+TARG=test
+
+OFILES=	test-$TARGMODEL.$O\
+
+HFILES= 
+
+LIBS=9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+test-Posix.c test-Inferno.c:QV:
+	echo 'test is only built on Windows NT or Windows 95'
+	exit 1
--- /dev/null
+++ b/utils/test/test-Nt.c
@@ -1,0 +1,278 @@
+/*
+ * POSIX standard
+ *	test expression
+ *	[ expression ]
+ */
+
+#include <lib9.h>
+#include <windows.h>
+
+#define EQ(a,b)	((tmp=a)==0?0:(strcmp(tmp,b)==0))
+
+int	ap;
+int	ac;
+char	**av;
+char	*tmp;
+
+void	synbad(char *, char *);
+int	length(char *);
+int	fsizep(char *);
+int	isdir(char *);
+int	isreg(char *);
+int	Ntisatty(int);
+int	isint(char *, int *);
+int	tio(char *, int);
+int	e(void), e1(void), e2(void), e3(void);
+
+void
+main(int argc, char *argv[])
+{
+
+	ac = argc; av = argv; ap = 1;
+	if(EQ(argv[0],"[")) {
+		if(!EQ(argv[--ac],"]"))
+			synbad("] missing","");
+	}
+	argv[ac] = 0;
+	if (ac<=1) exits("usage");
+	exits(e()?0:"false");
+}
+
+char *
+nxtarg(int mt)
+{
+	if(ap>=ac){
+		if(mt){
+			ap++;
+			return(0);
+		}
+		synbad("argument expected","");
+	}
+	return(av[ap++]);
+}
+
+int
+nxtintarg(int *pans)
+{
+	if(ap<ac && isint(av[ap], pans)){
+		ap++;
+		return 1;
+	}
+	return 0;
+}
+
+int
+e(void) {
+	int p1;
+
+	p1 = e1();
+	if (EQ(nxtarg(1), "-o")) return(p1 || e());
+	ap--;
+	return(p1);
+}
+
+int
+e1(void) {
+	int p1;
+
+	p1 = e2();
+	if (EQ(nxtarg(1), "-a")) return (p1 && e1());
+	ap--;
+	return(p1);
+}
+
+int
+e2(void) {
+	if (EQ(nxtarg(0), "!"))
+		return(!e2());
+	ap--;
+	return(e3());
+}
+
+int
+e3(void) {
+	int p1;
+	char *a;
+	char *p2;
+	int int1, int2;
+
+	a = nxtarg(0);
+	if(EQ(a, "(")) {
+		p1 = e();
+		if(!EQ(nxtarg(0), ")")) synbad(") expected","");
+		return(p1);
+	}
+
+	if(EQ(a, "-f"))
+		return(isreg(nxtarg(0)));
+
+	if(EQ(a, "-d"))
+		return(isdir(nxtarg(0)));
+
+	if(EQ(a, "-r"))
+		return(tio(nxtarg(0), 4));
+
+	if(EQ(a, "-w"))
+		return(tio(nxtarg(0), 2));
+
+	if(EQ(a, "-x"))
+		return(tio(nxtarg(0), 1));
+
+	if(EQ(a, "-e"))
+		return(tio(nxtarg(0), 0));
+
+	if(EQ(a, "-c"))
+		return(0);
+
+	if(EQ(a, "-b"))
+		return(0);
+
+	if(EQ(a, "-u"))
+		return(0);
+
+	if(EQ(a, "-g"))
+		return(0);
+
+	if(EQ(a, "-s"))
+		return(fsizep(nxtarg(0)));
+
+	if(EQ(a, "-t"))
+		if(ap>=ac || !nxtintarg(&int1))
+			return(Ntisatty(1));
+		else
+			return(Ntisatty(int1));
+
+	if(EQ(a, "-n"))
+		return(!EQ(nxtarg(0), ""));
+	if(EQ(a, "-z"))
+		return(EQ(nxtarg(0), ""));
+
+	p2 = nxtarg(1);
+	if (p2==0)
+		return(!EQ(a,""));
+	if(EQ(p2, "="))
+		return(EQ(nxtarg(0), a));
+
+	if(EQ(p2, "!="))
+		return(!EQ(nxtarg(0), a));
+
+	if(!isint(a, &int1))
+		return(!EQ(a,""));
+
+	if(nxtintarg(&int2)){
+		if(EQ(p2, "-eq"))
+			return(int1==int2);
+		if(EQ(p2, "-ne"))
+			return(int1!=int2);
+		if(EQ(p2, "-gt"))
+			return(int1>int2);
+		if(EQ(p2, "-lt"))
+			return(int1<int2);
+		if(EQ(p2, "-ge"))
+			return(int1>=int2);
+		if(EQ(p2, "-le"))
+			return(int1<=int2);
+	}
+
+	synbad("unknown operator ",p2);
+	return 0;		/* to shut ken up */
+}
+
+int
+tio(char *a, int f)
+{
+	return access (a, f) >= 0;
+}
+
+/*
+ * dirstat fails for:
+ * [drivename]:
+ * [drivename]:/
+ * <dirname>/
+ * [drivename]:<dirname>/
+ */
+int
+isdir(char *f)
+{
+	Dir *dir;
+	int	len;
+
+	len = strlen(f);
+	if (f[(len-1)] == '/') {
+		/* zap trailing '/' */
+		f[(len-1)] = '\0';
+	}
+	if (len == 2) {
+		if (f[1] == ':') {
+			return DMDIR;
+		}
+	}
+	if((dir = dirstat(f))==nil)
+		return(0);
+	return(dir->mode&DMDIR);
+}
+
+int
+isreg(char *f)
+{
+	Dir *dir;
+
+	if((dir = dirstat(f))==nil)
+		return(0);
+	return(!(dir->mode&DMDIR));
+}
+
+int
+Ntisatty(int fd)
+{
+	HANDLE h;
+
+	if(fd < 0)
+		return 0;
+
+	h = (HANDLE)_get_osfhandle(fd);
+	if(h < 0)
+		return 0;
+	return _isatty((int)h);
+}
+
+int
+fsizep(char *f)
+{
+	Dir *dir;
+
+	if((dir = dirstat(f))==nil)
+		return(0);
+	return(dir->length>0);
+}
+
+void
+synbad(char *s1, char *s2)
+{
+	int len;
+
+	write(2, "test: ", 6);
+	if ((len = strlen(s1)) != 0)
+		write(2, s1, len);
+	if ((len = strlen(s2)) != 0)
+		write(2, s2, len);
+	write(2, "\n", 1);
+	exits("bad syntax");
+}
+
+int
+length(char *s)
+{
+	char *es=s;
+	while(*es++);
+	return(es-s-1);
+}
+
+int
+isint(char *s, int *pans)
+{
+	char *ep;
+
+	*pans = strtol(s, &ep, 0);
+	return (*ep == 0);
+}
--- /dev/null
+++ b/utils/tl/asm.c
@@ -1,0 +1,1801 @@
+#include	"l.h"
+
+long	OFFSET;
+
+static Prog *PP;
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	switch(s->type) {
+	case STEXT:
+	case SLEAF:
+		break;
+	case SDATA:
+		if(dlm)
+			return s->value+INITDAT;
+	default:
+		diag("entry not text: %s", s->name);
+	}
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t, etext;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	OFFSET = HEADR;
+	seek(cout, OFFSET, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 4;
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(thumb)
+			thumbasmout(p, o);
+		else
+			asmout(p, o);
+		pc += o->size;
+	}
+	while(pc-INITTEXT < textsize) {
+		cput(0);
+		pc++;
+	}
+
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	/* output strings in text segment */
+	etext = INITTEXT + textsize;
+	for(t = pc; t < etext; t += sizeof(buf)-100) {
+		if(etext-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 1);
+		else
+			datblk(t, etext-t, 1);
+	}
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 1:
+	case 2:
+	case 5:
+		OFFSET = HEADR+textsize;
+		seek(cout, OFFSET, 0);
+		break;
+	case 3:
+		OFFSET = rnd(HEADR+textsize, 4096);
+		seek(cout, OFFSET, 0);
+		break;
+	}
+	if(dlm){
+		char buf[8];
+
+		write(cout, buf, INITDAT-textsize);
+		textsize = INITDAT;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 0);
+		else
+			datblk(t, datsize-t, 0);
+	}
+	cflush();
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 1:
+		case 4:
+		case 5:
+			debug['s'] = 1;
+			break;
+		case 2:
+			OFFSET = HEADR+textsize+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		case 3:
+			OFFSET += rnd(datsize, 4096);
+			seek(cout, OFFSET, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		if(!debug['s'])
+			asmthumbmap();
+		if(dlm)
+			asmdyn();
+		cflush();
+	}
+	else if(dlm){
+		seek(cout, HEADR+textsize+datsize, 0);
+		asmdyn();
+		cflush();
+	}
+
+	curtext = P;
+	if(debug['v'])
+		Bprint(&bso, "%5.2f header\n", cputime());
+	Bflush(&bso);
+	OFFSET = 0;
+	seek(cout, OFFSET, 0);
+	switch(HEADTYPE) {
+	case 0:	/* no header */
+		break;
+	case 1:	/* aif for risc os */
+		lputl(0xe1a00000);		/* NOP - decompress code */
+		lputl(0xe1a00000);		/* NOP - relocation code */
+		lputl(0xeb000000 + 12);		/* BL - zero init code */
+		lputl(0xeb000000 +
+			(entryvalue()
+			 - INITTEXT
+			 + HEADR
+			 - 12
+			 - 8) / 4);		/* BL - entry code */
+
+		lputl(0xef000011);		/* SWI - exit code */
+		lputl(textsize+HEADR);		/* text size */
+		lputl(datsize);			/* data size */
+		lputl(0);			/* sym size */
+
+		lputl(bsssize);			/* bss size */
+		lputl(0);			/* sym type */
+		lputl(INITTEXT-HEADR);		/* text addr */
+		lputl(0);			/* workspace - ignored */
+
+		lputl(32);			/* addr mode / data addr flag */
+		lputl(0);			/* data addr */
+		for(t=0; t<2; t++)
+			lputl(0);		/* reserved */
+
+		for(t=0; t<15; t++)
+			lputl(0xe1a00000);	/* NOP - zero init code */
+		lputl(0xe1a0f00e);		/* B (R14) - zero init return */
+		break;
+	case 2:	/* plan 9 */
+		if(dlm)
+			lput(0x80000000|0x647);	/* magic */
+		else
+			lput(0x647);			/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 3:	/* boot for NetBSD */
+		lput((143<<16)|0413);		/* magic */
+		lputl(rnd(HEADR+textsize, 4096));
+		lputl(rnd(datsize, 4096));
+		lputl(bsssize);
+		lputl(symsize);			/* nsyms */
+		lputl(entryvalue());		/* va of entry */
+		lputl(0L);
+		lputl(0L);
+		break;
+	case 4: /* boot for IXP1200 */
+		break;
+	case 5: /* boot for ipaq */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		lputl(0xe3300000);		/* nop */
+		break;
+	}
+	cflush();
+	if(debug['c']){
+		print("textsize=%ld\n", textsize);
+		print("datsize=%ld\n", datsize);
+		print("bsssize=%ld\n", bsssize);
+		print("symsize=%ld\n", symsize);
+		print("lcsize=%ld\n", lcsize);
+		print("total=%ld\n", textsize+datsize+bsssize+symsize+lcsize);
+	}
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		cput(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		cput(0);
+}
+
+void
+cput(int c)
+{
+	cbp[0] = c;
+	cbp++;
+	cbc--;
+	if(cbc <= 0)
+		cflush();
+}
+
+/*
+void
+cput(long c)
+{
+	*cbp++ = c;
+	if(--cbc <= 0)
+		cflush();
+}
+*/
+
+void
+wput(long l)
+{
+
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+hput(long l)
+{
+
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)
+{
+
+	cbp[0] = l>>24;
+	cbp[1] = l>>16;
+	cbp[2] = l>>8;
+	cbp[3] = l;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lputl(long l)
+{
+
+	cbp[3] = l>>24;
+	cbp[2] = l>>16;
+	cbp[1] = l>>8;
+	cbp[0] = l;
+	cbp += 4;
+	cbc -= 4;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	/* no bug if cbc < 0 since obuf(cbuf) followed by ibuf in buf! */
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+	if(c->outof)
+	Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+		c->outof - c->count, c->outof,
+		(double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SSTRING:
+				putsymb(s->name, 'T', s->value, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	lput(v);
+	if(ver)
+		t += 'a' - 'A';
+	cput(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		cput(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			cput(s[i]);
+			cput(s[i+1]);
+		}
+		cput(0);
+		cput(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			cput(s[i]);
+		cput(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['L'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['L'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			cput(s+128);	/* 129-255 +pc */
+			if(debug['L'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			cput(0);	/* 0 vv +lc */
+			cput(s>>24);
+			cput(s>>16);
+			cput(s>>8);
+			cput(s);
+			if(debug['L']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			cput(0+s);	/* 1-64 +lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			cput(64-s);	/* 65-128 -lc */
+			if(debug['L']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		cput(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['L'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+static void
+outt(long f, long l)
+{
+	if(debug['L'])
+		Bprint(&bso, "tmap: %lux-%lux\n", f, l);
+	lput(f);
+	lput(l);
+}
+
+void
+asmthumbmap(void)
+{
+	long pc, lastt;
+	Prog *p;
+
+	if(!seenthumb)
+		return;
+	pc = 0;
+	lastt = -1;
+	for(p = firstp; p != P; p = p->link){
+		pc = p->pc - INITTEXT;
+		if(p->as == ATEXT){
+			setarch(p);
+			if(thumb){
+				if(p->from.sym->foreign){	// 8 bytes of ARM first
+					if(lastt >= 0){
+						outt(lastt, pc-1);
+						lastt = -1;
+					}
+					pc += 8;
+				}
+				if(lastt < 0)
+					lastt = pc;
+			}
+			else{
+				if(p->from.sym->foreign){	// 4 bytes of THUMB first
+					if(lastt < 0)
+						lastt = pc;
+					pc += 4;
+				}
+				if(lastt >= 0){
+					outt(lastt, pc-1);
+					lastt = -1;
+				}
+			}
+		}
+	}
+	if(lastt >= 0)
+		outt(lastt, pc+1);
+}
+
+void
+datblk(long s, long n, int str)
+{
+	Sym *v;
+	Prog *p;
+	char *cast;
+	long a, l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		if(str != (p->from.sym->type == SSTRING))
+			continue;
+		curp = p;
+		a = p->from.sym->value + p->from.offset;
+		l = a - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			v = p->to.sym;
+			if(v) {
+				switch(v->type) {
+				case SUNDEF:
+					ckoff(v, d);
+					d += v->value;
+					break;
+				case STEXT:
+				case SLEAF:
+					d += v->value;
+#ifdef CALLEEBX
+					d += fnpinc(v);
+#else
+					if(v->thumb)
+						d++;		// T bit
+#endif
+					break;
+				case SSTRING:
+					d += v->value;
+					break;
+				case SDATA:
+				case SBSS:
+					d += v->value + INITDAT;
+					break;
+				}
+				if(dlm)
+					dynreloc(v, a+INITDAT, 1);
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+void
+asmout(Prog *p, Optab *o)
+{
+	long o1, o2, o3, o4, o5, o6, v;
+	int r, rf, rt, rt2;
+	Sym *s;
+
+PP = p;
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	o6 = 0;
+	armsize += o->size;
+if(debug['P']) print("%ulx: %P	type %d\n", (ulong)(p->pc), p, o->type);
+	switch(o->type) {
+	default:
+		diag("unknown asm %d", o->type);
+		prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (ulong)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr, p->from.sym->used);
+		break;
+
+	case 1:		/* op R,[R],R */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= rf | (r<<16) | (rt<<12);
+		break;
+
+	case 2:		/* movbu $I,[R],R */
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		o1 |= immrot(instoffset);
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 3:		/* add R<<[IR],[R],R */
+	mov:
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		o1 |= p->from.offset;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 4:		/* add $I,[R],R */
+		aclass(&p->from);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(instoffset);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 |= r << 16;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 5:		/* bra s */
+		v = -8;
+		if(p->cond == UP) {
+			s = p->to.sym;
+			if(s->type != SUNDEF)
+				diag("bad branch sym type");
+			v = (ulong)s->value >> (Roffset-2);
+			dynreloc(s, p->pc, 0);
+		}
+		else if(p->cond != P)
+			v = (p->cond->pc - pc) - 8;
+#ifdef CALLEEBX
+		if(p->as == ABL)
+			v += fninc(p->to.sym);
+#endif
+		o1 = opbra(p->as, p->scond);
+		o1 |= (v >> 2) & 0xffffff;
+		break;
+
+	case 6:		/* b ,O(R) -> add $O,R,PC */
+		aclass(&p->to);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(instoffset);
+		o1 |= p->to.reg << 16;
+		o1 |= REGPC << 12;
+		break;
+
+	case 7:		/* bl ,O(R) -> mov PC,link; add $O,R,PC */
+		aclass(&p->to);
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(0);
+		o1 |= REGPC << 16;
+		o1 |= REGLINK << 12;
+
+		o2 = oprrr(AADD, p->scond);
+		o2 |= immrot(instoffset);
+		o2 |= p->to.reg << 16;
+		o2 |= REGPC << 12;
+		break;
+
+	case 8:		/* sll $c,[R],R -> mov (R<<$c),R */
+		aclass(&p->from);
+		o1 = oprrr(p->as, p->scond);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 |= r;
+		o1 |= (instoffset&31) << 7;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 9:		/* sll R,[R],R -> mov (R<<R),R */
+		o1 = oprrr(p->as, p->scond);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 |= r;
+		o1 |= (p->from.reg << 8) | (1<<4);
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 10:	/* swi [$con] */
+		o1 = oprrr(p->as, p->scond);
+		if(p->to.type != D_NONE) {
+			aclass(&p->to);
+			o1 |= instoffset & 0xffffff;
+		}
+		break;
+
+	case 11:	/* word */
+		switch(aclass(&p->to)) {
+		case C_LCON:
+			if(!dlm)
+				break;
+			if(p->to.name != D_EXTERN && p->to.name != D_STATIC)
+				break;
+		case C_ADDR:
+			if(p->to.sym->type == SUNDEF)
+				ckoff(p->to.sym, p->to.offset);
+			dynreloc(p->to.sym, p->pc, 1);
+		}
+		o1 = instoffset;
+		break;
+
+	case 12:	/* movw $lcon, reg */
+		o1 = omvl(p, &p->from, p->to.reg);
+		break;
+
+	case 13:	/* op $lcon, [R], R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = oprrr(p->as, p->scond);
+		o2 |= REGTMP;
+		r = p->reg;
+		if(p->as == AMOVW || p->as == AMVN)
+			r = 0;
+		else if(r == NREG)
+			r = p->to.reg;
+		o2 |= r << 16;
+		if(p->to.type != D_NONE)
+			o2 |= p->to.reg << 12;
+		break;
+
+	case 14:	/* movb/movbu/movh/movhu R,R */
+		o1 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o2 = oprrr(ASRL, p->scond);
+		else
+			o2 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o1 |= (p->from.reg)|(r<<12);
+		o2 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o1 |= (24<<7);
+			o2 |= (24<<7);
+		} else {
+			o1 |= (16<<7);
+			o2 |= (16<<7);
+		}
+		break;
+
+	case 15:	/* mul r,[r,]r */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(r == NREG)
+			r = rt;
+		if(rt == r) {
+			r = rf;
+			rf = rt;
+		}
+		if(0)
+		if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) {
+			diag("bad registers in MUL");
+			prasm(p);
+		}
+		o1 |= (rf<<8) | r | (rt<<16);
+		break;
+
+
+	case 16:	/* div r,[r,]r */
+		o1 = 0xf << 28;
+		o2 = 0;
+		break;
+
+	case 17:
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		rt2 = p->to.offset;
+		r = p->reg;
+		o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12);
+		break;
+
+	case 20:	/* mov/movb/movbu R,O(R) */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = osr(p->as, p->from.reg, instoffset, r, p->scond);
+		break;
+
+	case 21:	/* mov/movbu O(R),R -> lr */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olr(instoffset, r, p->to.reg, p->scond);
+		if(p->as != AMOVW)
+			o1 |= 1<<22;
+		break;
+
+	case 22:	/* movb/movh/movhu O(R),R -> lr,shl,shr */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olr(instoffset, r, p->to.reg, p->scond);
+
+		o2 = oprrr(ASLL, p->scond);
+		o3 = oprrr(ASRA, p->scond);
+		r = p->to.reg;
+		if(p->as == AMOVB) {
+			o2 |= (24<<7)|(r)|(r<<12);
+			o3 |= (24<<7)|(r)|(r<<12);
+		} else {
+			o2 |= (16<<7)|(r)|(r<<12);
+			if(p->as == AMOVHU)
+				o3 = oprrr(ASRL, p->scond);
+			o3 |= (16<<7)|(r)|(r<<12);
+		}
+		break;
+
+	case 23:	/* movh/movhu R,O(R) -> sb,sb */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = osr(AMOVH, p->from.reg, instoffset, r, p->scond);
+
+		o2 = oprrr(ASRL, p->scond);
+		o2 |= (8<<7)|(p->from.reg)|(REGTMP<<12);
+
+		o3 = osr(AMOVH, REGTMP, instoffset+1, r, p->scond);
+		break;
+
+	case 30:	/* mov/movb/movbu R,L(R) */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = osrr(p->from.reg, REGTMP,r, p->scond);
+		if(p->as != AMOVW)
+			o2 |= 1<<22;
+		break;
+
+	case 31:	/* mov/movbu L(R),R -> lr[b] */
+	case 32:	/* movh/movb L(R),R -> lr[b] */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = olrr(REGTMP,r, p->to.reg, p->scond);
+		if(p->as == AMOVBU || p->as == AMOVB)
+			o2 |= 1<<22;
+		if(o->type == 31)
+			break;
+
+		o3 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o4 = oprrr(ASRL, p->scond);
+		else
+			o4 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o3 |= (r)|(r<<12);
+		o4 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o3 |= (24<<7);
+			o4 |= (24<<7);
+		} else {
+			o3 |= (16<<7);
+			o4 |= (16<<7);
+		}
+		break;
+
+	case 33:	/* movh/movhu R,L(R) -> sb, sb */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = osrr(p->from.reg, REGTMP, r, p->scond);
+		o2 |= (1<<22) ;
+
+		o3 = oprrr(ASRL, p->scond);
+		o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+		o3 |= (1<<6);	/* ROR 8 */
+
+		o4 = oprrr(AADD, p->scond);
+		o4 |= (REGTMP << 12) | (REGTMP << 16);
+		o4 |= immrot(1);
+
+		o5 = osrr(p->from.reg, REGTMP,r,p->scond);
+		o5 |= (1<<22);
+
+		o6 = oprrr(ASRL, p->scond);
+		o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+		o6 |= (1<<6);	/* ROL 8 */
+
+		break;
+		
+	case 34:	/* mov $lacon,R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+
+		o2 = oprrr(AADD, p->scond);
+		o2 |= REGTMP;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 |= r << 16;
+		if(p->to.type != D_NONE)
+			o2 |= p->to.reg << 12;
+		break;
+
+	case 35:	/* mov PSR,R */
+		o1 = (2<<23) | (0xf<<16) | (0<<0);
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= (p->from.reg & 1) << 22;
+		o1 |= p->to.reg << 12;
+		break;
+
+	case 36:	/* mov R,PSR */
+		o1 = (2<<23) | (0x29f<<12) | (0<<4);
+		if(p->scond & C_FBIT)
+			o1 ^= 0x010 << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= (p->to.reg & 1) << 22;
+		o1 |= p->from.reg << 0;
+		break;
+
+	case 37:	/* mov $con,PSR */
+		aclass(&p->from);
+		o1 = (2<<23) | (0x29f<<12) | (0<<4);
+		if(p->scond & C_FBIT)
+			o1 ^= 0x010 << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		o1 |= immrot(instoffset);
+		o1 |= (p->to.reg & 1) << 22;
+		o1 |= p->from.reg << 0;
+		break;
+
+	case 38:	/* movm $con,oreg -> stm */
+		o1 = (0x4 << 25);
+		o1 |= p->from.offset & 0xffff;
+		o1 |= p->to.reg << 16;
+		aclass(&p->to);
+		goto movm;
+
+	case 39:	/* movm oreg,$con -> ldm */
+		o1 = (0x4 << 25) | (1 << 20);
+		o1 |= p->to.offset & 0xffff;
+		o1 |= p->from.reg << 16;
+		aclass(&p->from);
+	movm:
+		if(instoffset != 0)
+			diag("offset must be zero in MOVM");
+		o1 |= (p->scond & C_SCOND) << 28;
+		if(p->scond & C_PBIT)
+			o1 |= 1 << 24;
+		if(p->scond & C_UBIT)
+			o1 |= 1 << 23;
+		if(p->scond & C_SBIT)
+			o1 |= 1 << 22;
+		if(p->scond & C_WBIT)
+			o1 |= 1 << 21;
+		break;
+
+	case 40:	/* swp oreg,reg,reg */
+		aclass(&p->from);
+		if(instoffset != 0)
+			diag("offset must be zero in SWP");
+		o1 = (0x2<<23) | (0x9<<4);
+		if(p->as != ASWPW)
+			o1 |= 1 << 22;
+		o1 |= p->from.reg << 16;
+		o1 |= p->reg << 0;
+		o1 |= p->to.reg << 12;
+		o1 |= (p->scond & C_SCOND) << 28;
+		break;
+
+	case 41:	/* rfe -> movm.s.w.u 0(r13),[r15] */
+		o1 = 0xe8fd8000;
+		break;
+
+	case 50:	/* floating point store */
+		v = regoff(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = ofsr(p->as, p->from.reg, v, r, p->scond, p);
+		break;
+
+	case 51:	/* floating point load */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = ofsr(p->as, p->to.reg, v, r, p->scond, p) | (1<<20);
+		break;
+
+	case 52:	/* floating point store, long offset UGLY */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+		o3 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+		break;
+
+	case 53:	/* floating point load, long offset UGLY */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r;
+		o3 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+		break;
+
+	case 54:	/* floating point arith */
+		o1 = oprrr(p->as, p->scond);
+		if(p->from.type == D_FCONST) {
+			rf = chipfloat(p->from.ieee);
+			if(rf < 0){
+				diag("invalid floating-point immediate\n%P", p);
+				rf = 0;
+			}
+			rf |= (1<<3);
+		} else
+			rf = p->from.reg;
+		rt = p->to.reg;
+		r = p->reg;
+		if(p->to.type == D_NONE)
+			rt = 0;	/* CMP[FD] */
+		else if(o1 & (1<<15))
+			r = 0;	/* monadic */
+		else if(r == NREG)
+			r = rt;
+		o1 |= rf | (r<<16) | (rt<<12);
+		break;
+
+	case 55:	/* floating point fix and float */
+		o1 = oprrr(p->as, p->scond);
+		rf = p->from.reg;
+		rt = p->to.reg;
+		if(p->to.type == D_NONE){
+			rt = 0;
+			diag("to.type==D_NONE (asm/fp)");
+		}
+		if(p->from.type == D_REG)
+			o1 |= (rf<<12) | (rt<<16);
+		else
+			o1 |= rf | (rt<<12);
+		break;
+
+	case 56:	/* move to FP[CS]R */
+		o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+		o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12);
+		break;
+
+	case 57:	/* move from FP[CS]R */
+		o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4);
+		o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20);
+		break;
+	case 58:	/* movbu R,R */
+		o1 = oprrr(AAND, p->scond);
+		o1 |= immrot(0xff);
+		rt = p->to.reg;
+		r = p->from.reg;
+		if(p->to.type == D_NONE)
+			rt = 0;
+		if(r == NREG)
+			r = rt;
+		o1 |= (r<<16) | (rt<<12);
+		break;
+
+	case 59:	/* movw/bu R<<I(R),R -> ldr indexed */
+		if(p->from.reg == NREG) {
+			if(p->as != AMOVW)
+				diag("byte MOV from shifter operand");
+			goto mov;
+		}
+		if(p->from.offset&(1<<4))
+			diag("bad shift in LDR");
+		o1 = olrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+		if(p->as == AMOVBU)
+			o1 |= 1<<22;
+		break;
+
+	case 60:	/* movb R(R),R -> ldrsb indexed */
+		if(p->from.reg == NREG) {
+			diag("byte MOV from shifter operand");
+			goto mov;
+		}
+		if(p->from.offset&(~0xf))
+			diag("bad shift in LDRSB");
+		o1 = olhrr(p->from.offset, p->from.reg, p->to.reg, p->scond);
+		o1 ^= (1<<5)|(1<<6);
+		break;
+
+	case 61:	/* movw/b/bu R,R<<[IR](R) -> str indexed */
+		if(p->to.reg == NREG)
+			diag("MOV to shifter operand");
+		o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond);
+		if(p->as == AMOVB || p->as == AMOVBU)
+			o1 |= 1<<22;
+		break;
+
+	case 62:	/* case R -> movw	R<<2(PC),PC */
+		o1 = olrr(p->from.reg, REGPC, REGPC, p->scond);
+		o1 |= 2<<7;
+		break;
+
+	case 63:	/* bcase */
+		if(p->cond != P) {
+			o1 = p->cond->pc;
+			if(dlm)
+				dynreloc(S, p->pc, 1);
+		}
+		break;
+
+	/* reloc ops */
+	case 64:	/* mov/movb/movbu R,addr */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+		break;
+
+	case 65:	/* mov/movbu addr,R */
+	case 66:	/* movh/movhu/movb addr,R */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = olr(0, REGTMP, p->to.reg, p->scond);
+		if(p->as == AMOVBU || p->as == AMOVB)
+			o2 |= 1<<22;
+		if(o->type == 65)
+			break;
+
+		o3 = oprrr(ASLL, p->scond);
+
+		if(p->as == AMOVBU || p->as == AMOVHU)
+			o4 = oprrr(ASRL, p->scond);
+		else
+			o4 = oprrr(ASRA, p->scond);
+
+		r = p->to.reg;
+		o3 |= (r)|(r<<12);
+		o4 |= (r)|(r<<12);
+		if(p->as == AMOVB || p->as == AMOVBU) {
+			o3 |= (24<<7);
+			o4 |= (24<<7);
+		} else {
+			o3 |= (16<<7);
+			o4 |= (16<<7);
+		}
+		break;
+
+	case 67:	/* movh/movhu R,addr -> sb, sb */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+		o3 = oprrr(ASRL, p->scond);
+		o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12);
+		o3 |= (1<<6);	/* ROR 8 */
+
+		o4 = oprrr(AADD, p->scond);
+		o4 |= (REGTMP << 12) | (REGTMP << 16);
+		o4 |= immrot(1);
+
+		o5 = osr(p->as, p->from.reg, 0, REGTMP, p->scond);
+
+		o6 = oprrr(ASRL, p->scond);
+		o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12);
+		o6 |= (1<<6);	/* ROL 8 */
+		break;
+
+	case 68:	/* floating point store -> ADDR */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p);
+		break;
+
+	case 69:	/* floating point load <- ADDR */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20);
+		break;
+
+	/* ArmV4 ops: */
+	case 70:	/* movh/movhu R,O(R) -> strh */
+		aclass(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = oshr(p->from.reg, instoffset, r, p->scond);
+		break;	
+	case 71:	/* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */
+		aclass(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = olhr(instoffset, r, p->to.reg, p->scond);
+		if(p->as == AMOVB)
+			o1 ^= (1<<5)|(1<<6);
+		else if(p->as == AMOVH)
+			o1 ^= (1<<6);
+		break;
+	case 72:	/* movh/movhu R,L(R) -> strh */
+		o1 = omvl(p, &p->to, REGTMP);
+		if(!o1)
+			break;
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = oshrr(p->from.reg, REGTMP,r, p->scond);
+		break;	
+	case 73:	/* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */
+		o1 = omvl(p, &p->from, REGTMP);
+		if(!o1)
+			break;
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o2 = olhrr(REGTMP, r, p->to.reg, p->scond);
+		if(p->as == AMOVB)
+			o2 ^= (1<<5)|(1<<6);
+		else if(p->as == AMOVH)
+			o2 ^= (1<<6);
+		break;
+	case 74:	/* bx $I */
+#ifdef CALLEEBX
+		diag("bx $i case (arm)");
+#endif
+		if(!seenthumb)
+			diag("ABX $I and seenthumb==0");
+		v = p->cond->pc;
+		if(p->to.sym->thumb)
+			v |= 1;	// T bit
+		o1 = olr(8, REGPC, REGTMP, p->scond&C_SCOND);	// mov 8(PC), Rtmp
+		o2 = 	oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12);	// add 8,PC, LR
+		o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP;		// bx Rtmp
+		o4 = opbra(AB, 14);	// B over o6
+		o5 = v;
+		break;
+	case 75:	/* bx O(R) */
+		aclass(&p->to);
+		if(instoffset != 0)
+			diag("non-zero offset in ABX");
+/*
+		o1 = 	oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12);	// mov PC, LR
+		o2 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg;		// BX R
+*/
+		// p->to.reg may be REGLINK
+		o1 = oprrr(AADD, p->scond);
+		o1 |= immrot(instoffset);
+		o1 |= p->to.reg << 16;
+		o1 |= REGTMP << 12;
+		o2 = 	oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12);	// mov PC, LR
+		o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP;		// BX Rtmp
+		break;
+	case 76:	/* bx O(R) when returning from fn*/
+		if(!seenthumb)
+			diag("ABXRET and seenthumb==0");
+		aclass(&p->to);
+// print("ARM BXRET %d(R%d)\n", instoffset, p->to.reg);
+		if(instoffset != 0)
+			diag("non-zero offset in ABXRET");
+		// o1 = olr(instoffset, p->to.reg, REGTMP, p->scond);	// mov O(R), Rtmp
+		o1 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg;		// BX R
+		break;
+	}
+
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		lputl(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		lputl(o1);
+		lputl(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		lputl(o5);
+		break;
+	case 24:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, o6, p);
+		lputl(o1);
+		lputl(o2);
+		lputl(o3);
+		lputl(o4);
+		lputl(o5);
+		lputl(o6);
+		break;
+	}
+}
+
+long
+oprrr(int a, int sc)
+{
+	long o;
+
+	o = (sc & C_SCOND) << 28;
+	if(sc & C_SBIT)
+		o |= 1 << 20;
+	if(sc & (C_PBIT|C_WBIT))
+		diag(".P/.W on dp instruction");
+	switch(a) {
+	case AMULU:
+	case AMUL:	return o | (0x0<<21) | (0x9<<4);
+	case AMULA:	return o | (0x1<<21) | (0x9<<4);
+	case AMULLU:	return o | (0x4<<21) | (0x9<<4);
+	case AMULL:	return o | (0x6<<21) | (0x9<<4);
+	case AMULALU:	return o | (0x5<<21) | (0x9<<4);
+	case AMULAL:	return o | (0x7<<21) | (0x9<<4);
+	case AAND:	return o | (0x0<<21);
+	case AEOR:	return o | (0x1<<21);
+	case ASUB:	return o | (0x2<<21);
+	case ARSB:	return o | (0x3<<21);
+	case AADD:	return o | (0x4<<21);
+	case AADC:	return o | (0x5<<21);
+	case ASBC:	return o | (0x6<<21);
+	case ARSC:	return o | (0x7<<21);
+	case ATST:	return o | (0x8<<21) | (1<<20);
+	case ATEQ:	return o | (0x9<<21) | (1<<20);
+	case ACMP:	return o | (0xa<<21) | (1<<20);
+	case ACMN:	return o | (0xb<<21) | (1<<20);
+	case AORR:	return o | (0xc<<21);
+	case AMOVW:	return o | (0xd<<21);
+	case ABIC:	return o | (0xe<<21);
+	case AMVN:	return o | (0xf<<21);
+	case ASLL:	return o | (0xd<<21) | (0<<5);
+	case ASRL:	return o | (0xd<<21) | (1<<5);
+	case ASRA:	return o | (0xd<<21) | (2<<5);
+	case ASWI:	return o | (0xf<<24);
+
+	case AADDD:	return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7);
+	case AADDF:	return o | (0xe<<24) | (0x0<<20) | (1<<8);
+	case AMULD:	return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7);
+	case AMULF:	return o | (0xe<<24) | (0x1<<20) | (1<<8);
+	case ASUBD:	return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7);
+	case ASUBF:	return o | (0xe<<24) | (0x2<<20) | (1<<8);
+	case ADIVD:	return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7);
+	case ADIVF:	return o | (0xe<<24) | (0x4<<20) | (1<<8);
+	case ACMPD:
+	case ACMPF:	return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4);	/* arguably, ACMPF should expand to RNDF, CMPD */
+
+	case AMOVF:
+	case AMOVDF:	return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8);
+	case AMOVD:
+	case AMOVFD:	return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7);
+
+	case AMOVWF:	return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4);
+	case AMOVWD:	return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7);
+	case AMOVFW:	return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4);
+	case AMOVDW:	return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7);
+	}
+	diag("bad rrr %d", a);
+	prasm(curp);
+	return 0;
+}
+
+long
+opbra(int a, int sc)
+{
+
+	if(sc & (C_SBIT|C_PBIT|C_WBIT))
+		diag(".S/.P/.W on bra instruction");
+	sc &= C_SCOND;
+	if(a == ABL)
+		return (sc<<28)|(0x5<<25)|(0x1<<24);
+	if(sc != 0xe)
+		diag(".COND on bcond instruction");
+	switch(a) {
+	case ABEQ:	return (0x0<<28)|(0x5<<25);
+	case ABNE:	return (0x1<<28)|(0x5<<25);
+	case ABCS:	return (0x2<<28)|(0x5<<25);
+	case ABHS:	return (0x2<<28)|(0x5<<25);
+	case ABCC:	return (0x3<<28)|(0x5<<25);
+	case ABLO:	return (0x3<<28)|(0x5<<25);
+	case ABMI:	return (0x4<<28)|(0x5<<25);
+	case ABPL:	return (0x5<<28)|(0x5<<25);
+	case ABVS:	return (0x6<<28)|(0x5<<25);
+	case ABVC:	return (0x7<<28)|(0x5<<25);
+	case ABHI:	return (0x8<<28)|(0x5<<25);
+	case ABLS:	return (0x9<<28)|(0x5<<25);
+	case ABGE:	return (0xa<<28)|(0x5<<25);
+	case ABLT:	return (0xb<<28)|(0x5<<25);
+	case ABGT:	return (0xc<<28)|(0x5<<25);
+	case ABLE:	return (0xd<<28)|(0x5<<25);
+	case AB:	return (0xe<<28)|(0x5<<25);
+	}
+	diag("bad bra %A", a);
+	prasm(curp);
+	return 0;
+}
+
+long
+olr(long v, int b, int r, int sc)
+{
+	long o;
+
+	if(sc & C_SBIT)
+		diag(".S on LDR/STR instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(!(sc & C_UBIT))
+		o |= 1 << 23;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (0x1<<26) | (1<<20);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v >= (1<<12))
+		diag("literal span too large: %d (R%d)\n%P", v, b, PP);
+	o |= v;
+	o |= b << 16;
+	o |= r << 12;
+	return o;
+}
+
+long
+olhr(long v, int b, int r, int sc)
+{
+	long o;
+
+	if(sc & C_SBIT)
+		diag(".S on LDRH/STRH instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (1<<23) | (1<<20)|(0xb<<4);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v >= (1<<8))
+		diag("literal span too large: %d (R%d)\n%P", v, b, PP);
+	o |= (v&0xf)|((v>>4)<<8)|(1<<22);
+	o |= b << 16;
+	o |= r << 12;
+	return o;
+}
+
+long
+osr(int a, int r, long v, int b, int sc)
+{
+	long o;
+
+	o = olr(v, b, r, sc) ^ (1<<20);
+	if(a != AMOVW)
+		o |= 1<<22;
+	return o;
+}
+
+long
+oshr(int r, long v, int b, int sc)
+{
+	long o;
+
+	o = olhr(v, b, r, sc) ^ (1<<20);
+	return o;
+}
+	
+
+long
+osrr(int r, int i, int b, int sc)
+{
+
+	return olr(i, b, r, sc) ^ ((1<<25) | (1<<20));
+}
+
+long
+oshrr(int r, int i, int b, int sc)
+{
+	return olhr(i, b, r, sc) ^ ((1<<22) | (1<<20));
+}
+
+long
+olrr(int i, int b, int r, int sc)
+{
+
+	return olr(i, b, r, sc) ^ (1<<25);
+}
+
+long
+olhrr(int i, int b, int r, int sc)
+{
+	return olhr(i, b, r, sc) ^ (1<<22);
+}
+
+long
+ofsr(int a, int r, long v, int b, int sc, Prog *p)
+{
+	long o;
+
+	if(sc & C_SBIT)
+		diag(".S on FLDR/FSTR instruction");
+	o = (sc & C_SCOND) << 28;
+	if(!(sc & C_PBIT))
+		o |= 1 << 24;
+	if(sc & C_WBIT)
+		o |= 1 << 21;
+	o |= (6<<25) | (1<<24) | (1<<23);
+	if(v < 0) {
+		v = -v;
+		o ^= 1 << 23;
+	}
+	if(v & 3)
+		diag("odd offset for floating point op: %d\n%P", v, p);
+	else if(v >= (1<<10))
+		diag("literal span too large: %d\n%P", v, p);
+	o |= (v>>2) & 0xFF;
+	o |= b << 16;
+	o |= r << 12;
+	o |= 1 << 8;
+
+	switch(a) {
+	default:
+		diag("bad fst %A", a);
+	case AMOVD:
+		o |= 1<<15;
+	case AMOVF:
+		break;
+	}
+	return o;
+}
+
+long
+omvl(Prog *p, Adr *a, int dr)
+{	
+	long v, o1;
+	if(!p->cond) {
+		aclass(a);
+		v = immrot(~instoffset);
+		if(v == 0) {
+			diag("missing literal");
+			prasm(p);
+			return 0;
+		}
+		o1 = oprrr(AMVN, p->scond&C_SCOND);
+		o1 |= v;
+		o1 |= dr << 12;
+	} else {
+		v = p->cond->pc - p->pc - 8;
+		o1 = olr(v, REGPC, dr, p->scond&C_SCOND);
+	}
+	return o1;
+}
+
+static Ieee chipfloats[] = {
+	{0x00000000, 0x00000000}, /* 0 */
+	{0x00000000, 0x3ff00000}, /* 1 */
+	{0x00000000, 0x40000000}, /* 2 */
+	{0x00000000, 0x40080000}, /* 3 */
+	{0x00000000, 0x40100000}, /* 4 */
+	{0x00000000, 0x40140000}, /* 5 */
+	{0x00000000, 0x3fe00000}, /* .5 */
+	{0x00000000, 0x40240000}, /* 10 */
+};
+
+int
+chipfloat(Ieee *e)
+{
+	Ieee *p;
+	int n;
+
+	for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){
+		p = &chipfloats[n];
+		if(p->l == e->l && p->h == e->h)
+			return n;
+	}
+	return -1;
+}
--- /dev/null
+++ b/utils/tl/l.h
@@ -1,0 +1,444 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../5c/5.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+/* do not undefine this - code will be removed eventually */
+#define	CALLEEBX
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+typedef	struct	Oprang	Oprang;
+typedef	uchar	Opcross[32][2][32];
+typedef	struct	Count	Count;
+typedef	struct	Use	Use;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	U		((Use*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char*	u0sval;
+		Ieee*	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	union
+	{
+		long	u0regused;
+		Prog*	u0forwd;
+	} u0;
+	Prog*	cond;
+	Prog*	link;
+	long	pc;
+	long	line;
+	uchar	mark;
+	uchar	optab;
+	uchar	as;
+	uchar	scond;
+	uchar	reg;
+	uchar	align;
+};
+#define	regused	u0.u0regused
+#define	forwd	u0.u0forwd
+
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	uchar	subtype;
+	ushort	file;
+	long	value;
+	long	sig;
+	uchar	used;
+	uchar	thumb;	// thumb code
+	uchar	foreign;	// called by arm if thumb, by thumb if arm
+	uchar	fnptr;	// used as fn ptr
+	Use*		use;
+	Sym*	link;
+};
+
+#define SIGNINTERN	(1729*325*1729)
+
+struct	Autom
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	char	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+	char	flag;
+};
+struct	Oprang
+{
+	Optab*	start;
+	Optab*	stop;
+};
+struct	Count
+{
+	long	count;
+	long	outof;
+};
+struct	Use
+{
+	Prog*	p;	/* use */
+	Prog*	ct;	/* curtext */
+	Use*		link;
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+	SSTRING,
+	SUNDEF,
+	SREMOVED,
+
+	SIMPORT,
+	SEXPORT,
+
+	LFROM		= 1<<0,
+	LTO		= 1<<1,
+	LPOOL		= 1<<2,
+	V4		= 1<<3,	/* arm v4 arch */
+
+	C_NONE		= 0,
+	C_REG,
+	C_REGREG,
+	C_SHIFT,
+	C_FREG,
+	C_PSR,
+	C_FCR,
+
+	C_RCON,		/* 0xff rotated */
+	C_NCON,		/* ~RCON */
+	C_SCON,		/* 0xffff */
+	C_BCON,		/* thumb */
+	C_LCON,
+	C_FCON,
+	C_GCON,		/* thumb */
+
+	C_RACON,
+	C_SACON,	/* thumb */
+	C_LACON,
+	C_GACON,	/* thumb */
+
+	C_RECON,
+	C_LECON,
+
+	C_SBRA,
+	C_LBRA,
+	C_GBRA,		/* thumb */
+
+	C_HAUTO,	/* halfword insn offset (-0xff to 0xff) */
+	C_FAUTO,	/* float insn offset (0 to 0x3fc, word aligned) */
+	C_HFAUTO,	/* both H and F */
+	C_SAUTO,	/* -0xfff to 0xfff */
+	C_LAUTO,
+
+	C_HEXT,
+	C_FEXT,
+	C_HFEXT,
+	C_SEXT,
+	C_LEXT,
+
+	C_HOREG,
+	C_FOREG,
+	C_HFOREG,
+	C_SOREG,
+	C_ROREG,
+	C_SROREG,	/* both S and R */
+	C_LOREG,
+	C_GOREG,		/* thumb */
+
+	C_PC,
+	C_SP,
+	C_HREG,
+	C_OFFPC,		/* thumb */
+
+	C_ADDR,		/* relocatable address */
+
+	C_GOK,
+
+/* mark flags */
+	FOLL		= 1<<0,
+	LABEL		= 1<<1,
+	LEAF		= 1<<2,
+
+	BIG		= (1<<12)-4,
+	STRINGSZ	= 200,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	MAXIO		= 8192,
+	MAXHIST		= 20,	/* limit of path elements for history symbols */
+
+	Roffset	= 22,		/* no. bits for offset in relocation address */
+	Rindex	= 10,		/* no. bits for index in relocation address */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+#define	setarch(p)		if((p)->as==ATEXT) thumb=(p)->reg&ALLTHUMBS
+#define	setthumb(p)	if((p)->as==ATEXT) seenthumb|=(p)->reg&ALLTHUMBS
+
+#ifndef COFFCVT
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi4[4];
+EXTERN	char	fnuxi8[8];
+EXTERN	char*	noname;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	instoffset;
+EXTERN	Opcross	opcross[8];
+EXTERN	Oprang	oprange[ALAST];
+EXTERN	Oprang	thumboprange[ALAST];
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	uchar	repop[ALAST];
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	char	xcmp[C_GOK+1][C_GOK+1];
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+EXTERN	int	armv4;
+EXTERN	int	thumb;
+EXTERN	int	seenthumb;
+EXTERN	int	armsize;
+
+EXTERN	int	doexp, dlm;
+EXTERN	int	imports, nimports;
+EXTERN	int	exports, nexports;
+EXTERN	char*	EXPTAB;
+EXTERN	Prog	undefp;
+
+#define	UP	(&undefp)
+
+extern	char*	anames[];
+extern	Optab	optab[];
+extern	Optab	thumboptab[];
+
+void	addpool(Prog*, Adr*);
+EXTERN	Prog*	blitrl;
+EXTERN	Prog*	elitrl;
+
+void	initdiv(void);
+EXTERN	Prog*	prog_div;
+EXTERN	Prog*	prog_divu;
+EXTERN	Prog*	prog_mod;
+EXTERN	Prog*	prog_modu;
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"C"	int
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+int	Aconv(Fmt*);
+int	Cconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+int	thumbaclass(Adr*, Prog*);
+void	addhist(long, int);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmdyn(void);
+void	asmlc(void);
+void	asmthumbmap(void);
+void	asmout(Prog*, Optab*);
+void	thumbasmout(Prog*, Optab*);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	thumbbuildop(void);
+void	buildrep(int, int);
+void	cflush(void);
+void	ckoff(Sym*, long);
+int	chipfloat(Ieee*);
+int	cmp(int, int);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long, int);
+void	diag(char*, ...);
+void	divsig(void);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+void	dynreloc(Sym*, long, int);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+void	export(void);
+int	find1(long, int);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+void	hputl(int);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+void	import(void);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	cput(int);
+void	hput(long);
+void	lput(long);
+void	lputl(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opirr(int);
+Optab*	oplook(Prog*);
+long	oprrr(int, int);
+long	olr(long, int, int, int);
+long	olhr(long, int, int, int);
+long	olrr(int, int, int, int);
+long	olhrr(int, int, int, int);
+long	osr(int, int, long, int, int);
+long	oshr(int, long, int, int);
+long	ofsr(int, int, long, int, int, Prog*);
+long	osrr(int, int, int, int);
+long	oshrr(int, int, int, int);
+long	omvl(Prog*, Adr*, int);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+void	readundefs(char*, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	undefsym(Sym*);
+void	wput(long);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+void	zerosig(char*);
+void	noops(void);
+long	immrot(ulong);
+long	immaddr(long);
+long	opbra(int, int);
+int	brextra(Prog*);
+int	isbranch(Prog*);
+int	fnpinc(Sym *);
+int	fninc(Sym *);
+void	thumbcount(void);
+void reachable(void);
+void fnptrs(void);
+
+#endif
--- /dev/null
+++ b/utils/tl/list.c
@@ -1,0 +1,360 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('C', Cconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	switch(a) {
+	default:
+		s = str;
+		s += sprint(s, "(%ld)", p->line);
+		if(p->reg == NREG)
+			sprint(s, "	%A%C	%D,%D",
+				a, p->scond, &p->from, &p->to);
+		else
+		if(p->from.type != D_FREG)
+			sprint(s, "	%A%C	%D,R%d,%D",
+				a, p->scond, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%A%C	%D,F%d,%D",
+				a, p->scond, &p->from, p->reg, &p->to);
+		break;
+
+	case ASWPW:
+	case ASWPBU:
+		sprint(str, "(%ld)	%A%C	R%d,%D,%D",
+			p->line, a, p->scond, p->reg, &p->from, &p->to);
+		break;
+
+	case ADATA:
+	case AINIT:
+	case ADYNT:
+		sprint(str, "(%ld)	%A%C	%D/%d,%D",
+			p->line, a, p->scond, &p->from, p->reg, &p->to);
+		break;
+
+	case AWORD:
+		sprint(str, "WORD %ld", p->to.offset);
+		break;
+
+	case ADWORD:
+		sprint(str, "DWORD %ld %ld", p->from.offset, p->to.offset);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+char*	strcond[16] =
+{
+	".EQ",
+	".NE",
+	".HS",
+	".LO",
+	".MI",
+	".PL",
+	".VS",
+	".VC",
+	".HI",
+	".LS",
+	".GE",
+	".LT",
+	".GT",
+	".LE",
+	"",
+	".NV"
+};
+
+int
+Cconv(Fmt *fp)
+{
+	char s[20];
+	int c;
+
+	c = va_arg(fp->args, int);
+	strcpy(s, strcond[c & C_SCOND]);
+	if(c & C_SBIT)
+		strcat(s, ".S");
+	if(c & C_PBIT)
+		strcat(s, ".P");
+	if(c & C_WBIT)
+		strcat(s, ".W");
+	if(c & C_UBIT)		/* ambiguous with FBIT */
+		strcat(s, ".U");
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	char *op;
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg == NREG)
+			sprint(str, "$%N", a);
+		else
+			sprint(str, "$%N(R%d)", a, a->reg);
+		break;
+
+	case D_SHIFT:
+		v = a->offset;
+		op = "<<>>->@>" + (((v>>5) & 3) << 1);
+		if(v & (1<<4))
+			sprint(str, "R%ld%c%cR%ld", v&15, op[0], op[1], (v>>8)&15);
+		else
+			sprint(str, "R%ld%c%c%ld", v&15, op[0], op[1], (v>>7)&31);
+		if(a->reg != NREG)
+			sprint(str+strlen(str), "(R%d)", a->reg);
+		break;
+
+	case D_OCONST:
+		sprint(str, "$*$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_REGREG:
+		sprint(str, "(R%d,R%d)", a->reg, (int)a->offset);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_PSR:
+		switch(a->reg) {
+		case 0:
+			sprint(str, "CPSR");
+			break;
+		case 1:
+			sprint(str, "SPSR");
+			break;
+		default:
+			sprint(str, "PSR%d", a->reg);
+			break;
+		}
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(PSR%d)(REG)", a, a->reg);
+		break;
+
+	case D_FPCR:
+		switch(a->reg){
+		case 0:
+			sprint(str, "FPSR");
+			break;
+		case 1:
+			sprint(str, "FPCR");
+			break;
+		default:
+			sprint(str, "FCR%d", a->reg);
+			break;
+		}
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(FCR%d)(REG)", a, a->reg);
+
+		break;
+
+	case D_BRANCH:	/* botch */
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%e", ieeedtod(a->ieee));
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		if(s == S)
+			sprint(str, "%ld(SB)", a->offset);
+		else
+			sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		if(s == S)
+			sprint(str, "<>+%ld(SB)", a->offset);
+		else
+			sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		if(s == S)
+			sprint(str, "%ld(SP)", a->offset);
+		else
+			sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		if(s == S)
+			sprint(str, "%ld(FP)", a->offset);
+		else
+			sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/tl/mkfile
@@ -1,0 +1,34 @@
+<../../mkconfig
+
+TARG=5l
+
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+	thumb.$O\
+
+HFILES=\
+	l.h\
+	../5c/5.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+CFLAGS=$CFLAGS -I../include -I.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+enam.$O:	../5c/enam.c
+	$CC $CFLAGS ../5c/enam.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/tl/noop.c
@@ -1,0 +1,894 @@
+#include	"l.h"
+
+static	Sym*	sym_div;
+static	Sym*	sym_divu;
+static	Sym*	sym_mod;
+static	Sym*	sym_modu;
+
+static void setdiv(int);
+
+static Prog *
+movrr(Prog *q, int rs, int rd, Prog *p)
+{
+	if(q == nil)
+		q = prg();
+	q->as = AMOVW;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = rs;
+	q->to.type = D_REG;
+	q->to.reg = rd;
+	q->link = p->link;
+	return q;
+}
+
+static Prog *
+fnret(Prog *q, int rs, int foreign, Prog *p)
+{
+	q = movrr(q, rs, REGPC, p);
+	if(foreign){	// BX rs
+		q->as = ABXRET;
+		q->from.type = D_NONE;
+		q->from.reg = NREG;
+		q->to.reg = rs;
+	}
+	return q;
+}
+
+static Prog *
+aword(long w, Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = AWORD;
+	q->line = p->line;
+	q->from.type = D_NONE;
+	q->reg = NREG;
+	q->to.type = D_CONST;
+	q->to.offset = w;
+	q->link = p->link;
+	p->link = q;
+	return q;
+}
+
+static Prog *
+adword(long w1, long w2, Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = ADWORD;
+	q->line = p->line;
+	q->from.type = D_CONST;
+	q->from.offset = w1;
+	q->reg = NREG;
+	q->to.type = D_CONST;
+	q->to.offset = w2;
+	q->link = p->link;
+	p->link = q;
+	return q;
+}
+
+void
+noops(void)
+{
+	Prog *p, *q, *q1, *q2;
+	int o, curframe, curbecome, maxbecome, foreign;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			p->mark |= LEAF;
+			curtext = p;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+			break;
+
+		case ADIV:
+		case ADIVU:
+		case AMOD:
+		case AMODU:
+			q = p;
+			if(prog_div == P)
+				initdiv();
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+			setdiv(p->as);
+			continue;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		case ABL:
+		case ABX:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case ABCASE:
+		case AB:
+
+		case ABEQ:
+		case ABNE:
+		case ABCS:
+		case ABHS:
+		case ABCC:
+		case ABLO:
+		case ABMI:
+		case ABPL:
+		case ABVS:
+		case ABVC:
+		case ABHI:
+		case ABLS:
+		case ABGE:
+		case ABLT:
+		case ABGT:
+		case ABLE:
+
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+			}
+			break;
+		}
+		q = p;
+	}
+
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case ABL:
+		// case ABX:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if(autosize <= 4)
+			if(curtext->mark & LEAF) {
+				p->to.offset = -4;
+				autosize = 0;
+			}
+
+			if(!autosize && !(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				Bflush(&bso);
+				curtext->mark |= LEAF;
+			}
+#ifdef CALLEEBX
+			if(p->from.sym->foreign){
+				if(thumb)
+					// don't allow literal pool to seperate these
+					p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7
+					// p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7
+				else
+					p = aword(0x4778, p);	// thumb bx pc and 2 bytes padding
+			}
+#endif
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+#ifdef optimise_time
+				if(autosize) {
+					q = prg();
+					q->as = ASUB;
+					q->line = p->line;
+					q->from.type = D_CONST;
+					q->from.offset = autosize;
+					q->to.type = D_REG;
+					q->to.reg = REGSP;
+
+					q->link = p->link;
+					p->link = q;
+				}
+				break;
+#else
+				if(!autosize)
+					break;
+#endif
+			}
+
+			if(thumb){
+				if(!(curtext->mark & LEAF)){
+					q = movrr(nil, REGLINK, REGTMPT-1, p);
+					p->link = q;
+					q1 = prg();
+					q1->as = AMOVW;
+					q1->line = p->line;
+					q1->from.type = D_REG;
+					q1->from.reg = REGTMPT-1;
+					q1->to.type = D_OREG;
+					q1->to.name = D_NONE;
+					q1->to.reg = REGSP;
+					q1->to.offset = 0;
+					q1->link = q->link;
+					q->link = q1;
+				}
+				if(autosize){
+					q2 = prg();
+					q2->as = ASUB;
+					q2->line = p->line;
+					q2->from.type = D_CONST;
+					q2->from.offset = autosize;
+					q2->to.type = D_REG;
+					q2->to.reg = REGSP;
+					q2->link = p->link;
+					p->link = q2;
+				}
+				break;
+			}
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->scond |= C_WBIT;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->to.offset = -autosize;
+			q1->to.reg = REGSP;
+			q1->link = p->link;
+			p->link = q1;
+			break;
+
+		case ARET:
+			nocache(p);
+			foreign = seenthumb && curtext->from.sym != S && (curtext->from.sym->foreign || curtext->from.sym->fnptr);
+// print("%s %d %d\n", curtext->from.sym->name, curtext->from.sym->foreign, curtext->from.sym->fnptr);
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					if(thumb){
+						p = fnret(p, REGLINK, foreign, p);
+						break;
+					}
+// if(foreign) print("ABXRET 1 %s\n", curtext->from.sym->name);
+					p->as = foreign ? ABXRET : AB;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 0;
+					p->to.reg = REGLINK;
+					break;
+				}
+
+#ifdef optimise_time
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+				if(thumb){
+					p->link = fnret(nil, REGLINK, foreign, p);
+					break;
+				}
+				q = prg();
+// if(foreign) print("ABXRET 2 %s\n", curtext->from.sym->name);
+				q->as = foreign ? ABXRET : AB;
+				q->scond = p->scond;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 0;
+				q->to.reg = REGLINK;
+
+				q->link = p->link;
+				p->link = q;
+
+				break;
+#endif
+			}
+			if(thumb){
+				if(curtext->mark & LEAF){
+					if(autosize){
+						p->as = AADD;
+						p->from.type = D_CONST;
+						p->from.offset = autosize;
+						p->to.type = D_REG;
+						p->to.reg = REGSP;
+						q = nil;
+					}
+					else
+						q = p;
+					q = fnret(q, REGLINK, foreign, p);
+					if(q != p)
+						p->link = q;
+				}
+				else{
+					p->as = AMOVW;
+					p->from.type = D_OREG;
+					p->from.name = D_NONE;
+					p->from.reg = REGSP;
+					p->from.offset = 0;
+					p->to.type = D_REG;
+					p->to.reg = REGTMPT-1;
+					if(autosize){
+						q = prg();
+						q->as = AADD;
+						q->from.type = D_CONST;
+						q->from.offset = autosize;
+						q->to.type = D_REG;
+						q->to.reg = REGSP;
+						q->link = p->link;
+						p->link = 	q;
+					}
+					else
+						q = p;
+					q1 = fnret(nil, REGTMPT-1, foreign, p);
+					q1->link = q->link;
+					q->link = q1;
+				}
+				break;
+			}
+			if(foreign) {
+// if(foreign) print("ABXRET 3 %s\n", curtext->from.sym->name);
+#define	R	1
+				p->as = AMOVW;
+				p->from.type = D_OREG;
+				p->from.name = D_NONE;
+				p->from.reg = REGSP;
+				p->from.offset = 0;
+				p->to.type = D_REG;
+				p->to.reg = R;
+				q = prg();
+				q->as = AADD;
+				q->scond = p->scond;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+				q->link = p->link;
+				p->link = q;
+				q1 = prg();
+				q1->as = ABXRET;
+				q1->scond = p->scond;
+				q1->line = p->line;
+				q1->to.type = D_OREG;
+				q1->to.offset = 0;
+				q1->to.reg = R;
+				q1->link = q->link;
+				q->link = q1;
+#undef	R
+			}
+			else {
+				p->as = AMOVW;
+				p->scond |= C_PBIT;
+				p->from.type = D_OREG;
+				p->from.offset = autosize;
+				p->from.reg = REGSP;
+				p->to.type = D_REG;
+				p->to.reg = REGPC;
+			}
+			break;
+
+		become:
+			if(foreign){
+				diag("foreign become - help");
+				break;
+			}
+			if(thumb){
+				diag("thumb become - help");
+				break;
+			}
+			print("arm become\n");
+			if(curtext->mark & LEAF) {
+
+				if(!autosize) {
+					p->as = AB;
+					p->from = zprg.from;
+					break;
+				}
+
+#ifdef optimise_time
+				q = prg();
+				q->scond = p->scond;
+				q->line = p->line;
+				q->as = AB;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+#endif
+			}
+			q = prg();
+			q->scond = p->scond;
+			q->line = p->line;
+			q->as = AB;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			p->link = q;
+			if(thumb){
+				q1 = prg();
+				q1->line = p->line;
+				q1->as = AADD;
+				q1->from.type = D_CONST;
+				q1->from.offset = autosize;
+				q1->to.type = D_REG;
+				q1->to.reg = REGSP;
+				p->as = AMOVW;
+				p->line = p->line;
+				p->from.type = D_OREG;
+				p->from.name = D_NONE;
+				p->from.reg = REGSP;
+				p->from.offset = 0;
+				p->to.type = D_REG;
+				p->to.reg = REGTMPT-1;
+				q1->link = q;
+				p->link = q1;
+				q2 = movrr(nil, REGTMPT-1, REGLINK, p);
+				q2->link = q;
+				q1->link = q2;
+				break;
+			}
+			p->as = AMOVW;
+			p->scond |= C_PBIT;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = autosize;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+
+		case ADIV:
+		case ADIVU:
+		case AMOD:
+		case AMODU:
+			if(debug['M'])
+				break;
+			if(p->from.type != D_REG)
+				break;
+			if(p->to.type != D_REG)
+				break;
+			q1 = p;
+
+			/* MOV a,4(SP) */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->from.reg;
+			p->to.type = D_OREG;
+			p->to.reg = REGSP;
+			p->to.offset = 4;
+
+			/* MOV b,REGTMP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = q1->reg;
+			if(q1->reg == NREG)
+				p->from.reg = q1->to.reg;
+			p->to.type = D_REG;
+			p->to.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP;
+			p->to.offset = 0;
+
+			/* CALL appropriate */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+#ifdef CALLEEBX
+			p->as = ABL;
+#else
+			if(prog_div != UP && prog_div->from.sym->thumb)
+				p->as = thumb ? ABL : ABX;
+			else
+				p->as = thumb ? ABX : ABL;
+#endif
+			p->line = q1->line;
+			p->to.type = D_BRANCH;
+			p->cond = p;
+			switch(o) {
+			case ADIV:
+				p->cond = prog_div;
+				p->to.sym = sym_div;
+				break;
+			case ADIVU:
+				p->cond = prog_divu;
+				p->to.sym = sym_divu;
+				break;
+			case AMOD:
+				p->cond = prog_mod;
+				p->to.sym = sym_mod;
+				break;
+			case AMODU:
+				p->cond = prog_modu;
+				p->to.sym = sym_modu;
+				break;
+			}
+
+			/* MOV REGTMP, b */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AMOVW;
+			p->line = q1->line;
+			p->from.type = D_REG;
+			p->from.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP;
+			p->from.offset = 0;
+			p->to.type = D_REG;
+			p->to.reg = q1->to.reg;
+
+			/* ADD $8,SP */
+			q = prg();
+			q->link = p->link;
+			p->link = q;
+			p = q;
+
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.reg = NREG;
+			p->from.offset = 8;
+			p->reg = NREG;
+			p->to.type = D_REG;
+			p->to.reg = REGSP;
+
+			/* SUB $8,SP */
+			q1->as = ASUB;
+			q1->from.type = D_CONST;
+			q1->from.offset = 8;
+			q1->from.reg = NREG;
+			q1->reg = NREG;
+			q1->to.type = D_REG;
+			q1->to.reg = REGSP;
+
+			break;
+		case AMOVW:
+			if(thumb){
+				Adr *a = &p->from;
+
+				if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3))
+					diag("SP offset not multiple of 4");
+			}
+			break;
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+			if(thumb){
+				if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){
+					q = prg();
+					*q = *p;
+					if(p->from.name == D_AUTO)
+						q->from.offset += autosize;
+					else if(p->from.name == D_PARAM)
+						q->from.offset += autosize+4;
+					q->from.name = D_NONE;
+					q->from.reg = REGTMPT;
+					p = movrr(p, REGSP, REGTMPT, p);
+					q->link = p->link;
+					p->link = q;
+				}
+				if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){
+					q = prg();
+					*q = *p;
+					if(p->to.name == D_AUTO)
+						q->to.offset += autosize;
+					else if(p->to.name == D_PARAM)
+						q->to.offset += autosize+4;
+					q->to.name = D_NONE;
+					q->to.reg = REGTMPT;
+					p = movrr(p, REGSP, REGTMPT, p);
+					q->link = p->link;
+					p->link = q;
+					if(q->to.offset < 0 || q->to.offset > 255){	// complicated
+						p->to.reg = REGTMPT+1;			// mov sp, r8
+						q1 = prg();
+						q1->line = p->line;
+						q1->as = AMOVW;
+						q1->from.type = D_CONST;
+						q1->from.offset = q->to.offset;
+						q1->to.type = D_REG;
+						q1->to.reg = REGTMPT;			// mov $o, r7
+						p->link = q1;
+						q1->link = q;
+						q1 = prg();
+						q1->line = p->line;
+						q1->as = AADD;
+						q1->from.type = D_REG;
+						q1->from.reg = REGTMPT+1;
+						q1->to.type = D_REG;
+						q1->to.reg = REGTMPT;			// add r8, r7
+						p->link->link = q1;
+						q1->link = q;
+						q->to.offset = 0;				// mov* r, 0(r7)
+						/* phew */
+					}
+				}
+			}
+			break;
+		case AMOVM:
+			if(thumb){
+				if(p->from.type == D_OREG){
+					if(p->from.offset == 0)
+						p->from.type = D_REG;
+					else
+						diag("non-zero AMOVM offset");
+				}
+				else if(p->to.type == D_OREG){
+					if(p->to.offset == 0)
+						p->to.type = D_REG;
+					else
+						diag("non-zero AMOVM offset");
+				}
+			}
+			break;
+		case AB:
+			if(thumb && p->to.type == D_OREG){
+				if(p->to.offset == 0){
+					p->as = AMOVW;
+					p->from.type = D_REG;
+					p->from.reg = p->to.reg;
+					p->to.type = D_REG;
+					p->to.reg = REGPC;
+				}
+				else{
+					p->as = AADD;
+					p->from.type = D_CONST;
+					p->from.offset = p->to.offset;
+					p->reg = p->to.reg;
+					p->to.type = D_REG;
+					p->to.reg = REGTMPT-1;
+					q = prg();
+					q->as = AMOVW;
+					q->line = p->line;
+					q->from.type = D_REG;
+					q->from.reg = REGTMPT-1;
+					q->to.type = D_REG;
+					q->to.reg = REGPC;
+					q->link = p->link;
+					p->link = q;
+				}
+			}
+			if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){	
+				// print("warn %s:	b	(R%d)	assuming a return\n", curtext->from.sym->name, p->to.reg);
+				p->as = ABXRET;
+			}
+			break;
+		case ABL:
+		case ABX:
+			if(thumb && p->to.type == D_OREG){
+				if(p->to.offset == 0){
+					p->as = o;
+					p->from.type = D_NONE;
+					p->to.type = D_REG;
+				}
+				else{
+					p->as = AADD;
+					p->from.type = D_CONST;
+					p->from.offset = p->to.offset;
+					p->reg = p->to.reg;
+					p->to.type = D_REG;
+					p->to.reg = REGTMPT-1;
+					q = prg();
+					q->as = o;
+					q->line = p->line;
+					q->from.type = D_NONE;
+					q->to.type = D_REG;
+					q->to.reg = REGTMPT-1;
+					q->link = p->link;
+					p->link = q;
+				}
+			}
+			break;
+		}
+	}
+}
+
+static void
+sigdiv(char *n)
+{
+	Sym *s;
+
+	s = lookup(n, 0);
+	if(s->type == STEXT){
+		if(s->sig == 0)
+			s->sig = SIGNINTERN;
+	}
+	else if(s->type == 0 || s->type == SXREF)
+		s->type = SUNDEF;
+}
+
+void
+divsig(void)
+{
+	sigdiv("_div");
+	sigdiv("_divu");
+	sigdiv("_mod");
+	sigdiv("_modu");
+}
+
+static void
+sdiv(Sym *s)
+{
+	if(s->type == 0 || s->type == SXREF){
+		/* undefsym(s); */
+		s->type = SXREF;
+		if(s->sig == 0)
+			s->sig = SIGNINTERN;
+		s->subtype = SIMPORT;
+	}
+	else if(s->type != STEXT)
+		diag("undefined: %s", s->name);
+}
+
+void
+initdiv(void)
+{
+	Sym *s2, *s3, *s4, *s5;
+	Prog *p;
+
+	if(prog_div != P)
+		return;
+	sym_div = s2 = lookup("_div", 0);
+	sym_divu = s3 = lookup("_divu", 0);
+	sym_mod = s4 = lookup("_mod", 0);
+	sym_modu = s5 = lookup("_modu", 0);
+	if(dlm) {
+		sdiv(s2); if(s2->type == SXREF) prog_div = UP;
+		sdiv(s3); if(s3->type == SXREF) prog_divu = UP;
+		sdiv(s4); if(s4->type == SXREF) prog_mod = UP;
+		sdiv(s5); if(s5->type == SXREF) prog_modu = UP;
+	}
+	for(p = firstp; p != P; p = p->link)
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2)
+				prog_div = p;
+			if(p->from.sym == s3)
+				prog_divu = p;
+			if(p->from.sym == s4)
+				prog_mod = p;
+			if(p->from.sym == s5)
+				prog_modu = p;
+		}
+	if(prog_div == P) {
+		diag("undefined: %s", s2->name);
+		prog_div = curtext;
+	}
+	if(prog_divu == P) {
+		diag("undefined: %s", s3->name);
+		prog_divu = curtext;
+	}
+	if(prog_mod == P) {
+		diag("undefined: %s", s4->name);
+		prog_mod = curtext;
+	}
+	if(prog_modu == P) {
+		diag("undefined: %s", s5->name);
+		prog_modu = curtext;
+	}
+}
+
+static void
+setdiv(int as)
+{
+	Prog *p = nil;
+
+	switch(as){
+	case ADIV: p = prog_div; break;
+	case ADIVU: p = prog_divu; break;
+	case AMOD: p = prog_mod; break;
+	case AMODU: p = prog_modu; break;
+	}
+	if(p != UP && thumb != p->from.sym->thumb)
+		p->from.sym->foreign = 1;
+}
+
+void
+nocache(Prog *p)
+{
+	p->optab = 0;
+	p->from.class = 0;
+	p->to.class = 0;
+}
--- /dev/null
+++ b/utils/tl/obj.c
@@ -1,0 +1,1558 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= '5';
+char	*thestring 	= "arm";
+
+/*
+ *	-H1 -T0x10005000 -R4		is aif for risc os
+ *	-H2 -T4128 -R4096		is plan9 format
+ *	-H3 -T0xF0000020 -R4		is NetBSD format
+ *	-H4				is IXP1200 (raw)
+ *	-H5 -T0xC0008010 -R1024		is ipaq
+ */
+
+static int
+isobjfile(char *f)
+{
+	int n, v;
+	Biobuf *b;
+	char buf1[5], buf2[SARMAG];
+
+	b = Bopen(f, OREAD);
+	if(b == nil)
+		return 0;
+	n = Bread(b, buf1, 5);
+	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
+		v = 1;	/* good enough for our purposes */
+	else{
+		Bseek(b, 0, 0);
+		n = Bread(b, buf2, SARMAG);
+		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
+	}
+	Bterm(b);
+	return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+
+	Binit(&bso, 1, OWRITE);
+	srand(time(0));
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		/* do something about setting INITTEXT */
+		break;
+	case 'x':	/* produce export table */
+		doexp = 1;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SEXPORT);
+		break;
+	case 'u':	/* produce dynamically loadable module */
+		dlm = 1;
+		debug['l']++;
+		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
+			readundefs(ARGF(), SIMPORT);
+		break;
+	} ARGEND
+
+	USED(argc);
+
+	if(*argv == 0) {
+		diag("usage: 5l [-options] objects");
+		errorexit();
+	}
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+	case 0:	/* no header */
+		HEADR = 0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 1:	/* aif for risc os */
+		HEADR = 128L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x10005000 + HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* boot for NetBSD */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xF0000020L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 4: /* boot for IXP1200 */
+		HEADR = 0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x0;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 5: /* boot for ipaq */
+		HEADR = 16L;
+		if(INITTEXT == -1)
+			INITTEXT = 0xC0008010;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 1024;
+		break;
+	}
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.scond = 14;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	thumbbuildop();	// could build on demand
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0)
+		outfile = "5.out";
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("%s: cannot create", outfile);
+		errorexit();
+	}
+	nuxiinit();
+
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	if(doexp || dlm){
+		EXPTAB = "_exporttab";
+		zerosig(EXPTAB);
+		zerosig("etext");
+		zerosig("edata");
+		zerosig("end");
+		if(dlm){
+			initdiv();
+			import();
+			HEADTYPE = 2;
+			INITTEXT = INITDAT = 0;
+			INITRND = 8;
+			INITENTRY = EXPTAB;
+		}
+		else
+			divsig();
+		export();
+	}
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	if(debug['u'])
+		reachable();
+	dodata();
+	if(seenthumb && debug['f'])
+		fnptrs();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['c']){
+		thumbcount();
+		print("ARM size = %d\n", armsize);
+	}
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+	errorexit();
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	Bflush(&bso);
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[100], pname[150];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(file[0] == '-' && file[1] == 'l') {
+		if(debug['9'])
+			sprint(name, "/%s/lib/lib", thestring);
+		else
+			sprint(name, "/usr/%clib/lib", thechar);
+		strcat(name, file+2);
+		strcat(name, ".a");
+		file = name;
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open file: %s", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			l = read(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	long l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = ALAST+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	if(a->type == D_CONST || a->type == D_OCONST) {
+		if(a->name == D_EXTERN || a->name == D_STATIC) {
+			s = a->sym;
+			if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF)) {
+				if(0 && !s->fnptr && s->name[0] != '.')
+					print("%s used as function pointer\n", s->name);
+				s->fnptr = 1;	// over the top cos of SXREF
+			}
+		}
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+	case D_FPCR:
+		break;
+
+	case D_REGREG:
+		a->offset = p[4];
+		c++;
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+	case D_SHIFT:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		while(nhunk < NSNAME)
+			gethunk();
+		a->sval = (char*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		while(nhunk < sizeof(Ieee))
+			gethunk();
+		a->ieee = (Ieee*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		a->ieee->l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee->h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		return c;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char name[1024], comp[256], *p;
+	int i;
+
+	if(histfrogp <= 0)
+		return;
+
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else
+	if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		if(debug['9'])
+			sprint(name, "/%s/lib", thestring);
+		else
+			sprint(name, "/usr/%clib", thechar);
+		i = 0;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) {
+			diag("library component too long");
+			return;
+		}
+		strcat(name, "/");
+		strcat(name, comp);
+	}
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+static void puntfp(Prog *);
+
+void
+ldobj(int f, long c, char *pn)
+{
+	long ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+	ulong sig;
+	static int files;
+	static char **filen;
+	char **nfilen;
+
+	if((files&15) == 0){
+		nfilen = malloc((files+16)*sizeof(char*));
+		memmove(nfilen, filen, files*sizeof(char*));
+		free(filen);
+		filen = nfilen;
+	}
+	filen[files++] = strdup(pn);
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= AXXX || o >= ALAST) {
+		diag("%s: line %ld: opcode out of range %d", pn, pc-ipc, o);
+		print("	probably not a .5 file\n");
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		sig = 0;
+		if(o == ASIGNAME){
+			sig = bloc[1] | (bloc[2]<<8) | (bloc[3]<<16) | (bloc[4]<<24);
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(sig != 0){
+			if(s->sig != 0 && s->sig != sig)
+				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
+			s->sig = sig;
+			s->file = files-1;
+		}
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->scond = bloc[1];
+	p->reg = bloc[2];
+	p->line = bloc[3] | (bloc[4]<<8) | (bloc[5]<<16) | (bloc[6]<<24);
+
+	r = zaddr(bloc+7, &p->from, h) + 7;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+	
+	case ADATA:
+		if(p->from.sym == S) {
+			diag("DATA without a sym\n%P", p);
+			break;
+		}
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		setarch(p);
+		setthumb(p);
+		p->align = 4;
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		autosize = (p->to.offset+3L) & ~3L;
+		p->to.offset = autosize;
+		autosize += 4;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		s->thumb = thumb;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->cond = p;
+		etextp = p;
+		break;
+
+	case ASUB:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE)
+		if(p->from.offset < 0) {
+			p->from.offset = -p->from.offset;
+			p->as = AADD;
+		}
+		goto casedef;
+
+	case AADD:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE)
+		if(p->from.offset < 0) {
+			p->from.offset = -p->from.offset;
+			p->as = ASUB;
+		}
+		goto casedef;
+
+	case AMOVWD:
+	case AMOVWF:
+	case AMOVDW:
+	case AMOVFW:
+	case AMOVFD:
+	case AMOVDF:
+	// case AMOVF:
+	// case AMOVD:
+	case ACMPF:
+	case ACMPD:
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		if(thumb)
+			puntfp(p);
+		goto casedef;
+
+	case AMOVF:
+		if(thumb)
+			puntfp(p);
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVD:
+		if(thumb)
+			puntfp(p);
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->l, p->from.ieee->h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	s->sig = 0;
+	s->used = s->thumb = s->foreign = s->fnptr = 0;
+	s->use = nil;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		setarch(p);
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = thumb ? REGTMPT : REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADD;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = thumb ? REGTMPT : REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = thumb ? REGTMPT : REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+	s2 = lookup("_profin", 0);
+	s4 = lookup("_profout", 0);
+	if(s2->type != STEXT || s4->type != STEXT) {
+		diag("_profin/_profout not defined");
+		return;
+	}
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				ps2 = p;
+				p->reg = 1;
+			}
+			if(p->from.sym == s4) {
+				ps4 = p;
+				p->reg = 1;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		if(p->as == ATEXT) {
+			if(p->reg & NOPROF) {
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * BL	profin, R2
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = ABL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * BL	profout
+			 */
+			p->as = ABL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+
+	int i, c;
+
+	for(i=0; i<4; i++) {
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		fnuxi4[i] = c;
+		if(!debug['d']){
+			fnuxi8[i] = c;
+			fnuxi8[i+4] = c+4;
+		}
+		else{
+			fnuxi8[i] = c+4;		/* ms word first, then ls, even in little endian mode */
+			fnuxi8[i+4] = c;
+		}
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
+
+static void
+puntfp(Prog *p)
+{
+	USED(p);
+	/* floating point - punt for now */
+	curtext->reg = NREG;	/* ARM */
+	curtext->from.sym->thumb = 0;
+	thumb = 0;
+	// print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line);
+}
+
+void
+undefsym(Sym *s)
+{
+	int n;
+
+	n = imports;
+	if(s->value != 0)
+		diag("value != 0 on SXREF");
+	if(n >= 1<<Rindex)
+		diag("import index %d out of range", n);
+	s->value = n<<Roffset;
+	s->type = SUNDEF;
+	imports++;
+}
+
+void
+zerosig(char *sp)
+{
+	Sym *s;
+
+	s = lookup(sp, 0);
+	s->sig = 0;
+}
+
+void
+readundefs(char *f, int t)
+{
+	int i, n;
+	Sym *s;
+	Biobuf *b;
+	char *l, buf[256], *fields[64];
+
+	if(f == nil)
+		return;
+	b = Bopen(f, OREAD);
+	if(b == nil){
+		diag("could not open %s: %r", f);
+		errorexit();
+	}
+	while((l = Brdline(b, '\n')) != nil){
+		n = Blinelen(b);
+		if(n >= sizeof(buf)){
+			diag("%s: line too long", f);
+			errorexit();
+		}
+		memmove(buf, l, n);
+		buf[n-1] = '\0';
+		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
+		if(n == nelem(fields)){
+			diag("%s: bad format", f);
+			errorexit();
+		}
+		for(i = 0; i < n; i++){
+			s = lookup(fields[i], 0);
+			s->type = SXREF;
+			s->subtype = t;
+			if(t == SIMPORT)
+				nimports++;
+			else
+				nexports++;
+		}
+	}
+	Bterm(b);
+}
--- /dev/null
+++ b/utils/tl/optab.c
@@ -1,0 +1,253 @@
+#include	"l.h"
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_ADDR,	C_REG,	C_LCON, 	 0, 0, 0 },
+
+	{ AADD,		C_REG,	C_REG,	C_REG,		 1, 4, 0 },
+	{ AADD,		C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMVN,		C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ ACMP,		C_REG,	C_REG,	C_NONE,		 1, 4, 0 },
+
+	{ AADD,		C_RCON,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMOVW,	C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMVN,		C_RCON,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ ACMP,		C_RCON,	C_REG,	C_NONE,		 2, 4, 0 },
+
+	{ AADD,		C_SHIFT,C_REG,	C_REG,		 3, 4, 0 },
+	{ AADD,		C_SHIFT,C_NONE,	C_REG,		 3, 4, 0 },
+	{ AMVN,		C_SHIFT,C_NONE,	C_REG,		 3, 4, 0 },
+	{ ACMP,		C_SHIFT,C_REG,	C_NONE,		 3, 4, 0 },
+
+	{ AMOVW,	C_RECON,C_NONE,	C_REG,		 4, 4, REGSB },
+	{ AMOVW,	C_RACON,C_NONE,	C_REG,		 4, 4, REGSP },
+
+	{ AB,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0,	LPOOL },
+	{ ABL,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0 },
+	{ ABX,		C_NONE,	C_NONE,	C_SBRA,		 74, 20, 0 },
+	{ ABEQ,		C_NONE,	C_NONE,	C_SBRA,		 5, 4, 0 },
+
+	{ AB,		C_NONE,	C_NONE,	C_ROREG,	 6, 4, 0,	LPOOL },
+	{ ABL,		C_NONE,	C_NONE,	C_ROREG,	 7, 8, 0 },
+	{ ABX,		C_NONE,	C_NONE,	C_ROREG,	 75, 12, 0 },
+	{ ABXRET,		C_NONE,	C_NONE,	C_ROREG,	 76, 4, 0 },
+
+	{ ASLL,		C_RCON,	C_REG,	C_REG,		 8, 4, 0 },
+	{ ASLL,		C_RCON,	C_NONE,	C_REG,		 8, 4, 0 },
+
+	{ ASLL,		C_REG,	C_NONE,	C_REG,		 9, 4, 0 },
+	{ ASLL,		C_REG,	C_REG,	C_REG,		 9, 4, 0 },
+
+	{ ASWI,		C_NONE,	C_NONE,	C_NONE,		10, 4, 0 },
+	{ ASWI,		C_NONE,	C_NONE,	C_LOREG,	10, 4, 0 },
+
+	{ AWORD,	C_NONE,	C_NONE,	C_LCON,		11, 4, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_GCON,		11, 4, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_LEXT,		11, 4, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_ADDR,		11, 4, 0 },
+
+	{ AMOVW,	C_NCON,	C_NONE,	C_REG,		12, 4, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		12, 4, 0,	LFROM },
+
+	{ AADD,		C_NCON,	C_REG,	C_REG,		13, 8, 0 },
+	{ AADD,		C_NCON,	C_NONE,	C_REG,		13, 8, 0 },
+	{ AMVN,		C_NCON,	C_NONE,	C_REG,		13, 8, 0 },
+	{ ACMP,		C_NCON,	C_REG,	C_NONE,		13, 8, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		13, 8, 0,	LFROM },
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		13, 8, 0,	LFROM },
+	{ AMVN,		C_LCON,	C_NONE,	C_REG,		13, 8, 0,	LFROM },
+	{ ACMP,		C_LCON,	C_REG,	C_NONE,		13, 8, 0,	LFROM },
+
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_REG,		58, 4, 0 },
+	{ AMOVH,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+
+	{ AMUL,		C_REG,	C_REG,	C_REG,		15, 4, 0 },
+	{ AMUL,		C_REG,	C_NONE,	C_REG,		15, 4, 0 },
+
+	{ ADIV,		C_REG,	C_REG,	C_REG,		16, 4, 0 },
+	{ ADIV,		C_REG,	C_NONE,	C_REG,		16, 4, 0 },
+
+	{ AMULL,	C_REG,	C_REG,	C_REGREG,	17, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SEXT,		20, 4, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SAUTO,	20, 4, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SOREG,	20, 4, 0 },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		21, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		21, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		21, 4, 0 },
+	{ AMOVBU,	C_SEXT,	C_NONE,	C_REG,		21, 4, REGSB },
+	{ AMOVBU,	C_SAUTO,C_NONE,	C_REG,		21, 4, REGSP },
+	{ AMOVBU,	C_SOREG,C_NONE,	C_REG,		21, 4, 0 },
+
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+	{ AMOVH,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVH,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVH,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+	{ AMOVHU,	C_SEXT,	C_NONE,	C_REG,		22, 12, REGSB },
+	{ AMOVHU,	C_SAUTO,C_NONE,	C_REG,		22, 12, REGSP },
+	{ AMOVHU,	C_SOREG,C_NONE,	C_REG,		22, 12, 0 },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_SEXT,		23, 12, REGSB },
+	{ AMOVH,	C_REG,	C_NONE,	C_SAUTO,	23, 12, REGSP },
+	{ AMOVH,	C_REG,	C_NONE,	C_SOREG,	23, 12, 0 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SEXT,		23, 12, REGSB },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SAUTO,	23, 12, REGSP },
+	{ AMOVHU,	C_REG,	C_NONE,	C_SOREG,	23, 12, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVW,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVB,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LEXT,		30, 8, REGSB,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LAUTO,	30, 8, REGSP,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LOREG,	30, 8, 0,	LTO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_ADDR,		64, 8, 0,	LTO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		31, 8, REGSB,	LFROM },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		31, 8, REGSP,	LFROM },
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		31, 8, 0,	LFROM },
+	{ AMOVW,	C_ADDR,	C_NONE,	C_REG,		65, 8, 0,	LFROM },
+	{ AMOVBU,	C_LEXT,	C_NONE,	C_REG,		31, 8, REGSB,	LFROM },
+	{ AMOVBU,	C_LAUTO,C_NONE,	C_REG,		31, 8, REGSP,	LFROM },
+	{ AMOVBU,	C_LOREG,C_NONE,	C_REG,		31, 8, 0,	LFROM },
+	{ AMOVBU,	C_ADDR,	C_NONE,	C_REG,		65, 8, 0,	LFROM },
+
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVB,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+	{ AMOVH,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVH,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVH,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVH,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+	{ AMOVHU,	C_LEXT,	C_NONE,	C_REG,		32, 16, REGSB,	LFROM },
+	{ AMOVHU,	C_LAUTO,C_NONE,	C_REG,		32, 16, REGSP,	LFROM },
+	{ AMOVHU,	C_LOREG,C_NONE,	C_REG,		32, 16, 0,	LFROM },
+	{ AMOVHU,	C_ADDR,	C_NONE,	C_REG,		66, 16, 0,	LFROM },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_LEXT,		33, 24, REGSB,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_LAUTO,	33, 24, REGSP,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_LOREG,	33, 24, 0,	LTO },
+	{ AMOVH,	C_REG,	C_NONE,	C_ADDR,		67, 24, 0,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LEXT,		33, 24, REGSB,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LAUTO,	33, 24, REGSP,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LOREG,	33, 24, 0,	LTO },
+	{ AMOVHU,	C_REG,	C_NONE,	C_ADDR,		67, 24, 0,	LTO },
+
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		34, 8, REGSB,	LFROM },
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		34, 8, REGSP,	LFROM },
+
+	{ AMOVW,	C_PSR,	C_NONE,	C_REG,		35, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_PSR,		36, 4, 0 },
+	{ AMOVW,	C_RCON,	C_NONE,	C_PSR,		37, 4, 0 },
+
+	{ AMOVM,	C_LCON,	C_NONE,	C_SOREG,	38, 4, 0 },
+	{ AMOVM,	C_SOREG,C_NONE,	C_LCON,		39, 4, 0 },
+
+	{ ASWPW,	C_SOREG,C_REG,	C_REG,		40, 4, 0 },
+
+	{ ARFE,		C_NONE,	C_NONE,	C_NONE,		41, 4, 0 },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_FEXT,		50, 4, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FAUTO,	50, 4, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FOREG,	50, 4, 0 },
+
+	{ AMOVF,	C_FEXT,	C_NONE,	C_FREG,		51, 4, REGSB },
+	{ AMOVF,	C_FAUTO,C_NONE,	C_FREG,		51, 4, REGSP },
+	{ AMOVF,	C_FOREG,C_NONE,	C_FREG,		51, 4, 0 },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_LEXT,		52, 12, REGSB,	LTO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LAUTO,	52, 12, REGSP,	LTO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LOREG,	52, 12, 0,	LTO },
+
+	{ AMOVF,	C_LEXT,	C_NONE,	C_FREG,		53, 12, REGSB,	LFROM },
+	{ AMOVF,	C_LAUTO,C_NONE,	C_FREG,		53, 12, REGSP,	LFROM },
+	{ AMOVF,	C_LOREG,C_NONE,	C_FREG,		53, 12, 0,	LFROM },
+
+	{ AMOVF,	C_FREG,	C_NONE,	C_ADDR,		68, 8, 0,	LTO },
+	{ AMOVF,	C_ADDR,	C_NONE,	C_FREG,		69, 8, 0,	LFROM },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FCON,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AADDF,	C_FCON,	C_REG,	C_FREG,		54, 4, 0 },
+	{ AMOVF,	C_FCON,	C_NONE,	C_FREG,		54, 4, 0 },
+	{ AMOVF,	C_FREG, C_NONE, C_FREG,		54, 4, 0 },
+
+	{ ACMPF,	C_FREG,	C_REG,	C_NONE,		54, 4, 0 },
+	{ ACMPF,	C_FCON,	C_REG,	C_NONE,		54, 4, 0 },
+
+	{ AMOVFW,	C_FREG,	C_NONE,	C_REG,		55, 4, 0 },
+	{ AMOVFW,	C_REG,	C_NONE,	C_FREG,		55, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCR,		56, 4, 0 },
+	{ AMOVW,	C_FCR,	C_NONE,	C_REG,		57, 4, 0 },
+
+	{ AMOVW,	C_SHIFT,C_NONE,	C_REG,		59, 4, 0 },
+	{ AMOVBU,	C_SHIFT,C_NONE,	C_REG,		59, 4, 0 },
+
+	{ AMOVB,	C_SHIFT,C_NONE,	C_REG,		60, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SHIFT,	61, 4, 0 },
+
+	{ ACASE,	C_REG,	C_NONE,	C_NONE,		62, 4, 0 },
+	{ ABCASE,	C_NONE, C_NONE, C_SBRA,		63, 4, 0 },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_HEXT,		70, 4, REGSB,	V4 },
+	{ AMOVH,	C_REG,	C_NONE, C_HAUTO,	70, 4, REGSP,	V4 },
+	{ AMOVH,	C_REG,	C_NONE,	C_HOREG,	70, 4, 0,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_HEXT,		70, 4, REGSB,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE, C_HAUTO,	70, 4, REGSP,	V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_HOREG,	70, 4, 0,	V4 },
+
+	{ AMOVB,	C_HEXT,	C_NONE, C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVB,	C_HAUTO,C_NONE,	C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVB,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+	{ AMOVH,	C_HEXT,	C_NONE,	C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVH,	C_HAUTO,C_NONE, C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVH,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+	{ AMOVHU,	C_HEXT,	C_NONE,	C_REG,		71, 4, REGSB,	V4 },
+	{ AMOVHU,	C_HAUTO,C_NONE, C_REG,		71, 4, REGSP,	V4 },
+	{ AMOVHU,	C_HOREG,C_NONE,	C_REG,		71, 4, 0,	V4 },
+
+	{ AMOVH,	C_REG,	C_NONE,	C_LEXT,		72, 8, REGSB,	LTO|V4 },
+	{ AMOVH,	C_REG,	C_NONE, C_LAUTO,	72, 8, REGSP,	LTO|V4 },
+	{ AMOVH,	C_REG,	C_NONE,	C_LOREG,	72, 8, 0,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LEXT,		72, 8, REGSB,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE, C_LAUTO,	72, 8, REGSP,	LTO|V4 },
+	{ AMOVHU,	C_REG,	C_NONE,	C_LOREG,	72, 8, 0,	LTO|V4 },
+
+	{ AMOVB,	C_LEXT,	C_NONE, C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+	{ AMOVH,	C_LEXT,	C_NONE,	C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVH,	C_LAUTO,C_NONE, C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVH,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+	{ AMOVHU,	C_LEXT,	C_NONE,	C_REG,		73, 8, REGSB,	LFROM|V4 },
+	{ AMOVHU,	C_LAUTO,C_NONE, C_REG,		73, 8, REGSP,	LFROM|V4 },
+	{ AMOVHU,	C_LOREG,C_NONE,	C_REG,		73, 8, 0,	LFROM|V4 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/tl/pass.c
@@ -1,0 +1,942 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p;
+	long orig, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+		if((s->type == SBSS || s->type == SDATA) && (p->to.type == D_CONST || p->to.type == D_OCONST) && (p->to.name == D_EXTERN || p->to.name == D_STATIC)){
+			s = p->to.sym;
+			if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF))
+				s->fnptr = 1;
+		}
+	}
+
+	if(debug['t']) {
+		/*
+		 * pull out string constants
+		 */
+		for(p = datap; p != P; p = p->link) {
+			s = p->from.sym;
+			if(p->to.type == D_SCONST)
+				s->type = SSTRING;
+		}
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on R12)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 3)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	/*
+	 * pass 2
+	 *	assign large 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		s->value = orig;
+		orig += v;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	xdefine("setR12", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+Prog*
+brchain(Prog *p)
+{
+	int i;
+
+	for(i=0; i<20; i++) {
+		if(p == P || p->as != AB)
+			return p;
+		p = p->cond;
+	}
+	return P;
+}
+
+int
+relinv(int a)
+{
+	switch(a) {
+	case ABEQ:	return ABNE;
+	case ABNE:	return ABEQ;
+	case ABCS:	return ABCC;
+	case ABHS:	return ABLO;
+	case ABCC:	return ABCS;
+	case ABLO:	return ABHS;
+	case ABMI:	return ABPL;
+	case ABPL:	return ABMI;
+	case ABVS:	return ABVC;
+	case ABVC:	return ABVS;
+	case ABHI:	return ABLS;
+	case ABLS:	return ABHI;
+	case ABGE:	return ABLT;
+	case ABLT:	return ABGE;
+	case ABGT:	return ABLE;
+	case ABLE:	return ABGT;
+	}
+	diag("unknown relation: %s", anames[a]);
+	return a;
+}
+
+void
+follow(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, i;
+
+loop:
+	if(p == P)
+		return;
+	setarch(p);
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AB) {
+		q = p->cond;
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp)
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			if(a != ABEQ && a != ABNE)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AB || (a == ARET && q->scond == 14) || a == ARFE)
+					return;
+				r->as = ABNE;
+				if(a == ABNE)
+					r->as = ABEQ;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+		a = AB;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){
+		return;
+	}
+	if(p->cond != P)
+	if(a != ABL && a != ABX && p->link != P) {
+		q = brchain(p->link);
+		if(a != ATEXT && a != ABCASE)
+		if(q != P && (q->mark&FOLL)) {
+			p->as = relinv(a);
+			p->link = p->cond;
+			p->cond = q;
+		}
+		xfol(p->link);
+		q = brchain(p->cond);
+		if(q == P)
+			q = p->cond;
+		if(q->mark&FOLL) {
+			p->cond = q;
+			return;
+		}
+		p = q;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s, *s1;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if(seenthumb && a == ABL){
+			// if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S)
+			//	print("%s calls %s\n", s1->name, s->name);
+			 if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S && s->thumb != s1->thumb)
+				s->foreign = 1;
+		}
+		if((a == ABL || a == ABX || a == AB || a == ARET) &&
+		   p->to.type != D_BRANCH && p->to.sym != S) {
+			s = p->to.sym;
+			switch(s->type) {
+			default:
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+				break;
+			case STEXT:
+				p->to.offset = s->value;
+				p->to.type = D_BRANCH;
+				break;
+			case SUNDEF:
+				if(p->as != ABL)
+					diag("help: SUNDEF in AB || ARET");
+				p->to.offset = 0;
+				p->to.type = D_BRANCH;
+				p->cond = UP;
+				break;
+			}
+		}
+		if(p->to.type != D_BRANCH || p->cond == UP)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		setarch(p);
+		a = p->as;
+		if(p->as == ATEXT)
+			curtext = p;
+		if(seenthumb && a == ABL) {
+#ifdef CALLEEBX
+			if(0)
+				{}
+#else
+			if((s = p->to.sym) != S && (s->foreign || s->fnptr))
+				p->as = ABX;
+#endif
+			else if(p->to.type == D_OREG)
+				p->as = ABX;
+		}
+		if(p->cond != P && p->cond != UP) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AB)
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
+
+#define Reachable(n)	if((s = lookup(n, 0)) != nil) s->used++
+
+static void
+rused(Adr *a)
+{
+	Sym *s = a->sym;
+
+	if(s == S)
+		return;
+	if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){
+		if(a->name == D_EXTERN || a->name == D_STATIC){
+			if(s->used == 0)
+				s->used = 1;
+		}
+	}
+	else if(a->type == D_BRANCH){
+		if(s->used == 0)
+			s->used = 1;
+	}
+}
+
+void
+reachable()
+{
+	Prog *p, *prev, *prevt, *nextt, *q;
+	Sym *s, *s0;
+	int i, todo;
+	char *a;
+
+	Reachable("_div");
+	Reachable("_divu");
+	Reachable("_mod");
+	Reachable("_modu");
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return;
+	s = lookup(a, 0);
+	if(s == nil)
+		return;
+	if(s->type == 0){
+		s->used = 1;	// to stop asm complaining
+		for(p = firstp; p != P && p->as != ATEXT; p = p->link)
+			;
+		if(p == nil)
+			return;
+		s = p->from.sym;
+	}	
+	s->used = 1;
+	do{
+		todo = 0;
+		for(p = firstp; p != P; p = p->link){
+			if(p->as == ATEXT && (s0 = p->from.sym)->used == 1){
+				todo = 1;
+				for(q = p->link; q != P && q->as != ATEXT; q = q->link){
+					rused(&q->from);
+					rused(&q->to);
+				}
+				s0->used = 2;
+			}
+		}
+		for(p = datap; p != P; p = p->link){
+			if((s0 = p->from.sym)->used == 1){
+				todo = 1;
+				for(q = p; q != P; q = q->link){	// data can be scattered
+					if(q->from.sym == s0)
+						rused(&q->to);
+				}
+				s0->used = 2;
+			}
+		}
+	}while(todo);
+	prev = nil;
+	prevt = nextt = nil;
+	for(p = firstp; p != P; ){
+		if(p->as == ATEXT){
+			prevt = nextt;
+			nextt = p;
+		}
+		if(p->as == ATEXT && (s0 = p->from.sym)->used == 0){
+			s0->type = SREMOVED;
+			for(q = p->link; q != P && q->as != ATEXT; q = q->link)
+				;
+			if(q != p->cond)
+				diag("bad ptr in reachable()");
+			if(prev == nil)
+				firstp = q;
+			else
+				prev->link = q;
+			if(q == nil)
+				lastp = prev;
+			if(prevt == nil)
+				textp = q;
+			else
+				prevt->cond = q;
+			if(q == nil)
+				etextp = prevt;
+			nextt = prevt;
+			if(debug['V'])
+				print("%s unused\n", s0->name);
+			p = q;
+		}
+		else{
+			prev = p;
+			p = p->link;
+		}
+	}
+	prevt = nil;
+	for(p = datap; p != nil; ){
+		if((s0 = p->from.sym)->used == 0){
+			s0->type = SREMOVED;
+			prev = prevt;
+			for(q = p; q != nil; q = q->link){
+				if(q->from.sym == s0){
+					if(prev == nil)
+						datap = q->link;
+					else
+						prev->link = q->link;
+				}
+				else
+					prev = q;
+			}
+			if(debug['V'])
+				print("%s unused (data)\n", s0->name);
+			p = prevt->link;
+		}
+		else{
+			prevt = p;
+			p = p->link;
+		}
+	}
+	for(i=0; i<NHASH; i++){
+		for(s = hash[i]; s != S; s = s->link){
+			if(s->used == 0)
+				s->type = SREMOVED;
+		}
+	}
+}
+
+static void
+fused(Adr *a, Prog *p, Prog *ct)
+{
+	Sym *s = a->sym;
+	Use *u;
+
+	if(s == S)
+		return;
+	if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){
+		if(a->name == D_EXTERN || a->name == D_STATIC){
+			u = malloc(sizeof(Use));
+			u->p = p;
+			u->ct = ct;
+			u->link = s->use;
+			s->use = u;
+		}
+	}
+	else if(a->type == D_BRANCH){
+		u = malloc(sizeof(Use));
+		u->p = p;
+		u->ct = ct;
+		u->link = s->use;
+		s->use = u;
+	}
+}
+
+static int
+ckfpuse(Prog *p, Prog *ct, Sym *fp, Sym *r)
+{
+	int reg;
+
+	USED(fp);
+	USED(ct);
+	if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){
+		reg = p->to.reg;
+		for(p = p->link; p != P && p->as != ATEXT; p = p->link){
+			if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg)
+				return 1;
+			if(!debug['F'] && (isbranch(p) || p->as == ARET)){
+				// print("%s: branch %P in %s\n", fp->name, p, ct->from.sym->name);
+				return 0;
+			}
+			if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){
+				if(!debug['F'] && p->to.type != D_REG){
+					// print("%s: store %P in %s\n", fp->name, p, ct->from.sym->name);
+					return 0;
+				}
+				reg = p->to.reg;
+			}
+		}
+	}
+	// print("%s: no MOVW O(R), R\n", fp->name);
+	return debug['F'];
+}
+
+static void
+setfpuse(Prog *p, Sym *fp, Sym *r)
+{
+	int reg;
+
+	if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){
+		reg = p->to.reg;
+		for(p = p->link; p != P && p->as != ATEXT; p = p->link){
+			if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg){
+				fp->fnptr = 0;
+				p->as = ABL;	// safe to do so
+// print("simplified %s call\n", fp->name);
+				break;
+			}
+			if(!debug['F'] && (isbranch(p) || p->as == ARET))
+				diag("bad setfpuse call");
+			if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){
+				if(!debug['F'] && p->to.type != D_REG)
+					diag("bad setfpuse call");
+				reg = p->to.reg;
+			}
+		}
+	}
+}
+
+static int
+cksymuse(Sym *s, int t)
+{
+	Prog *p;
+
+	for(p = datap; p != P; p = p->link){
+		if(p->from.sym == s && p->to.sym != nil && strcmp(p->to.sym->name, ".string") != 0 && p->to.sym->thumb != t){
+			// print("%s %s %d %d ", p->from.sym->name, p->to.sym->name, p->to.sym->thumb, t);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* check the use of s at the given point */
+static int
+ckuse(Sym *s, Sym *s0, Use *u)
+{
+	Sym *s1;
+
+	s1 = u->p->from.sym;
+// print("ckuse %s %s %s\n", s->name, s0->name, s1 ? s1->name : "nil");
+	if(u->ct == nil){	/* in data area */
+		if(s0 == s && !cksymuse(s1, s0->thumb)){
+			// print("%s: cksymuse fails\n", s0->name);
+			return 0;
+		}
+		for(u = s1->use; u != U; u = u->link)
+			if(!ckuse(s1, s0, u))
+				return 0;
+	}
+	else{		/* in text area */
+		if(u->ct->from.sym->thumb != s0->thumb){
+			// print("%s(%d): foreign call %s(%d)\n", s0->name, s0->thumb, u->ct->from.sym->name, u->ct->from.sym->thumb);
+			return 0;
+		}
+		return ckfpuse(u->p, u->ct, s0, s);
+	}
+	return 1;
+}
+		
+static void
+setuse(Sym *s, Sym *s0, Use *u)
+{
+	Sym *s1;
+
+	s1 = u->p->from.sym;
+	if(u->ct == nil){	/* in data area */
+		for(u = s1->use; u != U; u = u->link)
+			setuse(s1, s0, u);
+	}
+	else{		/* in text area */
+		setfpuse(u->p, s0, s);
+	}
+}
+		
+/* detect BX O(R) which can be done as BL O(R) */
+void
+fnptrs()
+{
+	int i;
+	Sym *s;
+	Prog *p;
+	Use *u;
+	
+	for(i=0; i<NHASH; i++){
+		for(s = hash[i]; s != S; s = s->link){
+			if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){
+				// print("%s : fnptr %d %d\n", s->name, s->thumb, s->foreign);
+			}
+		}
+	}	
+	/* record use of syms */
+	for(p = firstp; p != P; p = p->link){
+		if(p->as == ATEXT)
+			curtext = p;
+		else{
+			fused(&p->from, p, curtext);
+			fused(&p->to, p, curtext);
+		}
+	}
+	for(p = datap; p != P; p = p->link)
+		fused(&p->to, p, nil);
+
+	/* now look for fn ptrs */
+	for(i=0; i<NHASH; i++){
+		for(s = hash[i]; s != S; s = s->link){
+			if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){
+				for(u = s->use; u != U; u = u->link){
+					if(!ckuse(s, s, u))
+						break;
+				}
+				if(u == U){		// can simplify
+					for(u = s->use; u != U; u = u->link)
+						setuse(s, s, u);
+				}
+			}	
+		}
+	}
+
+	/*  now free Use structures */
+}
+
+void
+import(void)
+{
+	int i;
+	Sym *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){
+				undefsym(s);
+				Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value);
+			}
+}
+
+void
+ckoff(Sym *s, long v)
+{
+	if(v < 0 || v >= 1<<Roffset)
+		diag("relocation offset %ld for %s out of range", v, s->name);
+}
+
+static Prog*
+newdata(Sym *s, int o, int w, int t)
+{
+	Prog *p;
+
+	p = prg();
+	p->link = datap;
+	datap = p;
+	p->as = ADATA;
+	p->reg = w;
+	p->from.type = D_OREG;
+	p->from.name = t;
+	p->from.sym = s;
+	p->from.offset = o;
+	p->to.type = D_CONST;
+	p->to.name = D_NONE;
+	return p;
+}
+
+void
+export(void)
+{
+	int i, j, n, off, nb, sv, ne;
+	Sym *s, *et, *str, **esyms;
+	Prog *p;
+	char buf[NSNAME], *t;
+
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				n++;
+	esyms = malloc(n*sizeof(Sym*));
+	ne = n;
+	n = 0;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT))
+				esyms[n++] = s;
+	for(i = 0; i < ne-1; i++)
+		for(j = i+1; j < ne; j++)
+			if(strcmp(esyms[i]->name, esyms[j]->name) > 0){
+				s = esyms[i];
+				esyms[i] = esyms[j];
+				esyms[j] = s;
+			}
+
+	nb = 0;
+	off = 0;
+	et = lookup(EXPTAB, 0);
+	if(et->type != 0 && et->type != SXREF)
+		diag("%s already defined", EXPTAB);
+	et->type = SDATA;
+	str = lookup(".string", 0);
+	if(str->type == 0)
+		str->type = SDATA;
+	sv = str->value;
+	for(i = 0; i < ne; i++){
+		s = esyms[i];
+		Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type);
+
+		/* signature */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.offset = s->sig;
+
+		/* address */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_EXTERN;
+		p->to.sym = s;
+
+		/* string */
+		t = s->name;
+		n = strlen(t)+1;
+		for(;;){
+			buf[nb++] = *t;
+			sv++;
+			if(nb >= NSNAME){
+				p = newdata(str, sv-NSNAME, NSNAME, D_STATIC);
+				p->to.type = D_SCONST;
+				p->to.sval = malloc(NSNAME);
+				memmove(p->to.sval, buf, NSNAME);
+				nb = 0;
+			}
+			if(*t++ == 0)
+				break;
+		}
+
+		/* name */
+		p = newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+		p->to.name = D_STATIC;
+		p->to.sym = str;
+		p->to.offset = sv-n;
+	}
+
+	if(nb > 0){
+		p = newdata(str, sv-nb, nb, D_STATIC);
+		p->to.type = D_SCONST;
+		p->to.sval = malloc(NSNAME);
+		memmove(p->to.sval, buf, nb);
+	}
+
+	for(i = 0; i < 3; i++){
+		newdata(et, off, sizeof(long), D_EXTERN);
+		off += sizeof(long);
+	}
+	et->value = off;
+	if(sv == 0)
+		sv = 1;
+	str->value = sv;
+	exports = ne;
+	free(esyms);
+}
--- /dev/null
+++ b/utils/tl/span.c
@@ -1,0 +1,1262 @@
+#include	"l.h"
+
+static struct {
+	ulong	start;
+	ulong	size;
+	ulong	extra;
+} pool;
+
+int	checkpool(Prog*, int);
+int 	flushpool(Prog*, int, int);
+
+int
+isbranch(Prog *p)
+{
+	int as = p->as;
+	return (as >= ABEQ && as <= ABLE) || as == AB || as == ABL || as == ABX;
+}
+
+static int
+ispad(Prog *p)
+{
+	if(p->as != AMOVW)
+		return 0;
+	if(p->from.type != D_REG || p->from.reg != REGSB)
+		return 0;
+	if(p->to.type != D_REG || p->to.reg != REGSB)
+		return 0;
+	return 1;
+}
+
+int
+fninc(Sym *s)
+{
+	if(thumb){
+		if(s->thumb){
+			if(s->foreign)
+				return 8;
+			else
+				return 0;
+		}
+		else{
+			if(s->foreign)
+				return 0;
+			else
+				diag("T A !foreign in fninc");
+		}
+	}
+	else{
+		if(s->thumb){
+			if(s->foreign)
+				return 0;
+			else
+				diag("A T !foreign in fninc");
+		}
+		else{
+			if(s->foreign)
+				return 4;
+			else
+				return 0;
+		}			
+	}
+	return 0;
+}
+
+int 
+fnpinc(Sym *s)
+{
+	if(!s->fnptr){	// a simplified case BX O(R) -> BL O(R)
+		if(!debug['f'])
+			diag("fnptr == 0 in fnpinc");
+		if(s->foreign)
+			diag("bad usage in fnpinc %s %d %d %d", s->name, s->used, s->foreign, s->thumb);
+		return 0;
+	}
+	/* 0, 1, 2, 3 squared */
+	if(s->thumb)
+		return s->foreign ? 9 : 1;
+	else
+		return s->foreign ? 4 : 0;
+}
+
+static Prog *
+pad(Prog *p, int pc)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = AMOVW;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGSB;
+	q->to.type = D_REG;
+	q->to.reg = REGSB;
+	q->pc = pc;
+	q->link = p->link;
+	return q;
+}
+
+static int
+scan(Prog *op, Prog *p, int c)
+{
+	Prog *q;
+
+	for(q = op->link; q != p; q = q->link){
+		q->pc = c;
+		c += oplook(q)->size;
+		nocache(q);
+	}
+	return c;
+}
+
+/* size of a case statement including jump table */
+static long
+casesz(Prog *p)
+{
+	int jt = 0;
+	long n = 0;
+	Optab *o;
+
+	for( ; p != P; p = p->link){
+		if(p->as == ABCASE)
+			jt = 1;
+		else if(jt)
+			break;
+		o = oplook(p);
+		n += o->size;
+	}
+	return n;
+}
+
+void
+span(void)
+{
+	Prog *p, *op;
+	Sym *setext, *s;
+	Optab *o;
+	int m, bflag, i;
+	long c, otxt, v;
+	int lastthumb = -1;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	op = nil;
+	otxt = c;
+	for(p = firstp; p != P; op = p, p = p->link) {
+		setarch(p);
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		// must check literal pool here in case p generates many instructions
+		if(blitrl){
+			if(thumb && isbranch(p))
+				pool.extra += brextra(p);
+			if(checkpool(op, p->as == ACASE ? casesz(p) : m))
+				c = p->pc = scan(op, p, c);
+		}
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				if(blitrl && lastthumb != -1 && lastthumb != thumb){	// flush literal pool
+					if(flushpool(op, 0, 1))
+						c = p->pc = scan(op, p, c);
+				}
+				lastthumb = thumb;
+				curtext = p;
+				autosize = p->to.offset + 4;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches */
+				if(c-otxt >= 1L<<17)
+					bflag = 1;
+				otxt = c;
+				if(thumb && blitrl)
+					pool.extra += brextra(p);
+				continue;
+			}
+			diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		switch(o->flag & (LFROM|LTO|LPOOL)) {
+		case LFROM:
+			addpool(p, &p->from);
+			break;
+		case LTO:
+			addpool(p, &p->to);
+			break;
+		case LPOOL:
+			if ((p->scond&C_SCOND) == 14)
+				flushpool(p, 0, 0);
+			break;
+		}
+		if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14)
+			flushpool(p, 0, 0);
+		c += m;
+		if(blitrl && p->link == P){
+			if(thumb && isbranch(p))
+				pool.extra += brextra(p);
+			checkpool(p, 0);
+		}
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			setarch(p);
+			p->pc = c;
+			if(thumb && isbranch(p))
+				nocache(p);
+			o = oplook(p);
+/* very larg branches
+			if(o->type == 6 && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < 0)
+					otxt = -otxt;
+				if(otxt >= (1L<<17) - 10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AB;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AB;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					bflag = 1;
+				}
+			}
+ */
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 4;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				diag("zero-width instruction\n%P", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+
+	if(seenthumb){		// branch resolution
+		int passes = 0;
+		int lastc = 0;
+		int again;
+		Prog *oop;
+
+	loop:
+		passes++;
+		if(passes > 150){
+			diag("span looping !");
+			errorexit();
+		}
+		c = INITTEXT;
+		oop = op = nil;
+		again = 0;
+		for(p = firstp; p != P; oop = op, op = p, p = p->link){
+			setarch(p);
+			if(p->pc != c)
+				again = 1;
+			p->pc = c;
+			if(thumb && isbranch(p))
+				nocache(p);
+			o = oplook(p);
+			m = o->size;
+			if(passes == 1 && thumb && isbranch(p)){	// start conservative so unneeded alignment is not added
+				if(p->as == ABL)
+					m = 4;
+				else
+					m = 2;
+				p->align = 0;
+			}	
+			if(p->align){
+				if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){
+					if(ispad(op)){
+						oop->link = p;
+						op = oop;
+						c -= 2;
+						p->pc = c;
+					}
+					else{
+						op->link = pad(op, c);
+						op = op->link;
+						c += 2;
+						p->pc = c;
+					}
+					again = 1;
+				}
+			}
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 4;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+			}
+			c += m;
+		}
+		if(c != lastc || again){
+			lastc = c;
+			goto loop;
+		}
+	}
+
+	if(0 && seenthumb){		// rm redundant padding - obsolete
+		int d;
+
+		op = nil;
+		d = 0;
+		for(p = firstp; p != P; op = p, p = p->link){
+			p->pc -= d;
+			if(p->as == ATEXT){
+				if(p->from.sym != S)
+					p->from.sym->value -= d;
+// if(p->from.sym != S) print("%s %ux %d %d %d\n", p->from.sym->name ? p->from.sym->name : "?", p->from.sym->value, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr);
+			}
+			if(ispad(p) && p->link != P && ispad(p->link)){
+				op->link = p->link->link;
+				d += 4;
+				p = op;
+			}
+		}
+		// print("%d bytes removed (padding)\n", d);
+		c -= d;
+	}
+	
+	if(debug['t']) {
+		/* 
+		 * add strings to text segment
+		 */
+		c = rnd(c, 8);
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SSTRING)
+				continue;
+			v = s->value;
+			while(v & 3)
+				v++;
+			s->value = c;
+			c += v;
+		}
+	}
+			
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+
+/*
+ * when the first reference to the literal pool threatens
+ * to go out of range of a 12-bit PC-relative offset,
+ * drop the pool now, and branch round it.
+ * this happens only in extended basic blocks that exceed 4k.
+ */
+int
+checkpool(Prog *p, int sz)
+{
+	if(thumb){
+		if(pool.size >= 0x3fc || (p->pc+sz+pool.extra+2+2)+(pool.size-4)-pool.start-4 >= 0x3fc)
+			return flushpool(p, 1, 0);
+		else if(p->link == P)
+			return flushpool(p, 2, 0);
+		return 0;
+	}
+	if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0)
+		return flushpool(p, 1, 0);
+	else if(p->link == P)
+		return flushpool(p, 2, 0);
+	return 0;
+}
+
+int
+flushpool(Prog *p, int skip, int force)
+{
+	Prog *q;
+
+	if(blitrl) {
+		if(skip){
+			if(0 && skip==1)print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);
+			q = prg();
+			q->as = AB;
+			q->to.type = D_BRANCH;
+			q->cond = p->link;
+			q->link = blitrl;
+			blitrl = q;
+		}
+		else if(!force && (p->pc+pool.size-pool.start < (thumb ? 0x3fc+4-pool.extra : 2048)))
+			return 0;
+		elitrl->link = p->link;
+		p->link = blitrl;
+		blitrl = 0;	/* BUG: should refer back to values until out-of-range */
+		elitrl = 0;
+		pool.size = 0;
+		pool.start = 0;
+		pool.extra = 0;
+		return 1;
+	}
+	return 0;
+}
+
+void
+addpool(Prog *p, Adr *a)
+{
+	Prog *q, t;
+	int c;
+
+	if(thumb)
+		c = thumbaclass(a, p);
+	else
+		c = aclass(a);
+
+	t = zprg;
+	t.as = AWORD;
+
+	switch(c) {
+	default:
+		t.to = *a;
+		break;
+
+	case	C_SROREG:
+	case C_LOREG:
+	case C_ROREG:
+	case C_FOREG:
+	case C_SOREG:
+	case C_HOREG:
+	case C_GOREG:
+	case C_FAUTO:
+	case C_SAUTO:
+	case C_LAUTO:
+	case C_LACON:
+	case C_GACON:
+		t.to.type = D_CONST;
+		t.to.offset = instoffset;
+		break;
+	}
+
+	for(q = blitrl; q != P; q = q->link)	/* could hash on t.t0.offset */
+		if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {
+			p->cond = q;
+			return;
+		}
+
+	q = prg();
+	*q = t;
+	q->pc = pool.size;
+
+	if(blitrl == P) {
+		blitrl = q;
+		pool.start = p->pc;
+		q->align = 4;
+	} else
+		elitrl->link = q;
+	elitrl = q;
+	pool.size += 4;
+
+	p->cond = q;
+}
+
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+long
+immrot(ulong v)
+{
+	int i;
+
+	for(i=0; i<16; i++) {
+		if((v & ~0xff) == 0)
+			return (i<<8) | v | (1<<25);
+		v = (v<<2) | (v>>30);
+	}
+	return 0;
+}
+
+long
+immaddr(long v)
+{
+	if(v >= 0 && v <= 0xfff)
+		return (v & 0xfff) |
+			(1<<24) |	/* pre indexing */
+			(1<<23);	/* pre indexing, up */
+	if(v >= -0xfff && v < 0)
+		return (-v & 0xfff) |
+			(1<<24);	/* pre indexing */
+	return 0;
+}
+
+int
+immfloat(long v)
+{
+	return (v & 0xC03) == 0;	/* offset will fit in floating-point load/store */
+}
+
+int
+immhalf(long v)
+{
+	if(v >= 0 && v <= 0xff)
+		return v|
+			(1<<24)|	/* pre indexing */
+			(1<<23);	/* pre indexing, up */
+	if(v >= -0xff && v < 0)
+		return (-v & 0xff)|
+			(1<<24);	/* pre indexing */
+	return 0;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_REGREG:
+		return C_REGREG;
+
+	case D_SHIFT:
+		return C_SHIFT;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_FPCR:
+		return C_FCR;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			if(dlm) {
+				switch(t) {
+				default:
+					instoffset = s->value + a->offset + INITDAT;
+					break;
+				case SUNDEF:
+				case STEXT:
+				case SCONST:
+				case SLEAF:
+				case SSTRING:
+					instoffset = s->value + a->offset;
+					break;
+				}
+				return C_ADDR;
+			}
+			instoffset = s->value + a->offset - BIG;
+			t = immaddr(instoffset);
+			if(t) {
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFEXT : C_HEXT;
+				if(immfloat(t))
+					return C_FEXT;
+				return C_SEXT;
+			}
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			t = immaddr(instoffset);
+			if(t){
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFAUTO : C_HAUTO;
+				if(immfloat(t))
+					return C_FAUTO;
+				return C_SAUTO;
+			}
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			t = immaddr(instoffset);
+			if(t){
+				if(immhalf(instoffset))
+					return immfloat(t) ? C_HFAUTO : C_HAUTO;
+				if(immfloat(t))
+					return C_FAUTO;
+				return C_SAUTO;
+			}
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			t = immaddr(instoffset);
+			if(t) {
+				if(immhalf(instoffset))		 /* n.b. that it will also satisfy immrot */
+					return immfloat(t) ? C_HFOREG : C_HOREG;
+				if(immfloat(t))
+					return C_FOREG; /* n.b. that it will also satisfy immrot */
+				t = immrot(instoffset);
+				if(t)
+					return C_SROREG;
+				if(immhalf(instoffset))
+					return C_HOREG;
+				return C_SOREG;
+			}
+			t = immrot(instoffset);
+			if(t)
+				return C_ROREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_PSR:
+		return C_PSR;
+
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF) {
+				instoffset = s->value + a->offset;
+#ifdef CALLEEBX
+				instoffset += fnpinc(s);
+#else
+				if(s->thumb)
+					instoffset++;	// T bit
+#endif
+				return C_LCON;
+			}
+			return C_LCON;
+		}
+		return C_GOK;
+
+	case D_FCONST:
+		return C_FCON;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+			if(a->reg != NREG)
+				goto aconsize;
+
+			t = immrot(instoffset);
+			if(t)
+				return C_RCON;
+			t = immrot(~instoffset);
+			if(t)
+				return C_NCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SUNDEF:
+			case STEXT:
+			case SSTRING:
+			case SCONST:
+			case SLEAF:
+				instoffset = s->value + a->offset;
+#ifdef CALLEEBX
+				instoffset += fnpinc(s);
+#else
+				if(s->thumb)
+					instoffset++;	// T bit
+#endif
+				return C_LCON;
+			}
+			if(!dlm) {
+				instoffset = s->value + a->offset - BIG;
+				t = immrot(instoffset);
+				if(t && instoffset != 0)
+					return C_RECON;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			goto aconsize;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+		aconsize:
+			t = immrot(instoffset);
+			if(t)
+				return C_RACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r;
+	char *c1, *c3;
+	Optab *o, *e;
+	Optab *otab;
+	Oprang *orange;
+
+	if(thumb){
+		otab = thumboptab;
+		orange = thumboprange;
+	}
+	else{
+		otab = optab;
+		orange = oprange;
+	}
+	a1 = p->optab;
+	if(a1)
+		return otab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		if(thumb)
+			a1 = thumbaclass(&p->from, p) + 1;
+		else
+			a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		if(thumb)
+			a3 = thumbaclass(&p->to, p) + 1;
+		else
+			a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = orange[r].start;
+	if(o == 0) {
+		a1 = opcross[repop[r]][a1][a2][a3];
+		if(a1) {
+			p->optab = a1+1;
+			return otab+a1;
+		}
+		o = orange[r].stop; /* just generate an error */
+	}
+	if(0) {
+		print("oplook %A %d %d %d\n",
+			(int)p->as, a1, a2, a3);
+		print("		%d %d\n", p->from.type, p->to.type);
+	}
+	e = orange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-otab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, a1, a2, a3);
+	prasm(p);
+	if(o == 0)
+		o = otab;
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_RCON || b == C_NCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_RACON)
+			return 1;
+		break;
+	case C_LECON:
+		if(b == C_RECON)
+			return 1;
+		break;
+
+	case C_HFEXT:
+		return b == C_HEXT || b == C_FEXT;
+	case C_FEXT:
+	case C_HEXT:
+		return b == C_HFEXT;
+	case C_SEXT:
+		return cmp(C_HFEXT, b);
+	case C_LEXT:
+		return cmp(C_SEXT, b);
+
+	case C_HFAUTO:
+		return b == C_HAUTO || b == C_FAUTO;
+	case C_FAUTO:
+	case C_HAUTO:
+		return b == C_HFAUTO;
+	case C_SAUTO:
+		return cmp(C_HFAUTO, b);
+	case C_LAUTO:
+		return cmp(C_SAUTO, b);
+
+	case C_HFOREG:
+		return b == C_HOREG || b == C_FOREG;
+	case C_FOREG:
+	case C_HOREG:
+		return b == C_HFOREG;
+	case C_SROREG:
+		return cmp(C_SOREG, b) || cmp(C_ROREG, b);
+	case C_SOREG:
+	case C_ROREG:
+		return b == C_SROREG || cmp(C_HFOREG, b);
+	case C_LOREG:
+		return cmp(C_SROREG, b);
+
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_GBRA:
+		if(b == C_SBRA || b == C_LBRA)
+			return 1;
+
+	case C_HREG:
+		return cmp(C_SP, b) || cmp(C_PC, b);
+
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = (p2->flag&V4) - (p1->flag&V4);	/* architecture version */
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	armv4 = !debug['h'];
+	for(i=0; i<C_GOK; i++)
+		for(n=0; n<C_GOK; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		if((optab[n].flag & V4) && !armv4) {
+			optab[n].as = AXXX;
+			break;
+		}
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case AADD:
+			oprange[AAND] = oprange[r];
+			oprange[AEOR] = oprange[r];
+			oprange[ASUB] = oprange[r];
+			oprange[ARSB] = oprange[r];
+			oprange[AADC] = oprange[r];
+			oprange[ASBC] = oprange[r];
+			oprange[ARSC] = oprange[r];
+			oprange[AORR] = oprange[r];
+			oprange[ABIC] = oprange[r];
+			break;
+		case ACMP:
+			oprange[ATST] = oprange[r];
+			oprange[ATEQ] = oprange[r];
+			oprange[ACMN] = oprange[r];
+			break;
+		case AMVN:
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			oprange[ABCS] = oprange[r];
+			oprange[ABHS] = oprange[r];
+			oprange[ABCC] = oprange[r];
+			oprange[ABLO] = oprange[r];
+			oprange[ABMI] = oprange[r];
+			oprange[ABPL] = oprange[r];
+			oprange[ABVS] = oprange[r];
+			oprange[ABVC] = oprange[r];
+			oprange[ABHI] = oprange[r];
+			oprange[ABLS] = oprange[r];
+			oprange[ABGE] = oprange[r];
+			oprange[ABLT] = oprange[r];
+			oprange[ABGT] = oprange[r];
+			oprange[ABLE] = oprange[r];
+			break;
+		case ASLL:
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			break;
+		case AMUL:
+			oprange[AMULU] = oprange[r];
+			break;
+		case ADIV:
+			oprange[AMOD] = oprange[r];
+			oprange[AMODU] = oprange[r];
+			oprange[ADIVU] = oprange[r];
+			break;
+		case AMOVW:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+			break;
+		case ASWPW:
+			oprange[ASWPBU] = oprange[r];
+			break;
+		case AB:
+		case ABL:
+		case ABX:
+		case ABXRET:
+		case ASWI:
+		case AWORD:
+		case AMOVM:
+		case ARFE:
+		case ATEXT:
+		case ACASE:
+		case ABCASE:
+			break;
+		case AADDF:
+			oprange[AADDD] = oprange[r];
+			oprange[ASUBF] = oprange[r];
+			oprange[ASUBD] = oprange[r];
+			oprange[AMULF] = oprange[r];
+			oprange[AMULD] = oprange[r];
+			oprange[ADIVF] = oprange[r];
+			oprange[ADIVD] = oprange[r];
+			oprange[AMOVFD] = oprange[r];
+			oprange[AMOVDF] = oprange[r];
+			break;
+			
+		case ACMPF:
+			oprange[ACMPD] = oprange[r];
+			break;
+
+		case AMOVF:
+			oprange[AMOVD] = oprange[r];
+			break;
+
+		case AMOVFW:
+			oprange[AMOVWF] = oprange[r];
+			oprange[AMOVWD] = oprange[r];
+			oprange[AMOVDW] = oprange[r];
+			break;
+
+		case AMULL:
+			oprange[AMULA] = oprange[r];
+			oprange[AMULAL] = oprange[r];
+			oprange[AMULLU] = oprange[r];
+			oprange[AMULALU] = oprange[r];
+			break;
+		}
+	}
+}
+
+/*
+void
+buildrep(int x, int as)
+{
+	Opcross *p;
+	Optab *e, *s, *o;
+	int a1, a2, a3, n;
+
+	if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
+		diag("assumptions fail in buildrep");
+		errorexit();
+	}
+	repop[as] = x;
+	p = (opcross + x);
+	s = oprange[as].start;
+	e = oprange[as].stop;
+	for(o=e-1; o>=s; o--) {
+		n = o-optab;
+		for(a2=0; a2<2; a2++) {
+			if(a2) {
+				if(o->a2 == C_NONE)
+					continue;
+			} else
+				if(o->a2 != C_NONE)
+					continue;
+			for(a1=0; a1<32; a1++) {
+				if(!xcmp[a1][o->a1])
+					continue;
+				for(a3=0; a3<32; a3++)
+					if(xcmp[a3][o->a3])
+						(*p)[a1][a2][a3] = n;
+			}
+		}
+	}
+	oprange[as].start = 0;
+}
+*/
+
+enum{
+	ABSD = 0,
+	ABSU = 1,
+	RELD = 2,
+	RELU = 3,
+};
+
+int modemap[4] = { 0, 1, -1, 2, };
+
+typedef struct Reloc Reloc;
+
+struct Reloc
+{
+	int n;
+	int t;
+	uchar *m;
+	ulong *a;
+};
+
+Reloc rels;
+
+static void
+grow(Reloc *r)
+{
+	int t;
+	uchar *m, *nm;
+	ulong *a, *na;
+
+	t = r->t;
+	r->t += 64;
+	m = r->m;
+	a = r->a;
+	r->m = nm = malloc(r->t*sizeof(uchar));
+	r->a = na = malloc(r->t*sizeof(ulong));
+	memmove(nm, m, t*sizeof(uchar));
+	memmove(na, a, t*sizeof(ulong));
+	free(m);
+	free(a);
+}
+
+void
+dynreloc(Sym *s, long v, int abs)
+{
+	int i, k, n;
+	uchar *m;
+	ulong *a;
+	Reloc *r;
+
+	if(v&3)
+		diag("bad relocation address");
+	v >>= 2;
+	if(s != S && s->type == SUNDEF)
+		k = abs ? ABSU : RELU;
+	else
+		k = abs ? ABSD : RELD;
+	/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
+	k = modemap[k];
+	r = &rels;
+	n = r->n;
+	if(n >= r->t)
+		grow(r);
+	m = r->m;
+	a = r->a;
+	for(i = n; i > 0; i--){
+		if(v < a[i-1]){	/* happens occasionally for data */
+			m[i] = m[i-1];
+			a[i] = a[i-1];
+		}
+		else
+			break;
+	}
+	m[i] = k;
+	a[i] = v;
+	r->n++;
+}
+
+static int
+sput(char *s)
+{
+	char *p;
+
+	p = s;
+	while(*s)
+		cput(*s++);
+	cput(0);
+	return  s-p+1;
+}
+
+void
+asmdyn()
+{
+	int i, n, t, c;
+	Sym *s;
+	ulong la, ra, *a;
+	vlong off;
+	uchar *m;
+	Reloc *r;
+
+	cflush();
+	off = seek(cout, 0, 1);
+	lput(0);
+	t = 0;
+	lput(imports);
+	t += 4;
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			if(s->type == SUNDEF){
+				lput(s->sig);
+				t += 4;
+				t += sput(s->name);
+			}
+	
+	la = 0;
+	r = &rels;
+	n = r->n;
+	m = r->m;
+	a = r->a;
+	lput(n);
+	t += 4;
+	for(i = 0; i < n; i++){
+		ra = *a-la;
+		if(*a < la)
+			diag("bad relocation order");
+		if(ra < 256)
+			c = 0;
+		else if(ra < 65536)
+			c = 1;
+		else
+			c = 2;
+		cput((c<<6)|*m++);
+		t++;
+		if(c == 0){
+			cput(ra);
+			t++;
+		}
+		else if(c == 1){
+			wput(ra);
+			t += 2;
+		}
+		else{
+			lput(ra);
+			t += 4;
+		}
+		la = *a++;
+	}
+
+	cflush();
+	seek(cout, off, 0);
+	lput(t);
+
+	if(debug['v']){
+		Bprint(&bso, "import table entries = %d\n", imports);
+		Bprint(&bso, "export table entries = %d\n", exports);
+	}
+}
--- /dev/null
+++ b/utils/tl/thumb.c
@@ -1,0 +1,1636 @@
+#include "l.h"
+
+static long thumboprr(int);
+static long thumboprrr(int, int);
+static long thumbopirr(int , int);
+static long thumbopri(int);
+static long thumbophh(int);
+static long thumbopbra(int);
+static long thumbopmv(int, int);
+static void lowreg(Prog *, int);
+static void mult(Prog *, int, int);
+static void numr(Prog *, int, int, int);
+static void regis(Prog *, int, int, int);
+static void dis(int, int);
+
+// build a constant using neg, add and shift - only worth it if < 6 bytes */
+static int
+immbuildcon(int c, Prog *p)
+{
+	int n = 0;
+
+	USED(p);
+	if(c >= 0 && c <= 255)
+		return 0;			// mv
+	if(c >= -255 && c < 0)	// mv, neg
+		return 1;
+	if(c >= 256 && c <= 510)	// mv, add
+		return 1;
+	if(c < 0)
+		return 0;
+	while(!(c & 1)){
+		n++;
+		c >>= 1;
+	}
+	if(c >= 0 && c <= 255)	// mv, lsl
+		return 1;
+	return 0;
+}
+
+// positive 5 bit offset from register - O(R)
+// positive 8 bit offset from register - mov O, R then [R, R]
+// otherwise O goes in literal pool - mov O1(PC), R then [R, R]
+static int
+immoreg(int off, Prog *p)
+{
+	int v = 1;
+	int as = p->as;
+
+	if(off < 0)
+		return C_GOREG;
+	if(as == AMOVW)
+		v = 4;
+	else if(as == AMOVH || as == AMOVHU)
+		v = 2;
+	else if(as == AMOVB || as == AMOVBU)
+		v = 1;
+	else
+		diag("bad op in immoreg");
+	if(off/v <= 31)
+		return C_SOREG;
+	if(off <= 255)
+		return C_LOREG;
+	return C_GOREG;
+}
+
+// positive 8 bit - mov O, R then 0(R)
+// otherwise O goes in literal pool - mov O1(PC), R then 0(R)
+static int
+immacon(int off, Prog *p, int t1, int t2)
+{
+	USED(p);
+	if(off < 0)
+		return t2;
+	if(off <= 255)
+		return t1;
+	return t2;
+}
+
+// unsigned 8 bit in words
+static int
+immauto(int off, Prog *p)
+{
+	if(p->as != AMOVW)
+		diag("bad op in immauto");
+	mult(p, off, 4);
+	if(off >= 0 && off <= 1020)
+		return C_SAUTO;
+	return C_LAUTO;
+}
+
+static int
+immsmall(int off, Prog *p, int t1, int t2, int t3)
+{
+	USED(p);
+	if(off >= 0 && off <= 7)
+		return t1;
+	if(off >= 0 && off <= 255)
+		return t2;
+	return t3;
+}
+
+static int
+immcon(int off, Prog *p)
+{
+	int as = p->as;
+
+	if(as == ASLL || as == ASRL || as == ASRA)
+		return C_SCON;
+	if(p->to.type == D_REG && p->to.reg == REGSP){
+		if(as == AADD || as == ASUB){
+			if(off >= 0 && off <= 508)
+				return C_SCON;
+			if(as == ASUB){
+				p->as = AADD;
+				p->from.offset = -p->from.offset;
+			}
+			return C_LCON;
+		}
+		diag("unknown type in immcon");
+	}
+	if(as == AADD || as == ASUB){
+		if(p->reg != NREG)
+			return immsmall(off, p, C_SCON, C_LCON, C_GCON);
+		return immacon(off, p, C_SCON, C_LCON);
+	}
+	if(as == AMOVW && p->from.type == D_CONST && p->to.type == D_REG && immbuildcon(off, p))
+		return C_BCON;
+	if(as == ACMP && p->from.type == D_CONST && immbuildcon(off, p))
+		return C_BCON;
+	if(as == ACMP || as == AMOVW)
+		return immacon(off, p, C_SCON, C_LCON);
+	return C_LCON;
+}
+
+int
+thumbaclass(Adr *a, Prog *p)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+	case D_REG:
+		if(a->reg == REGSP)
+			return C_SP;
+		if(a->reg == REGPC)
+			return C_PC;
+		if(a->reg >= 8)
+			return C_HREG;
+		return C_REG;
+	case D_SHIFT:
+		diag("D_SHIFT in thumbaclass");
+		return C_SHIFT;
+	case D_FREG:
+		diag("D_FREG in thumbaclass");
+		return C_FREG;
+	case D_FPCR:
+		diag("D_FPCR in thumbaclass");
+		return C_FCR;
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s\n",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			instoffset = a->sym->value + a->offset + INITDAT;
+			return C_LEXT;	/* INITDAT unknown at this stage */
+			// return immacon(instoffset, p, C_SEXT, C_LEXT);
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			return immauto(instoffset, p);
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+// print("D_PARAM %s %d+%d+%d = %d\n", a->sym != S ? a->sym->name : "noname", autosize, a->offset, 4, autosize+a->offset+4);
+			return immauto(instoffset, p);
+		case D_NONE:
+			instoffset = a->offset;
+			if(a->reg == REGSP)
+				return immauto(instoffset, p);
+			else
+				return immoreg(instoffset, p);
+		}
+		return C_GOK;
+	case D_PSR:
+		diag("D_PSR in thumbaclass");
+		return C_PSR;
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s\n",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF){
+				instoffset = s->value + a->offset;
+#ifdef CALLEEBX
+				instoffset += fnpinc(s);
+#else
+				if(s->thumb)
+					instoffset++;	// T bit
+#endif
+				return C_LCON;
+			}
+			return C_LCON;	/* INITDAT unknown at this stage */
+			// return immcon(instoffset, p);
+		}
+		return C_GOK;
+	case D_FCONST:
+		diag("D_FCONST in thumaclass");
+		return C_FCON;
+	case D_CONST:
+		switch(a->name) {
+		case D_NONE:
+			instoffset = a->offset;
+			if(a->reg != NREG)
+				goto aconsize;
+			return immcon(instoffset, p);
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s\n",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SCONST:
+			case STEXT:
+			case SLEAF:
+				instoffset = s->value + a->offset;
+#ifdef CALLEEBX
+				instoffset += fnpinc(s);
+#else
+				if(s->thumb)
+					instoffset++;	// T bit
+#endif
+				return C_LCON;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;	/* INITDAT unknown at this stage */
+			// return immcon(instoffset, p);
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			goto aconsize;
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+		aconsize:
+			if(p->from.reg == REGSP || p->from.reg == NREG)
+				return instoffset >= 0 && instoffset < 1024 ? C_SACON : C_GACON;
+			else if(p->from.reg == p->to.reg)
+				return immacon(instoffset, p, C_SACON, C_GACON);
+			return immsmall(instoffset, p, C_SACON, C_LACON, C_GACON);
+		}
+		return C_GOK;
+	case D_BRANCH: {
+		int v, va;
+
+		p->align = 0;
+		v = -4;
+		va = 0;
+		if(p->cond != P){
+			v = (p->cond->pc - p->pc) - 4;
+			va = p->cond->pc;
+		}
+		instoffset = v;
+		if(p->as == AB){
+			if(v >= -2048 && v <= 2046)
+				return C_SBRA;
+			p->align = 4;
+			instoffset = va;
+			return C_LBRA;
+		}
+		if(p->as == ABL){
+#ifdef CALLEEBX
+			int e;
+
+			if((e = fninc(p->to.sym))) {
+				v += e;
+				va += e;
+				instoffset += e;
+			}		
+#endif
+			if(v >= -4194304 && v <= 4194302)
+				return C_SBRA;
+			p->align = 2;
+			instoffset = va;
+			return C_LBRA;
+		}
+		if(p->as == ABX){
+			v = va;
+			if(v >= 0 && v <= 255)
+				return C_SBRA;
+			p->align = 2;
+			instoffset = va;
+			return C_LBRA;
+		}
+		if(v >= -256 && v <= 254)
+			return C_SBRA;
+		if(v >= -(2048-2) && v <= (2046+2))
+			return C_LBRA;
+		p->align = 2;
+		instoffset = va;
+		return C_GBRA;
+	}
+	}
+	return C_GOK;
+}
+
+// as a1 a2 a3 type size param lit vers
+Optab thumboptab[] =
+{
+	{ ATEXT,		C_LEXT,		C_NONE,		C_LCON,		0,	0,	0 },
+	{ ATEXT,		C_LEXT,		C_REG,		C_LCON,		0,	0,	0 },
+	{ AMVN,		C_REG,		C_NONE,		C_REG,		1,	2,	0 },
+	{ ASRL,		C_REG,		C_NONE,		C_REG,		1,	2,	0 },
+	{ ACMP,		C_REG,		C_REG,		C_NONE,		1,	2,	0 },
+	{ ACMN,		C_REG,		C_REG,		C_NONE,		1,	2,	0 },
+	{ AADD,		C_REG,		C_REG,		C_REG,		2,	2,	0 },
+	{ AADD,		C_REG,		C_NONE,		C_REG,		2,	2,	0 },
+	{ AADD,		C_SCON,		C_REG,		C_REG,		3,	2,	0 },
+	{ AADD,		C_LCON,		C_REG,		C_REG,		49,	4,	0 },
+	{ AADD,		C_GCON,		C_REG,		C_REG,		36,	4,	0,	LFROM },
+	// { AADD,		C_LCON,		C_NONE,		C_REG,		3,	2,	0,	LFROM },
+	{ ASRL,		C_SCON,		C_REG,		C_REG,		4,	2,	0 },
+	{ ASRL,		C_SCON,		C_NONE,		C_REG,		4,	2,	0 },
+	{ AADD,		C_SCON,		C_NONE,		C_REG,		5,	2,	0 },
+	{ AADD,		C_LCON,		C_NONE,		C_REG,		37,	4,	0,	LFROM },
+	{ ACMP,		C_SCON,		C_REG,		C_NONE,		5,	2,	0 },
+	{ ACMP,		C_BCON,		C_REG,		C_NONE,		48,	6,	0 },
+	{ ACMP,		C_LCON,		C_REG,		C_NONE,		39,	4,	0,	LFROM },
+	{ AMOVW,		C_SCON,		C_NONE,		C_REG,		5,	2,	0 },
+	{ AMOVW,		C_BCON,		C_NONE,		C_REG,		47,	4,	0 },
+	{ AMOVW,		C_LCON,		C_NONE,		C_REG,		38,	2,	0,	LFROM },
+	// { AADD,		C_LCON,		C_PC,		C_REG,		6,	2,	0,	LFROM },
+	// { AADD,		C_LCON,		C_SP,		C_REG,		6,	2,	0,	LFROM },
+	{ AADD,		C_SCON,		C_NONE,		C_SP,		7,	2,	0 },
+	{ AADD,		C_LCON,		C_NONE,		C_SP,		40,	4,	0,	LFROM },
+	{ AADD,		C_REG,		C_NONE,		C_HREG,		8,	2,	0 },
+	{ AADD,		C_HREG,		C_NONE,		C_REG,		8,	2,	0 },
+	{ AADD,		C_HREG,		C_NONE,		C_HREG,		8,	2,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_HREG,		8,	2,	0 },
+	{ AMOVW,		C_HREG,		C_NONE,		C_REG,		8,	2,	0 },
+	{ AMOVW,		C_HREG,		C_NONE,		C_HREG,		8,	2,	0 },
+	{ ACMP,		C_REG,		C_HREG,		C_NONE,		8,	2,	0 },
+	{ ACMP,		C_HREG,		C_REG,		C_NONE,		8,	2,	0 },
+	{ ACMP,		C_HREG,		C_HREG,		C_NONE,		8,	2,	0 },
+	{ AB,			C_NONE,		C_NONE,		C_SBRA,		9,	2,	0,	LPOOL },
+	{ ABEQ,		C_NONE,		C_NONE,		C_SBRA,		10,	2,	0 },
+	{ ABL,		C_NONE,		C_NONE,		C_SBRA,		11,	4,	0 },
+	{ ABX,		C_NONE,		C_NONE,		C_SBRA,		12,	10,	0 },
+	{ AB,			C_NONE,		C_NONE,		C_LBRA,		41,	8,	0,	LPOOL },
+	{ ABEQ,		C_NONE,		C_NONE,		C_LBRA,		46,	4,	0 },
+	{ ABL,		C_NONE,		C_NONE,		C_LBRA,		43,	14,	0 },
+	{ ABX,		C_NONE,		C_NONE,		C_LBRA,		44,	14,	0 },
+	{ ABEQ,		C_NONE,		C_NONE,		C_GBRA,		42,  10, 	0 },
+	// { AB,		C_NONE,		C_NONE,		C_SOREG,		13,	0,	0 },
+	// { ABL,		C_NONE,		C_NONE,		C_SOREG,		14,	0,	0 },
+	{ ABL,		C_NONE,		C_NONE,		C_REG,		51,	4,	0 },
+	{ ABX,		C_NONE,		C_NONE,		C_REG,		15,	8,	0 },
+	{ ABX,		C_NONE,		C_NONE,		C_HREG,		15,	8,	0 },
+	{ ABXRET,		C_NONE,		C_NONE,		C_REG,		45,	2,	0 },
+	{ ABXRET,		C_NONE,		C_NONE,		C_HREG,		45,	2,	0 },
+	{ ASWI,		C_NONE,		C_NONE,		C_LCON,		16,	2,	0 },
+	{ AWORD,		C_NONE,		C_NONE,		C_LCON,		17,	4,	0 },
+	{ AWORD,		C_NONE,		C_NONE,		C_GCON,		17,	4,	0 },
+	{ AWORD,		C_NONE,		C_NONE,		C_LEXT,		17,	4, 	0 },
+	{ ADWORD,	C_LCON,		C_NONE,		C_LCON,		50,	8,	0 },
+	{ AMOVW,		C_SAUTO,		C_NONE,		C_REG,		18,	2,	REGSP },
+	{ AMOVW,		C_LAUTO,		C_NONE,		C_REG,		33,	6,	0,	LFROM  },
+	// { AMOVW,		C_OFFPC,		C_NONE,		C_REG,		18,	2,	REGPC,	LFROM  },
+	{ AMOVW,		C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
+	{ AMOVW,		C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
+	{ AMOVHU,	C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
+	{ AMOVHU,	C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
+	{ AMOVBU,	C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
+	{ AMOVBU,	C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_SAUTO,		20,	2,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_LAUTO,		34,	6,	0,	LTO },
+	{ AMOVW,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
+	{ AMOVH,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
+	{ AMOVH,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
+	{ AMOVB,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
+	{ AMOVB,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
+	{ AMOVHU,	C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
+	{ AMOVHU,	C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
+	{ AMOVBU,	C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
+	{ AMOVBU,	C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_REG,		22,	2,	0 },
+	{ AMOVB,		C_REG,		C_NONE,		C_REG,		23,	4,	0 },
+	{ AMOVH,		C_REG,		C_NONE,		C_REG,		23,	4,	0 },
+	{ AMOVBU,	C_REG,		C_NONE,		C_REG,		23,	4,	0 },
+	{ AMOVHU,	C_REG,		C_NONE,		C_REG,		23,	4,	0 },
+	{ AMOVH,		C_SEXT,		C_NONE,		C_REG,		32,	6,	0 },
+	{ AMOVH,		C_SOREG,		C_NONE,		C_REG,		24,	4,	0 },
+	{ AMOVB,		C_SEXT,		C_NONE,		C_REG,		32,	6,	0 },
+	{ AMOVB,		C_SOREG,		C_NONE,		C_REG,		24,	4,	0 },
+	{ AMOVW,		C_SACON,	C_NONE,		C_REG,		25,	2,	0 },
+	{ AMOVW,		C_LACON,	C_NONE,		C_REG,		35,	4,	0 },
+	{ AMOVW,		C_GACON,	C_NONE,		C_REG,		35,	4,	0,	LFROM },
+	{ AMOVM,		C_LCON,		C_NONE,		C_REG,		26,	2,	0 },
+	{ AMOVM,		C_REG,		C_NONE,		C_LCON,		27,	2,	0 },
+	{ AMOVW,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
+	{ AMOVH,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
+	{ AMOVB,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
+	{ AMOVHU,	C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
+	{ AMOVBU,	C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
+	{ AMOVW,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
+	{ AMOVH,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
+	{ AMOVB,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
+	{ AMOVHU,	C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
+	{ AMOVBU,	C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
+	{ AMOVW,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
+	{ AMOVH,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
+	{ AMOVB,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
+	{ AMOVHU,	C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
+	{ AMOVBU,	C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
+	{ AMOVW,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
+	{ AMOVH,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
+	{ AMOVB,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
+	{ AMOVHU,	C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
+	{ AMOVBU,	C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
+	{ AMOVW,		C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
+	{ AMOVH,		C_LEXT,		C_NONE,		C_REG,		32,	6,	0,	LFROM },
+	{ AMOVB,		C_LEXT,		C_NONE,		C_REG,		32,	6,	0,	LFROM },
+	{ AMOVHU,	C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
+	{ AMOVBU,	C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
+	{ AMOVW,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
+	{ AMOVH,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
+	{ AMOVB,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
+	{ AMOVHU,	C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
+	{ AMOVBU,	C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
+
+	{ AXXX,		C_NONE,		C_NONE,		C_NONE,		0,	2,	0 },
+};
+
+#define OPCNTSZ	52
+int opcount[OPCNTSZ];
+
+// is this too pessimistic ?
+int
+brextra(Prog *p)
+{
+	int c;
+
+	// +2 is for padding
+	if(p->as == ATEXT)
+		return 0-0+2;
+	if(!isbranch(p))
+		diag("bad op in brextra()");
+	c = thumbaclass(&p->to, p);
+	switch(p->as){
+	case AB:
+		if(c != C_SBRA)
+			return 0;
+		return 8-2+2;
+	case ABL:
+		if(c != C_SBRA)
+			return 0;
+		return 14-4+2;
+	case ABX:
+		if(c == C_REG || c == C_HREG)
+			return 0;
+#ifdef CALLEEBX
+		diag("ABX $I in brextra");
+#endif
+		if(c != C_SBRA)
+			return 0;
+		return 14-10+2;
+	default:
+		if(c == C_GBRA)
+			return 0;
+		if(c == C_LBRA)
+			return 10-4+2;
+		return 10-2+2;
+	}
+}
+
+#define high(r)	((r)>=8)
+
+static long
+mv(Prog *p, int r, int off)
+{
+	int v, o;
+	if(p != nil && p->cond != nil){	// in literal pool
+		v = p->cond->pc - p->pc - 4;
+		if(p->cond->pc & 3)
+			diag("mv: bad literal pool alignment");
+		if(v & 3)
+			v += 2;	// ensure M(4) offset
+		mult(p, v, 4);
+		off = v/4;
+		numr(p, off, 0, 255);
+		o = 0x9<<11;
+	}
+	else{
+		numr(p, off, 0, 255);
+		o = 0x4<<11;
+	}
+	o |= (r<<8) | off;
+	return o;
+}
+
+static void
+mvcon(Prog *p, int r, int c, long *o1, long *o2)
+{
+	int op = 0, n = 0;
+
+	if(c >= 0 && c <= 255)
+		diag("bad c in mvcon");
+	if(c >= -255 && c < 0)	// mv, neg
+		c = -c;
+	else if(c >= 256 && c <= 510){	// mv, add
+		n = rand()%(511-c) + (c-255);
+		c -= n;
+		// n = c-255;
+		// c = 255;
+		op = AADD;
+	}
+	else{
+		if(c < 0)
+			diag("-ve in mvcon");
+		while(!(c & 1)){
+			n++;
+			c >>= 1;
+		}
+		if(c >= 0 && c <= 255)	// mv, lsl
+			op = ASLL;
+		else
+			diag("bad shift in mvcon");
+	}
+	*o1 = mv(p, r, c);
+	switch(op){
+		case 0:
+			*o2 = (1<<14) | (9<<6) | (r<<3) | r;
+			break;
+		case AADD:
+			*o2 = (6<<11) | (r<<8) | n;
+			break;
+		case ASLL:
+			*o2 = (n<<6) | (r<<3) | r;
+			break;
+	}
+}
+
+static long
+mvlh(int rs, int rd)
+{
+	int o = 0x46<<8;
+
+	if(high(rs)){
+		rs -= 8;
+		o |= 1<<6;
+	}
+	if(high(rd)){
+		rd -= 8;
+		o |= 1<<7;
+	}
+	o |= (rs<<3) | rd;
+	return o;
+}
+
+void
+thumbbuildop()
+{
+	int i, n, r;
+	Optab *optab = thumboptab;
+	Oprang *oprange = thumboprange;
+
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+
+		switch(r)
+		{
+		default:
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			oprange[ABCS] = oprange[r];
+			oprange[ABHS] = oprange[r];
+			oprange[ABCC] = oprange[r];
+			oprange[ABLO] = oprange[r];
+			oprange[ABMI] = oprange[r];
+			oprange[ABPL] = oprange[r];
+			oprange[ABVS] = oprange[r];
+			oprange[ABVC] = oprange[r];
+			oprange[ABHI] = oprange[r];
+			oprange[ABLS] = oprange[r];
+			oprange[ABGE] = oprange[r];
+			oprange[ABLT] = oprange[r];
+			oprange[ABGT] = oprange[r];
+			oprange[ABLE] = oprange[r];
+			break;
+		case AMVN:
+			oprange[AADC] = oprange[r];
+			oprange[ASBC] = oprange[r];
+			oprange[AMUL] = oprange[r];
+			oprange[AAND] = oprange[r];
+			oprange[AEOR] = oprange[r];
+			oprange[AORR] = oprange[r];
+			oprange[ABIC] = oprange[r];
+			oprange[AMULU] = oprange[r];
+			break;
+		case ACMN:
+			oprange[ATST] = oprange[r];
+			break;
+		case ASRL:
+			oprange[ASRA] = oprange[r];
+			oprange[ASLL] = oprange[r];
+			break;
+		case AADD:
+			oprange[ASUB] = oprange[r];
+			break;
+		}
+	}
+}
+
+void
+thumbasmout(Prog *p, Optab *o)
+{
+	long o1, o2, o3, o4, o5, o6, o7, v;
+	int r, rf, rt;
+
+	rf = p->from.reg;
+	rt = p->to.reg;
+	r = p->reg;
+	o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0;
+if(debug['P']) print("%ulx: %P	type %d %d\n", (ulong)(p->pc), p, o->type, p->align);
+	opcount[o->type] += o->size;
+	switch(o->type) {
+	default:
+		diag("unknown asm %d", o->type);
+		prasm(p);
+		break;
+	case 0:		/* pseudo ops */
+if(debug['G']) print("%ulx: %s: thumb\n", (ulong)(p->pc), p->from.sym->name);
+		break;
+	case 1:		/* op R, -, R or op R, R, - */
+		o1 = thumboprr(p->as);
+		if(rt == NREG)
+			rt = r;
+		lowreg(p, rf);
+		lowreg(p, rt);
+		o1 |= (0x10<<10) | (rf<<3) | rt;
+		break;
+	case 2:		/* add/sub R, R, R or add/sub R, -, R */
+		o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
+		if(r == NREG)
+			r = rt;
+		lowreg(p, rf);
+		lowreg(p, r);
+		lowreg(p, rt);
+		o1 |= (0x6<<10) | (rf<<6) | (r<<3) | rt;
+		break;
+	case 3:		/* add/sub $I, R, R or add/sub $I, -, R */
+		thumbaclass(&p->from, p);
+		o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
+		if(r == NREG)
+			r = rt;
+		numr(p, instoffset, 0, 7);
+		lowreg(p, r);
+		lowreg(p, rt);
+		o1 |= (0x7<<10) | (instoffset<<6) | (r<<3) | rt;
+		break;
+	case 4:		/* shift $I, R, R or shift $I, -, R */
+		thumbaclass(&p->from, p);
+		if(instoffset < 0)
+			diag("negative shift in thumbasmout");
+		instoffset %= 32;
+		o1 = thumbopri(p->as);
+		if(r == NREG)
+			r = rt;
+		numr(p, instoffset, 0, 31);
+		lowreg(p, r);
+		lowreg(p, rt);
+		o1 |= (0x0<<13) | (instoffset<<6) | (r<<3) | rt;
+		break;
+	case 5:		/* add/sub/mov $I, -, R or cmp $I, R, - */
+		thumbaclass(&p->from, p);
+		o1 = thumbopri(p->as);	
+		if(rt == NREG)
+			rt = r;
+		numr(p, instoffset, 0, 255);
+		lowreg(p, rt);
+		o1 |= (0x1<<13) | (rt<<8) | instoffset;
+		break;
+	case 6:		/* add $I, PC/SP, R */
+		if(p->as == ASUB)
+			diag("subtract in add $I, PC/SP, R");
+		thumbaclass(&p->from, p);
+		o1 = r == REGSP ? 0x1<<11 : 0x0<<11;
+		numr(p, instoffset, 0, 255);
+		regis(p, r, REGSP, REGPC);
+		lowreg(p, rt);
+		o1 |= (0xa<<12) | (rt<<8) | instoffset;
+		break;
+	case 7:		/* add, sub $I, SP */
+		thumbaclass(&p->from, p);
+		o1 = p->as == AADD ? 0x0<<7 : 0x1<<7;
+		numr(p, instoffset, 0, 508);
+		mult(p, instoffset, 4);
+		regis(p, rt, REGSP, REGSP);
+		o1 |= (0xb0<<8) | (instoffset>>2);
+		break;
+	case 8:		/* add/mov/cmp R, R where at least 1 reg is high */
+		o1 = 0;
+		if(rt == NREG)
+			rt = r;
+		if(high(rf)){
+			o1 |= 1<<6;
+			rf -= 8;
+		}
+		if(high(rt)){
+			o1 |= 2<<6;
+			rt -= 8;
+		}
+		if(o1 == 0)
+			diag("no high register(%P)", p);
+		o1 |= thumbophh(p->as);
+		o1 |= (0x11<<10) | (rf<<3) | rt;
+		break;
+	case 9:		/* B	$I */
+		thumbaclass(&p->to, p);
+		numr(p, instoffset, -2048, 2046);
+		o1 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
+		break;
+	case 10:		/* Bcc $I */
+		thumbaclass(&p->to, p);
+		numr(p, instoffset, -256, 254);
+		o1 = thumbopbra(p->as);
+		o1 |= (0xd<<12) | ((instoffset>>1)&0xff);
+		break;
+	case 11:		/* BL $I */
+		thumbaclass(&p->to, p);
+		numr(p, instoffset, -4194304, 4194302);
+		o1 = (0x1e<<11) | ((instoffset>>12)&0x7ff);
+		o2 = (0x1f<<11) | ((instoffset>>1)&0x7ff);
+		break;
+	case 12:		/* BX $I */
+#ifdef CALLEEBX
+		diag("BX $I case");
+#endif
+		thumbaclass(&p->to, p);
+		if(p->to.sym->thumb)
+			instoffset  |= 1;	// T bit
+		o1 = mvlh(REGPC, REGTMPT);
+		o2 = (0x6<<11) | (REGTMPT<<8) | 7;	// add 7, RTMP	(T bit + PC offset)
+		o3 = mvlh(REGTMPT, REGLINK);
+		o4 = mv(nil, REGTMPT, instoffset);
+		o5 = (0x11c<<6) | (REGTMPT<<3);
+		// o1 = mv(nil, REGTMPT, v);
+		// o2 = (0x11b<<6) | (REGPC<<3) | REGLINK;
+		// o3 = (0x11c<<6) | (REGTMPT<<3);
+		break;
+	case 13:		/* B O(R)  */
+		diag("B O(R)");
+		break;
+	case 14:		/* BL O(R) */
+		diag("BL O(R)");
+		break;
+	case 15:		/* BX R */
+		o1 = mvlh(REGPC, REGTMPT);
+		o2 = (0x6<<11) | (REGTMPT<<8) | 5;	// add 5, RTMP (T bit + PC offset)
+		o3 = mvlh(REGTMPT, REGLINK);
+		o4 = 0;
+		if(high(rt)){
+			rt -= 8;
+			o4 |= 1<<6;
+		}
+		o4 |= (0x8e<<7) | (rt<<3);
+		// o1 = (0x11c<<6) | (rt<<3);
+		break;
+	case 16:		/* SWI $I */
+		thumbaclass(&p->to, p);
+		numr(p, instoffset, 0, 255);
+		o1 = (0xdf<<8) | instoffset;
+		break;
+	case 17:		/* AWORD */
+		thumbaclass(&p->to, p);
+		o1 = instoffset&0xffff;
+		o2 = (instoffset>>16)&0xffff;
+		break;
+	case 18:		/* AMOVW O(SP), R and AMOVW O(PC), R */
+		thumbaclass(&p->from, p);
+		rf = o->param;
+		o1 = rf == REGSP ? 0x13<<11 : 0x9<<11;
+		regis(p, rf, REGSP, REGPC);
+		lowreg(p, rt);
+		mult(p, instoffset, 4);
+		numr(p, instoffset/4, 0, 255);
+		o1 |= (rt<<8) | (instoffset/4);
+		break;
+	case 19:		/* AMOVW... O(R), R */
+		thumbaclass(&p->from, p);
+		o1 = thumbopmv(p->as, 1);
+		v = 4;
+		if(p->as == AMOVHU)
+			v = 2;
+		else if(p->as == AMOVBU)
+			v = 1;
+		mult(p, instoffset, v);
+		lowreg(p, rf);
+		lowreg(p, rt);
+		numr(p, instoffset/v, 0, 31);
+		o1 |= ((instoffset/v)<<6) | (rf<<3) | rt;
+		break;
+	case 20:		/* AMOVW R, O(SP) */
+		thumbaclass(&p->to, p);
+		o1 = 0x12<<11;
+		if(rt != NREG) regis(p, rt, REGSP, REGSP);
+		lowreg(p, rf);
+		mult(p, instoffset, 4);
+		numr(p, instoffset/4, 0, 255);
+		o1 |= (rf<<8) | (instoffset/4);
+		break;
+	case 21:		/* AMOVW... R, O(R) */
+		thumbaclass(&p->to, p);
+		o1 = thumbopmv(p->as, 0);
+		v = 4;
+		if(p->as == AMOVHU || p->as == AMOVH)
+			v = 2;
+		else if(p->as == AMOVBU || p->as == AMOVB)
+			v = 1;
+		lowreg(p, rf);
+		lowreg(p, rt);
+		mult(p, instoffset, v);
+		numr(p, instoffset/v, 0, 31);
+		o1 |= ((instoffset/v)<<6) | (rt<<3) | rf;
+		break;
+	case 22:		/* AMOVW R, R -> ASLL $0, R, R */
+		o1 = thumbopri(ASLL);
+		lowreg(p, rf);
+		lowreg(p, rt);
+		o1 |= (0x0<<13) | (rf<<3) | rt;
+		break;
+	case 23:		/* AMOVB/AMOVH/AMOVBU/AMOVHU R, R */
+		o1 = thumbopri(ASLL);
+		o2 = p->as == AMOVB || p->as == AMOVH ? thumbopri(ASRA) : thumbopri(ASRL);
+		v = p->as == AMOVB || p->as == AMOVBU ? 24 : 16;
+		lowreg(p, rf);
+		lowreg(p, rt);
+		o1 |= (0x0<<13) | (v<<6) | (rf<<3) | rt;
+		o2 |= (0x0<<13) | (v<<6) | (rt<<3) | rt;
+		break;
+	case 24:	/* AMOVH/AMOVB O(R), R -> AMOVH/AMOVB [R, R], R */
+		thumbaclass(&p->from, p);
+		lowreg(p, rf);
+		lowreg(p, rt);
+		if(rf == rt)
+			r = REGTMPT;
+		else
+			r = rt;
+		if(p->as == AMOVB)
+			numr(p, instoffset, 0, 31);
+		else{
+			mult(p, instoffset, 2);
+			numr(p, instoffset, 0, 62);
+		}
+		o1 = mv(p, r, instoffset);
+		o2 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
+		o2 |= (r<<6) | (rf<<3) | rt;
+		break;
+	case 25:	/* MOVW $sacon, R */
+		thumbaclass(&p->from, p);
+// print("25: %d %d %d %d\n", instoffset, rf, r, rt);
+		if(rf == NREG)
+			rf = REGSP;
+		lowreg(p, rt);
+		if(rf == REGSP){
+			mult(p, instoffset, 4);
+			numr(p, instoffset>>2, 0, 255);
+			o1 = (0x15<<11) | (rt<<8) | (instoffset>>2);	// add $O, SP, R
+		}
+		else if(rf == rt){
+			numr(p, instoffset, 0, 255);
+			o1 = (0x6<<11) | (rt<<8) | instoffset;		// add $O, R
+		}
+		else{
+			lowreg(p, rf);
+			numr(p, instoffset, 0, 7);
+			o1 = (0xe<<9) | (instoffset<<6) | (rf<<3) | rt;	// add $O, Rs, Rd
+		}
+		break;
+	case 26:	/* AMOVM $c, oreg -> stmia */
+		lowreg(p, rt);
+		numr(p, p->from.offset, -256, 255);
+		o1 = (0x18<<11) | (rt<<8) | (p->from.offset&0xff);
+		break;
+	case 27:	/* AMOVM oreg, $c ->ldmia */
+		lowreg(p, rf);
+		numr(p, p->to.offset, -256, 256);
+		o1 = (0x19<<11) | (rf<<8) | (p->to.offset&0xff);
+		break;
+	case 28:	/* AMOV* O(R), R -> AMOV* [R, R], R 	(offset large)	*/
+		thumbaclass(&p->from, p);
+		lowreg(p, rf);
+		lowreg(p, rt);
+		if(rf == rt)
+			r = REGTMPT;
+		else
+			r = rt;
+		o1 = mv(p, r, instoffset);
+		o2 = thumboprrr(p->as, 1);
+		o2 |= (r<<6) | (rf<<3) | rt;
+		break;
+	case 29:	/* AMOV* R, O(R) -> AMOV* R, [R, R]	(offset large)	*/
+		thumbaclass(&p->to, p);
+		lowreg(p, rf);
+		lowreg(p, rt);
+		if(rt == REGTMPT){	// used as tmp reg
+			if(instoffset >= 0 && instoffset <= 255){
+				o1 = (1<<13) | (2<<11) | (rt<<8) | instoffset;	// add $O, R7
+				o2 = thumbopirr(p->as, 0);
+				o2 |= (0<<6) | (rt<<3) | rf;					// mov* R, 0(R)
+			}
+			else
+				diag("big offset - case 29");
+		}
+		else{
+			o1 = mv(p, REGTMPT, instoffset);
+			o2 = thumboprrr(p->as, 0);
+			o2 |= (REGTMPT<<6) | (rt<<3) | rf;
+		}
+		break;
+	case 30:		/* AMOVW... *addr, R */
+		thumbaclass(&p->from, p);
+		o1 = mv(p, rt, instoffset);		// MOV addr, rtmp
+		o2 = thumbopmv(p->as, 1);
+		lowreg(p, rt);
+		o2 |= (rt<<3) | rt;			// MOV* 0(rtmp), R
+		break;
+	case 31:		/* AMOVW... R, *addr */
+		thumbaclass(&p->to, p);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = thumbopmv(p->as, 0);
+		lowreg(p, rf);
+		o2 |= (REGTMPT<<3) | rf;
+		break;
+	case 32:	/* AMOVH/AMOVB *addr, R -> AMOVH/AMOVB [R, R], R */
+		thumbaclass(&p->from, p);
+		o1 = mv(p, rt, instoffset);
+		lowreg(p, rt);
+		o2 = mv(nil, REGTMPT, 0);
+		o3 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
+		o3 |= (REGTMPT<<6) | (rt<<3) | rt;
+		break;
+	case 33:	/* AMOVW O(SP), R	(O large) */
+		thumbaclass(&p->from, p);
+		lowreg(p, rt);
+		o1 = mv(p, rt, instoffset);
+		o2 = (0x111<<6) | (REGSP-8)<<3 | rt;	// add SP, rt
+		o3 = thumbopmv(p->as, 1);
+		o3 |= (rt<<3) | rt;
+		break;
+	case 34:	/* AMOVW R, O(SP)	(O large) */
+		thumbaclass(&p->to, p);
+		lowreg(p, rf);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = (0x111<<6) | (REGSP-8)<<3 | REGTMPT;	// add SP, REGTMP
+		o3 = thumbopmv(p->as, 0);
+		o3 |= (REGTMPT<<3) | rf;
+		break;
+	case 35:	/* AMOVW $lacon, R */
+		thumbaclass(&p->from, p);
+		lowreg(p, rt);
+		if(rf == NREG)
+			rf = REGSP;
+		if(rf == rt)
+			rf = r = REGTMPT;
+		else
+			r = rt;
+// print("35: io=%d rf=%d rt=%d\n", instoffset, rf, rt);
+		o1 = mv(p, r, instoffset);		// mov O, Rd
+		if(high(rf))
+			o2 = (0x44<<8) | (0x1<<6) | ((rf-8)<<3) | rt;	// add Rs, Rd
+		else
+			o2 = (0x6<<10) | (rf<<6) | (rt<<3) | rt;		// add Rs, Rd
+		break;
+	case 36:	/* AADD/ASUB $i, r, r when $i too big */
+		thumbaclass(&p->from, p);
+		lowreg(p, r);
+		lowreg(p, rt);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
+		o2 |= (REGTMPT<<6) | (r<<3) | rt;
+		break;
+	case 37:	/* AADD/ASUB $i, r when $i too big */
+		thumbaclass(&p->from, p);
+		lowreg(p, rt);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
+		o2 |= (REGTMPT<<6) | (rt<<3) | rt;
+		break;
+	case 38:	/* AMOVW $i, r when $i too big */
+		thumbaclass(&p->from, p);
+		lowreg(p, rt);
+		o1 = mv(p, rt, instoffset);
+		break;
+	case 39:	/* ACMP $i, r when $i too big */
+		thumbaclass(&p->from, p);
+		lowreg(p, r);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = (0x10a<<6) | (REGTMPT<<3) | r;
+		break;
+	case 40:		/* add, sub $I, SP when $I large*/
+		thumbaclass(&p->from, p);
+		if(p->as == ASUB)
+			instoffset = -instoffset;
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = (0x112<<6) | (REGTMPT<<3) | (REGSP-8);
+		regis(p, rt, REGSP, REGSP);
+		break;
+	case	41:		/* BL LBRA */
+		thumbaclass(&p->to, p);
+		o1 = (0x9<<11) | (REGTMPT<<8);	// mov 0(pc), r7
+		o2 = mvlh(REGTMPT, REGPC);		// mov r7, pc
+		o3 = instoffset&0xffff;			// $lab
+		o4 = (instoffset>>16)&0xffff;
+		break;
+	case 42:		/* Bcc GBRA */
+		thumbaclass(&p->to, p);
+		o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (6>>1);		// bccnot 
+		// ab lbra
+		o2 = (0x9<<11) | (REGTMPT<<8);	// mov 0(pc), r7
+		o3 = mvlh(REGTMPT, REGPC);		// mov r7, pc
+		o4 = instoffset&0xffff;			// $lab
+		o5 = (instoffset>>16)&0xffff;
+		break;
+	case 43:		/* BL LBRA */
+		thumbaclass(&p->to, p);
+		o1 = mvlh(REGPC, REGTMPT);						// mov pc, r7
+		o2 = (0x6<<11) | (REGTMPT<<8) | 10;				// add 10, r7
+		o3 = mvlh(REGTMPT, REGLINK);					// mov r7, lr
+		o4 = (0x9<<11) | (REGTMPT<<8);					// mov o(pc), r7
+		o5 = mvlh(REGTMPT, REGPC);						// mov r7, pc
+		o6 = instoffset&0xffff;							// $lab
+		o7 = (instoffset>>16)&0xffff;
+		break;
+	case 44:		/* BX LBRA */
+#ifdef CALLEEBX
+		diag("BX LBRA case");
+#endif
+		thumbaclass(&p->to, p);
+		if(p->to.sym->thumb)
+			instoffset  |= 1;	// T bit
+		o1 = mvlh(REGPC, REGTMPT);						// mov pc, r7
+		o2 = (0x6<<11) | (REGTMPT<<8) | 11;				// add 11, r7
+		o3 = mvlh(REGTMPT, REGLINK);					// mov r7, lr
+		o4 = (0x9<<11) | (REGTMPT<<8);					// mov o(pc), r7
+		o5 = (0x11c<<6) | (REGTMPT<<3);					// bx r7
+		o6 = instoffset&0xffff;							// $lab
+		o7 = (instoffset>>16)&0xffff;
+		break;
+	case 45:	/* BX R when returning from fn */
+		o1 = 0;
+		if(high(rt)){
+			rt -= 8;
+			o1 |= 1<<6;
+		}
+		o1 |= (0x8e<<7) | (rt<<3);
+		break;
+	case 46:		/* Bcc LBRA */
+		thumbaclass(&p->to, p);
+		o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (0>>1);		// bccnot 
+		// ab lbra
+		instoffset -= 2;
+		numr(p, instoffset, -2048, 2046);
+		o2 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
+		break;
+	case 47:	/* mov $i, R where $i can be built */
+		thumbaclass(&p->from, p);
+		mvcon(p, rt, instoffset, &o1, &o2);
+		break;
+	case 48: /* ACMP $i, r when $i built up */
+		thumbaclass(&p->from, p);
+		lowreg(p, r);
+		mvcon(p, REGTMPT, instoffset, &o1, &o2);
+		o3 = (0x10a<<6) | (REGTMPT<<3) | r;
+		break;
+	case 49:	/* AADD $i, r, r when $i is between 0 and 255 - could merge with case 36 */
+		thumbaclass(&p->from, p);
+		lowreg(p, r);
+		lowreg(p, rt);
+		numr(p, instoffset, 0, 255);
+		o1 = mv(p, REGTMPT, instoffset);
+		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
+		o2 |= (REGTMPT<<6) | (r<<3) | rt;
+		break;
+	case 50:		/* ADWORD */
+		thumbaclass(&p->from, p);
+		o1 = instoffset&0xffff;
+		o2 = (instoffset>>16)&0xffff;
+		thumbaclass(&p->to, p);
+		o3 = instoffset&0xffff;
+		o4 = (instoffset>>16)&0xffff;
+		break;
+	case 51:	/* BL r */
+		o1 = mvlh(REGPC, REGLINK);	// mov pc, lr
+		o2 = mvlh(rt, REGPC);		// mov r, pc
+		break;
+	}
+
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 2:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		hputl(o1);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux\t%P\n", v, o1, o2, p);
+		hputl(o1);
+		hputl(o2);
+		break;
+	case 6:
+		if(debug['a'])
+			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p);
+		hputl(o1);
+		hputl(o2);
+		hputl(o3);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, p);
+		hputl(o1);
+		hputl(o2);
+		hputl(o3);
+		hputl(o4);
+		break;
+	case 10:
+		if(debug['a'])
+			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, p);
+		hputl(o1);
+		hputl(o2);
+		hputl(o3);
+		hputl(o4);
+		hputl(o5);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, p);
+		hputl(o1);
+		hputl(o2);
+		hputl(o3);
+		hputl(o4);
+		hputl(o5);
+		hputl(o6);
+		break;
+	case 14:
+		if(debug['a'])
+			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p);
+		hputl(o1);
+		hputl(o2);
+		hputl(o3);
+		hputl(o4);
+		hputl(o5);
+		hputl(o6);
+		hputl(o7);
+		break;
+	}
+	if(debug['G']){
+		if(o->type == 17){
+			print("%lx:	word %ld\n", p->pc, (o2<<16)+o1);
+			return;
+		}
+		if(o->type == 50){
+			print("%lx:	word %ld\n", p->pc, (o2<<16)+o1);
+			print("%lx:	word %ld\n", p->pc, (o4<<16)+o3);
+			return;
+		}
+		if(o->size > 0) dis(o1, p->pc);
+		if(o->size > 2) dis(o2, p->pc+2);
+		if(o->size > 4) dis(o3, p->pc+4);
+		if(o->size > 6) dis(o4, p->pc+6);
+		if(o->size > 8) dis(o5, p->pc+8);
+		if(o->size > 10) dis(o6, p->pc+10);
+		if(o->size > 12) dis(o7, p->pc+12);
+		// if(o->size > 14) dis(o8, p->pc+14);
+	}
+}
+
+static long
+thumboprr(int a)
+{
+	switch(a) {
+	case AMVN:	return 0xf<<6;
+	case ACMP:	return 0xa<<6;
+	case ACMN:	return 0xb<<6;
+	case ATST:	return 0x8<<6;
+	case AADC:	return 0x5<<6;
+	case ASBC:	return 0x6<<6;
+	case AMUL:
+	case AMULU:	return 0xd<<6;
+	case AAND:	return 0x0<<6;
+	case AEOR:	return 0x1<<6;
+	case AORR:	return 0xc<<6;
+	case ABIC:	return 0xe<<6;
+	case ASRL:	return 0x3<<6;
+	case ASRA:	return 0x4<<6;
+	case ASLL:	return 0x2<<6;
+	}
+	diag("bad thumbop oprr %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static long
+thumbopirr(int a, int ld)
+{
+	if(ld)
+		diag("load in thumbopirr");
+	switch(a){
+		case AMOVW:	return 0xc<<11;
+		case AMOVH:
+		case AMOVHU:	return 0x10<<11;
+		case AMOVB:
+		case AMOVBU:	return 0xe<<11;
+	}
+	return 0;
+}
+	
+static long
+thumboprrr(int a, int ld)
+{
+	if(ld){
+		switch(a){
+		case AMOVW:	return 0x2c<<9;
+		case AMOVH:	return 0x2f<<9;
+		case AMOVB:	return 0x2b<<9;
+		case AMOVHU:	return 0x2d<<9;
+		case AMOVBU:	return 0x2e<<9;
+		}
+	}
+	else{
+		switch(a){
+		case AMOVW:	return 0x28<<9;
+		case AMOVHU:
+		case AMOVH:	return 0x29<<9;
+		case AMOVBU:
+		case AMOVB:	return 0x2a<<9;
+		}
+	}
+	diag("bad thumbop oprrr %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static long
+thumbopri(int a)
+{
+	switch(a) {
+	case ASRL:	return 0x1<<11;
+	case ASRA:	return 0x2<<11;
+	case ASLL:	return 0x0<<11;
+	case AADD:	return 0x2<<11;
+	case ASUB:	return 0x3<<11;
+	case AMOVW:	return 0x0<<11;
+	case ACMP:	return 0x1<<11;
+	}
+	diag("bad thumbop opri %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static long
+thumbophh(int a)
+{
+	switch(a) {
+	case AADD:	return 0x0<<8;
+	case AMOVW:	return 0x2<<8;
+	case ACMP:	return 0x1<<8;
+	}
+	diag("bad thumbop ophh %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static long
+thumbopbra(int a)
+{
+	switch(a) {
+	case ABEQ:	return 0x0<<8;
+	case ABNE:	return 0x1<<8;
+	case ABCS:	return 0x2<<8;
+	case ABHS:	return 0x2<<8;
+	case ABCC:	return 0x3<<8;
+	case ABLO:	return 0x3<<8;
+	case ABMI:	return 0x4<<8;
+	case ABPL:	return 0x5<<8;
+	case ABVS:	return 0x6<<8;
+	case ABVC:	return 0x7<<8;
+	case ABHI:	return 0x8<<8;
+	case ABLS:	return 0x9<<8;
+	case ABGE:	return 0xa<<8;
+	case ABLT:	return 0xb<<8;
+	case ABGT:	return 0xc<<8;
+	case ABLE:	return 0xd<<8;
+	}
+	diag("bad thumbop opbra %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static long
+thumbopmv(int a, int ld)
+{
+	switch(a) {
+	case AMOVW: 	return (ld ? 0xd : 0xc)<<11;
+	case AMOVH:
+	case AMOVHU:	return (ld ? 0x11: 0x10)<<11;
+	case AMOVB:
+	case AMOVBU:	return (ld ? 0xf : 0xe)<<11;
+	}
+	diag("bad thumbop opmv %d", a);
+	prasm(curp);
+	return 0;
+}
+
+static void 
+lowreg(Prog *p, int r)
+{
+	if(high(r))
+		diag("high reg [%P]", p);
+}
+
+static void
+mult(Prog *p, int n, int m)
+{
+	if(m*(n/m) != n)
+		diag("%d not M(%d) [%P]", n, m, p);
+}
+
+static void 
+numr(Prog *p, int n, int min, int max)
+{
+	if(n < min || n > max)
+		diag("%d not in %d-%d [%P]", n, min, max, p);
+}
+
+static void 
+regis(Prog *p, int r, int r1, int r2)
+{
+	if(r != r1 && r != r2)
+		diag("reg %d not %d or %d [%P]", r, r1, r2, p);
+}
+
+void
+hputl(int n)
+{
+	cbp[1] = n>>8;
+	cbp[0] = n;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+thumbcount()
+{
+	int i, c = 0, t = 0;
+
+	for (i = 0; i < OPCNTSZ; i++)
+		t += opcount[i];
+	if(t == 0)
+		return;
+	for (i = 0; i < OPCNTSZ; i++){
+		c += opcount[i];
+		print("%d:	%d %d %d%%\n", i, opcount[i], c, (opcount[i]*100+t/2)/t);
+	}
+}
+	
+char *op1[] = { "lsl", "lsr", "asr" };
+char *op2[] = { "add", "sub" };
+char *op3[] = { "movw", "cmp", "add", "sub" };
+char *op4[] = { "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror",
+		        "tst", "neg", "cmp", "cmpn", "or", "mul", "bitc", "movn" };
+char *op5[] = { "add", "cmp", "movw", "bx" };
+char *op6[] = { "smovw", "smovh", "smovb", "lmovb", "lmovw", "lmovhu", "lmovbu", "lmovh" };
+char *op7[] = { "smovw", "lmovw", "smovb", "lmovbu" };
+char *op8[] = { "smovh", "lmovhu" };
+char *op9[] = { "smovw", "lmovw" };
+char *op10[] = { "push", "pop" };
+char *op11[] = { "stmia", "ldmia" };
+
+char *cond[] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
+			 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" };
+
+#define B(h, l)		bits(i, h, l)
+#define IMM(h, l)	B(h, l)
+#define REG(h, l)	reg(B(h, l))
+#define LHREG(h, l, lh)	lhreg(B(h, l), B(lh, lh))
+#define COND(h, l)	cond[B(h, l)]
+#define OP1(h, l)	op1[B(h, l)]
+#define OP2(h, l)	op2[B(h, l)]
+#define OP3(h, l)	op3[B(h, l)]
+#define OP4(h, l)	op4[B(h, l)]
+#define OP5(h, l)	op5[B(h, l)]
+#define OP6(h, l)	op6[B(h, l)]
+#define OP7(h, l)	op7[B(h, l)]
+#define OP8(h, l)	op8[B(h, l)]
+#define OP9(h, l)	op9[B(h, l)]
+#define OP10(h, l)	op10[B(h, l)]
+#define OP11(h, l)	op11[B(h, l)]
+#define SBZ(h, l)	if(IMM(h, l) != 0) diag("%x: %x bits %d,%d not zero", pc, i, h, l)
+#define SNBZ(h, l)	if(IMM(h, l) == 0) diag("%x: %x bits %d,%d zero", pc, i, h, l)
+#define SBO(h, l)	if(IMM(h, l) != 1) diag("%x: %x bits %d,%d not one", pc, i, h, l)
+
+static int
+bits(int i, int h, int l)
+{
+	if(h < l)
+		diag("h < l in bits");
+	return (i&(((1<<(h-l+1))-1)<<l))>>l;
+}
+
+static char *
+reg(int r)
+{
+	static char s[4][4];
+	static int i = 0;
+
+	if(r < 0 || r > 7)
+		diag("register %d out of range", r);
+	i++;
+	if(i == 4)
+		i = 0;
+	sprint(s[i], "r%d", r);
+	return s[i];
+}
+
+static char *regnames[] = { "sp", "lr", "pc" };
+
+static char *
+lhreg(int r, int lh)
+{
+	static char s[4][4];
+	static int i = 0;
+
+	if(lh == 0)
+		return reg(r);
+	if(r < 0 || r > 7)
+		diag("high register %d out of range", r);
+	i++;
+	if(i == 4)
+		i = 0;
+	if(r >= 5)
+		sprint(s[i], "%s", regnames[r-5]);
+	else
+		sprint(s[i], "r%d", r+8);
+	return s[i];
+}
+	
+static void
+illegal(int i, int pc)
+{
+	diag("%x: %x illegal instruction", pc, i);
+}
+
+static void
+dis(int i, int pc)
+{
+	static int lasto;
+	int o, l;
+	char *op;
+
+	print("%x: %x:	", pc, i);
+	if(i&0xffff0000)
+		illegal(i, pc);
+	o = B(15, 13);
+	switch(o){
+	case 0:
+		o = B(12, 11);
+		switch(o){
+			case 0:
+			case 1:
+			case 2:
+				print("%s	%d, %s, %s\n", OP1(12, 11), IMM(10, 6), REG(5, 3), REG(2, 0));
+				return;
+			case 3:
+				if(B(10, 10) == 0)
+					print("%s	%s, %s, %s\n", OP2(9, 9), REG(8, 6), REG(5, 3), REG(2, 0));
+				else
+					print("%s	%d, %s, %s\n", OP2(9, 9), IMM(8, 6), REG(5, 3), REG(2, 0));
+				return;
+		}
+	case 1:
+		print("%s	%d, %s\n", OP3(12, 11), IMM(7, 0), REG(10, 8));
+		return;
+	case 2:
+		o = B(12, 10);
+		if(o == 0){
+			print("%s	%s, %s\n", OP4(9, 6), REG(5, 3), REG(2, 0));
+			return;
+		}
+		if(o == 1){
+			o = B(9, 8);
+			if(o == 3){
+				SBZ(7, 7);
+				SBZ(2, 0);
+				print("%s	%s\n", OP5(9, 8), LHREG(5, 3, 6));
+				return;
+			}
+			SNBZ(7, 6);
+			print("%s	%s, %s\n", OP5(9, 8), LHREG(5, 3, 6), LHREG(2, 0, 7));
+			return;
+		}
+		if(o == 2 || o == 3){
+			print("movw	%d(pc)[%x], %s\n", 4*IMM(7, 0), 4*IMM(7, 0)+pc+4, REG(10, 8));
+			return;
+		}
+		op = OP6(11, 9);
+		if(*op == 'l')
+			print("%s	[%s, %s], %s\n", op+1, REG(8, 6), REG(5, 3), REG(2, 0));
+		else
+			print("%s	%s, [%s, %s]\n", op+1, REG(2, 0), REG(8, 6), REG(5, 3));
+		return;
+	case 3:
+		op = OP7(12, 11);
+		if(B(12, 11) == 0 || B(12,11) == 1)
+			l = 4;
+		else
+			l = 1;
+		if(*op == 'l')
+			print("%s	%d(%s), %s\n", op+1, l*IMM(10, 6), REG(5, 3), REG(2, 0));
+		else
+			print("%s	%s, %d(%s)\n", op+1, REG(2, 0), l*IMM(10, 6), REG(5, 3));
+		return;
+	case 4:
+		if(B(12, 12) == 0){
+			op = OP8(11, 11);
+			if(*op == 'l')
+				print("%s	%d(%s), %s\n", op+1, 2*IMM(10, 6), REG(5, 3), REG(2, 0));
+			else
+				print("%s	%s, %d(%s)\n", op+1, REG(2, 0), 2*IMM(10, 6), REG(5, 3));
+			return;
+		}
+		op = OP9(11, 11);
+		if(*op == 'l')
+			print("%s	%d(sp), %s\n", op+1, 4*IMM(7, 0), REG(10, 8));
+		else
+			print("%s	%s, %d(sp)\n", op+1, REG(10, 8), 4*IMM(7, 0));
+		return;
+	case 5:
+		if(B(12, 12) == 0){
+			if(B(11, 11) == 0)
+				print("add	%d, pc, %s\n", 4*IMM(7, 0), REG(10, 8));
+			else
+				print("add	%d, sp, %s\n", 4*IMM(7, 0), REG(10, 8));
+			return;
+		}
+		if(B(11, 8) == 0){
+			print("%s	%d, sp\n", OP2(7, 7), 4*IMM(6, 0));
+			return;
+		}
+		SBO(10, 10);
+		SBZ(9, 9);
+		if(B(8, 8) == 0)
+			print("%s	sp, %d\n", OP10(11, 11), IMM(7, 0));
+		else
+			print("%s	sp, %d|15\n", OP10(11, 11), IMM(7, 0));
+		return;
+	case 6:
+		if(B(12, 12) == 0){
+			print("%s	%s, %d\n", OP11(11, 11), REG(10, 8), IMM(7, 0));
+			return;
+		}
+		if(B(11, 8) == 0xf){
+			print("swi	%d\n", IMM(7, 0));
+			return;
+		}
+		o = IMM(7, 0);
+		if(o&0x80)
+			o |= 0xffffff00;
+		o = pc+4+(o<<1);
+		print("b%s	%x\n", COND(11, 8), o);
+		return;
+	case 7:
+		o = B(12, 11);
+		switch(o){
+			case 0:
+				o = IMM(10, 0);
+				if(o&0x400)
+					o |= 0xfffff800;
+				o = pc+4+(o<<1);
+				print("b	%x\n", o);
+				return;
+			case 1:
+				illegal(i, pc);
+				return;
+			case 2:
+				lasto = IMM(10, 0);
+				print("bl\n");
+				return;
+			case 3:
+				if(lasto&0x400)
+					lasto |= 0xfffff800;
+				o = IMM(10, 0);
+				o = (pc-2)+4+(o<<1)+(lasto<<12);
+				print("bl %x\n", o);
+				return;
+		}
+	}
+}
--- /dev/null
+++ b/utils/tr/mkfile
@@ -1,0 +1,13 @@
+<../../mkconfig
+
+TARG=tr
+
+OFILES=	tr.$O\
+
+HFILES=
+
+LIBS=9		# libbio.a uses lib9.a so order matters.
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
--- /dev/null
+++ b/utils/tr/tr.c
@@ -1,0 +1,355 @@
+#include 	<lib9.h>
+
+typedef struct PCB	/* Control block controlling specification parse */
+{
+	char	*base;		/* start of specification */
+	char	*current;	/* current parse point */
+	long	last;		/* last Rune returned */
+	long	final;		/* final Rune in a span */
+} Pcb;
+
+uchar	bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+#define	SETBIT(a, c)		((a)[(c)/8] |= bits[(c)&07])
+#define	CLEARBIT(a,c)		((a)[(c)/8] &= ~bits[(c)&07])
+#define	BITSET(a,c)		((a)[(c)/8] & bits[(c)&07])
+
+#define	MAXRUNE	0xFFFF
+
+uchar	f[(MAXRUNE+1)/8];
+uchar	t[(MAXRUNE+1)/8];
+char 	wbuf[4096];
+char	*wptr;
+
+Pcb pfrom, pto;
+
+int cflag;
+int dflag;
+int sflag;
+
+void	complement(void);
+void	delete(void);
+void	squeeze(void);
+void	translit(void);
+void	error(char*);
+long	canon(Pcb*);
+char	*getrune(char*, Rune*);
+void	Pinit(Pcb*, char*);
+void	Prewind(Pcb *p);
+int	readrune(int, long*);
+void	wflush(int);
+void	writerune(int, Rune);
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 's':	sflag++; break;
+	case 'd':	dflag++; break;
+	case 'c':	cflag++; break;
+	default:	error("bad option");
+	}ARGEND
+	if(argc>0)
+		Pinit(&pfrom, argv[0]);
+	if(argc>1)
+		Pinit(&pto, argv[1]);
+	if(argc>2)
+		error("arg count");
+	if(dflag) {
+		if ((sflag && argc != 2) || (!sflag && argc != 1))
+			error("arg count");
+		delete();
+	} else {
+		if (argc != 2)
+			error("arg count");
+		if (cflag)
+			complement();
+		else translit();
+	}
+	exits(0);
+}
+
+void
+delete(void)
+{
+	long c, last;
+
+	if (cflag) {
+		memset((char *) f, 0xff, sizeof f);
+		while ((c = canon(&pfrom)) >= 0)
+			CLEARBIT(f, c);
+	} else {
+		while ((c = canon(&pfrom)) >= 0)
+			SETBIT(f, c);
+	}
+	if (sflag) {
+		while ((c = canon(&pto)) >= 0)
+			SETBIT(t, c);
+	}
+
+	last = 0x10000;
+	while (readrune(0, &c) > 0) {
+		if(!BITSET(f, c) && (c != last || !BITSET(t,c))) {
+			last = c;
+			writerune(1, (Rune) c);
+		}
+	}
+	wflush(1);
+}
+
+void
+complement(void)
+{
+	Rune *p;
+	int i;
+	long from, to, lastc, high;
+
+	lastc = 0;
+	high = 0;
+	while ((from = canon(&pfrom)) >= 0) {
+		if (from > high) high = from;
+		SETBIT(f, from);
+	}
+	while ((to = canon(&pto)) > 0) {
+		if (to > high) high = to;
+		SETBIT(t,to);
+	}
+	Prewind(&pto);
+	if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+		error("can't allocate memory");
+	for (i = 0; i <= high; i++){
+		if (!BITSET(f,i)) {
+			if ((to = canon(&pto)) < 0)
+				to = lastc;
+			else lastc = to;
+			p[i] = to;
+		}
+		else p[i] = i;
+	}
+	if (sflag){
+		lastc = 0x10000;
+		while (readrune(0, &from) > 0) {
+			if (from > high)
+				from = to;
+			else
+				from = p[from];
+			if (from != lastc || !BITSET(t,from)) {
+				lastc = from;
+				writerune(1, (Rune) from);
+			}
+		}
+				
+	} else {
+		while (readrune(0, &from) > 0){
+			if (from > high)
+				from = to;
+			else
+				from = p[from];
+			writerune(1, (Rune) from);
+		}
+	}
+	wflush(1);
+}
+
+void
+translit(void)
+{
+	Rune *p;
+	int i;
+	long from, to, lastc, high;
+
+	lastc = 0;
+	high = 0;
+	while ((from = canon(&pfrom)) >= 0)
+		if (from > high) high = from;
+	Prewind(&pfrom);
+	if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+		error("can't allocate memory");
+	for (i = 0; i <= high; i++)
+		p[i] = i;
+	while ((from = canon(&pfrom)) >= 0) {
+		if ((to = canon(&pto)) < 0)
+			to = lastc;
+		else lastc = to;
+		if (BITSET(f,from) && p[from] != to)
+			error("ambiguous translation");
+		SETBIT(f,from);
+		p[from] = to;
+		SETBIT(t,to);
+	}
+	while ((to = canon(&pto)) >= 0) {
+		SETBIT(t,to);
+	}
+	if (sflag){
+		lastc = 0x10000;
+		while (readrune(0, &from) > 0) {
+			if (from <= high)
+				from = p[from];
+			if (from != lastc || !BITSET(t,from)) {
+				lastc = from;
+				writerune(1, (Rune) from);
+			}
+		}
+				
+	} else {
+		while (readrune(0, &from) > 0) {
+			if (from <= high)
+				from = p[from];
+			writerune(1, (Rune) from);
+		}
+	}
+	wflush(1);
+}
+
+int
+readrune(int fd, long *rp)
+{
+	Rune r;
+	int j;
+	static int i, n;
+	static char buf[4096];
+
+	j = i;
+	for (;;) {
+		if (i >= n) {
+			wflush(1);
+			if (j != i)
+				memcpy(buf, buf+j, n-j);
+			i = n-j;
+			n = read(fd, &buf[i], sizeof(buf)-i);
+			if (n < 0)
+				error("read error");
+			if (n == 0)
+				return 0;
+			j = 0;
+			n += i;
+		}
+		i++;
+		if (fullrune(&buf[j], i-j))
+			break;
+	}
+	chartorune(&r, &buf[j]);
+	*rp = r;
+	return 1;
+}
+
+void
+writerune(int fd, Rune r)
+{
+	char buf[UTFmax];
+	int n;
+
+	if (!wptr)
+		wptr = wbuf;
+	n = runetochar(buf, (Rune*)&r);
+	if (wptr+n >= wbuf+sizeof(wbuf))
+		wflush(fd);
+	memcpy(wptr, buf, n);
+	wptr += n;
+}
+
+void
+wflush(int fd)
+{
+	if (wptr && wptr > wbuf)
+		if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf)
+			error("write error");
+	wptr = wbuf;
+}
+
+char *
+getrune(char *s, Rune *rp)
+{
+	Rune r;
+	char *save;
+	int i, n;
+
+	s += chartorune(rp, s);
+	if((r = *rp) == '\\' && *s){
+		n = 0;
+		if (*s == 'x') {
+			s++;
+			for (i = 0; i < 4; i++) {
+				save = s;
+				s += chartorune(&r, s);
+				if ('0' <= r && r <= '9')
+					n = 16*n + r - '0';
+				else if ('a' <= r && r <= 'f')
+					n = 16*n + r - 'a' + 10;
+				else if ('A' <= r && r <= 'F')
+					n = 16*n + r - 'A' + 10;
+				else {
+					if (i == 0)
+						*rp = 'x';
+					else *rp = n;
+					return save;
+				}
+			}
+		} else {
+			for(i = 0; i < 3; i++) {
+				save = s;
+				s += chartorune(&r, s);
+				if('0' <= r && r <= '7')
+					n = 8*n + r - '0';
+				else {
+					if (i == 0)
+					{
+						*rp = r;
+						return s;
+					}
+					*rp = n;
+					return save;
+				}
+			}
+			if(n > 0377)
+				error("char>0377");
+		}
+		*rp = n;
+	}
+	return s;
+}
+
+long
+canon(Pcb *p)
+{
+	Rune r;
+
+	if (p->final >= 0) {
+		if (p->last < p->final)
+			return ++p->last;
+		p->final = -1;
+	}
+	if (*p->current == '\0')
+		return -1;
+	if(*p->current == '-' && p->last >= 0 && p->current[1]){
+		p->current = getrune(p->current+1, &r);
+		if (r < p->last)
+			error ("Invalid range specification");
+		if (r > p->last) {
+			p->final = r;
+			return ++p->last;
+		}
+	}
+	p->current = getrune(p->current, &r);
+	p->last = r;
+	return p->last;
+}
+
+void
+Pinit(Pcb *p, char *cp)
+{
+	p->current = p->base = cp;
+	p->last = p->final = -1;
+}
+void
+Prewind(Pcb *p)
+{
+	p->current = p->base;
+	p->last = p->final = -1;
+}
+void
+error(char *s)
+{
+	fprint(2, "tr: %s\n", s);
+	exits(s);
+}
--- /dev/null
+++ b/utils/va/a.h
@@ -1,0 +1,181 @@
+#include <lib9.h>
+#include <bio.h>
+#include "../vc/v.out.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		8192
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	vlong	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+EXTERN	struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN	struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym*	sym;
+	vlong	offset;
+	short	type;
+	short	reg;
+	short	name;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	vlong	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	nosched;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	alloc(long);
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+int	isreg(Gen*);
+void	outcode(int, Gen*, int, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	maclin(void);
+void	macprag(void);
+void	macif(int);
+void	macend(void);
+void	outhist(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/utils/va/a.y
@@ -1,0 +1,588 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	vlong	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token	<lval>	LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token	<lval>	LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token	<lval>	LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token	<lval>	LCONST LSP LSB LFP LPC LHI LLO LMREG 
+%token	<lval>	LTYPEX LREG LFREG LFCREG LR LM LF
+%token	<lval>	LFCR LSCHED
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg
+%type	<gen>	gen vgen lgen vlgen rel reg freg mreg fcreg
+%type	<gen>	imm ximm ireg name oreg imr nireg fgen
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	LSCHED ';'
+	{
+		nosched = $1;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * Immed-type
+ */
+	LTYPE1 imr ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE1 imr ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * NOR
+ */
+|	LTYPE2 imr ',' sreg ',' imr
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE2 imr ',' imr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * LOAD/STORE, but not MOVW
+ */
+|	LTYPE3 lgen ',' gen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * SPECIAL
+ */
+|	LTYPE4 comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * MOVW
+ */
+|	LTYPE5 vlgen ',' vgen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * MUL/DIV
+ */
+|	LTYPE6 reg ',' sreg comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTYPE6 reg ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * JMP/JAL
+ */
+|	LTYPE7 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE7 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 sreg ',' nireg
+	{
+		outcode($1, &nullgen, $2, &$4);
+	}
+/*
+ * BEQ/BNE
+ */
+|	LTYPE9 gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPE9 gen ',' sreg ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * B-other
+ */
+|	LTYPEA gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTYPEB name ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEB name ',' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * DATA
+ */
+|	LTYPEC name '/' con ',' ximm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * floating-type
+ */
+|	LTYPED freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' LFREG ',' freg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPEF freg ',' LFREG comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+/*
+ * coprocessor branch
+ */
+|	LTYPEG comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * word
+ */
+|	LTYPEH comma ximm
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * NOP
+ */
+|	LTYPEI comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEI ',' vgen
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPEI vgen comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * BREAK -- overloaded with CACHE opcode
+ */
+|	LTYPEJ comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEJ vgen ',' vgen
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+
+comma:
+|	','
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+vlgen:
+	lgen
+|	fgen
+|	mreg
+|	fcreg
+|	LHI
+	{
+		$$ = nullgen;
+		$$.type = D_HI;
+	}
+|	LLO
+	{
+		$$ = nullgen;
+		$$.type = D_LO;
+	}
+
+vgen:
+	gen
+|	fgen
+|	mreg
+|	fcreg
+|	LHI
+	{
+		$$ = nullgen;
+		$$.type = D_HI;
+	}
+|	LLO
+	{
+		$$ = nullgen;
+		$$.type = D_LO;
+	}
+
+lgen:
+	gen
+|	ximm
+
+fgen:
+	freg
+
+mreg:
+	LMREG
+	{
+		$$ = nullgen;
+		$$.type = D_MREG;
+		$$.reg = $1;
+	}
+|	LM '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_MREG;
+		$$.reg = $3;
+	}
+
+fcreg:
+	LFCREG
+	{
+		$$ = nullgen;
+		$$.type = D_FCREG;
+		$$.reg = $1;
+	}
+|	LFCR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCREG;
+		$$.reg = $3;
+	}
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+ximm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' oreg
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' '*' '$' oreg
+	{
+		$$ = $4;
+		$$.type = D_OCONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+nireg:
+	ireg
+|	con ireg
+	{
+		if($1 != 0)
+			yyerror("offset must be zero");
+		$$ = $2;
+	}
+|	name
+	{
+		$$ = $1;
+		if($1.name != D_EXTERN && $1.name != D_STATIC) {
+		}
+	}
+
+ireg:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+
+gen:
+	reg
+|	con
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.offset = $1;
+	}
+|	oreg
+
+oreg:
+	name
+|	name '(' sreg ')'
+	{
+		$$ = $1;
+		$$.type = D_OREG;
+		$$.reg = $3;
+	}
+|	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+imr:
+	reg
+|	imm
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+reg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+sreg:
+	LREG
+|	LR '(' con ')'
+	{
+		if($$ < 0 || $$ >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/utils/va/l.s
@@ -1,0 +1,703 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define	BY2PG		4096			/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)		/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+
+#define	MAXMACH		4			/* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define	MS2HZ		50			/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/20)		/* ticks to seconds */
+#define	TK2MS(t)	((t)*MS2HZ)		/* ticks to milliseconds */
+#define	MS2TK(t)	((t)/MS2HZ)		/* milliseconds to ticks */
+
+/*
+ * CP0 registers
+ */
+
+#define INDEX		0
+#define RANDOM		1
+#define TLBPHYS		2
+#define CONTEXT		4
+#define BADVADDR	8
+#define TLBVIRT		10
+#define STATUS		12
+#define CAUSE		13
+#define EPC		14
+#define	PRID		15
+
+/*
+ * M(STATUS) bits
+ */
+#define IEC		0x00000001
+#define KUC		0x00000002
+#define IEP		0x00000004
+#define KUP		0x00000008
+#define INTMASK		0x0000ff00
+#define SW0		0x00000100
+#define SW1		0x00000200
+#define INTR0		0x00000400
+#define INTR1		0x00000800
+#define INTR2		0x00001000
+#define INTR3		0x00002000
+#define INTR4		0x00004000
+#define INTR5		0x00008000
+#define ISC		0x00010000
+#define SWC		0x00020000
+#define CU1		0x20000000
+
+/*
+ * Traps
+ */
+
+#define	UTLBMISS	(KSEG0+0x00)
+#define	EXCEPTION	(KSEG0+0x80)
+
+/*
+ * Magic registers
+ */
+
+#define	MACH		25		/* R25 is m-> */
+#define	USER		24		/* R24 is u-> */
+#define	MPID		0xBF000000	/* long; low 3 bits identify mp bus slot */
+#define WBFLUSH		0xBC000000	/* D-CACHE data; used for write buffer flush */
+
+/*
+ * Fundamental addresses
+ */
+
+#define	MACHADDR	0x80014000
+#define	USERADDR	0xC0000000
+#define	UREGADDR	(USERADDR+BY2PG-4-0xA0)
+/*
+ * MMU
+ */
+
+#define	KUSEG	0x00000000
+#define KSEG0	0x80000000
+#define KSEG1	0xA0000000
+#define	KSEG2	0xC0000000
+#define	KSEGM	0xE0000000	/* mask to check which seg */
+
+#define	PTEGLOBL	(1<<8)
+#define	PTEVALID	(1<<9)
+#define	PTEWRITE	(1<<10)
+#define	PTEPID(n)	((n)<<6)
+
+#define	NTLBPID	64	/* number of pids */
+#define	NTLB	64	/* number of entries */
+#define	TLBROFF	8	/* offset of first randomly indexed entry */
+
+/*
+ * Address spaces
+ */
+
+#define	UZERO	KUSEG			/* base of user address space */
+#define	UTZERO	(UZERO+BY2PG)		/* first address in user text */
+#define	USTKTOP	KZERO			/* byte just beyond user stack */
+#define	TSTKTOP	(USERADDR+100*BY2PG)	/* top of temporary stack */
+#define	KZERO	KSEG0			/* base of kernel address space */
+#define	KTZERO	(KSEG0+0x20000)		/* first address in kernel text */
+#define	USTACKSIZE	(4*1024*1024)	/* size of user stack */
+/*
+ * Exception codes
+ */
+#define	CINT	 0		/* external interrupt */
+#define	CTLBM	 1		/* TLB modification */
+#define	CTLBL	 2		/* TLB miss (load or fetch) */
+#define	CTLBS	 3		/* TLB miss (store) */
+#define	CADREL	 4		/* address error (load or fetch) */
+#define	CADRES	 5		/* address error (store) */
+#define	CBUSI	 6		/* bus error (fetch) */
+#define	CBUSD	 7		/* bus error (data load or store) */
+#define	CSYS	 8		/* system call */
+#define	CBRK	 9		/* breakpoint */
+#define	CRES	10		/* reserved instruction */
+#define	CCPU	11		/* coprocessor unusable */
+#define	COVF	12		/* arithmetic overflow */
+#define	CUNK13	13		/* undefined 13 */
+#define	CUNK14	14		/* undefined 14 */
+#define	CUNK15	15		/* undefined 15 */
+
+#define	NSEG	5
+
+#define SP		R29
+
+#define PROM		(KSEG1+0x1FC00000)
+#define	NOOP		NOR R0,R0
+#define	WAIT		NOOP; NOOP
+
+/*
+ * Boot first processor
+ *   - why is the processor number loaded from R0 ?????
+ */
+TEXT	start(SB), $-4
+
+	MOVW	$setR30(SB), R30
+	MOVW	$(CU1|INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+	MOVW	R1, M(STATUS)
+	WAIT
+
+	MOVW	$(0x1C<<7), R1
+	MOVW	R1, FCR31	/* permit only inexact and underflow */
+	NOOP
+	MOVD	$0.5, F26
+	SUBD	F26, F26, F24
+	ADDD	F26, F26, F28
+	ADDD	F28, F28, F30
+
+	MOVD	F24, F0
+	MOVD	F24, F2
+	MOVD	F24, F4
+	MOVD	F24, F6
+	MOVD	F24, F8
+	MOVD	F24, F10
+	MOVD	F24, F12
+	MOVD	F24, F14
+	MOVD	F24, F16
+	MOVD	F24, F18
+	MOVD	F24, F20
+	MOVD	F24, F22
+
+	MOVW	$MACHADDR, R(MACH)
+	ADDU	$(BY2PG-4), R(MACH), SP
+	MOVW	$0, R(USER)
+	MOVW	R0, 0(R(MACH))
+
+	MOVW	$edata(SB), R1
+	MOVW	$end(SB), R2
+
+clrbss:
+	MOVB	$0, (R1)
+	ADDU	$1, R1
+	BNE	R1, R2, clrbss
+
+	MOVW	R4, _argc(SB)
+	MOVW	R5, _argv(SB)
+	MOVW	R6, _env(SB)
+	JAL	main(SB)
+	JMP	(R0)
+
+/*
+ * Take first processor into user mode
+ * 	- argument is stack pointer to user
+ */
+
+TEXT	touser(SB), $-4
+
+	MOVW	M(STATUS), R1
+	OR	$(KUP|IEP), R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	0(FP), SP
+	MOVW	$(UTZERO+32), R26	/* header appears in text */
+	RFE	(R26)
+
+/*
+ * Bring subsequent processors on line
+ */
+TEXT	newstart(SB), $0
+
+	MOVW	$setR30(SB), R30
+	MOVW	$(INTR5|INTR4|INTR3|INTR2|INTR1|SW1|SW0), R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	$MACHADDR, R(MACH)
+	MOVB	(MPID+3), R1
+	AND	$7, R1
+	SLL	$PGSHIFT, R1, R2
+	ADDU	R2, R(MACH)
+	ADDU	$(BY2PG-4), R(MACH), SP
+	MOVW	$0, R(USER)
+	MOVW	R1, 0(R(MACH))
+	JAL	online(SB)
+	JMP	(R0)
+
+TEXT	firmware(SB), $0
+
+	MOVW	$(PROM+0x18), R1 /**/
+/*	MOVW	$(PROM+0x00), R1 /**/
+	JMP	(R1)
+
+TEXT	splhi(SB), $0
+
+	MOVW	M(STATUS), R1
+	AND	$~IEC, R1, R2
+	MOVW	R2, M(STATUS)
+	NOOP
+	RET
+
+TEXT	spllo(SB), $0
+
+	MOVW	M(STATUS), R1
+	OR	$IEC, R1, R2
+	MOVW	R2, M(STATUS)
+	NOOP
+	RET
+
+TEXT	splx(SB), $0
+
+	MOVW	0(FP), R1
+	MOVW	M(STATUS), R2
+	AND	$IEC, R1
+	AND	$~IEC, R2
+	OR	R2, R1
+	MOVW	R1, M(STATUS)
+	NOOP
+	RET
+
+TEXT	wbflush(SB), $-4
+
+	MOVW	$WBFLUSH, R1
+	MOVW	0(R1), R1
+	RET
+
+TEXT	setlabel(SB), $0
+
+	MOVW	0(FP), R2
+	MOVW	$0, R1
+	MOVW	R31, 0(R2)
+	MOVW	R29, 4(R2)
+	RET
+
+TEXT	gotolabel(SB), $0
+
+	MOVW	0(FP), R2
+	MOVW	$1, R1
+	MOVW	0(R2), R31
+	MOVW	4(R2), R29
+	RET
+
+TEXT	gotopc(SB), $8
+
+	MOVW	0(FP), R7		/* save arguments for later */
+	MOVW	_argc(SB), R4
+	MOVW	_argv(SB), R5
+	MOVW	_env(SB), R6
+	MOVW	R0, 4(SP)
+	MOVW	$(64*1024), R1
+	MOVW	R1, 8(SP)
+	JAL	icflush(SB)
+	JMP	(R7)
+
+TEXT	puttlb(SB), $4
+
+	JAL	splhi(SB)
+	MOVW	0(FP), R2
+	MOVW	4(FP), R3
+	MOVW	R1, 4(SP)
+	MOVW	R2, M(TLBVIRT)
+	MOVW	R3, M(TLBPHYS)
+	NOOP
+	TLBP
+	NOOP
+	MOVW	M(INDEX), R4
+	BGEZ	R4, index
+	TLBWR
+	NOOP
+	JAL	splx(SB)
+	RET
+index:
+	TLBWI
+	NOOP
+	JAL	splx(SB)
+	RET
+
+TEXT	puttlbx(SB), $0
+
+	MOVW	0(FP), R4
+	MOVW	4(FP), R2
+	MOVW	8(FP), R3
+	SLL	$8, R4
+	MOVW	R2, M(TLBVIRT)
+	MOVW	R3, M(TLBPHYS)
+	MOVW	R4, M(INDEX)
+	NOOP
+	TLBWI
+	NOOP
+	RET
+
+TEXT	tlbp(SB), $0
+	TLBP
+	NOOP
+	MOVW	M(INDEX), R1
+	RET
+	
+TEXT	tlbvirt(SB), $0
+	TLBP
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	RET
+	
+
+TEXT	gettlb(SB), $0
+
+	MOVW	0(FP), R3
+	MOVW	4(FP), R4
+	SLL	$8, R3
+	MOVW	R3, M(INDEX)
+	NOOP
+	TLBR
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	MOVW	M(TLBPHYS), R2
+	NOOP
+	MOVW	R1, 0(R4)
+	MOVW	R2, 4(R4)
+	RET
+
+TEXT	gettlbvirt(SB), $0
+
+	MOVW	0(FP), R3
+	SLL	$8, R3
+	MOVW	R3, M(INDEX)
+	NOOP
+	TLBR
+	NOOP
+	MOVW	M(TLBVIRT), R1
+	NOOP
+	RET
+
+TEXT	vector80(SB), $-4
+
+	MOVW	$exception(SB), R26
+	JMP	(R26)
+
+TEXT	exception(SB), $-4
+
+	MOVW	M(STATUS), R26
+	AND	$KUP, R26
+	BEQ	R26, waskernel
+
+wasuser:
+	MOVW	SP, R26
+		/*
+		 * set kernel sp: ureg - ureg* - pc
+		 * done in 2 steps because R30 is not set
+		 * and the loader will make a literal
+		 */
+	MOVW	$((UREGADDR-2*BY2WD) & 0xffff0000), SP
+	OR	$((UREGADDR-2*BY2WD) & 0xffff), SP
+	MOVW	R26, 0x10(SP)			/* user SP */
+	MOVW	R31, 0x28(SP)
+	MOVW	R30, 0x2C(SP)
+	MOVW	M(CAUSE), R26
+	MOVW	R(MACH), 0x3C(SP)
+	MOVW	R(USER), 0x40(SP)
+	AND	$(0xF<<2), R26
+	SUB	$(CSYS<<2), R26
+
+	JAL	saveregs(SB)
+
+	MOVW	$setR30(SB), R30
+	SUBU	$(UREGADDR-2*BY2WD-USERADDR), SP, R(USER)
+	MOVW	$MPID, R1
+	MOVB	3(R1), R1
+	MOVW	$MACHADDR, R(MACH)		/* locn of mach 0 */
+	AND	$7, R1
+	SLL	$PGSHIFT, R1
+	ADDU	R1, R(MACH)			/* add offset for mach # */
+
+	BNE	R26, notsys
+
+	JAL	syscall(SB)
+
+	MOVW	0x28(SP), R31
+	MOVW	0x08(SP), R26
+	MOVW	0x2C(SP), R30
+	MOVW	R26, M(STATUS)
+	NOOP
+	MOVW	0x0C(SP), R26		/* old pc */
+	MOVW	0x10(SP), SP
+	RFE	(R26)
+
+notsys:
+	JAL	trap(SB)
+
+restore:
+	JAL	restregs(SB)
+	MOVW	0x28(SP), R31
+	MOVW	0x2C(SP), R30
+	MOVW	0x3C(SP), R(MACH)
+	MOVW	0x40(SP), R(USER)
+	MOVW	0x10(SP), SP
+	RFE	(R26)
+
+waskernel:
+	MOVW	$1, R26			/* not sys call */
+	MOVW	SP, -0x90(SP)		/* drop this if possible */
+	SUB	$0xA0, SP
+	MOVW	R31, 0x28(SP)
+	JAL	saveregs(SB)
+	JAL	trap(SB)
+	JAL	restregs(SB)
+	MOVW	0x28(SP), R31
+	ADD	$0xA0, SP
+	RFE	(R26)
+
+TEXT	saveregs(SB), $-4
+	MOVW	R1, 0x9C(SP)
+	MOVW	R2, 0x98(SP)
+	ADDU	$8, SP, R1
+	MOVW	R1, 0x04(SP)		/* arg to base of regs */
+	MOVW	M(STATUS), R1
+	MOVW	M(EPC), R2
+	MOVW	R1, 0x08(SP)
+	MOVW	R2, 0x0C(SP)
+
+	BEQ	R26, return		/* sys call, don't save */
+
+	MOVW	M(CAUSE), R1
+	MOVW	M(BADVADDR), R2
+	MOVW	R1, 0x14(SP)
+	MOVW	M(TLBVIRT), R1
+	MOVW	R2, 0x18(SP)
+	MOVW	R1, 0x1C(SP)
+	MOVW	HI, R1
+	MOVW	LO, R2
+	MOVW	R1, 0x20(SP)
+	MOVW	R2, 0x24(SP)
+					/* LINK,SB,SP missing */
+	MOVW	R28, 0x30(SP)
+					/* R27, R26 not saved */
+					/* R25, R24 missing */
+	MOVW	R23, 0x44(SP)
+	MOVW	R22, 0x48(SP)
+	MOVW	R21, 0x4C(SP)
+	MOVW	R20, 0x50(SP)
+	MOVW	R19, 0x54(SP)
+	MOVW	R18, 0x58(SP)
+	MOVW	R17, 0x5C(SP)
+	MOVW	R16, 0x60(SP)
+	MOVW	R15, 0x64(SP)
+	MOVW	R14, 0x68(SP)
+	MOVW	R13, 0x6C(SP)
+	MOVW	R12, 0x70(SP)
+	MOVW	R11, 0x74(SP)
+	MOVW	R10, 0x78(SP)
+	MOVW	R9, 0x7C(SP)
+	MOVW	R8, 0x80(SP)
+	MOVW	R7, 0x84(SP)
+	MOVW	R6, 0x88(SP)
+	MOVW	R5, 0x8C(SP)
+	MOVW	R4, 0x90(SP)
+	MOVW	R3, 0x94(SP)
+return:
+	RET
+
+TEXT	restregs(SB), $-4
+					/* LINK,SB,SP missing */
+	MOVW	0x30(SP), R28
+					/* R27, R26 not saved */
+					/* R25, R24 missing */
+	MOVW	0x44(SP), R23
+	MOVW	0x48(SP), R22
+	MOVW	0x4C(SP), R21
+	MOVW	0x50(SP), R20
+	MOVW	0x54(SP), R19
+	MOVW	0x58(SP), R18
+	MOVW	0x5C(SP), R17
+	MOVW	0x60(SP), R16
+	MOVW	0x64(SP), R15
+	MOVW	0x68(SP), R14
+	MOVW	0x6C(SP), R13
+	MOVW	0x70(SP), R12
+	MOVW	0x74(SP), R11
+	MOVW	0x78(SP), R10
+	MOVW	0x7C(SP), R9
+	MOVW	0x80(SP), R8
+	MOVW	0x84(SP), R7
+	MOVW	0x88(SP), R6
+	MOVW	0x8C(SP), R5
+	MOVW	0x90(SP), R4
+	MOVW	0x94(SP), R3
+	MOVW	0x24(SP), R2
+	MOVW	0x20(SP), R1
+	MOVW	R2, LO
+	MOVW	R1, HI
+	MOVW	0x08(SP), R1
+	MOVW	0x98(SP), R2
+	MOVW	R1, M(STATUS)
+	NOOP
+	MOVW	0x9C(SP), R1
+	MOVW	0x0C(SP), R26		/* old pc */
+	RET
+
+TEXT	rfnote(SB), $0
+	MOVW	0(FP), R26		/* 1st arg is &uregpointer */
+	SUBU	$(BY2WD), R26, SP	/* pc hole */
+	BNE	R26, restore
+	
+
+TEXT	clrfpintr(SB), $0
+	MOVW	FCR31, R1
+	MOVW	R1, R2
+	AND	$~(0x3F<<12), R2
+	MOVW	R2, FCR31
+	RET
+
+TEXT	savefpregs(SB), $0
+	MOVW	M(STATUS), R3
+	MOVW	0(FP), R1
+	MOVW	FCR31, R2
+
+	MOVD	F0, 0x00(R1)
+	MOVD	F2, 0x08(R1)
+	MOVD	F4, 0x10(R1)
+	MOVD	F6, 0x18(R1)
+	MOVD	F8, 0x20(R1)
+	MOVD	F10, 0x28(R1)
+	MOVD	F12, 0x30(R1)
+	MOVD	F14, 0x38(R1)
+	MOVD	F16, 0x40(R1)
+	MOVD	F18, 0x48(R1)
+	MOVD	F20, 0x50(R1)
+	MOVD	F22, 0x58(R1)
+	MOVD	F24, 0x60(R1)
+	MOVD	F26, 0x68(R1)
+	MOVD	F28, 0x70(R1)
+	MOVD	F30, 0x78(R1)
+
+	MOVW	R2, 0x80(R1)
+	AND	$~CU1, R3
+	MOVW	R3, M(STATUS)
+	RET
+
+TEXT	restfpregs(SB), $0
+
+	MOVW	M(STATUS), R3
+	MOVW	0(FP), R1
+	OR	$CU1, R3
+	MOVW	R3, M(STATUS)
+	MOVW	0x80(R1), R2
+
+	MOVD	0x00(R1), F0
+	MOVD	0x08(R1), F2
+	MOVD	0x10(R1), F4
+	MOVD	0x18(R1), F6
+	MOVD	0x20(R1), F8
+	MOVD	0x28(R1), F10
+	MOVD	0x30(R1), F12
+	MOVD	0x38(R1), F14
+	MOVD	0x40(R1), F16
+	MOVD	0x48(R1), F18
+	MOVD	0x50(R1), F20
+	MOVD	0x58(R1), F22
+	MOVD	0x60(R1), F24
+	MOVD	0x68(R1), F26
+	MOVD	0x70(R1), F28
+	MOVD	0x78(R1), F30
+
+	MOVW	R2, FCR31
+	AND	$~CU1, R3
+	MOVW	R3, M(STATUS)
+	RET
+
+/*
+ *  we avoid using R4, R5, R6, and R7 so gotopc can call us without saving them
+ */
+TEXT icflush(SB), $-4			/* icflush(physaddr, nbytes) */
+
+	MOVW	M(STATUS), R10
+	MOVW	0(FP), R8
+	MOVW	4(FP), R9
+	MOVW	$KSEG0, R3
+	OR	R3, R8
+	MOVW	$0, M(STATUS)
+	MOVW	$WBFLUSH, R1		/* wbflush */
+	MOVW	0(R1), R1
+	NOOP
+	MOVW	$KSEG1, R3
+	MOVW	$icflush0(SB), R2	/* make sure PC is in uncached address space */
+	MOVW	$(SWC|ISC), R1
+	OR	R3, R2
+	JMP	(R2)
+
+TEXT icflush0(SB), $-4
+
+	MOVW	R1, M(STATUS)		/* swap and isolate cache, splhi */
+	MOVW	$icflush1(SB), R2
+	JMP	(R2)
+
+TEXT icflush1(SB), $-4
+
+_icflush1:
+	MOVBU	R0, 0x00(R8)
+	MOVBU	R0, 0x04(R8)
+	MOVBU	R0, 0x08(R8)
+	MOVBU	R0, 0x0C(R8)
+	MOVBU	R0, 0x10(R8)
+	MOVBU	R0, 0x14(R8)
+	MOVBU	R0, 0x18(R8)
+	MOVBU	R0, 0x1C(R8)
+	MOVBU	R0, 0x20(R8)
+	MOVBU	R0, 0x24(R8)
+	MOVBU	R0, 0x28(R8)
+	MOVBU	R0, 0x2C(R8)
+	MOVBU	R0, 0x30(R8)
+	MOVBU	R0, 0x34(R8)
+	MOVBU	R0, 0x38(R8)
+	MOVBU	R0, 0x3C(R8)
+	SUB	$0x40, R9
+	ADD	$0x40, R8
+	BGTZ	R9, _icflush1
+	MOVW	$icflush2(SB), R2	/* make sure PC is in uncached address space */
+	OR	R3, R2
+	JMP	(R2)
+
+TEXT icflush2(SB), $-4
+
+	MOVW	$0, M(STATUS)		/* swap back caches, de-isolate them, and stay splhi */
+	NOOP				/* +++ */
+	MOVW	R10, M(STATUS)
+	RET
+
+TEXT dcflush(SB), $-4			/* dcflush(physaddr, nbytes) */
+
+	MOVW	M(STATUS), R6
+	MOVW	0(FP), R4
+	MOVW	4(FP), R5
+	MOVW	$KSEG0, R3
+	OR	R3, R4
+	MOVW	$0, M(STATUS)
+	MOVW	$WBFLUSH, R1
+	MOVW	0(R1), R1
+	NOOP
+	MOVW	$ISC, R1
+	MOVW	R1, M(STATUS)
+_dcflush0:
+	MOVBU	R0, 0x00(R4)
+	MOVBU	R0, 0x04(R4)
+	MOVBU	R0, 0x08(R4)
+	MOVBU	R0, 0x0C(R4)
+	MOVBU	R0, 0x10(R4)
+	MOVBU	R0, 0x14(R4)
+	MOVBU	R0, 0x18(R4)
+	MOVBU	R0, 0x1C(R4)
+	MOVBU	R0, 0x20(R4)
+	MOVBU	R0, 0x24(R4)
+	MOVBU	R0, 0x28(R4)
+	MOVBU	R0, 0x2C(R4)
+	MOVBU	R0, 0x30(R4)
+	MOVBU	R0, 0x34(R4)
+	MOVBU	R0, 0x38(R4)
+	MOVBU	R0, 0x3C(R4)
+	SUB	$0x40, R5
+	ADD	$0x40, R4
+	BGTZ	R5, _dcflush0
+	MOVW	$0, M(STATUS)
+	NOOP				/* +++ */
+	MOVW	R6, M(STATUS)
+	RET
--- /dev/null
+++ b/utils/va/lex.c
@@ -1,0 +1,701 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = 'v';
+	thestring = "mips";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	case  'L':			/* for little-endian mips */
+		thechar = '0';
+		thestring = "spim";
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ushort	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+	"HI",		LHI,	D_HI,
+	"LO",		LLO,	D_LO,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+	"R16",		LREG,	16,
+	"R17",		LREG,	17,
+	"R18",		LREG,	18,
+	"R19",		LREG,	19,
+	"R20",		LREG,	20,
+	"R21",		LREG,	21,
+	"R22",		LREG,	22,
+	"R23",		LREG,	23,
+	"R24",		LREG,	24,
+	"R25",		LREG,	25,
+	"R26",		LREG,	26,
+	"R27",		LREG,	27,
+	"R28",		LREG,	28,
+	"R29",		LREG,	29,
+	"R30",		LREG,	30,
+	"R31",		LREG,	31,
+
+	"M",		LM,	0,
+	"M0",		LMREG,	0,
+	"M1",		LMREG,	1,
+	"M2",		LMREG,	2,
+	"M3",		LMREG,	3,
+	"M4",		LMREG,	4,
+	"M5",		LMREG,	5,
+	"M6",		LMREG,	6,
+	"M7",		LMREG,	7,
+	"M8",		LMREG,	8,
+	"M9",		LMREG,	9,
+	"M10",		LMREG,	10,
+	"M11",		LMREG,	11,
+	"M12",		LMREG,	12,
+	"M13",		LMREG,	13,
+	"M14",		LMREG,	14,
+	"M15",		LMREG,	15,
+	"M16",		LMREG,	16,
+	"M17",		LMREG,	17,
+	"M18",		LMREG,	18,
+	"M19",		LMREG,	19,
+	"M20",		LMREG,	20,
+	"M21",		LMREG,	21,
+	"M22",		LMREG,	22,
+	"M23",		LMREG,	23,
+	"M24",		LMREG,	24,
+	"M25",		LMREG,	25,
+	"M26",		LMREG,	26,
+	"M27",		LMREG,	27,
+	"M28",		LMREG,	28,
+	"M29",		LMREG,	29,
+	"M30",		LMREG,	30,
+	"M31",		LMREG,	31,
+
+	"F",		LF,	0,
+
+	"F0",		LFREG,	0,
+	"F1",		LFREG,	1,
+	"F2",		LFREG,	2,
+	"F3",		LFREG,	3,
+	"F4",		LFREG,	4,
+	"F5",		LFREG,	5,
+	"F6",		LFREG,	6,
+	"F7",		LFREG,	7,
+	"F8",		LFREG,	8,
+	"F9",		LFREG,	9,
+	"F10",		LFREG,	10,
+	"F11",		LFREG,	11,
+	"F12",		LFREG,	12,
+	"F13",		LFREG,	13,
+	"F14",		LFREG,	14,
+	"F15",		LFREG,	15,
+	"F16",		LFREG,	16,
+	"F17",		LFREG,	17,
+	"F18",		LFREG,	18,
+	"F19",		LFREG,	19,
+	"F20",		LFREG,	20,
+	"F21",		LFREG,	21,
+	"F22",		LFREG,	22,
+	"F23",		LFREG,	23,
+	"F24",		LFREG,	24,
+	"F25",		LFREG,	25,
+	"F26",		LFREG,	26,
+	"F27",		LFREG,	27,
+	"F28",		LFREG,	28,
+	"F29",		LFREG,	29,
+	"F30",		LFREG,	30,
+	"F31",		LFREG,	31,
+
+	"FCR",		LFCR,	0,
+	"FCR0",		LFCREG,	0,
+	"FCR1",		LFCREG,	1,
+	"FCR2",		LFCREG,	2,
+	"FCR3",		LFCREG,	3,
+	"FCR4",		LFCREG,	4,
+	"FCR5",		LFCREG,	5,
+	"FCR6",		LFCREG,	6,
+	"FCR7",		LFCREG,	7,
+	"FCR8",		LFCREG,	8,
+	"FCR9",		LFCREG,	9,
+	"FCR10",	LFCREG,	10,
+	"FCR11",	LFCREG,	11,
+	"FCR12",	LFCREG,	12,
+	"FCR13",	LFCREG,	13,
+	"FCR14",	LFCREG,	14,
+	"FCR15",	LFCREG,	15,
+	"FCR16",	LFCREG,	16,
+	"FCR17",	LFCREG,	17,
+	"FCR18",	LFCREG,	18,
+	"FCR19",	LFCREG,	19,
+	"FCR20",	LFCREG,	20,
+	"FCR21",	LFCREG,	21,
+	"FCR22",	LFCREG,	22,
+	"FCR23",	LFCREG,	23,
+	"FCR24",	LFCREG,	24,
+	"FCR25",	LFCREG,	25,
+	"FCR26",	LFCREG,	26,
+	"FCR27",	LFCREG,	27,
+	"FCR28",	LFCREG,	28,
+	"FCR29",	LFCREG,	29,
+	"FCR30",	LFCREG,	30,
+	"FCR31",	LFCREG,	31,
+
+	"ADD",		LTYPE1, AADD,
+	"ADDU",		LTYPE1, AADDU,
+	"SUB",		LTYPE1, ASUB,	/* converted to ADD(-) in loader */
+	"SUBU",		LTYPE1, ASUBU,
+	"SGT",		LTYPE1, ASGT,
+	"SGTU",		LTYPE1, ASGTU,
+	"AND",		LTYPE1, AAND,
+	"OR",		LTYPE1, AOR,
+	"XOR",		LTYPE1, AXOR,
+	"SLL",		LTYPE1, ASLL,
+	"SRL",		LTYPE1, ASRL,
+	"SRA",		LTYPE1, ASRA,
+
+	"ADDV",		LTYPE1, AADDV,
+	"ADDVU",		LTYPE1, AADDVU,
+	"SUBV",		LTYPE1, ASUBV,	/* converted to ADD(-) in loader */
+	"SUBVU",		LTYPE1, ASUBVU,
+	"SLLV",		LTYPE1, ASLLV,
+	"SRLV",		LTYPE1, ASRLV,
+	"SRAV",		LTYPE1, ASRAV,
+
+	"NOR",		LTYPE2, ANOR,
+
+	"MOVB",		LTYPE3, AMOVB,
+	"MOVBU",	LTYPE3, AMOVBU,
+	"MOVH",		LTYPE3, AMOVH,
+	"MOVHU",	LTYPE3, AMOVHU,
+	"MOVWL",	LTYPE3, AMOVWL,
+	"MOVWR",	LTYPE3, AMOVWR,
+	"MOVVL",	LTYPE3, AMOVVL,
+	"MOVVR",	LTYPE3, AMOVVR,
+
+	"BREAK",	LTYPEJ, ABREAK,		/* overloaded CACHE opcode */
+	"END",		LTYPE4, AEND,
+	"REM",		LTYPE6, AREM,
+	"REMU",		LTYPE6, AREMU,
+	"RET",		LTYPE4, ARET,
+	"SYSCALL",	LTYPE4, ASYSCALL,
+	"TLBP",		LTYPE4, ATLBP,
+	"TLBR",		LTYPE4, ATLBR,
+	"TLBWI",	LTYPE4, ATLBWI,
+	"TLBWR",	LTYPE4, ATLBWR,
+
+	"MOVW",		LTYPE5, AMOVW,
+	"MOVV",		LTYPE5, AMOVV,
+	"MOVD",		LTYPE5, AMOVD,
+	"MOVF",		LTYPE5, AMOVF,
+
+	"DIV",		LTYPE6, ADIV,
+	"DIVU",		LTYPE6, ADIVU,
+	"MUL",		LTYPE6, AMUL,
+	"MULU",		LTYPE6, AMULU,
+	"DIVV",		LTYPE6, ADIVV,
+	"DIVVU",		LTYPE6, ADIVVU,
+	"MULV",		LTYPE6, AMULV,
+	"MULVU",		LTYPE6, AMULVU,
+
+	"RFE",		LTYPE7, ARFE,
+	"JMP",		LTYPE7, AJMP,
+
+	"JAL",		LTYPE8, AJAL,
+
+	"BEQ",		LTYPE9, ABEQ,
+	"BNE",		LTYPE9, ABNE,
+
+	"BGEZ",		LTYPEA, ABGEZ,
+	"BGEZAL",	LTYPEA, ABGEZAL,
+	"BGTZ",		LTYPEA, ABGTZ,
+	"BLEZ",		LTYPEA, ABLEZ,
+	"BLTZ",		LTYPEA, ABLTZ,
+	"BLTZAL",	LTYPEA, ABLTZAL,
+
+	"TEXT",		LTYPEB, ATEXT,
+	"GLOBL",	LTYPEB, AGLOBL,
+
+	"DATA",		LTYPEC, ADATA,
+
+	"MOVDF",	LTYPE5, AMOVDF,
+	"MOVDW",	LTYPE5, AMOVDW,
+	"MOVFD",	LTYPE5, AMOVFD,
+	"MOVFW",	LTYPE5, AMOVFW,
+	"MOVWD",	LTYPE5, AMOVWD,
+	"MOVWF",	LTYPE5, AMOVWF,
+
+	"ABSD",		LTYPED, AABSD,
+	"ABSF",		LTYPED, AABSF,
+	"ABSW",		LTYPED, AABSW,
+	"NEGD",		LTYPED, ANEGD,
+	"NEGF",		LTYPED, ANEGF,
+	"NEGW",		LTYPED, ANEGW,
+
+	"CMPEQD",	LTYPEF, ACMPEQD,
+	"CMPEQF",	LTYPEF, ACMPEQF,
+	"CMPGED",	LTYPEF, ACMPGED,
+	"CMPGEF",	LTYPEF, ACMPGEF,
+	"CMPGTD",	LTYPEF, ACMPGTD,
+	"CMPGTF",	LTYPEF, ACMPGTF,
+
+	"ADDD",		LTYPEE, AADDD,
+	"ADDF",		LTYPEE, AADDF,
+	"ADDW",		LTYPEE, AADDW,
+	"DIVD",		LTYPEE, ADIVD,
+	"DIVF",		LTYPEE, ADIVF,
+	"DIVW",		LTYPEE, ADIVW,
+	"MULD",		LTYPEE, AMULD,
+	"MULF",		LTYPEE, AMULF,
+	"MULW",		LTYPEE, AMULW,
+	"SUBD",		LTYPEE, ASUBD,
+	"SUBF",		LTYPEE, ASUBF,
+	"SUBW",		LTYPEE, ASUBW,
+
+	"BFPT",		LTYPEG, ABFPT,
+	"BFPF",		LTYPEG, ABFPF,
+
+	"WORD",		LTYPEH, AWORD,
+	"NOP",		LTYPEI, ANOP,
+	"SCHED",	LSCHED, 0,
+	"NOSCHED",	LSCHED, 0x80,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/?");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+	USED(g);
+	return 1;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+	case D_BRANCH:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+jackpot:
+	sf = 0;
+	s = g1->sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g1->name;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->name;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, reg|nosched);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
--- /dev/null
+++ b/utils/va/mkfile
@@ -1,0 +1,30 @@
+<../../mkconfig
+
+TARG=va
+
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../vc/v.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+YFLAGS=-D1 -d
+CFLAGS=	$CFLAGS -I../include
+
+lex.$O:	../cc/macbody ../cc/lexbody
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
--- /dev/null
+++ b/utils/vc/cgen.c
@@ -1,0 +1,1186 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesuv[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+			if(o != OAND)
+				break;
+			if(nn != Z)
+				gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case OMUL:
+	case ODIV:
+	case OMOD:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			gopcode(o, &nod, &nod1, &nod);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST)
+		if(!typefd[r->type->etype])
+		if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, r, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+		} else {
+			regalloc(&nod1, r, Z);
+			cgen(r, &nod1);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+
+		regalloc(&nod, n, nn);
+		gmove(&nod2, &nod);
+		gopcode(o, &nod1, Z, &nod);
+		gmove(&nod, &nod2);
+		if(nn != Z)
+			gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(o, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type)) {
+			if(nocast(n->type, nn->type)) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		if(nn && l->op == ONAME)	/* in x=++i, emit USED(i) */
+			gins(ANOP, l, Z);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r)) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		if(nn == Z || typefd[n->type->etype]) {
+			o = ONE;
+			if(true)
+				o = comrel[relindex(o)];
+			if(typefd[n->type->etype]) {
+				nodreg(&nod1, n, NREG+FREGZERO);
+				gopcode(o, &nod, &nod1, Z);
+			} else
+				gopcode(o, &nod, Z, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(true)
+			gopcode(OCOND, &nod, nodconst(0), &nod);
+		else
+			gopcode(OCOND, nodconst(1), &nod, &nod);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(nn != Z && !typefd[l->type->etype]) {
+			if(l->complex >= r->complex) {
+				regalloc(&nod1, l, nn);
+				cgen(l, &nod1);
+				regalloc(&nod, r, Z);
+				cgen(r, &nod);
+			} else {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+			}
+			switch(o) {
+			case OEQ:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, &nod, nodconst(0), &nod);
+				break;
+			case ONE:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, nodconst(1), &nod, &nod);
+				break;
+			case OLE:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				break;
+			case OGT:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLT:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OGE:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				break;
+			case OLS:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				break;
+			case OHI:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLO:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OHS:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				break;
+			}
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+			regfree(&nod1);
+			break;
+		}
+		if(sconst(l)) {
+			switch(o) {
+			default:
+				if(l->vconst != 0)
+					break;
+
+			case OGT:
+			case OHI:
+			case OLE:
+			case OLS:
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gopcode(o, l, &nod, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(sconst(r)) {
+			switch(o) {
+			default:
+				if(r->vconst != 0)
+					break;
+
+			case OGE:
+			case OHS:
+			case OLT:
+			case OLO:
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				gopcode(o, &nod, r, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod1, &nod, Z);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			nod1.xoffset += SZ_LONG;
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+
+			regfree(&nod1);
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no fn call
+		 */
+		if(nn != Z && nn->complex >= FNX) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 5) {
+		layout(&nod1, &nod2, w, 0, Z);
+		goto out;
+	}
+
+	/*
+	 * minimize space for unrolling loop
+	 * 3,4,5 times. (6 or more is never minimum)
+	 * if small structure, try 2 also.
+	 */
+	c = 0; /* set */
+	m = 100;
+	i = 3;
+	if(w <= 15)
+		i = 2;
+	for(; i<=5; i++)
+		if(i + w%i <= m) {
+			c = i;
+			m = c + w%c;
+		}
+
+	regalloc(&nod3, &regnode, Z);
+	layout(&nod1, &nod2, w%c, w/c, &nod3);
+	
+	pc1 = pc;
+	layout(&nod1, &nod2, c, 0, Z);
+
+	gopcode(OSUB, nodconst(1), Z, &nod3);
+	nod1.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1);
+	nod2.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2);
+	
+	gopcode(OEQ, &nod3, Z, Z);
+	p->as = ABGTZ;
+	patch(p, pc1);
+
+	regfree(&nod3);
+out:
+	regfree(&nod1);
+	regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &regnode, Z);
+	regalloc(&t2, &regnode, Z);
+	t1.type = types[TLONG];
+	t2.type = types[TLONG];
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
--- /dev/null
+++ b/utils/vc/enam.c
@@ -1,0 +1,118 @@
+char*	anames[] =
+{
+	"XXX",
+	"ABSD",
+	"ABSF",
+	"ABSW",
+	"ADD",
+	"ADDD",
+	"ADDF",
+	"ADDU",
+	"ADDW",
+	"AND",
+	"BEQ",
+	"BFPF",
+	"BFPT",
+	"BGEZ",
+	"BGEZAL",
+	"BGTZ",
+	"BLEZ",
+	"BLTZ",
+	"BLTZAL",
+	"BNE",
+	"BREAK",
+	"CMPEQD",
+	"CMPEQF",
+	"CMPGED",
+	"CMPGEF",
+	"CMPGTD",
+	"CMPGTF",
+	"DATA",
+	"DIV",
+	"DIVD",
+	"DIVF",
+	"DIVU",
+	"DIVW",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"JAL",
+	"JMP",
+	"MOVB",
+	"MOVBU",
+	"MOVD",
+	"MOVDF",
+	"MOVDW",
+	"MOVF",
+	"MOVFD",
+	"MOVFW",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVWD",
+	"MOVWF",
+	"MOVWL",
+	"MOVWR",
+	"MUL",
+	"MULD",
+	"MULF",
+	"MULU",
+	"MULW",
+	"NAME",
+	"NEGD",
+	"NEGF",
+	"NEGW",
+	"NOP",
+	"NOR",
+	"OR",
+	"REM",
+	"REMU",
+	"RET",
+	"RFE",
+	"SGT",
+	"SGTU",
+	"SLL",
+	"SRA",
+	"SRL",
+	"SUB",
+	"SUBD",
+	"SUBF",
+	"SUBU",
+	"SUBW",
+	"SYSCALL",
+	"TEXT",
+	"TLBP",
+	"TLBR",
+	"TLBWI",
+	"TLBWR",
+	"WORD",
+	"XOR",
+	"END",
+	"MOVV",
+	"MOVVL",
+	"MOVVR",
+	"SLLV",
+	"SRAV",
+	"SRLV",
+	"DIVV",
+	"DIVVU",
+	"REMV",
+	"REMVU",
+	"MULV",
+	"MULVU",
+	"ADDV",
+	"ADDVU",
+	"SUBV",
+	"SUBVU",
+	"DYNT",
+	"INIT",
+	"BCASE",
+	"CASE",
+	"TRUNCFV",
+	"TRUNCDV",
+	"TRUNCFW",
+	"TRUNCDW",
+	"MOVWU",
+	"SIGNAME",
+	"LAST",
+};
--- /dev/null
+++ b/utils/vc/gc.h
@@ -1,0 +1,333 @@
+#include	"../cc/cc.h"
+#include	"../vc/v.out.h"
+
+/*
+ * vc/mips
+ * Mips 3000
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		4
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+struct	Adr
+{
+	long	offset;
+	double	dval;
+	char	sval[NSNAME];
+	Ieee	ieee;
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	char	as;
+	char	reg;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	long	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	600
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Multab	multab[20];
+EXTERN	int	hintabsize;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	int	reg[NREG+NREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	4
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+EXTERN	int	suppress;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+void	usedset(Node*, int);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	swit2(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(long);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/utils/vc/list.c
@@ -1,0 +1,244 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('D', Dconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			sprint(ss, "$%ld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		sprint(str, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCREG:
+		sprint(str, "FCR%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_LO:
+		sprint(str, "LO");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(LO)(REG)", a);
+		break;
+
+	case D_HI:
+		sprint(str, "HI");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(HI)(REG)", a);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%ld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		sprint(str, "%ld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/utils/vc/mkenam
@@ -1,0 +1,15 @@
+ed - ../vc/v.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/utils/vc/mkfile
@@ -1,0 +1,40 @@
+<../../mkconfig
+
+TARG=vc
+
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+	mul.$O\
+
+HFILES=\
+	gc.h\
+	v.out.h\
+	../cc/cc.h\
+
+LIBS=cc bio 9		# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include
+
+$ROOT/$OBJDIR/lib/libcc.a:
+	cd ../cc
+	mk $MKFLAGS install
+	mk $MKFLAGS clean
+
+%.$O: ../cc/%.c
+	$CC -I. $CFLAGS ../cc/$stem.c
+
+#enam.c:	v.out.h
+#	rc mkenam
--- /dev/null
+++ b/utils/vc/mul.c
@@ -1,0 +1,608 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("multiply table failure %ld\n", v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=6; g++) {
+		if(g >= 6 && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("multiply table failure %ld\n", v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/utils/vc/peep.c
@@ -1,0 +1,694 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
+		if(regtyp(&p->to)) {
+			if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			if(regzer(&p->from))
+			if(p->to.type == D_REG) {
+				p->from.type = D_REG;
+				p->from.reg = 0;
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+int
+regzer(Adr *a)
+{
+
+	if(a->type == D_CONST)
+		if(a->sym == S)
+			if(a->offset == 0)
+				return 1;
+	if(a->type == D_REG)
+		if(a->reg == 0)
+			return 1;
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG) {
+		if(a->reg != 0)
+			return 1;
+		return 0;
+	}
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case AJAL:
+			return 0;
+
+		case ASGT:
+		case ASGTU:
+
+		case AADD:
+		case AADDU:
+		case ASUB:
+		case ASUBU:
+		case ASLL:
+		case ASRL:
+		case ASRA:
+		case AOR:
+		case AAND:
+		case AXOR:
+		case AMUL:
+		case AMULU:
+		case ADIV:
+		case ADIVU:
+
+		case AADDD:
+		case AADDF:
+		case ASUBD:
+		case ASUBF:
+		case AMULD:
+		case AMULF:
+		case ADIVD:
+		case ADIVF:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case AMOVF:
+		case AMOVD:
+		case AMOVW:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (?)");
+		return 2;
+
+
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVF:
+	case AMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVDW:
+	case AMOVWD:
+	case AMOVFD:
+	case AMOVDF:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ASGT:	/* read, read, write */
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case ANOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:	/* read, read */
+	case ABNE:
+	case ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+	case ABFPF:
+	case ABFPT:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub1(p, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		return 0;
+
+	case AJMP:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case AJAL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(REGARG && v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG)
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+	case ABEQ:
+	case ABNE:
+	case ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ASGT:
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+		return D_REG;
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v))
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG)
+		if(a->type == D_OREG)
+			if(v->reg == a->reg)
+				return 1;
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v))
+		if(p->from.type == v->type || p->to.type == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v))
+		a->reg = s->reg;
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
--- /dev/null
+++ b/utils/vc/reg.c
@@ -1,0 +1,1149 @@
+#include "gc.h"
+
+void	addsplits(void);
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARET:
+		case AJMP:
+		case ARFE:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVF:
+		case AMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case AJAL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+	addsplits();
+
+	if(debug['R'] && debug['v']) {
+		print("\nprop structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->set.b[z] |
+					r->refahead.b[z] | r->calahead.b[z] |
+					r->refbehind.b[z] | r->calbehind.b[z] |
+					r->use1.b[z] | r->use2.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+				if(bany(&r->refahead))
+					print(" ra=%B", r->refahead);
+				if(bany(&r->calahead))
+					print(" ca=%B", r->calahead);
+				if(bany(&r->refbehind))
+					print(" rb=%B", r->refbehind);
+				if(bany(&r->calbehind))
+					print(" cb=%B", r->calbehind);
+				if(bany(&r->regdiff))
+					print(" rd=%B", r->regdiff);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L $%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L $%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L $%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+void
+addsplits(void)
+{
+	Reg *r, *r1;
+	int z, i;
+	Bits bit;
+
+	for(r = firstr; r != R; r = r->link) {
+		if(r->loop > 1)
+			continue;
+		if(r->prog->as == AJAL)
+			continue;
+		for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+			if(r1->loop <= 1)
+				continue;
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r1->calbehind.b[z] &
+					(r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+					~(r->calahead.b[z] & addrs.b[z]);
+			while(bany(&bit)) {
+				i = bnum(bit);
+				bit.b[i/32] &= ~(1L << (i%32));
+			}
+		}
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !typechlpfd[et])	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case AJAL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost >= 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R3
+ *	1	R4
+ *	...	...
+ *	19	R22
+ *	20	R23
+ */
+long
+RtoB(int r)
+{
+
+	if(r < 3 || r > 23)
+		return 0;
+	return 1L << (r-3);
+}
+
+int
+BtoR(long b)
+{
+
+	b &= 0x001fffffL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + 3;
+}
+
+/*
+ *	bit	reg
+ *	22	F4
+ *	23	F6
+ *	...	...
+ *	31	F22
+ */
+long
+FtoB(int f)
+{
+
+	if(f < 4 || f > 22 || (f&1))
+		return 0;
+	return 1L << (f/2 + 20);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0xffc00000L;
+	if(b == 0)
+		return 0;
+	return bitno(b)*2 - 40;
+}
--- /dev/null
+++ b/utils/vc/sgen.c
@@ -1,0 +1,227 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASLMUL:
+	case OASMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(l);
+		if(t >= 0) {
+			n->left = r;
+			n->right = l;
+			l = r;
+			r = n->right;
+		}
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		xcom(l);
+		xcom(r);
+		simplifyshift(n);
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+	if(com64(n))
+		return;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+	case OEQ:
+	case ONE:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
+
--- /dev/null
+++ b/utils/vc/swt.c
@@ -1,0 +1,586 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	Node tn;
+	
+	regalloc(&tn, &regnode, Z);
+	swit2(q, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gmove(nodconst(q->val), tn);
+			gopcode(OEQ, n, tn, Z);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+	i = nc / 2;
+	r = q+i;
+	if(debug['K'])
+		print("case > %.8llux\n", r->val);
+	gmove(nodconst(r->val), tn);
+	gopcode(OLT, tn, n, Z);
+	sp = p;
+	gopcode(OEQ, n, tn, Z);
+	patch(p, r->label);
+	swit2(q, i, def, n, tn);
+
+	if(debug['K'])
+		print("case < %.8llux\n", r->val);
+	patch(sp, pc);
+	swit2(r+1, nc-i-1, def, n, tn);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	if(debug['M'] && debug['v'])
+		print("%L multiply: %ld\n", n->lineno, v);
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	if(v < 0)
+		gopcode(OSUB, &nod1, nodconst(0), &nod1);
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+	if(a->op == OCONST && typev[a->type->etype]) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+
+	bf[0] = p->as;
+	bf[1] = p->reg;
+	bf[2] = p->lineno;
+	bf[3] = p->lineno>>8;
+	bf[4] = p->lineno>>16;
+	bf[5] = p->lineno>>24;
+	bp = zaddr(bf+6, &p->from, sf);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[7];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = sig;
+		bf[2] = sig>>8;
+		bf[3] = sig>>16;
+		bf[4] = sig>>24;
+		bf[5] = t;
+		bf[6] = s->sym;
+		Bwrite(b, bf, 7);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = t;	/* type */
+		bf[2] = s->sym;	/* sym */
+		Bwrite(b, bf, 3);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	long l;
+	Ieee e;
+
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	}
+	return bp;
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial allign of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_LONG)
+			w = SZ_LONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesuv[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_LONG) {
+			w = SZ_LONG;
+			break;
+		}
+		if(thechar == 'v')
+			o += SZ_LONG - w;	/* big endian adjustment */
+		w = 1;
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_LONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_LONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/utils/vc/txt.c
@@ -1,0 +1,1440 @@
+#include "gc.h"
+
+void
+ginit(void)
+{
+	int i;
+	Type *t;
+
+	thechar = 'v';
+	thestring = "mips";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMP;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	for(i=0; i<nelem(reg); i++) {
+		reg[i] = 0;
+		if(i == REGZERO ||
+		  (i >= NREG && ((i-NREG)&1)))
+			reg[i] = 1;
+	}
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(i != REGZERO)
+			if(reg[i])
+				diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NREG; i+=2)
+		if(reg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesuv[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gopcode(OAS, n, Z, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = regnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+int
+tmpreg(void)
+{
+	int i;
+
+	for(i=REGRET+1; i<NREG; i++)
+		if(reg[i] == 0)
+			return i;
+	diag(Z, "out of fixed registers");
+	return 0;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i > 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+	case TVLONG:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NREG)
+				goto out;
+		}
+		j = 0*2 + NREG;
+		for(i=NREG; i<NREG+NREG; i+=2) {
+			if(j >= NREG+NREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j += 2;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	nodreg(n, tn, 0);
+	return;
+out:
+	reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_LONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = 0;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod, nod1, nod2;
+	Prog *p1;
+	double d;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0.0) {
+			a = FREGZERO;
+			goto ffreg;
+		}
+		if(d == 0.5) {
+			a = FREGHALF;
+			goto ffreg;
+		}
+		if(d == 1.0) {
+			a = FREGONE;
+			goto ffreg;
+		}
+		if(d == 2.0) {
+			a = FREGTWO;
+			goto ffreg;
+		}
+		if(d == -.5) {
+			fop(OSUB, FREGHALF, FREGZERO, t);
+			return;
+		}
+		if(d == -1.0) {
+			fop(OSUB, FREGONE, FREGZERO, t);
+			return;
+		}
+		if(d == -2.0) {
+			fop(OSUB, FREGTWO, FREGZERO, t);
+			return;
+		}
+		if(d == 1.5) {
+			fop(OADD, FREGONE, FREGHALF, t);
+			return;
+		}
+		if(d == 2.5) {
+			fop(OADD, FREGTWO, FREGHALF, t);
+			return;
+		}
+		if(d == 3.0) {
+			fop(OADD, FREGTWO, FREGONE, t);
+			return;
+		}
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+		d = f->fconst;
+		if(d == 0) {
+			a = FREGZERO;
+		ffreg:
+			nodreg(&nod, f, NREG+a);
+			gmove(&nod, t);
+			return;
+		}
+	}
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			if(typefd[tt]) {
+				/* special case can load mem to Freg */
+				regalloc(&nod, t, t);
+				gins(AMOVW, f, &nod);
+				a = AMOVWD;
+				if(tt == TFLOAT)
+					a = AMOVWF;
+				gins(a, &nod, &nod);
+				gmove(&nod, t);
+				regfree(&nod);
+				return;
+			}
+			a = AMOVW;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		}
+		if(typechlp[ft] && typeilp[tt])
+			regalloc(&nod, t, t);
+		else
+			regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		}
+		if(!typefd[ft] && vconst(f) == 0) {
+			gins(a, f, t);
+			return;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TVLONG:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			a = AMOVD;
+			if(ft == TFLOAT)
+				a = AMOVFD;
+			break;
+		case TFLOAT:
+			a = AMOVDF;
+			if(ft == TFLOAT)
+				a = AMOVF;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			if(fproundflg) {
+				/* convert f, t */
+				regalloc(&nod, f, Z);
+				gins(AMOVDW, f, &nod);
+				if(ft == TFLOAT)
+					p->as = AMOVFW;
+				gins(AMOVW, &nod, t);
+				regfree(&nod);
+				gins(AMOVW, t, t);
+				return;
+			}
+			regalloc(&nod1, &regnode, Z);
+			regalloc(&nod2, &regnode, Z);
+
+			/* movw fcr, rx */
+			gins(AMOVW, Z, &nod1);
+			p->from.type = D_FCREG;
+			p->from.reg = 31;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			/* or $3, rx, ry */
+			gins(AOR, nodconst(3), &nod2);
+			p->reg = nod1.reg;
+
+			/* xor $2, ry */
+			gins(AXOR, nodconst(2), &nod2);
+
+			/* movw ry, fcr */
+			gins(AMOVW, &nod2, Z);
+			p->to.type = D_FCREG;
+			p->to.reg = 31;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			/* convert f, t */
+			regalloc(&nod, f, Z);
+			gins(AMOVDW, f, &nod);
+			if(ft == TFLOAT)
+				p->as = AMOVFW;
+			gins(AMOVW, &nod, t);
+			regfree(&nod);
+			gins(AMOVW, t, t);
+
+			/* movw rx, fcr */
+			gins(AMOVW, &nod1, Z);
+			p->to.type = D_FCREG;
+			p->to.reg = 31;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			/* nop */
+			gins(ANOR, nodconst(0), nodconst(0));
+			p->to.type = D_REG;
+			p->to.reg = 0;
+
+			regfree(&nod1);
+			regfree(&nod2);
+			return;
+		}
+		break;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			gins(AMOVW, f, t);
+			gins(AMOVWD, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGED, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVD, nodfconst(4294967296.), &nod);
+				gins(AADDD, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TFLOAT:
+			gins(AMOVW, f, t);
+			gins(AMOVWF, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGEF, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVF, nodfconst(4294967296.), &nod);
+				gins(AADDF, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+		case TVLONG:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOVW || a == AMOVF || a == AMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et;
+	Adr ta;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		et = f1->type->etype;
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADDU;
+		if(et == TFLOAT)
+			a = AADDF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = AADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBU;
+		if(et == TFLOAT)
+			a = ASUBF;
+		else
+		if(et == TDOUBLE || et == TVLONG)
+			a = ASUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		break;
+
+	case OFUNC:
+		a = AJAL;
+		break;
+
+	case OCOND:
+		a = ASGTU;
+		break;
+
+	case OCOMMA:
+		a = ASGT;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT) {
+			a = AMULF;
+			break;
+		} else
+		if(et == TDOUBLE || et == TVLONG) {
+			a = AMULD;
+			break;
+		}
+		a = AMUL;
+		goto muldiv;
+
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = ADIVF;
+			break;
+		} else
+		if(et == TDOUBLE || et == TVLONG) {
+			a = ADIVD;
+			break;
+		}
+		a = ADIV;
+		goto muldiv;
+
+	case OASMOD:
+	case OMOD:
+		a = ADIV;
+		o = OMOD;
+		goto muldiv;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULU;
+		goto muldiv;
+
+	case OASLMOD:
+	case OLMOD:
+		a = ADIVU;
+		o = OMOD;
+		goto muldiv;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVU;
+		goto muldiv;
+
+	muldiv:
+		nextpc();
+		naddr(f1, &p->from);
+		if(f2 == Z)
+			raddr(t, p);
+		else
+			raddr(f2, p);
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+
+		nextpc();
+		p->as = AMOVW;
+		a = D_LO;
+		if(o == OMOD)
+			a = D_HI;
+		p->from.type = a;
+		naddr(t, &p->to);
+		if(debug['g'])
+			print("%P\n", p);
+		return;
+
+	case OEQ:
+		if(!typefd[et]) {
+			a = ABEQ;
+			break;
+		}
+
+	case ONE:
+		if(!typefd[et]) {
+			a = ABNE;
+			break;
+		}
+
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+		if(typefd[et]) {
+			nextpc();
+			if(et == TFLOAT) {
+				a = ACMPGTF;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQF;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGEF;
+			} else {
+				a = ACMPGTD;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQD;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGED;
+			}
+			p->as = a;
+			naddr(f1, &p->from);
+			raddr(f2, p);
+			if(debug['g'])
+				print("%P\n", p);
+			nextpc();
+			a = ABFPF;
+			if(o == OEQ || o == OGE || o == OGT)
+				a = ABFPT;
+			p->as = a;
+			if(debug['g'])
+				print("%P\n", p);
+			return;
+		}
+		if(vconst(f1) == 0 || vconst(f2) == 0) {
+			if(vconst(f1) == 0) {
+				o = invrel[relindex(o)];
+				f1 = f2;
+			}
+			switch(o) {
+			case OLT:
+				a = ABLTZ;
+				break;
+			case OLE:
+				a = ABLEZ;
+				break;
+			case OGE:
+				a = ABGEZ;
+				break;
+			case OGT:
+				a = ABGTZ;
+				break;
+			}
+			f2 = Z;
+			break;
+		}
+
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		nextpc();
+		if(o == OLE || o == OGT || o == OLS || o == OHI) {
+			naddr(f1, &p->from);
+			raddr(f2, p);
+		} else {
+			naddr(f2, &p->from);
+			raddr(f1, p);
+		}
+		naddr(&regnode, &p->to);
+		p->to.reg = tmpreg();
+		a = ASGT;
+		if(o == OLO || o == OLS || o == OHS || o == OHI)
+			a = ASGTU;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+
+		nextpc();
+		naddr(&regnode, &p->from);
+		p->from.reg = tmpreg();
+		a = ABEQ;
+		if(o == OLT || o == OGT || o == OLO || o == OHI)
+			a = ABNE;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+		return;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+		if(ta.type == D_CONST && ta.offset == 0)
+			p->reg = REGZERO;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	p->from.name = D_EXTERN;
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= (vlong)(-32766) && vv < (vlong)32766)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+sval(long v)
+{
+	if(v >= -32766L && v < 32766L)
+		return 1;
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[t->etype]) {
+		if(exregoffset <= 16)
+			return 0;
+		o = exregoffset;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= 16)
+			return 0;
+		o = exfregoffset + NREG;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG|BIND,	/* [TULONG] */
+	BVLONG|BUVLONG,			/* [TVLONG] */
+	BVLONG|BUVLONG,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BLONG|BULONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};
--- /dev/null
+++ b/utils/vc/v.out.h
@@ -1,0 +1,201 @@
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+#define	REGZERO		0
+#define	REGRET		1
+#define	REGARG		1
+/* compiler allocates R1 up as temps */
+/* compiler allocates register variables R3-R23 */
+#define	REGEXT		25
+/* compiler allocates external registers R25 down */
+/* dont use R26 R27 */
+#define	REGTMP		28
+#define	REGSP		29
+#define	REGSB		30
+#define	REGLINK		31
+
+#define	FREGRET		0
+/* compiler allocates register variables F4-F22 */
+/* compiler allocates external registers F22 down */
+#define	FREGEXT		22
+#define	FREGZERO	24	/* both float and double */
+#define	FREGHALF	26	/* double */
+#define	FREGONE		28	/* double */
+#define	FREGTWO		30	/* double */
+
+enum	as
+{
+	AXXX,
+
+	AABSD,
+	AABSF,
+	AABSW,
+	AADD,
+	AADDD,
+	AADDF,
+	AADDU,
+	AADDW,
+	AAND,
+	ABEQ,
+	ABFPF,
+	ABFPT,
+	ABGEZ,
+	ABGEZAL,
+	ABGTZ,
+	ABLEZ,
+	ABLTZ,
+	ABLTZAL,
+	ABNE,
+	ABREAK,
+	ACMPEQD,
+	ACMPEQF,
+	ACMPGED,
+	ACMPGEF,
+	ACMPGTD,
+	ACMPGTF,
+	ADATA,
+	ADIV,
+	ADIVD,
+	ADIVF,
+	ADIVU,
+	ADIVW,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AJAL,
+	AJMP,
+	AMOVB,
+	AMOVBU,
+	AMOVD,
+	AMOVDF,
+	AMOVDW,
+	AMOVF,
+	AMOVFD,
+	AMOVFW,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVWD,
+	AMOVWF,
+	AMOVWL,
+	AMOVWR,
+	AMUL,
+	AMULD,
+	AMULF,
+	AMULU,
+	AMULW,
+	ANAME,
+	ANEGD,
+	ANEGF,
+	ANEGW,
+	ANOP,
+	ANOR,
+	AOR,
+	AREM,
+	AREMU,
+	ARET,
+	ARFE,
+	ASGT,
+	ASGTU,
+	ASLL,
+	ASRA,
+	ASRL,
+	ASUB,
+	ASUBD,
+	ASUBF,
+	ASUBU,
+	ASUBW,
+	ASYSCALL,
+	ATEXT,
+	ATLBP,
+	ATLBR,
+	ATLBWI,
+	ATLBWR,
+	AWORD,
+	AXOR,
+
+	AEND,
+
+	AMOVV,
+	AMOVVL,
+	AMOVVR,
+	ASLLV,
+	ASRAV,
+	ASRLV,
+	ADIVV,
+	ADIVVU,
+	AREMV,
+	AREMVU,
+	AMULV,
+	AMULVU,
+	AADDV,
+	AADDVU,
+	ASUBV,
+	ASUBVU,
+
+	ADYNT,
+	AINIT,
+
+	ABCASE,
+	ACASE,
+
+	ATRUNCFV,
+	ATRUNCDV,
+	ATRUNCFW,
+	ATRUNCDW,
+	AMOVWU,
+	AMOVFV,
+	AMOVDV,
+	AMOVVF,
+	AMOVVD,
+
+	ASIGNAME,
+
+	ALAST,
+};
+
+/* type/name */
+#define	D_GOK	0
+#define	D_NONE	1
+
+/* type */
+#define	D_BRANCH (D_NONE+1)
+#define	D_OREG	(D_NONE+2)
+#define	D_EXTERN (D_NONE+3)	/* name */
+#define	D_STATIC (D_NONE+4)	/* name */
+#define	D_AUTO	(D_NONE+5)	/* name */
+#define	D_PARAM	(D_NONE+6)	/* name */
+#define	D_CONST	(D_NONE+7)
+#define	D_FCONST (D_NONE+8)
+#define	D_SCONST (D_NONE+9)
+#define	D_HI	(D_NONE+10)
+#define	D_LO	(D_NONE+11)
+#define	D_REG	(D_NONE+12)
+#define	D_FREG	(D_NONE+13)
+#define	D_FCREG	(D_NONE+14)
+#define	D_MREG	(D_NONE+15)
+#define	D_FILE	(D_NONE+16)
+#define	D_OCONST (D_NONE+17)
+#define	D_FILE1	(D_NONE+18)
+#define	D_VCONST (D_NONE+19)
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	ieee	Ieee;
+struct	ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/utils/vl/asm.c
@@ -1,0 +1,1482 @@
+#include	"l.h"
+
+long	OFFSET;
+/*
+long	BADOFFSET	=	-1;
+
+		if(OFFSET <= BADOFFSET && OFFSET+4 > BADOFFSET)\
+			abort();\
+		OFFSET += 4;\
+
+		if(OFFSET == BADOFFSET)\
+			abort();\
+		OFFSET++;\
+*/
+
+#define LPUT(l) { \
+		if (little) { \
+			LLEPUT(l); \
+		} else { \
+			LBEPUT(l); \
+		} \
+	}
+
+#define	LLEPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp[1] = (c)>>8;\
+		cbp[2] = (c)>>16;\
+		cbp[3] = (c)>>24;\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	LBEPUT(c)\
+	{\
+		cbp[0] = (c)>>24;\
+		cbp[1] = (c)>>16;\
+		cbp[2] = (c)>>8;\
+		cbp[3] = (c);\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define HPUT(h) { \
+		if (little) { \
+			HLEPUT(h); \
+		} else { \
+			HBEPUT(h); \
+		} \
+	}
+
+#define	HLEPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp[1] = (c)>>8;\
+		cbp += 2;\
+		cbc -= 2;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	HBEPUT(c)\
+	{\
+		cbp[0] = (c)>>8;\
+		cbp[1] = (c);\
+		cbp += 2;\
+		cbc -= 2;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+
+#define	CPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp++;\
+		cbc--;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+void
+cput(long l)
+{
+	CPUT(l);
+}
+
+void
+objput(long l)	/* emit long in byte order appropriate to object machine */
+{
+	LPUT(l);
+}
+
+void
+objhput(short s)
+{
+	HPUT(s);
+}
+
+void
+wput(long l)
+{
+
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+wputl(long l)
+{
+
+	cbp[0] = l;
+	cbp[1] = l>>8;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)		/* emit long in big-endian byte order */
+{
+	LBEPUT(l);
+}
+
+void
+lputl(long l)		/* emit long in big-endian byte order */
+{
+	LLEPUT(l);
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+long
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	if(s->type != STEXT && s->type != SLEAF)
+		diag("entry not text: %s", s->name);
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long t, etext;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	OFFSET = HEADR;
+	seek(cout, OFFSET, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 4;
+		}
+		if(p->pc != pc) {
+			diag("phase error %lux sb %lux",
+				p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(asmout(p, o, 0)) {
+			p = p->link;
+			pc += 4;
+		}
+		pc += o->size;
+	}
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	etext = INITTEXT + textsize;
+	for(t = pc; t < etext; t += sizeof(buf)-100) {
+		if(etext-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 1);
+		else
+			datblk(t, etext-t, 1);
+	}
+
+	Bflush(&bso);
+	cflush();
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 4:
+		OFFSET = rnd(HEADR+textsize, 4096);
+		seek(cout, OFFSET, 0);
+		break;
+	case 1:
+	case 2:
+	case 3:
+	case 5:
+	case 6:
+		OFFSET = HEADR+textsize;
+		seek(cout, OFFSET, 0);
+		break;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 0);
+		else
+			datblk(t, datsize-t, 0);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+		case 4:
+			OFFSET = rnd(HEADR+textsize, 4096)+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		case 3:
+		case 2:
+		case 1:
+		case 5:
+		case 6:
+			OFFSET = HEADR+textsize+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		cflush();
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f header\n", cputime());
+	Bflush(&bso);
+	OFFSET = 0;
+	seek(cout, OFFSET, 0);
+	switch(HEADTYPE) {
+	case 0:
+		lput(0x160L<<16);		/* magic and sections */
+		lput(0L);			/* time and date */
+		lput(rnd(HEADR+textsize, 4096)+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+		lput((0413<<16)|0437L);		/* magic and version */
+		lput(rnd(HEADR+textsize, 4096));	/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT-HEADR);		/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT+datsize);		/* va of base of bss */
+		lput(~0L);			/* gp reg mask */
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(~0L);			/* gp value ?? */
+		break;
+	case 1:
+		lput(0x160L<<16);		/* magic and sections */
+		lput(0L);			/* time and date */
+		lput(HEADR+textsize+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		lput((0407<<16)|0437L);		/* magic and version */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT);			/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT+datsize);		/* va of base of bss */
+		lput(~0L);			/* gp reg mask */
+		lput(lcsize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(~0L);			/* gp value ?? */
+		lput(0L);			/* complete mystery */
+		break;
+	case 2:
+		if (little)
+			t = 24;
+		else
+			t = 16;
+		lput(((((4*t)+0)*t)+7));	/* magic */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);			/* nsyms */
+		lput(entryvalue());		/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		break;
+	case 3:
+		lput((0x160L<<16)|3L);		/* magic and sections */
+		lput(time(0));			/* time and date */
+		lput(HEADR+textsize+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		lput((0407<<16)|0437L);		/* magic and version */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT);			/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT+datsize);		/* va of base of bss */
+		lput(~0L);			/* gp reg mask */
+		lput(lcsize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(~0L);			/* gp value ?? */
+
+		strnput(".text", 8);		/* text segment */
+		lput(INITTEXT);			/* address */
+		lput(INITTEXT);
+		lput(textsize);
+		lput(HEADR);
+		lput(0L);
+		lput(HEADR+textsize+datsize+symsize);
+		lput(lcsize);			/* line number size */
+		lput(0x20L);			/* flags */
+
+		strnput(".data", 8);		/* data segment */
+		lput(INITDAT);			/* address */
+		lput(INITDAT);
+		lput(datsize);
+		lput(HEADR+textsize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x40L);			/* flags */
+
+		strnput(".bss", 8);		/* bss segment */
+		lput(INITDAT+datsize);		/* address */
+		lput(INITDAT+datsize);
+		lput(bsssize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x80L);			/* flags */
+		break;
+	case 4:
+
+		lput((0x160L<<16)|3L);		/* magic and sections */
+		lput(time(0));			/* time and date */
+		lput(rnd(HEADR+textsize, 4096)+datsize);
+		lput(symsize);			/* nsyms */
+		lput((0x38L<<16)|7L);		/* size of optional hdr and flags */
+
+		lput((0413<<16)|01012L);	/* magic and version */
+		lput(textsize);			/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(entryvalue());		/* va of entry */
+		lput(INITTEXT);			/* va of base of text */
+		lput(INITDAT);			/* va of base of data */
+		lput(INITDAT+datsize);		/* va of base of bss */
+		lput(~0L);			/* gp reg mask */
+		lput(lcsize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(~0L);			/* gp value ?? */
+
+		strnput(".text", 8);		/* text segment */
+		lput(INITTEXT);			/* address */
+		lput(INITTEXT);
+		lput(textsize);
+		lput(HEADR);
+		lput(0L);
+		lput(HEADR+textsize+datsize+symsize);
+		lput(lcsize);			/* line number size */
+		lput(0x20L);			/* flags */
+
+		strnput(".data", 8);		/* data segment */
+		lput(INITDAT);			/* address */
+		lput(INITDAT);
+		lput(datsize);
+		lput(rnd(HEADR+textsize, 4096));	/* sizes */
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x40L);			/* flags */
+
+		strnput(".bss", 8);		/* bss segment */
+		lput(INITDAT+datsize);		/* address */
+		lput(INITDAT+datsize);
+		lput(bsssize);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0L);
+		lput(0x80L);			/* flags */
+		break;
+	case 5:
+		elf32(MIPS, little? ELFDATA2LSB: ELFDATA2MSB, 0, nil);
+		break;
+	case 6:
+		break;
+	}
+	cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		CPUT(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		CPUT(0);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+	if(c->outof)
+	Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+		c->outof - c->count, c->outof,
+		(double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SSTRING:
+				putsymb(s->name, 'T', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+4, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, long v, int ver)
+{
+	int i, f;
+
+	if(t == 'f')
+		s++;
+	LBEPUT(v);
+	if(ver)
+		t += 'a' - 'A';
+	CPUT(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		CPUT(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			CPUT(s[i]);
+			CPUT(s[i+1]);
+		}
+		CPUT(0);
+		CPUT(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			CPUT(s[i]);
+		CPUT(0);
+	}
+	symsize += 4 + 1 + i + 1;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8lux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8lux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldpc, oldlc;
+	Prog *p;
+	long v, s;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			CPUT(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			CPUT(0);	/* 0 vv +lc */
+			CPUT(s>>24);
+			CPUT(s>>16);
+			CPUT(s>>8);
+			CPUT(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			CPUT(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		} else {
+			CPUT(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6lux %P\n",
+					p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		CPUT(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n, int str)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j, d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		if(str != (p->from.sym->type == SSTRING))
+			continue;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization\n%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i+4]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			if(p->to.sym) {
+				switch(p->to.sym->type) {
+				case STEXT:
+				case SLEAF:
+				case SSTRING:
+					d += p->to.sym->value;
+					break;
+				case SDATA:
+				case SBSS:
+					d += p->to.sym->value + INITDAT;
+					break;
+				}
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+#define	OP_RRR(op,r1,r2,r3)\
+	(op|(((r1)&31L)<<16)|(((r2)&31L)<<21)|(((r3)&31L)<<11))
+#define	OP_IRR(op,i,r2,r3)\
+	(op|((i)&0xffffL)|(((r2)&31L)<<21)|(((r3)&31L)<<16))
+#define	OP_SRR(op,s,r2,r3)\
+	(op|(((s)&31L)<<6)|(((r2)&31L)<<16)|(((r3)&31L)<<11))
+#define	OP_FRRR(op,r1,r2,r3)\
+	(op|(((r1)&31L)<<16)|(((r2)&31L)<<11)|(((r3)&31L)<<6))
+#define	OP_JMP(op,i)\
+		((op)|((i)&0x3ffffffL))
+
+#define	OP(x,y)\
+	(((x)<<3)|((y)<<0))
+#define	SP(x,y)\
+	(((x)<<29)|((y)<<26))
+#define	BCOND(x,y)\
+	(((x)<<19)|((y)<<16))
+#define	MMU(x,y)\
+	(SP(2,0)|(16<<21)|((x)<<3)|((y)<<0))
+#define	FPF(x,y)\
+	(SP(2,1)|(16<<21)|((x)<<3)|((y)<<0))
+#define	FPD(x,y)\
+	(SP(2,1)|(17<<21)|((x)<<3)|((y)<<0))
+#define	FPW(x,y)\
+	(SP(2,1)|(20<<21)|((x)<<3)|((y)<<0))
+
+int vshift(int);
+
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+	long o1, o2, o3, o4, o5, o6, o7, v;
+	Prog *ct;
+	int r, a;
+
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	o6 = 0;
+	o7 = 0;
+	switch(o->type) {
+	default:
+		diag("unknown type %d", o->type);
+		if(!debug['a'])
+			prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		if(aflag) {
+			if(p->link) {
+				if(p->as == ATEXT) {
+					ct = curtext;
+					o2 = autosize;
+					curtext = p;
+					autosize = p->to.offset + 4;
+					o1 = asmout(p->link, oplook(p->link), aflag);
+					curtext = ct;
+					autosize = o2;
+				} else
+					o1 = asmout(p->link, oplook(p->link), aflag);
+			}
+			return o1;
+		}
+		break;
+
+	case 1:		/* mov[v] r1,r2 ==> OR r1,r0,r2 */
+		o1 = OP_RRR(oprrr(AOR), p->from.reg, REGZERO, p->to.reg);
+		break;
+
+	case 2:		/* add/sub r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 3:		/* mov $soreg, r ==> or/add $i,o,r */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		a = AADDU;
+		if(o->a1 == C_ANDCON)
+			a = AOR;
+		o1 = OP_IRR(opirr(a), v, r, p->to.reg);
+		break;
+
+	case 4:		/* add $scon,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_IRR(opirr(p->as), v, r, p->to.reg);
+		break;
+
+	case 5:		/* syscall */
+		if(aflag)
+			return 0;
+		o1 = oprrr(p->as);
+		break;
+
+	case 6:		/* beq r1,[r2],sbra */
+		if(aflag)
+			return 0;
+		if(p->cond == P)
+			v = -4 >> 2;
+		else
+			v = (p->cond->pc - pc-4) >> 2;
+		if(((v << 16) >> 16) != v)
+			diag("short branch too far: %ld\n%P", v, p);
+		o1 = OP_IRR(opirr(p->as), v, p->from.reg, p->reg);
+		break;
+
+	case 7:		/* mov r, soreg ==> sw o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = OP_IRR(opirr(p->as), v, r, p->from.reg);
+		break;
+
+	case 8:		/* mov soreg, r ==> lw o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(p->as+ALAST), v, r, p->to.reg);
+		break;
+
+	case 9:		/* asl r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
+		break;
+
+	case 10:	/* add $con,[r1],r2 ==> mov $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		r = AOR;
+		if(v < 0)
+			r = AADDU;
+		o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 11:	/* jmp lbra */
+		if(aflag)
+			return 0;
+		if(p->cond == P)
+			v = p->pc >> 2;
+		else
+			v = p->cond->pc >> 2;
+		o1 = OP_JMP(opirr(p->as), v);
+		if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
+			nop.branch.count--;
+			nop.branch.outof--;
+			nop.jump.outof++;
+			o2 = asmout(p->cond, oplook(p->cond), 1);
+			if(o2) {
+				o1 += 1;
+				if(debug['a'])
+					Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
+						p->pc, o1, o2, p);
+				LPUT(o1);
+				LPUT(o2);
+				return 1;
+			}
+		}
+		break;
+
+	case 12:	/* movbs r,r */
+		v = 16;
+		if(p->as == AMOVB)
+			v = 24;
+		o1 = OP_SRR(opirr(ASLL), v, p->from.reg, p->to.reg);
+		o2 = OP_SRR(opirr(ASRA), v, p->to.reg, p->to.reg);
+		break;
+
+	case 13:	/* movbu r,r */
+		if(p->as == AMOVBU)
+			o1 = OP_IRR(opirr(AAND), 0xffL, p->from.reg, p->to.reg);
+		else
+			o1 = OP_IRR(opirr(AAND), 0xffffL, p->from.reg, p->to.reg);
+		break;
+
+	case 16:	/* sll $c,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+
+		/* OP_SRR will use only the low 5 bits of the shift value */
+		if(v >= 32 && vshift(p->as))
+			o1 = OP_SRR(opirr(p->as+ALAST), v-32, r, p->to.reg);
+		else 
+			o1 = OP_SRR(opirr(p->as), v, r, p->to.reg);
+		break;
+
+	case 18:	/* jmp [r1],0(r2) */
+		if(aflag)
+			return 0;
+		r = p->reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = OP_RRR(oprrr(p->as), 0, p->to.reg, r);
+		break;
+
+	case 19:	/* mov $lcon,r ==> lu+or */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+		o2 = OP_IRR(opirr(AOR), v, p->to.reg, p->to.reg);
+		break;
+
+	case 20:	/* mov lohi,r */
+		r = OP(2,0);		/* mfhi */
+		if(p->from.type == D_LO)
+			r = OP(2,2);	/* mflo */
+		o1 = OP_RRR(r, REGZERO, REGZERO, p->to.reg);
+		break;
+
+	case 21:	/* mov r,lohi */
+		r = OP(2,1);		/* mthi */
+		if(p->to.type == D_LO)
+			r = OP(2,3);	/* mtlo */
+		o1 = OP_RRR(r, REGZERO, p->from.reg, REGZERO);
+		break;
+
+	case 22:	/* mul r1,r2 */
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, p->reg, REGZERO);
+		break;
+
+	case 23:	/* add $lcon,r1,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP || p->reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o3 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 24:	/* mov $ucon,,r ==> lu r */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
+		break;
+
+	case 25:	/* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 26:	/* mov $lsext/auto/oreg,,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o3 = OP_RRR(oprrr(AADDU), REGTMP, r, p->to.reg);
+		break;
+
+	case 27:		/* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		switch(o->size) {
+		case 20:
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(opirr(AMOVF+ALAST), 0, REGTMP, p->to.reg+1);
+			o5 = OP_IRR(opirr(AMOVF+ALAST), 4, REGTMP, p->to.reg);
+			break;
+		case 16:
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(opirr(AMOVF+ALAST), 0, REGTMP, p->to.reg);
+			break;
+		case 8:
+			o1 = OP_IRR(opirr(AMOVF+ALAST), v, r, p->to.reg+1);
+			o2 = OP_IRR(opirr(AMOVF+ALAST), v+4, r, p->to.reg);
+			break;
+		case 4:
+			o1 = OP_IRR(opirr(AMOVF+ALAST), v, r, p->to.reg);
+			break;
+		}
+		break;
+
+	case 28:		/* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		switch(o->size) {
+		case 20:
+			if(r == REGTMP)
+				diag("cant synthesize large constant\n%P", p);
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(opirr(AMOVF), 0, REGTMP, p->from.reg+1);
+			o5 = OP_IRR(opirr(AMOVF), 4, REGTMP, p->from.reg);
+			break;
+		case 16:
+			if(r == REGTMP)
+				diag("cant synthesize large constant\n%P", p);
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+			o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+			o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+			o4 = OP_IRR(opirr(AMOVF), 0, REGTMP, p->from.reg);
+			break;
+		case 8:
+			o1 = OP_IRR(opirr(AMOVF), v, r, p->from.reg+1);
+			o2 = OP_IRR(opirr(AMOVF), v+4, r, p->from.reg);
+			break;
+		case 4:
+			o1 = OP_IRR(opirr(AMOVF), v, r, p->from.reg);
+			break;
+		}
+		break;
+
+	case 30:	/* movw r,fr */
+		r = SP(2,1)|(4<<21);		/* mtc1 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 31:	/* movw fr,r */
+		r = SP(2,1)|(0<<21);		/* mfc1 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+
+	case 32:	/* fadd fr1,[fr2],fr3 */
+		r = p->reg;
+		if(r == NREG)
+			o1 = OP_FRRR(oprrr(p->as), p->from.reg, p->to.reg, p->to.reg);
+		else
+			o1 = OP_FRRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 33:	/* fabs fr1,fr3 */
+		o1 = OP_FRRR(oprrr(p->as), 0, p->from.reg, p->to.reg);
+		break;
+
+	case 34:	/* mov $con,fr ==> or/add $i,r,r2 */
+		v = regoff(&p->from);
+		r = AADDU;
+		if(o->a1 == C_ANDCON)
+			r = AOR;
+		o1 = OP_IRR(opirr(r), v, 0, REGTMP);
+		o2 = OP_RRR(SP(2,1)|(4<<21), REGTMP, 0, p->to.reg);	/* mtc1 */
+		break;
+
+	case 35:	/* mov r,lext/luto/oreg ==> sw o(r) */
+		/*
+		 * the lowbits of the constant cannot
+		 * be moved into the offset of the load
+		 * because the mips 4000 in 64-bit mode
+		 * does a 64-bit add and it will screw up.
+		 */
+		v = regoff(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+		o4 = OP_IRR(opirr(p->as), 0, REGTMP, p->from.reg);
+		break;
+
+	case 36:	/* mov lext/lauto/lreg,r ==> lw o(r30) */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
+		o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
+		o4 = OP_IRR(opirr(p->as+ALAST), 0, REGTMP, p->to.reg);
+		break;
+
+	case 37:	/* movw r,mr */
+		r = SP(2,0)|(4<<21);		/* mtc0 */
+		if(p->as == AMOVV)
+			r = SP(2,0)|(5<<21);	/* dmtc0 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 38:	/* movw mr,r */
+		r = SP(2,0)|(0<<21);		/* mfc0 */
+		if(p->as == AMOVV)
+			r = SP(2,0)|(1<<21);	/* dmfc0 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+
+	case 39:	/* rfe ==> jmp+rfe */
+		if(aflag)
+			return 0;
+		o1 = OP_RRR(oprrr(AJMP), 0, p->to.reg, REGZERO);
+		o2 = oprrr(p->as);
+		break;
+
+	case 40:	/* word */
+		if(aflag)
+			return 0;
+		o1 = regoff(&p->to);
+		break;
+
+	case 41:	/* movw r,fcr */
+		o1 = OP_RRR(SP(2,1)|(2<<21), REGZERO, 0, p->to.reg); 	/* mfcc1 */
+		o2 = OP_RRR(SP(2,1)|(6<<21), p->from.reg, 0, p->to.reg);/* mtcc1 */
+		break;
+
+	case 42:	/* movw fcr,r */
+		o1 = OP_RRR(SP(2,1)|(2<<21), p->to.reg, 0, p->from.reg);/* mfcc1 */
+		break;
+
+	case 45:	/* case r */
+		if(p->link == P)
+			v = p->pc+28;
+		else
+			v = p->link->pc;
+		if(v & (1<<15))
+			o1 = OP_IRR(opirr(ALAST), (v>>16)+1, REGZERO, REGTMP);
+		else
+			o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
+		o2 = OP_SRR(opirr(ASLL), 2, p->from.reg, p->from.reg);
+		o3 = OP_RRR(oprrr(AADD), p->from.reg, REGTMP, REGTMP);
+		o4 = OP_IRR(opirr(AMOVW+ALAST), v, REGTMP, REGTMP);
+		o5 = OP_RRR(oprrr(ANOR), REGZERO, REGZERO, REGZERO);
+		o6 = OP_RRR(oprrr(AJMP), 0, REGTMP, REGZERO);
+		o7 = OP_RRR(oprrr(ANOR), REGZERO, REGZERO, REGZERO);
+		break;
+
+	case 46:	/* bcase $con,lbra */
+		if(p->cond == P)
+			v = p->pc;
+		else
+			v = p->cond->pc;
+		o1 = v;
+		break;
+	}
+	if(aflag)
+		return o1;
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
+		LPUT(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
+		LPUT(o1);
+		LPUT(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		LPUT(o5);
+		break;
+
+	case 28:
+		if(debug['a'])
+			Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, o6, o7, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		LPUT(o5);
+		LPUT(o6);
+		LPUT(o7);
+		break;
+	}
+	return 0;
+}
+
+int
+isnop(Prog *p)
+{
+	if(p->as != ANOR)
+		return 0;
+	if(p->reg != REGZERO && p->reg != NREG)
+		return 0;
+	if(p->from.type != D_REG || p->from.reg != REGZERO)
+		return 0;
+	if(p->to.type != D_REG || p->to.reg != REGZERO)
+		return 0;
+	return 1;
+}
+
+long
+oprrr(int a)
+{
+	switch(a) {
+	case AADD:	return OP(4,0);
+	case AADDU:	return OP(4,1);
+	case ASGT:	return OP(5,2);
+	case ASGTU:	return OP(5,3);
+	case AAND:	return OP(4,4);
+	case AOR:	return OP(4,5);
+	case AXOR:	return OP(4,6);
+	case ASUB:	return OP(4,2);
+	case ASUBU:	return OP(4,3);
+	case ANOR:	return OP(4,7);
+	case ASLL:	return OP(0,4);
+	case ASRL:	return OP(0,6);
+	case ASRA:	return OP(0,7);
+
+	case AREM:
+	case ADIV:	return OP(3,2);
+	case AREMU:
+	case ADIVU:	return OP(3,3);
+	case AMUL:	return OP(3,0);
+	case AMULU:	return OP(3,1);
+
+	case AJMP:	return OP(1,0);
+	case AJAL:	return OP(1,1);
+
+	case ABREAK:	return OP(1,5);
+	case ASYSCALL:	return OP(1,4);
+	case ATLBP:	return MMU(1,0);
+	case ATLBR:	return MMU(0,1);
+	case ATLBWI:	return MMU(0,2);
+	case ATLBWR:	return MMU(0,6);
+	case ARFE:	return MMU(2,0);
+
+	case ADIVF:	return FPF(0,3);
+	case ADIVD:	return FPD(0,3);
+	case AMULF:	return FPF(0,2);
+	case AMULD:	return FPD(0,2);
+	case ASUBF:	return FPF(0,1);
+	case ASUBD:	return FPD(0,1);
+	case AADDF:	return FPF(0,0);
+	case AADDD:	return FPD(0,0);
+
+	case AMOVFW:	return FPF(4,4);
+	case AMOVDW:	return FPD(4,4);
+	case AMOVWF:	return FPW(4,0);
+	case AMOVDF:	return FPD(4,0);
+	case AMOVWD:	return FPW(4,1);
+	case AMOVFD:	return FPF(4,1);
+	case AABSF:	return FPF(0,5);
+	case AABSD:	return FPD(0,5);
+	case AMOVF:	return FPF(0,6);
+	case AMOVD:	return FPD(0,6);
+	case ANEGF:	return FPF(0,7);
+	case ANEGD:	return FPD(0,7);
+
+	case ACMPEQF:	return FPF(6,2);
+	case ACMPEQD:	return FPD(6,2);
+	case ACMPGTF:	return FPF(7,4);
+	case ACMPGTD:	return FPD(7,4);
+	case ACMPGEF:	return FPF(7,6);
+	case ACMPGED:	return FPD(7,6);
+
+	case ADIVV:	return OP(3,6);
+	case ADIVVU:	return OP(3,7);
+	case AADDV:	return OP(5,4);
+	case AADDVU:	return OP(5,5);
+	}
+	diag("bad rrr %d", a);
+	return 0;
+}
+
+long
+opirr(int a)
+{
+	switch(a) {
+	case AADD:	return SP(1,0);
+	case AADDU:	return SP(1,1);
+	case ASGT:	return SP(1,2);
+	case ASGTU:	return SP(1,3);
+	case AAND:	return SP(1,4);
+	case AOR:	return SP(1,5);
+	case AXOR:	return SP(1,6);
+	case ALAST:	return SP(1,7);
+	case ASLL:	return OP(0,0);
+	case ASRL:	return OP(0,2);
+	case ASRA:	return OP(0,3);
+
+	case AJMP:	return SP(0,2);
+	case AJAL:	return SP(0,3);
+	case ABEQ:	return SP(0,4);
+	case ABNE:	return SP(0,5);
+
+	case ABGEZ:	return SP(0,1)|BCOND(0,1);
+	case ABGEZAL:	return SP(0,1)|BCOND(2,1);
+	case ABGTZ:	return SP(0,7);
+	case ABLEZ:	return SP(0,6);
+	case ABLTZ:	return SP(0,1)|BCOND(0,0);
+	case ABLTZAL:	return SP(0,1)|BCOND(2,0);
+
+	case ABFPT:	return SP(2,1)|(257<<16);
+	case ABFPF:	return SP(2,1)|(256<<16);
+
+	case AMOVB:
+	case AMOVBU:	return SP(5,0);
+	case AMOVH:
+	case AMOVHU:	return SP(5,1);
+	case AMOVW:	return SP(5,3);
+	case AMOVV:	return SP(7,7);
+	case AMOVF:	return SP(7,1);
+	case AMOVWL:	return SP(5,2);
+	case AMOVWR:	return SP(5,6);
+	case AMOVVL:	return SP(5,4);
+	case AMOVVR:	return SP(5,5);
+
+	case ABREAK:	return SP(5,7);
+
+	case AMOVWL+ALAST:	return SP(4,2);
+	case AMOVWR+ALAST:	return SP(4,6);
+	case AMOVVL+ALAST:	return SP(3,2);
+	case AMOVVR+ALAST:	return SP(3,3);
+	case AMOVB+ALAST:	return SP(4,0);
+	case AMOVBU+ALAST:	return SP(4,4);
+	case AMOVH+ALAST:	return SP(4,1);
+	case AMOVHU+ALAST:	return SP(4,5);
+	case AMOVW+ALAST:	return SP(4,3);
+	case AMOVV+ALAST:	return SP(6,7);
+	case AMOVF+ALAST:	return SP(6,1);
+
+	case ASLLV:		return OP(7,0);
+	case ASRLV:		return OP(7,2);
+	case ASRAV:		return OP(7,3);
+	case ASLLV+ALAST:	return OP(7,4);
+	case ASRLV+ALAST:	return OP(7,6);
+	case ASRAV+ALAST:	return OP(7,7);
+
+	case AADDV:		return SP(3,0);
+	case AADDVU:		return SP(3,1);
+	}
+	diag("bad irr %d", a);
+abort();
+	return 0;
+}
+
+int
+vshift(int a)
+{
+	switch(a){
+	case ASLLV:		return 1;
+	case ASRLV:		return 1;
+	case ASRAV:		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/utils/vl/compat.c
@@ -1,0 +1,65 @@
+#include	"l.h"
+
+/*
+ * fake malloc
+ */
+void*
+malloc(ulong n)
+{
+	void *p;
+
+	while(n & 7)
+		n++;
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void
+free(void *p)
+{
+	USED(p);
+}
+
+void*
+calloc(ulong m, ulong n)
+{
+	void *p;
+
+	n *= m;
+	p = malloc(n);
+	memset(p, 0, n);
+	return p;
+}
+
+void*
+realloc(void*, ulong)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void*
+mysbrk(ulong size)
+{
+	return sbrk(size);
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+	USED(v, pc);
+}
+
+int
+fileexists(char *s)
+{
+	uchar dirbuf[400];
+
+	/* it's fine if stat result doesn't fit in dirbuf, since even then the file exists */
+	return stat(s, dirbuf, sizeof(dirbuf)) >= 0;
+}
--- /dev/null
+++ b/utils/vl/l.h
@@ -1,0 +1,344 @@
+#include	<lib9.h>
+#include	<bio.h>
+#include	"../vc/v.out.h"
+#include	"../ld/elf.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	LIBNAMELEN	300
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+typedef	struct	Oprang	Oprang;
+typedef	uchar	Opcross[32][2][32];
+typedef	struct	Count	Count;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		long	u0offset;
+		char*	u0sval;
+		Ieee*	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	union
+	{
+		long	u0regused;
+		Prog*	u0forwd;
+	} u0;
+	Prog*	cond;
+	Prog*	link;
+	long	pc;
+	long	line;
+	uchar	mark;
+	uchar	optab;
+	char	as;
+	char	reg;
+};
+#define	regused	u0.u0regused
+#define	forwd	u0.u0forwd
+
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	long	value;
+	Sym*	link;
+};
+struct	Autom
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	char	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+};
+struct	Oprang
+{
+	Optab*	start;
+	Optab*	stop;
+};
+struct	Count
+{
+	long	count;
+	long	outof;
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+	SSTRING,
+
+	C_NONE		= 0,
+	C_REG,
+	C_FREG,
+	C_FCREG,
+	C_MREG,
+	C_HI,
+	C_LO,
+	C_ZCON,
+	C_SCON,
+	C_ADD0CON,
+	C_AND0CON,
+	C_ADDCON,
+	C_ANDCON,
+	C_UCON,
+	C_LCON,
+	C_SACON,
+	C_SECON,
+	C_LACON,
+	C_LECON,
+	C_SBRA,
+	C_LBRA,
+	C_SAUTO,
+	C_SEXT,
+	C_LAUTO,
+	C_LEXT,
+	C_ZOREG,
+	C_SOREG,
+	C_LOREG,
+	C_GOK,
+
+	NSCHED		= 20,
+
+/* mark flags */
+	FOLL		= 1<<0,
+	LABEL		= 1<<1,
+	LEAF		= 1<<2,
+	SYNC		= 1<<3,
+	BRANCH		= 1<<4,
+	LOAD		= 1<<5,
+	FCMP		= 1<<6,
+	NOSCHED		= 1<<7,
+
+	BIG		= 32766,
+	STRINGSZ	= 200,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	long	INITDAT;		/* data location */
+EXTERN	long	INITRND;		/* data round above text location */
+EXTERN	long	INITTEXT;		/* text location */
+EXTERN	long	INITTEXTP;		/* text location (physical) */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi4[4];	/* for 3l [sic] */
+EXTERN	char	fnuxi8[8];
+EXTERN	char*	noname;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char*	hunk;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	long	instoffset;
+EXTERN	Opcross	opcross[10];
+EXTERN	Oprang	oprange[ALAST];
+EXTERN	char*	outfile;
+EXTERN	long	pc;
+EXTERN	uchar	repop[ALAST];
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	long	textsize;
+EXTERN	long	thunk;
+EXTERN	int	version;
+EXTERN	char	xcmp[32][32];
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+EXTERN	int	little;
+
+EXTERN	struct
+{
+	Count	branch;
+	Count	fcmp;
+	Count	load;
+	Count	mfrom;
+	Count	page;
+	Count	jump;
+} nop;
+
+extern	char*	anames[];
+extern	Optab	optab[];
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	addlibpath(char*);
+void	addnop(Prog*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmlc(void);
+int	asmout(Prog*, Optab*, int);
+void	asmsym(void);
+long	atolwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	buildrep(int, int);
+void	cflush(void);
+int	cmp(int, int);
+void	cput(long);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long, int);
+void	diag(char*, ...);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+long	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+int	fileexists(char*);
+int	find1(long, int);
+char*	findlib(char*);
+void	follow(void);
+void	gethunk(void);
+void	histtoauto(void);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	llput(vlong);
+void	llputl(vlong);
+void	lput(long);
+void	lputl(long);
+void	bput(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	noops(void);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(void*, void*);
+long	opirr(int);
+Optab*	oplook(Prog*);
+long	oprrr(int);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, long, int);
+long	regoff(Adr*);
+int	relinv(int);
+long	rnd(long, long);
+void	sched(Prog*, Prog*);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, long);
+void	xfol(Prog*);
+void	xfol(Prog*);
+void	nopstat(char*, Count*);
--- /dev/null
+++ b/utils/vl/list.c
@@ -1,0 +1,277 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	if(a == ADATA || a == ADYNT || a == AINIT)
+		sprint(str, "(%ld)	%A	%D/%d,%D",
+			p->line, a, &p->from, p->reg, &p->to);
+	else{
+		s = str;
+		s += sprint(s, "(%ld)", p->line);
+		if(p->mark & NOSCHED)
+			s += sprint(s, "*");
+		if(p->reg == NREG)
+			sprint(s, "	%A	%D,%D",
+				a, &p->from, &p->to);
+		else
+		if(p->from.type != D_FREG)
+			sprint(s, "	%A	%D,R%d,%D",
+				a, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%A	%D,F%d,%D",
+				a, &p->from, p->reg, &p->to);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "?";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		sprint(str, "$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OCONST:
+		sprint(str, "$*$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_MREG:
+		sprint(str, "M%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCREG:
+		sprint(str, "FC%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_LO:
+		sprint(str, "LO");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(LO)(REG)", a);
+		break;
+
+	case D_HI:
+		sprint(str, "HI");
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(HI)(REG)", a);
+		break;
+
+	case D_BRANCH:	/* botch */
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(v >= INITTEXT)
+				v -= INITTEXT-HEADR;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%ld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%ld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%e", ieeedtod(a->ieee));
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%ld", a->offset);
+		break;
+
+	case D_EXTERN:
+		if(s == S)
+			sprint(str, "%ld(SB)", a->offset);
+		else
+			sprint(str, "%s+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		if(s == S)
+			sprint(str, "<>+%ld(SB)", a->offset);
+		else
+			sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		if(s == S)
+			sprint(str, "%ld(SP)", a->offset);
+		else
+			sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		if(s == S)
+			sprint(str, "%ld(FP)", a->offset);
+		else
+			sprint(str, "%s+%ld(FP)", s->name, a->offset);
+		break;
+	}
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<sizeof(long); i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/utils/vl/mkfile
@@ -1,0 +1,37 @@
+<../../mkconfig
+
+TARG=vl
+
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	sched.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	$TARGMODEL.$O\
+	elf.$O\
+
+HFILES=\
+	l.h\
+	../vc/v.out.h\
+	../include/ar.h\
+
+LIBS=bio 9			# order is important
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS=	$CFLAGS -I../include -I.
+
+enam.$O:	../vc/enam.c
+	$CC $CFLAGS ../vc/enam.c
+elf.$O:	../ld/elf.c
+	$CC $CFLAGS ../ld/elf.c
+
+$TARGMODEL.$O:	../ld/$TARGMODEL.c
+	$CC $CFLAGS ../ld/$TARGMODEL.c
--- /dev/null
+++ b/utils/vl/noop.c
@@ -1,0 +1,416 @@
+#include	"l.h"
+
+void
+noops(void)
+{
+	Prog *p, *p1, *q, *q1;
+	int o, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			p->mark |= LABEL|LEAF|SYNC;
+			if(p->link)
+				p->link->mark |= LABEL;
+			curtext = p;
+			break;
+
+		/* too hard, just leave alone */
+		case AMOVW:
+			if(p->to.type == D_FCREG ||
+			   p->to.type == D_MREG) {
+				p->mark |= LABEL|SYNC;
+				break;
+			}
+			if(p->from.type == D_FCREG ||
+			   p->from.type == D_MREG) {
+				p->mark |= LABEL|SYNC;
+				addnop(p);
+				addnop(p);
+				nop.mfrom.count += 2;
+				nop.mfrom.outof += 2;
+				break;
+			}
+			break;
+
+		/* too hard, just leave alone */
+		case ACASE:
+		case ASYSCALL:
+		case AWORD:
+		case ATLBWR:
+		case ATLBWI:
+		case ATLBP:
+		case ATLBR:
+			p->mark |= LABEL|SYNC;
+			break;
+
+		case ANOR:
+			if(p->to.type == D_REG && p->to.reg == REGZERO)
+				p->mark |= LABEL|SYNC;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+
+			if(p->link != P)
+				p->link->mark |= LABEL;
+			break;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		case ABCASE:
+			p->mark |= LABEL|SYNC;
+			goto dstlab;
+
+		case ABGEZAL:
+		case ABLTZAL:
+		case AJAL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case AJMP:
+		case ABEQ:
+		case ABGEZ:
+		case ABGTZ:
+		case ABLEZ:
+		case ABLTZ:
+		case ABNE:
+		case ABFPT:
+		case ABFPF:
+			p->mark |= BRANCH;
+
+		dstlab:
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+				if(!(q1->mark & LEAF))
+					q1->mark |= LABEL;
+			} else
+				p->mark |= LABEL;
+			q1 = p->link;
+			if(q1 != P)
+				q1->mark |= LABEL;
+			break;
+		}
+		q = p;
+	}
+
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case AJAL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 4;
+			if(autosize <= 4)
+			if(curtext->mark & LEAF) {
+				p->to.offset = -4;
+				autosize = 0;
+			}
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = -autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			} else
+			if(!(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				Bflush(&bso);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+				break;
+			}
+
+			q1 = prg();
+			q1->as = AMOVW;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->from.offset = 0;
+			q1->to.reg = REGSP;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		case ARET:
+			nocache(p);
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = AJMP;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 0;
+					p->to.reg = REGLINK;
+					p->mark |= BRANCH;
+					break;
+				}
+
+				p->as = AADD;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = AJMP;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 0;
+				q->to.reg = REGLINK;
+				q->mark |= BRANCH;
+
+				q->link = p->link;
+				p->link = q;
+				break;
+			}
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = 2;
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = AADD;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			}
+
+			q1 = prg();
+			q1->as = AJMP;
+			q1->line = p->line;
+			q1->to.type = D_OREG;
+			q1->to.offset = 0;
+			q1->to.reg = 2;
+			q1->mark |= BRANCH;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				q = prg();
+				q->line = p->line;
+				q->as = AJMP;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				q->mark |= BRANCH;
+				p->link = q;
+
+				p->as = AADD;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+			}
+			q = prg();
+			q->line = p->line;
+			q->as = AJMP;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			q->mark |= BRANCH;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AADD;
+			q->from.type = D_CONST;
+			q->from.offset = autosize;
+			q->to.type = D_REG;
+			q->to.reg = REGSP;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVW;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+		}
+	}
+
+	curtext = P;
+	q = P;		/* p - 1 */
+	q1 = firstp;	/* top of block */
+	o = 0;		/* count of instructions */
+	for(p = firstp; p != P; p = p1) {
+		p1 = p->link;
+		o++;
+		if(p->mark & NOSCHED){
+			if(q1 != p){
+				sched(q1, q);
+			}
+			for(; p != P; p = p->link){
+				if(!(p->mark & NOSCHED))
+					break;
+				q = p;
+			}
+			p1 = p;
+			q1 = p;
+			o = 0;
+			continue;
+		}
+		if(p->mark & (LABEL|SYNC)) {
+			if(q1 != p)
+				sched(q1, q);
+			q1 = p;
+			o = 1;
+		}
+		if(p->mark & (BRANCH|SYNC)) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		if(o >= NSCHED) {
+			sched(q1, p);
+			q1 = p1;
+			o = 0;
+		}
+		q = p;
+	}
+}
+
+void
+addnop(Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = ANOR;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGZERO;
+	q->to.type = D_REG;
+	q->to.reg = REGZERO;
+
+	q->link = p->link;
+	p->link = q;
+}
+
+void
+nocache(Prog *p)
+{
+	p->optab = 0;
+	p->from.class = 0;
+	p->to.class = 0;
+}
--- /dev/null
+++ b/utils/vl/obj.c
@@ -1,0 +1,1475 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= 'v';
+char	*thestring 	= "mips";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H0 -T0x40004C -D0x10000000	is abbrev unix
+ *	-H1 -T0x80020000 -R4		is bootp() format for 3k
+ *	-H2 -T4128 -R4096		is plan9 format
+ *	-H3 -T0x80020000 -R8		is bootp() format for 4k
+ *	-H4 -T0x400000 -R4		is sgi unix coff executable
+ *	-H5 -T0x4000A0 -R4		is sgi unix elf executable
+ *	-H6						is headerless
+ */
+
+int little;
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+	char name[LIBNAMELEN];
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	HEADTYPE = -1;
+	INITTEXT = -1;
+	INITTEXTP = -1;
+	INITDAT = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case  'm':			/* for little-endian mips */
+		thechar = '0';
+		thestring = "spim";
+		little = 1;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		/* do something about setting INITTEXT */
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	} ARGEND
+
+	USED(argc);
+
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	a = getenv("ccroot");
+	if(a != nil && *a != '\0') {
+		if(!fileexists(a)) {
+			diag("nonexistent $ccroot: %s", a);
+			errorexit();
+		}
+	}else
+		a = "";
+	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
+	addlibpath(name);
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+
+	case 0:	/* unix simple */
+		HEADR = 20L+56L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x40004CL;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000L;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 1:	/* boot for 3k */
+		HEADR = 20L+60L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80020000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:	/* plan 9 */
+		HEADR = 32L;
+		if(INITTEXT == -1)
+			INITTEXT = 4128;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	case 3:	/* boot for 4k */
+		HEADR = 20L+56L+3*40L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80020000L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 8;
+		break;
+	case 4:	/* sgi unix coff executable */
+		HEADR = 20L+56L+3*40L;
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 5:	/* sgi unix elf executable */
+		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
+		if(INITTEXT == -1)
+			INITTEXT = 0x00400000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0x10000000;
+		if(INITRND == -1)
+			INITRND = 0;
+		break;
+	case 6:	/* headerless */
+		HEADR = 0;
+		if(INITTEXT == -1)
+			INITTEXT = 0x80000000L+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0) {
+		static char name[20];
+
+		snprint(name, sizeof name, "%c.out", thechar);
+		outfile = name;
+	}
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	nuxiinit();
+
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%ld memory used\n", thunk);
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+char*
+findlib(char *file)
+{
+	int i;
+	char name[LIBNAMELEN];
+
+	for(i = 0; i < nlibdir; i++) {
+		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
+		if(fileexists(name))
+			return libdir[i];
+	}
+	return nil;
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	long off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[100], pname[150];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(file[0] == '-' && file[1] == 'l') {
+		if(debug['9'])
+			sprint(name, "/%s/lib/lib", thestring);
+		else
+			sprint(name, "/usr/%clib/lib", thechar);
+		strcat(name, file+2);
+		strcat(name, ".a");
+		file = name;
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open file: %s", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			/* need readn to read the dumps (at least) */
+			l = readn(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	int l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = ALAST+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_MREG:
+	case D_FCREG:
+	case D_LO:
+	case D_HI:
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+		a->offset = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		c += 4;
+		break;
+
+	case D_SCONST:
+		while(nhunk < NSNAME)
+			gethunk();
+		a->sval = (char*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		while(nhunk < sizeof(Ieee))
+			gethunk();
+		a->ieee = (Ieee*)hunk;
+		nhunk -= NSNAME;
+		hunk += NSNAME;
+
+		a->ieee->l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee->h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		return c;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+
+	while(nhunk < sizeof(Auto))
+		gethunk();
+	u = (Auto*)hunk;
+	nhunk -= sizeof(Auto);
+	hunk += sizeof(Auto);
+
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+
+	cleanname(name);
+
+	if(search){
+		p = findlib(name);
+		if(p != nil){
+			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
+			name = fn2;
+		}
+	}
+
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	long ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= AXXX || o >= ALAST) {
+		diag("%s: line %ld: opcode out of range %d", pn, pc-ipc, o);
+		print("	probably not a .%c file\n", thechar);
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		if(o == ASIGNAME) {
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	if(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	p->as = o;
+	p->reg = bloc[1] & 0x7f;
+	if(bloc[1] & 0x80)
+		p->mark = NOSCHED;
+	p->line = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg < 0 || p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+	
+	case ADATA:
+		if(p->from.sym == S) {
+			diag("DATA without a sym\n%P", p);
+			break;
+		}
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		autosize = (p->to.offset+3L) & ~3L;
+		p->to.offset = autosize;
+		autosize += 4;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->cond = p;
+		etextp = p;
+		break;
+
+	case ASUB:
+	case ASUBU:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE) {
+			p->from.offset = -p->from.offset;
+			if(p->as == ASUB)
+				p->as = AADD;
+			else
+				p->as = AADDU;
+		}
+		goto casedef;
+
+	case AMOVF:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVD:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->l, p->from.ieee->h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	h &= 0xffffff;
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	while(nhunk < sizeof(Sym))
+		gethunk();
+	s = (Sym*)hunk;
+	nhunk -= sizeof(Sym);
+	hunk += sizeof(Sym);
+
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p;
+
+	while(nhunk < sizeof(Prog))
+		gethunk();
+	p = (Prog*)hunk;
+	nhunk -= sizeof(Prog);
+	hunk += sizeof(Prog);
+
+	*p = zprg;
+	return p;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 5L*NHUNK) {
+		nh = 5L*NHUNK;
+		if(thunk >= 25L*NHUNK)
+			nh = 25L*NHUNK;
+	}
+	h = mysbrk(nh);
+	if(h == (char*)-1) {
+		diag("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADDU;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				ps2 = p;
+				p->reg = 1;
+			}
+			if(p->from.sym == s4) {
+				ps4 = p;
+				p->reg = 1;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->reg & NOPROF) {
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JAL	profin, R2
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = AJMP;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->cond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = AJAL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JAL	profout
+			 */
+			p->as = AJAL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++)
+		if (!little) {			/* normal big-endian case */
+			c = find1(0x01020304L, i+1);
+			if(i >= 2)
+				inuxi2[i-2] = c;
+			if(i >= 3)
+				inuxi1[i-3] = c;
+			inuxi4[i] = c;
+			fnuxi8[i] = c+4;
+			fnuxi8[i+4] = c;
+		} else {			/* oddball little-endian case */
+			c = find1(0x04030201L, i+1);
+			if(i < 2)
+				inuxi2[i] = c;
+			if(i < 1)
+				inuxi1[i] = c;
+			inuxi4[i] = c;
+			fnuxi4[i] = c;
+			fnuxi8[i] = c;
+			fnuxi8[i+4] = c+4;
+		}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
--- /dev/null
+++ b/utils/vl/optab.c
@@ -1,0 +1,232 @@
+#include	"l.h"
+
+/* note: not finished
+ *	movd	fr,mem
+ *	movd	mem,fr
+ *	addv
+ *	addvu
+ *	subv
+ *	subvu
+ *	mulv
+ *	mulvu
+ *	divv
+ *	divvu
+ *	remv
+ *	remvu
+ */
+
+#define	X	99
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		12, 8, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_REG,		13, 4, 0 },
+
+	{ ASUB,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ ASUB,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+
+	{ ASLL,		C_REG,	C_NONE,	C_REG,		 9, 4, 0 },
+	{ ASLL,		C_REG,	C_REG,	C_REG,		 9, 4, 0 },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		32, 4, 0 },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		32, 4, 0 },
+	{ ACMPEQF,	C_FREG,	C_REG,	C_NONE,		32, 4, 0 },
+	{ AABSF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVD,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVV,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVBU,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVWL,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVV,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVBU,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVWL,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVV,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVBU,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVWL,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVV,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVBU,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVV,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVBU,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVV,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVBU,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+
+	{ AMOVW,	C_SECON,C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVW,	C_SACON,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		26, 12, REGSB },
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		26, 12, REGSP },
+	{ AMOVW,	C_ADDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVW,	C_ANDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+
+	{ AMOVW,	C_UCON, C_NONE, C_REG,		24, 4, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		19, 8, 0 },
+
+	{ AMOVW,	C_HI,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVV,	C_HI,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVW,	C_LO,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVV,	C_LO,	C_NONE,	C_REG,		20, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_HI,		21, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_HI,		21, 4, 0 },
+	{ AMOVW,	C_REG,	C_NONE,	C_LO,		21, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_LO,		21, 4, 0 },
+
+	{ AMUL,		C_REG,	C_REG,	C_NONE,		22, 4, 0 },
+
+	{ AADD,		C_ADD0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ADD0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ANDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AADD,		C_ANDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AAND,		C_AND0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AAND,		C_AND0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AAND,		C_ADDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AAND,		C_ADDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AADD,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AADD,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+
+	{ ASLL,		C_SCON,	C_REG,	C_REG,		16, 4, 0 },
+	{ ASLL,		C_SCON,	C_NONE,	C_REG,		16, 4, 0 },
+
+	{ ASYSCALL,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ ABEQ,		C_REG,	C_REG,	C_SBRA,		 6, 4, 0 },
+	{ ABEQ,		C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABLEZ,	C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABFPT,	C_NONE,	C_NONE,	C_SBRA,		 6, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+	{ AJAL,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGZERO },
+	{ AJAL,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGLINK },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVF,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVD,	C_SEXT,	C_NONE,	C_FREG,		27, 8, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVF,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVD,	C_SAUTO,C_NONE,	C_FREG,		27, 8, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVF,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVD,	C_SOREG,C_NONE,	C_FREG,		27, 8, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVF,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVD,	C_LEXT,	C_NONE,	C_FREG,		27, 20, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVF,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVD,	C_LAUTO,C_NONE,	C_FREG,		27, 20, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVF,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVD,	C_LOREG,C_NONE,	C_FREG,		27, 20, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SEXT,		28, 8, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SAUTO,	28, 8, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SOREG,	28, 8, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LEXT,		28, 20, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LAUTO,	28, 20, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LOREG,	28, 20, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FREG,		30, 4, 0 },
+	{ AMOVW,	C_FREG,	C_NONE,	C_REG,		31, 4, 0 },
+
+	{ AMOVW,	C_ADDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_ANDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_UCON, C_NONE, C_FREG,		35, 8, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_FREG,		36, 12, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_MREG,		37, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_MREG,		37, 4, 0 },
+	{ AMOVW,	C_MREG,	C_NONE,	C_REG,		38, 4, 0 },
+	{ AMOVV,	C_MREG,	C_NONE,	C_REG,		38, 4, 0 },
+
+	{ ARFE,		C_NONE,	C_NONE,	C_ZOREG,	39, 8, 0 },
+	{ AWORD,	C_NONE,	C_NONE,	C_LCON,		40, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCREG,	41, 8, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FCREG,	41, 8, 0 },
+	{ AMOVW,	C_FCREG,C_NONE,	C_REG,		42, 4, 0 },
+	{ AMOVV,	C_FCREG,C_NONE,	C_REG,		42, 4, 0 },
+
+	{ ABREAK,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },	/* really CACHE instruction */
+	{ ABREAK,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ ABREAK,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ ABREAK,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ ACASE,	C_REG,	C_NONE,	C_NONE,		 45, 28, 0 },
+	{ ABCASE,	C_LCON,	C_NONE,	C_LBRA,		 46, 4, 0 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/utils/vl/pass.c
@@ -1,0 +1,505 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p, *p1;
+	long orig, orig1, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%ld): %s\n%P",
+				s->value, s->name, p);
+	}
+
+	if(debug['t']) {
+		/*
+		 * pull out string constants
+		 */
+		for(p = datap; p != P; p = p->link) {
+			s = p->from.sym;
+			if(p->to.type == D_SCONST)
+				s->type = SSTRING;
+		}
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on R30)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 3)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+	orig1 = orig;
+
+	/*
+	 * pass 2
+	 *	assign 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	/*
+	 * pass 4
+	 *	add literals to all large values.
+	 *	at this time:
+	 *		small data is allocated DATA
+	 *		large data is allocated DATA1
+	 *		large bss is allocated BSS
+	 *	the new literals are loaded between
+	 *	small data and large data.
+	 */
+	orig = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as != AMOVW)
+			continue;
+		if(p->from.type != D_CONST)
+			continue;
+		if(s = p->from.sym) {
+			t = s->type;
+			if(t != SDATA && t != SDATA1 && t != SBSS)
+				continue;
+			t = p->from.name;
+			if(t != D_EXTERN && t != D_STATIC)
+				continue;
+			v = s->value + p->from.offset;
+			if(v >= 0 && v <= 0xffff)
+				continue;
+			if(!strcmp(s->name, "setR30"))
+				continue;
+			/* size should be 19 max */
+			if(strlen(s->name) >= 10)	/* has loader address */ 
+				sprint(literal, "$%p.%lux", s, p->from.offset);
+			else
+				sprint(literal, "$%s.%d.%lux", s->name, s->version, p->from.offset);
+		} else {
+			if(p->from.name != D_NONE)
+				continue;
+			if(p->from.reg != NREG)
+				continue;
+			v = p->from.offset;
+			if(v >= -0x7fff && v <= 0xffff)
+				continue;
+			if(!(v & 0xffff))
+				continue;
+			/* size should be 9 max */
+			sprint(literal, "$%lux", v);
+		}
+		s = lookup(literal, 0);
+		if(s->type == 0) {
+			s->type = SDATA;
+			s->value = orig1+orig;
+			orig += 4;
+			p1 = prg();
+			p1->line = p->line;
+			p1->as = ADATA;
+			p1->from.type = D_OREG;
+			p1->from.sym = s;
+			p1->from.name = D_EXTERN;
+			p1->reg = 4;
+			p1->to = p->from;
+			p1->link = datap;
+			datap = p1;
+		}
+		if(s->type != SDATA)
+			diag("literal not data: %s", s->name);
+		p->from.type = D_OREG;
+		p->from.sym = s;
+		p->from.name = D_EXTERN;
+		p->from.offset = 0;
+		nocache(p);
+		continue;
+	}
+	while(orig & 7)
+		orig++;
+	/*
+	 * pass 5
+	 *	re-adjust offsets
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t == SBSS) {
+			s->value += orig;
+			continue;
+		}
+		if(t == SDATA1) {
+			s->type = SDATA;
+			s->value += orig;
+			continue;
+		}
+	}
+	datsize += orig;
+	xdefine("setR30", SDATA, 0L+BIG);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+void
+follow(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AJMP) {
+		q = p->cond;
+		if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
+			p->mark |= FOLL;
+			lastp->link = p;
+			lastp = p;
+			p = p->link;
+			xfol(p);
+			p = q;
+			if(p && !(p->mark & FOLL))
+				goto loop;
+			return;
+		}
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp || (q->mark&NOSCHED))
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AJMP || a == ARET || a == ARFE)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			if(a != ABEQ && a != ABNE)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AJMP || a == ARET || a == ARFE)
+					return;
+				r->as = ABNE;
+				if(a == ABNE)
+					r->as = ABEQ;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+		a = AJMP;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AJMP || a == ARET || a == ARFE){
+		if(p->mark & NOSCHED){
+			p = p->link;
+			goto loop;
+		}
+		return;
+	}
+	if(p->cond != P)
+	if(a != AJAL && p->link != P) {
+		xfol(p->link);
+		p = p->cond;
+		if(p == P || (p->mark&FOLL))
+			return;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	long c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == AJAL || a == AJMP || a == ARET) &&
+		   p->to.type != D_BRANCH && p->to.sym != S) {
+			s = p->to.sym;
+			if(s->type != STEXT) {
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+			}
+			p->to.offset = s->value;
+			p->to.type = D_BRANCH;
+		}
+		if(p->to.type != D_BRANCH)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %ld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->cond != P) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AJMP || (p->mark&NOSCHED))
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+long
+atolwhex(char *s)
+{
+	long n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+long
+rnd(long v, long r)
+{
+	long c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/utils/vl/sched.c
@@ -1,0 +1,695 @@
+#include	"l.h"
+
+enum
+{
+	E_HILO	= 1<<0,
+	E_FCR	= 1<<1,
+	E_MCR	= 1<<2,
+	E_MEM	= 1<<3,
+	E_MEMSP	= 1<<4,	/* uses offset and size */
+	E_MEMSB	= 1<<5,	/* uses offset and size */
+	ANYMEM	= E_MEM|E_MEMSP|E_MEMSB,
+	DELAY	= BRANCH|LOAD|FCMP,
+};
+
+typedef	struct	Sch	Sch;
+typedef	struct	Dep	Dep;
+
+struct	Dep
+{
+	ulong	ireg;
+	ulong	freg;
+	ulong	cc;
+};
+struct	Sch
+{
+	Prog	p;
+	Dep	set;
+	Dep	used;
+	long	soffset;
+	char	size;
+	char	nop;
+	char	comp;
+};
+
+void	regsused(Sch*, Prog*);
+int	depend(Sch*, Sch*);
+int	conflict(Sch*, Sch*);
+int	offoverlap(Sch*, Sch*);
+void	dumpbits(Sch*, Dep*);
+
+void
+sched(Prog *p0, Prog *pe)
+{
+	Prog *p, *q;
+	Sch sch[NSCHED], *s, *t, *u, *se, stmp;
+
+	/*
+	 * build side structure
+	 */
+	s = sch;
+	for(p=p0;; p=p->link) {
+		memset(s, 0, sizeof(*s));
+		s->p = *p;
+		regsused(s, p);
+		if(debug['X']) {
+			Bprint(&bso, "%P\t\tset", &s->p);
+			dumpbits(s, &s->set);
+			Bprint(&bso, "; used");
+			dumpbits(s, &s->used);
+			if(s->comp)
+				Bprint(&bso, "; compound");
+			if(s->p.mark & LOAD)
+				Bprint(&bso, "; load");
+			if(s->p.mark & BRANCH)
+				Bprint(&bso, "; branch");
+			if(s->p.mark & FCMP)
+				Bprint(&bso, "; fcmp");
+			Bprint(&bso, "\n");
+		}
+		if(p == pe)
+			break;
+		s++;
+	}
+	se = s;
+
+	/*
+	 * prepass to move things around
+	 * does nothing, but tries to make
+	 * the actual scheduler work better
+	 */
+	for(s=sch; s<=se; s++) {
+		if(!(s->p.mark & LOAD))
+			continue;
+		/* always good to put nonconflict loads together */
+		for(t=s+1; t<=se; t++) {
+			if(!(t->p.mark & LOAD))
+				continue;
+			if(t->p.mark & BRANCH)
+				break;
+			if(conflict(s, t))
+				break;
+			for(u=t-1; u>s; u--)
+				if(depend(u, t))
+					goto no11;
+			u = s+1;
+			stmp = *t;
+			memmove(s+2, u, (uchar*)t - (uchar*)u);
+			*u = stmp;
+			break;
+		}
+	no11:
+
+		/* put schedule fodder above load */
+		for(t=s+1; t<=se; t++) {
+			if(t->p.mark & BRANCH)
+				break;
+			if(s > sch && conflict(s-1, t))
+				continue;
+			for(u=t-1; u>=s; u--)
+				if(depend(t, u))
+					goto no1;
+			stmp = *t;
+			memmove(s+1, s, (uchar*)t - (uchar*)s);
+			*s = stmp;
+			if(!(s->p.mark & LOAD))
+				break;
+		no1:;
+		}
+	}
+
+	for(s=se; s>=sch; s--) {
+		if(!(s->p.mark & DELAY))
+			continue;
+		if(s < se)
+			if(!conflict(s, s+1))
+				goto out3;
+		/*
+		 * s is load, s+1 is immediate use of result or end of block
+		 * t is the trial instruction to insert between s and s+1
+		 */
+		if(!debug['Y'])
+		for(t=s-1; t>=sch; t--) {
+			if(t->comp)
+				if(s->p.mark & BRANCH)
+					goto no2;
+			if(t->p.mark & DELAY)
+				if(s >= se || conflict(t, s+1))
+					goto no2;
+			for(u=t+1; u<=s; u++)
+				if(depend(u, t))
+					goto no2;
+			goto out2;
+		no2:;
+		}
+		if(debug['X'])
+			Bprint(&bso, "?l%P\n", &s->p);
+		s->nop = 1;
+		if(debug['v']) {
+			if(s->p.mark & LOAD) {
+				nop.load.count++;
+				nop.load.outof++;
+			}
+			if(s->p.mark & BRANCH) {
+				nop.branch.count++;
+				nop.branch.outof++;
+			}
+			if(s->p.mark & FCMP) {
+				nop.fcmp.count++;
+				nop.fcmp.outof++;
+			}
+		}
+		continue;
+
+	out2:
+		if(debug['X']) {
+			Bprint(&bso, "!l%P\n", &t->p);
+			Bprint(&bso, "%P\n", &s->p);
+		}
+		stmp = *t;
+		memmove(t, t+1, (uchar*)s - (uchar*)t);
+		*s = stmp;
+		s--;
+
+	out3:
+		if(debug['v']) {
+			if(s->p.mark & LOAD)
+				nop.load.outof++;
+			if(s->p.mark & BRANCH)
+				nop.branch.outof++;
+			if(s->p.mark & FCMP)
+				nop.fcmp.outof++;
+		}
+	}
+
+	/* Avoid HI/LO use->set */
+	t = sch+1;
+	for(s=sch; s<se-1; s++, t++) {
+		if((s->used.cc & E_HILO) == 0)
+			continue;
+		if(t->set.cc & E_HILO)
+			s->nop = 2;
+	}
+
+	/*
+	 * put it all back
+	 */
+	for(s=sch, p=p0; s<=se; s++, p=q) {
+		q = p->link;
+		if(q != s->p.link) {
+			*p = s->p;
+			p->link = q;
+		}
+		while(s->nop--)
+			addnop(p);
+	}
+	if(debug['X']) {
+		Bprint(&bso, "\n");
+		Bflush(&bso);
+	}
+}
+
+void
+regsused(Sch *s, Prog *realp)
+{
+	int c, ar, ad, ld, sz;
+	ulong m;
+	Prog *p;
+
+	p = &s->p;
+	s->comp = compound(p);
+	s->nop = 0;
+	if(s->comp) {
+		s->set.ireg |= 1<<REGTMP;
+		s->used.ireg |= 1<<REGTMP;
+	}
+
+	ar = 0;		/* dest is really reference */
+	ad = 0;		/* source/dest is really address */
+	ld = 0;		/* opcode is load instruction */
+	sz = 20;	/* size of load/store for overlap computation */
+
+/*
+ * flags based on opcode
+ */
+	switch(p->as) {
+	case ATEXT:
+		curtext = realp;
+		autosize = p->to.offset + 4;
+		ad = 1;
+		break;
+	case AJAL:
+		c = p->reg;
+		if(c == NREG)
+			c = REGLINK;
+		s->set.ireg |= 1<<c;
+		ar = 1;
+		ad = 1;
+		break;
+	case ABGEZAL:
+	case ABLTZAL:
+		s->set.ireg |= 1<<REGLINK;
+	case ABEQ:
+	case ABGEZ:
+	case ABGTZ:
+	case ABLEZ:
+	case ABLTZ:
+	case ABNE:
+		ar = 1;
+		ad = 1;
+		break;
+	case ABFPT:
+	case ABFPF:
+		ad = 1;
+		s->used.cc |= E_FCR;
+		break;
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+		ar = 1;
+		s->set.cc |= E_FCR;
+		p->mark |= FCMP;
+		break;
+	case AJMP:
+		ar = 1;
+		ad = 1;
+		break;
+	case AMOVB:
+	case AMOVBU:
+		sz = 1;
+		ld = 1;
+		break;
+	case AMOVH:
+	case AMOVHU:
+		sz = 2;
+		ld = 1;
+		break;
+	case AMOVF:
+	case AMOVW:
+	case AMOVWL:
+	case AMOVWR:
+		sz = 4;
+		ld = 1;
+		break;
+	case AMOVD:
+	case AMOVV:
+	case AMOVVL:
+	case AMOVVR:
+		sz = 8;
+		ld = 1;
+		break;
+	case ADIV:
+	case ADIVU:
+	case AMUL:
+	case AMULU:
+	case AREM:
+	case AREMU:
+		s->set.cc = E_HILO;
+	case AADD:
+	case AADDU:
+	case AAND:
+	case ANOR:
+	case AOR:
+	case ASGT:
+	case ASGTU:
+	case ASLL:
+	case ASRA:
+	case ASRL:
+	case ASUB:
+	case ASUBU:
+	case AXOR:
+
+	case AADDD:
+	case AADDF:
+	case AADDW:
+	case ASUBD:
+	case ASUBF:
+	case ASUBW:
+	case AMULF:
+	case AMULD:
+	case AMULW:
+	case ADIVF:
+	case ADIVD:
+	case ADIVW:
+		if(p->reg == NREG) {
+			if(p->to.type == D_REG || p->to.type == D_FREG)
+				p->reg = p->to.reg;
+			if(p->reg == NREG)
+				print("botch %P\n", p);
+		}
+		break;
+	}
+
+/*
+ * flags based on 'to' field
+ */
+	c = p->to.class;
+	if(c == 0) {
+		c = aclass(&p->to) + 1;
+		p->to.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->to);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_ADD0CON:
+	case C_AND0CON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+
+	case C_HI:
+	case C_LO:
+		s->set.cc |= E_HILO;
+		break;
+	case C_FCREG:
+		s->set.cc |= E_FCR;
+		break;
+	case C_MREG:
+		s->set.cc |= E_MCR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->to.reg;
+		s->used.ireg |= 1<<c;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		if(ar)
+			s->used.cc |= m;
+		else
+			s->set.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		if(ar)
+			s->used.ireg |= 1<<p->to.reg;
+		else
+			s->set.ireg |= 1<<p->to.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		if(ar) {
+			s->used.freg |= 1<<p->to.reg;
+			s->used.freg |= 1<<(p->to.reg|1);
+		} else {
+			s->set.freg |= 1<<p->to.reg;
+			s->set.freg |= 1<<(p->to.reg|1);
+		}
+		if(ld && p->from.type == D_REG)
+			p->mark |= LOAD;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSP;
+		else
+			s->set.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->to);
+
+		if(ar)
+			s->used.cc |= E_MEMSB;
+		else
+			s->set.cc |= E_MEMSB;
+		break;
+	}
+
+/*
+ * flags based on 'from' field
+ */
+	c = p->from.class;
+	if(c == 0) {
+		c = aclass(&p->from) + 1;
+		p->from.class = c;
+	}
+	c--;
+	switch(c) {
+	default:
+		print("unknown class %d %D\n", c, &p->from);
+
+	case C_ZCON:
+	case C_SCON:
+	case C_ADD0CON:
+	case C_AND0CON:
+	case C_ADDCON:
+	case C_ANDCON:
+	case C_UCON:
+	case C_LCON:
+	case C_NONE:
+	case C_SBRA:
+	case C_LBRA:
+		break;
+	case C_HI:
+	case C_LO:
+		s->used.cc |= E_HILO;
+		break;
+	case C_FCREG:
+		s->used.cc |= E_FCR;
+		break;
+	case C_MREG:
+		s->used.cc |= E_MCR;
+		break;
+	case C_ZOREG:
+	case C_SOREG:
+	case C_LOREG:
+		c = p->from.reg;
+		s->used.ireg |= 1<<c;
+		if(ld)
+			p->mark |= LOAD;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		m = ANYMEM;
+		if(c == REGSB)
+			m = E_MEMSB;
+		if(c == REGSP)
+			m = E_MEMSP;
+
+		s->used.cc |= m;
+		break;
+	case C_SACON:
+	case C_LACON:
+		s->used.ireg |= 1<<REGSP;
+		break;
+	case C_SECON:
+	case C_LECON:
+		s->used.ireg |= 1<<REGSB;
+		break;
+	case C_REG:
+		s->used.ireg |= 1<<p->from.reg;
+		break;
+	case C_FREG:
+		/* do better -- determine double prec */
+		s->used.freg |= 1<<p->from.reg;
+		s->used.freg |= 1<<(p->from.reg|1);
+		if(ld && p->to.type == D_REG)
+			p->mark |= LOAD;
+		break;
+	case C_SAUTO:
+	case C_LAUTO:
+		s->used.ireg |= 1<<REGSP;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSP;
+		break;
+	case C_SEXT:
+	case C_LEXT:
+		s->used.ireg |= 1<<REGSB;
+		if(ld)
+			p->mark |= LOAD;
+		if(ad)
+			break;
+		s->size = sz;
+		s->soffset = regoff(&p->from);
+
+		s->used.cc |= E_MEMSB;
+		break;
+	}
+
+	c = p->reg;
+	if(c != NREG) {
+		if(p->from.type == D_FREG || p->to.type == D_FREG) {
+			s->used.freg |= 1<<c;
+			s->used.freg |= 1<<(c|1);
+		} else
+			s->used.ireg |= 1<<c;
+	}
+	s->set.ireg &= ~(1<<REGZERO);		/* R0 cant be set */
+}
+
+/*
+ * test to see if 2 instrictions can be
+ * interchanged without changing semantics
+ */
+int
+depend(Sch *sa, Sch *sb)
+{
+	ulong x;
+
+	if(sa->set.ireg & (sb->set.ireg|sb->used.ireg))
+		return 1;
+	if(sb->set.ireg & sa->used.ireg)
+		return 1;
+
+	if(sa->set.freg & (sb->set.freg|sb->used.freg))
+		return 1;
+	if(sb->set.freg & sa->used.freg)
+		return 1;
+
+	/*
+	 * special case.
+	 * loads from same address cannot pass.
+	 * this is for hardware fifo's and the like
+	 */
+	if(sa->used.cc & sb->used.cc & E_MEM)
+		if(sa->p.reg == sb->p.reg)
+		if(regoff(&sa->p.from) == regoff(&sb->p.from))
+			return 1;
+
+	x = (sa->set.cc & (sb->set.cc|sb->used.cc)) |
+		(sb->set.cc & sa->used.cc);
+	if(x) {
+		/*
+		 * allow SB and SP to pass each other.
+		 * allow SB to pass SB iff doffsets are ok
+		 * anything else conflicts
+		 */
+		if(x != E_MEMSP && x != E_MEMSB)
+			return 1;
+		x = sa->set.cc | sb->set.cc |
+			sa->used.cc | sb->used.cc;
+		if(x & E_MEM)
+			return 1;
+		if(offoverlap(sa, sb))
+			return 1;
+	}
+
+	return 0; 
+}
+
+int
+offoverlap(Sch *sa, Sch *sb)
+{
+
+	if(sa->soffset < sb->soffset) {
+		if(sa->soffset+sa->size > sb->soffset)
+			return 1;
+		return 0;
+	}
+	if(sb->soffset+sb->size > sa->soffset)
+		return 1;
+	return 0;
+}
+
+/*
+ * test 2 adjacent instructions
+ * and find out if inserted instructions
+ * are desired to prevent stalls.
+ */
+int
+conflict(Sch *sa, Sch *sb)
+{
+
+	if(sa->set.ireg & sb->used.ireg)
+		return 1;
+	if(sa->set.freg & sb->used.freg)
+		return 1;
+	if(sa->set.cc & sb->used.cc)
+		return 1;
+
+	return 0;
+}
+
+int
+compound(Prog *p)
+{
+	Optab *o;
+
+	o = oplook(p);
+	if(o->size != 4)
+		return 1;
+	if(p->to.type == D_REG && p->to.reg == REGSB)
+		return 1;
+	return 0;
+}
+
+void
+dumpbits(Sch *s, Dep *d)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(d->ireg & (1<<i))
+			Bprint(&bso, " R%d", i);
+	for(i=0; i<32; i++)
+		if(d->freg & (1<<i))
+			Bprint(&bso, " F%d", i);
+	for(i=0; i<32; i++)
+		switch(d->cc & (1<<i)) {
+		default:
+			break;
+		case E_HILO:
+			Bprint(&bso, " HILO");
+			break;
+		case E_FCR:
+			Bprint(&bso, " FCR");
+			break;
+		case E_MCR:
+			Bprint(&bso, " MCR");
+			break;
+		case E_MEM:
+			Bprint(&bso, " MEM%d", s->size);
+			break;
+		case E_MEMSB:
+			Bprint(&bso, " SB%d", s->size);
+			break;
+		case E_MEMSP:
+			Bprint(&bso, " SP%d", s->size);
+			break;
+		}
+}
--- /dev/null
+++ b/utils/vl/span.c
@@ -1,0 +1,663 @@
+#include	"l.h"
+
+void
+pagebug(Prog *p)
+{
+	Prog *q;
+
+	switch(p->as) {
+	case ABGEZAL:
+	case ABLTZAL:
+	case AJAL:
+	case ABEQ:
+	case ABGEZ:
+	case ABGTZ:
+	case ABLEZ:
+	case ABLTZ:
+	case ABNE:
+	case ABFPT:
+	case ABFPF:
+	case AJMP:
+		q = prg();
+		*q = *p;
+		p->link = q;
+		p->as = ANOR;
+		p->optab = 0;
+		p->from = zprg.from;
+		p->from.type = D_REG;
+		p->from.reg = REGZERO;
+		p->to = p->from;
+	}
+}
+
+void
+span(void)
+{
+	Prog *p, *q;
+	Sym *setext, *s;
+	Optab *o;
+	int m, bflag, i;
+	long c, otxt, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	otxt = c;
+	for(p = firstp; p != P; p = p->link) {
+		/* bug in early 4000 chips delayslot on page boundary */
+		if((c&(0x1000-1)) == 0xffc)
+			pagebug(p);
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 4;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches */
+				if(c-otxt >= 1L<<17)
+					bflag = 1;
+				otxt = c;
+				continue;
+			}
+			diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		c += m;
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			/* bug in early 4000 chips delayslot on page boundary */
+			if((c&(0x1000-1)) == 0xffc)
+				pagebug(p);
+			p->pc = c;
+			o = oplook(p);
+			if(o->type == 6 && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < 0)
+					otxt = -otxt;
+				if(otxt >= (1L<<17) - 10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					addnop(p->link);
+					addnop(p);
+					bflag = 1;
+				}
+			}
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 4;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				diag("zero-width instruction\n%P", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+
+	if(debug['t']) {
+		/* 
+		 * add strings to text segment
+		 */
+		c = rnd(c, 8);
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SSTRING)
+				continue;
+			v = s->value;
+			while(v & 3)
+				v++;
+			s->value = c;
+			c += v;
+		}
+	}
+
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %lux\n", textsize);
+	Bflush(&bso);
+}
+		
+void
+xdefine(char *p, int t, long v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_FCREG:
+		return C_FCREG;
+
+	case D_MREG:
+		return C_MREG;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			instoffset = a->sym->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SEXT;
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			if(instoffset == 0)
+				return C_ZOREG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SOREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_HI:
+		return C_LO;
+	case D_LO:
+		return C_HI;
+
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF)
+				instoffset = s->value + a->offset;
+			return C_LCON;
+		}
+		return C_GOK;
+
+	case D_CONST:
+		switch(a->name) {
+
+		case D_NONE:
+			instoffset = a->offset;
+		consize:
+			if(instoffset > 0) {
+				if(instoffset <= 0x7fff)
+					return C_SCON;
+				if(instoffset <= 0xffff)
+					return C_ANDCON;
+				if((instoffset & 0xffff) == 0)
+					return C_UCON;
+				return C_LCON;
+			}
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x8000)
+				return C_ADDCON;
+			if((instoffset & 0xffff) == 0)
+				return C_UCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SCONST:
+				instoffset = s->value + a->offset;
+				goto consize;
+			case STEXT:
+			case SLEAF:
+			case SSTRING:
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			instoffset = s->value + a->offset - BIG;
+			if(instoffset >= -BIG && instoffset < BIG && instoffset != 0L)
+				return C_SECON;
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 4L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r;
+	char *c1, *c3;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0) {
+		a1 = opcross[repop[r]][a1][a2][a3];
+		if(a1) {
+			p->optab = a1+1;
+			return optab+a1;
+		}
+		o = oprange[r].stop; /* just generate an error */
+	}
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, a1, a2, a3);
+	if(!debug['a'])
+		prasm(p);
+	o = optab;
+	p->optab = (o-optab)+1;
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_ZCON || b == C_SCON || b == C_UCON ||
+		   b == C_ADDCON || b == C_ANDCON)
+			return 1;
+		break;
+	case C_ADD0CON:
+		if(b == C_ADDCON)
+			return 1;
+	case C_ADDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_AND0CON:
+		if(b == C_ANDCON)
+			return 1;
+	case C_ANDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_UCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_SCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_SACON)
+			return 1;
+		break;
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_LEXT:
+		if(b == C_SEXT)
+			return 1;
+		break;
+	case C_LAUTO:
+		if(b == C_SAUTO)
+			return 1;
+		break;
+	case C_REG:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LOREG:
+		if(b == C_ZOREG || b == C_SOREG)
+			return 1;
+		break;
+	case C_SOREG:
+		if(b == C_ZOREG)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+int
+ocmp(void *a1, void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	for(i=0; i<32; i++)
+		for(n=0; n<32; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+		
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case AABSF:
+			oprange[AMOVFD] = oprange[r];
+			oprange[AMOVDF] = oprange[r];
+			oprange[AMOVWF] = oprange[r];
+			oprange[AMOVFW] = oprange[r];
+			oprange[AMOVWD] = oprange[r];
+			oprange[AMOVDW] = oprange[r];
+			oprange[ANEGF] = oprange[r];
+			oprange[ANEGD] = oprange[r];
+			oprange[AABSD] = oprange[r];
+			break;
+		case AADD:
+			buildrep(1, AADD);
+			oprange[ASGT] = oprange[r];
+			repop[ASGT] = 1;
+			oprange[ASGTU] = oprange[r];
+			repop[ASGTU] = 1;
+			oprange[AADDU] = oprange[r];
+			repop[AADDU] = 1;
+			oprange[AADDVU] = oprange[r];
+			repop[AADDVU] = 1;
+			break;
+		case AADDF:
+			oprange[ADIVF] = oprange[r];
+			oprange[ADIVD] = oprange[r];
+			oprange[AMULF] = oprange[r];
+			oprange[AMULD] = oprange[r];
+			oprange[ASUBF] = oprange[r];
+			oprange[ASUBD] = oprange[r];
+			oprange[AADDD] = oprange[r];
+			break;
+		case AAND:
+			buildrep(2, AAND);
+			oprange[AXOR] = oprange[r];
+			repop[AXOR] = 2;
+			oprange[AOR] = oprange[r];
+			repop[AOR] = 2;
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			break;
+		case ABLEZ:
+			oprange[ABGEZ] = oprange[r];
+			oprange[ABGEZAL] = oprange[r];
+			oprange[ABLTZ] = oprange[r];
+			oprange[ABLTZAL] = oprange[r];
+			oprange[ABGTZ] = oprange[r];
+			break;
+		case AMOVB:
+			buildrep(3, AMOVB);
+			oprange[AMOVH] = oprange[r];
+			repop[AMOVH] = 3;
+			break;
+		case AMOVBU:
+			buildrep(4, AMOVBU);
+			oprange[AMOVHU] = oprange[r];
+			repop[AMOVHU] = 4;
+			break;
+		case AMUL:
+			oprange[AREM] = oprange[r];
+			oprange[AREMU] = oprange[r];
+			oprange[ADIVU] = oprange[r];
+			oprange[AMULU] = oprange[r];
+			oprange[ADIV] = oprange[r];
+			oprange[ADIVVU] = oprange[r];
+			oprange[ADIVV] = oprange[r];
+			break;
+		case ASLL:
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			oprange[ASLLV] = oprange[r];
+			oprange[ASRAV] = oprange[r];
+			oprange[ASRLV] = oprange[r];
+			break;
+		case ASUB:
+			oprange[ASUBU] = oprange[r];
+			oprange[ANOR] = oprange[r];
+			break;
+		case ASYSCALL:
+			oprange[ATLBP] = oprange[r];
+			oprange[ATLBR] = oprange[r];
+			oprange[ATLBWI] = oprange[r];
+			oprange[ATLBWR] = oprange[r];
+			break;
+		case ACMPEQF:
+			oprange[ACMPGTF] = oprange[r];
+			oprange[ACMPGTD] = oprange[r];
+			oprange[ACMPGEF] = oprange[r];
+			oprange[ACMPGED] = oprange[r];
+			oprange[ACMPEQD] = oprange[r];
+			break;
+		case ABFPT:
+			oprange[ABFPF] = oprange[r];
+			break;
+		case AMOVWL:
+			oprange[AMOVWR] = oprange[r];
+			oprange[AMOVVR] = oprange[r];
+			oprange[AMOVVL] = oprange[r];
+			break;
+		case AMOVW:
+			buildrep(5, AMOVW);
+			break;
+		case AMOVD:
+			buildrep(6, AMOVD);
+			break;
+		case AMOVF:
+			buildrep(7, AMOVF);
+			break;
+		case AMOVV:
+			buildrep(8, AMOVV);
+			break;
+		case ABREAK:
+		case AWORD:
+		case ARFE:
+		case AJAL:
+		case AJMP:
+		case ATEXT:
+		case ACASE:
+		case ABCASE:
+			break;
+		}
+	}
+}
+
+void
+buildrep(int x, int as)
+{
+	Opcross *p;
+	Optab *e, *s, *o;
+	int a1, a2, a3, n;
+
+	if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
+		diag("assumptions fail in buildrep");
+		errorexit();
+	}
+	repop[as] = x;
+	p = (opcross + x);
+	s = oprange[as].start;
+	e = oprange[as].stop;
+	for(o=e-1; o>=s; o--) {
+		n = o-optab;
+		for(a2=0; a2<2; a2++) {
+			if(a2) {
+				if(o->a2 == C_NONE)
+					continue;
+			} else
+				if(o->a2 != C_NONE)
+					continue;
+			for(a1=0; a1<32; a1++) {
+				if(!xcmp[a1][o->a1])
+					continue;
+				for(a3=0; a3<32; a3++)
+					if(xcmp[a3][o->a3])
+						(*p)[a1][a2][a3] = n;
+			}
+		}
+	}
+	oprange[as].start = 0;
+}