ftp.nice.ch/pub/next/developer/objc/mach/dis.N.bs.tar.gz#/disasm.c

This is disasm.c in view mode; [Download] [Up]

/*	disasm.c - 68000 instruction disassembler for NeXT
 * 
 *	Copyright (C) 1989 by Bill Spitzak
 *	See Copyright notice in makefile
 * 
 */
/* $Log:	disasm.c,v $
 * Revision 1.9  94/08/24  22:41:58  ediger
 * reformatted some static arrays of strings
 * 
 * Revision 1.8  94/08/22  22:30:50  ediger
 * small formatting of disasmblock() arguments
 * 
 * Revision 1.7  94/05/30  23:07:30  ediger
 * "readability" changes
 * 
 * Revision 1.6  94/01/25  23:37:10  ediger
 * clean, reformatting, add comments removed non-symbolic "magic" constants
 * 
 * Revision 1.5  94/01/19  00:12:02  ediger
 * bug fixes, reformatting
 * 
 * Revision 1.4  94/01/16  21:10:57  ediger
 * fixed bug in parameters sent to disasmblock()
 * 
 * Revision 1.3  94/01/16  17:23:05  ediger
 * got rid of phony struct relocation_info definition
 * 
 * Revision 1.2  94/01/16  17:17:44  ediger
 * ditched phony nlist struct def
 * 
 * Revision 1.1  94/01/16  16:48:13  ediger
 * Initial revision
 *  */

static char rcsident[] = "$Id: disasm.c,v 1.9 94/08/24 22:41:58 ediger Exp Locker: ediger $";

#include "dis.h"
#include "disasm.h"

/*	Semi-table-lookup.  An exact match must be found in this table,
	an exact match means (inst>=code && (inst&(~mask))==code).  This
	exact match is found by finding the largest entry <= inst, then
	searching backwards.
	  This means you can special-case sub-instructions that match some
	more general instruction.  If the special instruction has some 1
	bits set in the general instruction's masked-off area, it can be
	put after it.  If the masked-off bits are all zero, it must be
	placed before the general instruction, and the general instruction
	modified to have some 1 bits in it's masked-off area so it represents
	the lowest number that is a legal version of that instruction.
	  If no match is found, it is an illegal instruction, which the
	disassembler prints as a word of data.  [since this would normally
	require a backwards search of the whole table, it assummes the
	mask will never be larger than 0x0fff, so it can quit when the
	top 4 bits don't match].  You can also mark specific instructions
	as illegal by putting in an entry with no name.  This can mark
	illegal addressing modes.

	  When an entry is found, it prints the name.  It then calls the
	"extension" routine, which can print more name (such as a w or a
	condition code or a coprocessor instruction).  If there is a second
	routine a tab is printed and that is called, it is supposed to
	print the source (or only) argument.  If there is a third routine
	a comma is printed and it is called, it is supposed to print the
	destination.  All these routines are passed the original code, and
	they should advance the pc to after any data they use.
*/

extern int decimalrange;	/* disasm switch, largest decimal value */

typedef void parsefunc(unsigned short);

struct instruction
{
	unsigned  short code;
	unsigned  short mask;	/* mask is "inverted" so zero means full match */
	char      *symbol;		/* opcode mnemonic */
	parsefunc *extension;
	parsefunc *source;
	parsefunc *dest;
};

/* global disasm state: */
static flag           illegal;     /* set true if instruction is illegal */
static int            targettype;  /* 0 or disasmtype of args to instruction */
static void          *reloc; /* pointer to address 0 (not necessarily real) */
static void          *pc;          /* pointer into mapped area */
static void          *endpc;       /* never disasm something past here! */
static unsigned short inst2;       /* second word of long instructions */
static char          *cptr;        /* pointer into buffer for unparsed inst */
static flag           branched;    /* set by an unconditional branch */
static struct nlist  *sym;         /* source symbol, if any, for comments */

#define cput(c)	(*cptr++ = (c))

static void 
sput(char *c)
{
	while (*c)
		*cptr++ = *c++;
}

static void 
putf(char *fmt, int x)
{
	int l;
	sprintf(cptr, fmt, x);
	l = strlen(cptr);
	cptr += l;
	/* cptr += sprintf(cptr, fmt, x); */
}

/* eventually this will print aliases for the registers: */
static void 
printregister(n)
unsigned int n;
{
	n &= 0xf;

	switch (n)
	{
	case 0xf:
		sput("sp");
		break;
	case 0xe:
		sput("fp");
		break;
	default:
		cput(n > 7 ? 'a' : 'd');
		cput((n & 0x7) + '0');
		break;
	}
}

/* symbol extension routines: */

void 
branch(unsigned short inst)
{
	targettype = CODE;
	branched = TRUE;
}

void 
sizebyte(unsigned short inst)
{
	targettype = CHAR;
	cput('b');
}

void 
sizeword(unsigned short inst)
{
	targettype = SHORT;
	cput('w');
}

void 
sizelong(unsigned short inst)
{
	targettype = LONG;
	cput('l');
}

void 
size(unsigned short inst)
{
	switch ((inst >> 6) & 0x3)
	{
	case 0:
		sizebyte(inst);
		break;
	case 1:
		sizeword(inst);
		break;
	case 2:
		sizelong(inst);
		break;
	case 3:
		illegal = TRUE;
		break;
	}
}

void 
brsizew(unsigned short inst)
{
	branch(inst);
	sizeword(inst);
}

void 
condition(unsigned short inst)
{
	static char *table[16] = {
		"t", "f", "hi", "ls", "cc", "cs", "ne", "eq",
		"vc", "vs", "pl", "mi", "ge",
		"lt", "gt", "le"};

	sput(table[(inst >> 8) & 0xf]);
}

/* get rest of large inst */
void 
i2(unsigned short inst)
{
	inst2 = *((signed short *)pc)++;
}

/* get mask and size of movem inst */
void 
mmtype(unsigned short inst)
{	
	inst2 = *((signed short *)pc)++;
	inst & 0x40 ? sizelong(inst) : sizeword(inst);
}

/* add a s or u to the long multiply/divide instructions */
void 
multype(unsigned short inst)
{
	inst2 = *((signed short *)pc)++;
	cput(inst2 & 0x800 ? 's' : 'u');
	sizelong(inst);
}

/* argument printing routines: */

void
putlabel(struct nlist * sym)
{
	putf(sym->n_un.n_name != NULL ? sym->n_un.n_name : "D%x", sym->n_value);
}

void
printlabel(struct nlist * sym)
{
	fprint(sym->n_un.n_name != NULL ? sym->n_un.n_name : "D%x", sym->n_value);
}


void
putoffset(signed long i, int sign)
{
	if (sign && i > 0)
		cput('+');

	if (i > decimalrange)
		putf("0x%x", i);
	else if (i < -decimalrange)
		putf("-0x%x", -i);
	else if (!sign || i)
		putf("%d", i);
}

void
printoffset(signed long i, int sign)
{
	if (sign && i > 0)
		cprint('+');

	if (i > decimalrange || (!sign && i < -decimalrange))
		fprint("0x%x", i);
	else if (i < -decimalrange)
		fprint("-0x%x", -i);
	else if (!sign || i)
		fprint("%d", i);
}

void
putasciichar(int c)
{
	switch (c)
	{
	case '\t':
		sput("\\t");
		break;
	case '\n':
		sput("\\n");
		break;
	case '\r':
		sput("\\r");
		break;
	case '\\':
		sput("\\\\");
		break;
	case '\"':
		sput("\\\"");
		break;
	case '\'':
		sput("\\\'");
		break;
	default:
		if (c >= ' ')
			cput(c);
		else
			putf("\\0%o", c);
		break;
	}
}

/* most recent symbol, for case tables */
static struct nlist *lastrefsymbol = NULL;

void 
immediate(unsigned short inst)
{
	signed long i;
	void   *rpc;

	rpc = pc;

	cput('#');

	sym = 0;

	switch (targettype)
	{
	case LONG:
		i = *((long *)pc)++;
		sym = getlabel((long unsigned *)&i, rpc, FALSE);
		if (sym)
			putlabel(sym);
		break;
	case SHORT:
		i = *((signed short *)pc)++;
		break;
	case CHAR:
		i = (signed char)*((signed short *)pc)++;
		if (i >= ' ' && i < 127)
		{
			cput('\'');
			putasciichar(i);
			cput('\'');
			return;
		}
		break;
	case FLOAT:
		cptr += sprintf(cptr, "%g", *((float *)pc)++);
		return;
	case DOUBLE:
		cptr += sprintf(cptr, "%g", *((double *)pc)++);
		return;
	case FLOAT12:
		sput("0x");
		putf("%08x", *((long *)pc)++);
		putf("%08x", *((long *)pc)++);
		putf("%08x", *((long *)pc)++);
		return;
	default:
		illegal = TRUE;
		return;
	}
	putoffset(i, sym != 0);
}

/*	parse an effective address into the following syntax:
 *	The name of a register, or
 *	register@(register.s*scale+base){@(register.s*scale+offset)} or
 *	(label+register.s*scale+base){@(register.s*scale+offset)} or
 *	label{@(register.s*scale+offset)}
 */
void 
source(unsigned short inst)
{
	short unsigned i = 0;
	int baseregister = 0;  /* 0 = no register, else low bits are reg number */
	int     indexregister = 0;
	long int base = 0;	/* if no base register, figure out label from this! */
	long int postoffset = 0;
	flag    memindirect = 0;
	flag    postindex = 0;
	void   *rpc = 0;	/* pointer to potential relocation */
	flag    pcrel = 0;	/* true if address is naturally relocated */
	flag    lparen;

	switch ((inst >> 3) & 7)
	{
	case 1:
		if (targettype == UCHAR)
			illegal = TRUE;
	case 0:
		printregister(inst & 15);
		return;
	case 2:
		printregister((inst & 7) + 8);
		cput('@');
		return;
	case 3:
		printregister(inst & 15);
		cput('@');
		cput('+');
		return;
	case 4:
		cput('-');
		printregister((inst & 7) + 8);
		cput('@');
		return;
	case 5:
		baseregister = (inst & 0xf);
		rpc = pc;
		base = *((signed short *)pc)++;
		break;
	case 6:
		baseregister = (inst & 0x7) | 8;	/* a0-a7 */
indirect:
		i = *((short unsigned *)pc)++;
		indexregister = ((i >> 12) & 15) | 16;
		if (!(i & 0x100))
		{
			rpc = pc - 1;
			base += (signed char)i;
		} else {
			if (i & 0x80)
			{
				baseregister = 0;
				base = 0;
				pcrel = 0;
			}

			if (i & 0x40)
				indexregister = 0;

			switch (i & 0x30)
			{
			case 0x00:
				illegal = TRUE;
			case 0x10:	/* base+=0; */
				break;
			case 0x20:
				rpc = pc;
				base += *((signed short *)pc)++;
				break;
			case 0x30:
				rpc = pc;
				base += *((long *)pc)++;
				break;
			}

			if (i & 4)
				postindex = TRUE;

			memindirect = TRUE;

			switch (i & 3)
			{
			case 0:
				memindirect = FALSE;
				break;
			case 1:
				postoffset = 0;
				break;
				/* should check for relocation, but we don't: */
			case 2:
				postoffset = *((signed short *)pc)++;
				break;
			case 3:
				postoffset = *((long *)pc)++;
				break;
			}
		}
		break;
	case 7:
		switch (inst & 7)
		{
		case 0:
			rpc = pc;
			base = *((signed short *)pc)++;
			break;
		case 1:
			rpc = pc;
			base = *((long *)pc)++;
			break;
		case 2:
			pcrel = TRUE;
			rpc = pc;
			base = (rpc - reloc) + *((signed short *)pc)++;
			break;
		case 3:
			pcrel = TRUE;
			base = (pc - reloc);
			baseregister = 0;
			goto indirect;
		case 4:
			return (immediate(inst));
		default:
			illegal = TRUE;
			return;
		}
		break;
	}

	lparen = 0;
	if (baseregister || memindirect)
	{
		if (baseregister)
		{
			printregister(baseregister);
			cput('@');
		}
		cput('(');
		lparen = TRUE;
		sym = 0;
	} else
		sym = rpc ? getlabel((long unsigned *)&base, rpc, pcrel) : 0;

	if (sym)
	{
		settype(sym, targettype, 0);
		putlabel(sym);
	}

PMI:
	if (indexregister && !postindex)
	{
		if (sym)
			cput('+');
		printregister(indexregister);
		cput('.');
		cput((i & 0x800) ? 'l' : 'w');
		switch (i & 0x600)
		{
		case 0x0000:
			break;
		case 0x0200:
			sput("*2");
			break;
		case 0x0400:
			sput("*4");
			break;
		case 0x0600:
			sput("*8");
			break;
		}
	}
	putoffset(base, sym || (indexregister && !postindex));
	if (lparen)
		cput(')');
	if (memindirect)
	{
		postindex = !postindex;
		base = postoffset;
		memindirect = FALSE;
		sym = 0;
		cput('@');
		cput('(');
		lparen = TRUE;
		goto PMI;
	}
}

void 
data(unsigned short inst)
{				/* a0-a7 illegal */
	if ((inst & 0x38) == 8)
		illegal = TRUE;
	source(inst);
}

void 
memory(unsigned short inst)
{				/* a0-a7, d0-d7 illegal */
	if ((inst & 0x38) < 0x10)
		illegal = TRUE;
	source(inst);
}

void 
control(unsigned short inst)
{
	switch ((inst >> 3) & 7)
	{		
	/* registers, incr/decr, immediate illegal */
	case 0: case 1: case 3: case 4:
		illegal = TRUE;
		break;
	case 7:
		if ((inst & 7) == 4)
			illegal = TRUE;
		break;
	}
	source(inst);
}

void 
alterable(unsigned short inst)
{
	/* pc relative and imm illegal */
	if ((inst & 0x3f) > 0x39)
		illegal = TRUE;
	source(inst);
}

void 
dest(unsigned short inst)
{
	/* data && alterable */
	if ((inst & 0x3f) > 0x39)
		illegal = TRUE;
	if ((inst & 0x38) == 8)
		illegal = TRUE;

	source(inst);
}

void 
destmem(unsigned short inst)
{		
	/* memory && alterable */
	if ((inst & 0x38) < 0x10)
		illegal = TRUE;

	if ((inst & 0x38) > 0x39)
		illegal = TRUE;

	source(inst);
}

void 
bitfield(void)
{
	cput('{');
	if (inst2 & 0x800)
		printregister((inst2 >> 6) & 7);
	else
		putf("%d", (inst2 >> 6) & 31);

	cput(':');

	if (inst2 & 0x20)
		printregister(inst2 & 7);
	else
		putf("%d", (inst2 & 31));

	cput('}');
}

void 
bfdata(unsigned short inst)
{
	data(inst);
	bitfield();
}

void 
bfdest(unsigned short inst)
{
	dest(inst);
	bitfield();
}

void 
bfreg(unsigned short inst)
{
	printregister((inst2 >> 12));
}

/* 8,16,32 bit code displacement, as used by bra instruction */
void 
disp(unsigned short inst)
{
	long    n, disp;
	signed char c;
	short unsigned *rpc;
	struct nlist *sym;		/* local, because we don't want label comment */

	n = pc - reloc;  /* where program counter really would be at */
	c = (inst & 0xff);
	rpc = pc;

	/*
	 * calculate displacement, and advance program counter past displacement.
	 * what kind of displacement is encoded in low-byte of instruction.
	 */
	switch (c)
	{
	case 0xff:		/* 32 bit displacement in the next 4 bytes */
		disp = *((long unsigned *)pc)++;
		break;
	case 0:			/* 16 bit displacement in the next 2 bytes */
		disp = *((signed short *)pc)++;
		break;
	default:		/* 8 bit displacement right in the instruction */
		disp = c;
		rpc = (unsigned short *)pc - 1;  /* I guess pc doesn't need to
											be advanced? */
		break;
	}

	n += disp;
	sym = getlabel((long unsigned *)&n, rpc, TRUE);
	settype(sym, CODE,
		(inst & 0xFF00) == 0x6100 ? "F%x" : (disp > 0 ? "L%x" : "LP%x"));

	if (sym != NULL)
		putlabel(sym);

	putoffset(n, sym != NULL);
}

void 
dispw(unsigned short inst)
{
	disp(0);
}

/* print 2nd word in hex for badly disassembled instructions */
void 
word2(unsigned short inst)
{
	putf("#0x%x", *((unsigned short *)pc)++);
}

/* >>6 and swap 3-bit halves to get a dest ea in move instruction */
void 
dest6(unsigned short inst)
{
	inst = ((inst >> 9) & 7) | ((inst >> 3) & 0x38);
	alterable(inst);
}

void 
sr(unsigned short inst)
{
	sput("sr");
}
void 
ccr(unsigned short inst)
{
	sput("ccr");
}
void 
usp(unsigned short inst)
{
	sput("usp");
}

void 
mask(unsigned short inst)
{
	/* movem mask, also check pre-decrement in ea for order */
	unsigned short mask;
	int     i;
	flag    divider = FALSE;
	mask = inst2;
	for (i = 0; i < 16; i++)
	{
		if (mask & 1)
		{
			if (divider)
				cput('/');
			divider = TRUE;
			printregister(((inst & 0x38) == 0x20) ? 15 - i : i);
		}
		mask >>= 1;
	}
}

void 
dreg(unsigned short inst)
{
	printregister(inst & 7);
}

void 
areg(unsigned short inst)
{
	printregister((inst & 7) + 8);
}

void 
dreg9(unsigned short inst)
{
	printregister((inst >> 9) & 7);
}

void 
areg9(unsigned short inst)
{
	printregister(((inst >> 9) & 7) + 8);
}

void 
aregdisp(unsigned short inst)
{
	printregister((inst & 7) + 8);
	putf("@(0x%x)", *((signed short *)pc)++);
}

void 
aregpd(unsigned short inst)
{
	cput('-');
	printregister((inst & 7) + 8);
	cput('@');
}

void 
aregpi(unsigned short inst)
{
	printregister((inst & 7) + 8);
	cput('@');
	cput('+');
}

void 
aregpd9(unsigned short inst)
{
	cput('-');
	printregister(((inst >> 9) & 7) + 8);
	cput('@');
}

void 
aregpi9(unsigned short inst)
{
	printregister(((inst >> 9) & 7) + 8);
	cput('@');
	cput('+');
}

void 
qbits(unsigned short inst)
{
	/* number from the addq or subq instructions */
	int     i = (inst >> 9) & 7;
	if (!i)
		i = 8;
	putf("#%d", i);
}

void 
bits4(unsigned short inst)
{
	putf("#%d", inst & 15);
} /* trap #n */

void 
bits3(unsigned short inst)
{
	putf("#%d", inst & 7);
} /* bkpt #n */

void 
bits8(unsigned short inst)
{				/* moveq #n */
	cput('#');
	putoffset((signed char)inst, FALSE);
}

void 
mulregs(unsigned short inst)
{				/* print one or two register destination for
				 * long mul/div */
	if (inst2 & 0x400)
	{
		printregister(inst2 & 7);
		cput(':');
	}
	printregister((inst2 >> 12) & 7);
}

void floatdisp(short unsigned inst);
void fbranch(short unsigned inst);
void floatccw(short unsigned inst);
void floatccl(short unsigned inst);
void floatcc(short unsigned inst);
void floatop(short unsigned inst);
void floatsrc(short unsigned inst);
void floatdest(short unsigned inst);

static struct instruction thetable[] = {
	{0x0000, 0x00ff, "or",       size,     immediate, dest},
	{0x003c, 0x0000, "or",       size,     immediate, ccr},
	{0x007c, 0x0000, "or",       size,     immediate, sr},
	{0x00c0, 0x0037, "cmp/chk2", sizebyte, word2,     control},

	{0x0100, 0x0e3f, "btst",  sizebyte, dreg9,    data},
	{0x0108, 0x0e07, "movep", sizeword, aregdisp, dreg9},
	{0x0140, 0x0e3f, "bchg",  sizebyte, dreg9,    dest},
	{0x0148, 0x0e07, "movep", sizelong, aregdisp, dreg9},
	{0x0180, 0x0e3f, "bclr",  sizebyte, dreg9,    dest},
	{0x0188, 0x0e07, "movep", sizeword, dreg9,    aregdisp},
	{0x01c0, 0x0e3f, "bset",  sizebyte, dreg9,    dest},
	{0x01c8, 0x0e07, "movep", sizelong, dreg9,    aregdisp},

	{0x0200, 0x00ff, "and",       size, immediate, dest},
	{0x023c, 0x0000, "and",       size, immediate, ccr},
	{0x027c, 0x0000, "and",       size, immediate, sr},
	{0x02c0, 0x0037, "cmp/chk2",  sizeword, word2, control},

	{0x0400, 0x00ff, "sub",      size,     immediate, dest},
	{0x04c0, 0x0037, "cmp/chk2", sizelong, word2,     control},

	{0x0600, 0x00ff, "add",   size,     immediate, dest},
	{0x06c0, 0x0007, "rtm",   branch,   dreg,      0},
	{0x06c8, 0x0007, "rtm",   branch,   areg,      0},
	{0x06d0, 0x0037, "callm", 0,        word2,     control},

	{0x0800, 0x003f, "btst", sizebyte, immediate, data},
	{0x0840, 0x003f, "bchg", sizebyte, immediate, dest},
	{0x0880, 0x003f, "bclr", sizebyte, immediate, dest},
	{0x08C0, 0x003f, "bset", sizebyte, immediate, dest},

	{0x0a00, 0x00ff, "eor",  size,     immediate, dest},
	{0x0ac0, 0x003f, "cas",  sizebyte, word2,     destmem},
	{0x0aff, 0x0000, "cas2", sizebyte, word2,     word2},

	{0x0c00, 0x00ff, "cmp",  size,     immediate, dest},
	{0x0cc0, 0x003f, "cas",  sizeword, word2,     destmem},
	{0x0cff, 0x0000, "cas2", sizeword, word2,     word2},

	{0x0e00, 0x00ff, "moves", size,     word2, destmem},
	{0x0ec0, 0x003f, "cas",   sizelong, word2, destmem},
	{0x0eff, 0x0000, "cas2",  sizelong, word2, word2},

	{0x1000, 0x0fff, "move", sizebyte, source, dest6},

	{0x2000, 0x0fff, "move", sizelong, source, dest6},
	/*  {0x2f00,0x003f, "push",	sizelong,	source,	0}, */

	{0x3000, 0x0fff, "move", sizeword, source, dest6},

	{0x4000, 0x00ff, "negx", size, dest, 0},
	{0x40c0, 0x003f, "move", sizeword, sr, dest},

	{0x4100, 0x0e3f, "chk", sizelong, data, dreg9},
	{0x4180, 0x0e3f, "chk", sizeword, data, dreg9},
	{0x41c0, 0x0e3f, "lea", 0, control, areg9},

	{0x4200, 0x00ff, "clr", size, dest, 0},
	{0x42c0, 0x003f, "move", sizeword, ccr, dest},

	{0x4400, 0x00ff, "neg", size, dest, 0},
	{0x44c0, 0x003f, "move", sizeword, data, ccr},

	{0x4600, 0x00ff, "not", size, dest, 0},
	{0x46c0, 0x003f, "move", sizeword, data, sr},

	{0x4800, 0x003f, "nbcd", 0 /* byte */ , dest, 0},
	{0x4808, 0x0007, "link", sizelong, areg, immediate},
	{0x4840, 0x0007, "swap", 0, dreg, 0},
	{0x4848, 0x0007, "bkpt", 0, bits3, 0},
	{0x4850, 0x003f, "pea", 0, control, 0},
	{0x4880, 0x0007, "extw", 0, dreg, 0},
	{0x4890, 0x007f, "movem", mmtype, mask, dest},
	{0x48c0, 0x0007, "extl", 0, dreg, 0},
	{0x49c0, 0x0007, "extbl", 0, dreg, 0},

	{0x4a00, 0x00ff, "tst", size, source, 0},
	{0x4ac0, 0x003f, "tas", sizebyte, dest, 0},
	/*  "illegal" is 0x4afc, 0x4afa and 0x4afb also specified as illegal */

	{0x4c00, 0x007f, "mul", multype, data, mulregs},
	{0x4c40, 0x007f, "div", multype, data, mulregs},
	{0x4c80, 0x007f, "movem", mmtype, memory, mask},

	{0x4e40, 0x000f, "trap", 0, bits4, 0},
	{0x4e50, 0x0007, "link", sizeword, areg, immediate},
	{0x4e58, 0x0007, "unlk", 0, areg, 0},
	{0x4e60, 0x0007, "movel", 0, areg, usp},
	{0x4e68, 0x0007, "movel", 0, usp, areg},
	{0x4e70, 0x0000, "reset", 0, 0, 0},
	{0x4e71, 0x0000, "nop", 0, 0, 0},
	{0x4e72, 0x0000, "stop", 0, word2, 0},
	{0x4e73, 0x0000, "rte", branch, 0, 0},
	{0x4e74, 0x0000, "rtd", brsizew, immediate, 0},
	{0x4e75, 0x0000, "rts", branch, 0, 0},
	{0x4e76, 0x0000, "trapv", 0, 0, 0},
	{0x4e77, 0x0000, "rtr", branch, 0, 0},
	{0x4e7a, 0x0001, "movec", 0, word2, 0},
	{0x4e80, 0x003f, "jsr", 0, control, 0},
	{0x4ec0, 0x003f, "jmp", branch, control, 0},

	{0x5000, 0x0eff, "addq", size, qbits, alterable},
	{0x50c0, 0x0f3f, "s", condition, dest, 0},
	{0x50c8, 0x0f07, "db", condition, dreg, dispw},
	{0x50fa, 0x0f00, "trap", condition, 0, 0},
	{0x50fb, 0x0f00, "trap", condition, word2, 0},
	{0x50fc, 0x0f00, "trap", condition, word2, word2},	/* long arg */
	{0x5100, 0x0eff, "subq", size, qbits, alterable},
	{0x51c0, 0x0f3f, "s", condition, dest, 0},
	{0x51c8, 0x0f07, "db", condition, dreg, dispw},

	{0x6000, 0x00ff, "bra", branch, disp, 0},
	{0x6100, 0x00ff, "bsr", 0, disp, 0},
	{0x6200, 0x0fff, "b", condition, disp, 0},

	{0x7000, 0x0fff, "moveq", 0, bits8, dreg9},

	{0x8000, 0x0eff, "or", size, data, dreg9},
	{0x80c0, 0x0e3f, "divu", sizeword, data, dreg9},
	{0x8100, 0x0e07, "sbcd", 0, dreg, dreg9},
	{0x8108, 0x0e07, "sbcd", 0, aregpd, aregpd9},
	{0x8110, 0x0eff, "or", size, dreg9, destmem},
	{0x8140, 0x0e07, "pack", word2, dreg, dreg9},
	{0x8148, 0x0e07, "pack", word2, aregpd, aregpd9},
	{0x8180, 0x0e07, "unpk", 0, dreg, dreg9},
	{0x8188, 0x0e07, "unpk", 0, aregpd, aregpd9},
	{0x81c0, 0x0e3f, "divs", sizeword, data, dreg9},

	{0x9000, 0x0eff, "sub", size, source, dreg9},
	{0x90c0, 0x0e3f, "sub", sizeword, source, areg9},
	{0x9100, 0x0ec7, "subx", size, dreg, dreg9},
	{0x9108, 0x0ec7, "subx", size, aregpd, aregpd9},
	{0x9110, 0x0eff, "sub", size, dreg9, destmem},
	{0x91c0, 0x0e3f, "sub", sizelong, source, areg9},

	{0xb000, 0x0eff, "cmp", size, source, dreg9},
	{0xb0c0, 0x0e3f, "cmp", sizeword, source, areg9},
	{0xb100, 0x0eff, "eor", size, dreg9, dest},
	{0xb108, 0x0ec7, "cmp", size, aregpi, aregpi9},
	{0xb1c0, 0x0e3f, "cmp", sizelong, source, areg9},

	{0xc000, 0x0eff, "and", size, data, dreg9},
	{0xc0c0, 0x0e3f, "mulu", sizeword, data, dreg9},
	{0xc100, 0x0e07, "abcd", 0, dreg, dreg9},
	{0xc108, 0x0e07, "abcd", 0, aregpd, aregpd9},
	{0xc110, 0x0eff, "and", size, dreg9, destmem},
	{0xc140, 0x0e07, "exg", 0, dreg, dreg9},
	{0xc148, 0x0e07, "exg", 0, areg, areg9},
	{0xc188, 0x0e07, "exg", 0, areg, dreg9},
	{0xc1c0, 0x0e3f, "muls", sizeword, data, dreg9},

	{0xd000, 0x0eff, "add", size, source, dreg9},
	{0xd0c0, 0x0e3f, "add", sizeword, source, areg9},
	{0xd100, 0x0ec7, "addx", size, dreg, dreg9},
	{0xd108, 0x0ec7, "addx", size, aregpd, aregpd9},
	{0xd110, 0x0eff, "add", size, dreg9, destmem},
	{0xd1c0, 0x0e3f, "add", sizelong, source, areg9},

	{0xe000, 0x0ec7, "asr", size, qbits, dreg},
	{0xe008, 0x0ec7, "lsr", size, qbits, dreg},
	{0xe010, 0x0ec7, "roxr", size, qbits, dreg},
	{0xe018, 0x0ec7, "ror", size, qbits, dreg},
	{0xe020, 0x0ec7, "asr", size, dreg9, dreg},
	{0xe028, 0x0ec7, "lsr", size, dreg9, dreg},
	{0xe030, 0x0ec7, "roxr", size, dreg9, dreg},
	{0xe038, 0x0ec7, "ror", size, dreg9, dreg},

	{0xe0c0, 0x003f, "asr", sizeword, destmem, 0},

	{0xe100, 0x0ec7, "asl", size, qbits, dreg},
	{0xe108, 0x0ec7, "lsl", size, qbits, dreg},
	{0xe110, 0x0ec7, "roxl", size, qbits, dreg},
	{0xe118, 0x0ec7, "rol", size, qbits, dreg},
	{0xe120, 0x0ec7, "asl", size, dreg9, dreg},
	{0xe128, 0x0ec7, "lsl", size, dreg9, dreg},
	{0xe130, 0x0ec7, "roxl", size, dreg9, dreg},
	{0xe138, 0x0ec7, "rol", size, dreg9, dreg},

	{0xe1c0, 0x003f, "asl", sizeword, destmem, 0},
	{0xe2c0, 0x003f, "lsr", sizeword, destmem, 0},
	{0xe3c0, 0x003f, "lsl", sizeword, destmem, 0},
	{0xe4c0, 0x003f, "roxr", sizeword, destmem, 0},
	{0xe5c0, 0x003f, "roxl", sizeword, destmem, 0},
	{0xe6c0, 0x003f, "ror", sizeword, destmem, 0},
	{0xe7c0, 0x003f, "rol", sizeword, destmem, 0},

	{0xe8c0, 0x003f, "bftst", i2, bfdata, 0},
	{0xe9c0, 0x003f, "bfextu", i2, bfdata, bfreg},
	{0xeac0, 0x003f, "bfchg", i2, bfdest, 0},
	{0xebc0, 0x003f, "bfexts", i2, bfdata, bfreg},
	{0xecc0, 0x003f, "bfclr", i2, bfdest, 0},
	{0xedc0, 0x003f, "bfffo", i2, bfdata, bfreg},
	{0xeec0, 0x003f, "bfset", i2, bfdest, 0},
	{0xefc0, 0x003f, "bfins", i2, bfreg, bfdest},

	{0xf200, 0x003f, "f", floatop, floatsrc, floatdest},
	{0xf240, 0x003f, "fs", floatcc, dest, 0},
	{0xf248, 0x0007, "fdb", floatcc, dreg, dispw},
	{0xf27a, 0x0000, "ftrap", floatccw, immediate, 0},
	{0xf27b, 0x0000, "ftrap", floatccl, immediate, 0},
	{0xf27c, 0x0000, "ftrap", floatcc, 0, 0},
	{0xf280, 0x007f, "fb", fbranch, floatdisp, 0},
	{0xf300, 0x003f, "fsave", 0, destmem, 0},
	{0xf340, 0x003f, "frestore", 0, source, 0}
};

/*----- floating point internals -------*/

static parsefunc *fsrc, *fdest;
void 
floatsrc(short unsigned inst)
{
	(*fsrc) (inst);
}
void 
floatdest(short unsigned inst)
{
	(*fdest) (inst);
}

void 
nodest(short unsigned inst)
{
	cptr--;
} /* delete the comma */

void 
fp7(short unsigned inst)
{
	sput("fp");
	cput('0' + ((inst2 >> 7) & 7));
}
void 
fp10(short unsigned inst)
{
	sput("fp");
	cput('0' + ((inst2 >> 10) & 7));
}

void 
fextension(short unsigned inst)
{
	putf("#0x%x", inst2 & 0x7f);
}

void 
fpcr(short unsigned inst)
{
	flag    divider = FALSE;
	if (inst2 & 0x1000)
	{
		sput("fpcr");
		divider = TRUE;
	}
	if (inst2 & 0x0800)
	{
		if (divider)
			cput('/');
		sput("fpsr");
		divider = TRUE;
	}
	if (inst2 & 0x0400)
	{
		if (divider)
			cput('/');
		sput("fpiar");
	}
}

void 
fmask(short unsigned inst)
{
	/* movem mask, also check pre-decrement in ea for order */
	unsigned short mask;
	int     i;
	flag    divider = FALSE;
	mask = inst2;
	for (i = 0; i < 8; i++)
	{
		if (mask & 1)
		{
			if (divider)
				cput('/');
			divider = TRUE;
			sput("fp");
			cput('0' + (((inst & 0x38) == 0x20) ? 7 - i : i));
		}
		mask >>= 1;
	}
}

static char *floatops[0x3B] = {
	"move", "int", "sinh", "intrz", "sqrt", 0, "lognp1", 0,
	"etoxm1", "tanh", "atan", 0, "asin", "atanh", "sin", "tan",
	"etox", "twotox", "tentox", 0, "logn", "log10", "log2", 0,
	"abs", "cosh", "neg", 0, "acos", "cos", "getexp", "getman",
	"div", "mod", "add", "mul", "sgldiv", "rem", "scale", "sglmul",
	"sub", 0, 0, 0, 0, 0, 0, 0,
	"sincos", "sincos", "sincos", "sincos", "sincos", "sincos", "sincos", "sincos",
	"cmp", 0, "tst"
};

void 
floatop(short unsigned inst)
{
	int     i;
	inst2 = *((unsigned short *)pc)++;
	switch ((inst2 >> 13) & 7)
	{
	case 0:
		i = inst2 & 0x7f;
		if (i > 0x3A || !floatops[i])
		{
			illegal = TRUE;
			return;
		}
		sput(floatops[i]);
		fsrc = fp10;
		fdest = fp7;
		break;
	case 2:
		fdest = fp7;
		if ((inst2 & 0x1c00) == 0x1c00)
		{
			sput("movecr");
			fsrc = fextension;
			break;
		}
		fsrc = source;
		i = inst2 & 0x7f;
		if (i > 0x3A || !floatops[i])
		{
			illegal = TRUE;
			return;
		}
		sput(floatops[i]);
SIZEIT:
		switch ((inst2 >> 10) & 7)
		{
		case 0:
			sizelong(inst);
			break;
		case 1:
			cput('s');
			targettype = FLOAT;
			break;
		case 2:
			cput('x');
			targettype = FLOAT12;
			break;
		case 3:
			cput('p');
			targettype = FLOAT12;
			break;
		case 4:
			sizeword(inst);
			break;
		case 5:
			cput('d');
			targettype = DOUBLE;
			break;
		case 6:
			sizebyte(inst);
			break;
		}
		break;
	case 3:
		sput("move");
		fsrc = fp7;
		fdest = dest;
		goto SIZEIT;
	case 4:
		sput("movem");
		fsrc = source;
		fdest = fpcr;
		break;
	case 5:
		sput("movem");
		fsrc = fpcr;
		fdest = dest;
		break;
	case 6:
		sput("movem");
		fsrc = memory;
		fdest = fmask;
		break;
	case 7:
		sput("movem");
		fsrc = fmask;
		fdest = destmem;
		break;
	}
}

static char *fcond[32] = {
	"f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or",
	"un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t",
	"sf", "seq", "gt", "ge", "lt", "le", "gl", "gle",
	"ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st"
};

void 
floatcc(short unsigned inst)
{
	int     i;
	i = (*((short unsigned *)pc)++) & 0x3f;
	if (i > 31)
		illegal = TRUE;
	else
		sput(fcond[i]);
}

void 
floatccw(short unsigned inst)
{
	floatcc(inst);
	sizeword(inst);
}
void 
floatccl(short unsigned inst)
{
	floatcc(inst);
	sizelong(inst);
}

void 
fbranch(short unsigned inst)
{
	int     i;
	i = inst & 0x3f;
	if (i > 31)
		illegal = TRUE;
	else
		sput(fcond[i]);
}

void 
floatdisp(short unsigned inst)
{
	/* displacement is long or short depending on bit 6 */
	disp((inst & 0x40) ? -1 : 0);
}

/*==================== The disassembler ======================*/

/*	Multiple passes are done by setting newlabels to zero, disasmblock
	for each section of the program, and if newlabels is not zero,
	doing it again.  If newlabels is zero, we are done, turn on printit
	and disasmblock again.
	Disasmblock cuts up the code at each symbol, and calls a routine
	for each of these symbol sections, depending on the type of symbol.
	pc points at the start, endpc points at the end of the section.
	The routine may quit earlier if it feels the type it is disassembling
	is not allowed (in particular it can not move pc at all) but be
	very careful in how guesstype is done so all possible states will
	eventually be disassembled, it is extremely easy to make a loop!
*/
int         newlabels;  /* incremented only by labels that require a pass */
extern flag printit;    /* set true during printing pass */
flag        incode;     /* true for text segment to encourage code guessing */

static struct relocation_info *rtab;
static struct relocation_info *lastr;
static struct relocation_info *curr;
static int cursection;
static unsigned curstart;	/* used to address relocation entries */
static flag samelineflag;	/* true if label small enough to be on same line */
static unsigned nextsymat;	/* set by created symbols */
static unsigned mapstart, mapend;

static void 
tab()
{				/* tab over to the disassemble column */
	if (samelineflag)
		samelineflag = FALSE;	/* tab after label */
	else
		startline(pc - reloc);
	cprint('\t');
}

/* our own disassembly types: */
#define ZEROBLOCK -1		/* a set of zeros */
#define CASEWTABLE -2		/* a gcc case table, word offsets from start */

/*
 *	Return pointer to next relocatable thing after pc.
 *	or endpc if none.
 *	The table is expected to be in high to low order, and is
 *	linearlly searched backwards from the last entry found.
 *	(the reason it is sorted backwards is that gcc produces
 *	them that way, and it saves some time to not have to
 *	reverse it)
 */
void *
nextreloc()
{
	void                   *ret;
	struct relocation_info *x;
	unsigned                a = pc - reloc - curstart;

	for (x = curr; x >= rtab; x--)
	{
		if (x->r_address >= a)
		{
			ret = x->r_address + curstart + reloc;
			return (ret < endpc ? ret : endpc);
		}
	}

	return (endpc);
}

extern flag globalscode;

int 
guesstype(void)
{
	signed char *p;
	void        *end = nextreloc();
	int          n = end - pc;

	if (!n)
		return (ULONG);	/* get pointer at start of block */

	for (p = (signed char *)pc; !*p++;)
		if ((void *)p >= end)
			return (ZEROBLOCK);

	/* see if it looks like code: */
	if (incode && !(((unsigned)pc | (unsigned)end) & 1) && n > 2)
	{
		if (*(unsigned short *)pc == 0x4e56
			&& !(*((unsigned short *)pc + 1) & 3))
			return (CODE);	/* link a6,n*4 */

		if (*(unsigned short *)pc == 0x204f)
			return (CODE);	/* move sp,a0 */
	}

	/* see if it looks like a string: */
	p = (signed char *)pc;
	for (p = (signed char *)pc; p < (signed char *)end; p++)
	{
		if (*p >= ' ')
		{
			if ((void *)p - pc >= 8)
				return (STRTY);
		} else if (*p == '\n' || *p == '\r' || *p == '\t')
			;
		else if (!*p && (void *)p > pc)
			return (STRTY);
		else
			break;
	}

	if ((unsigned)pc & 1 || n == 1)
		return (UCHAR);

	if (incode || n < 4)
		return (USHORT);

	return (ULONG);
}

void 
disasmcode(void)
{
	void          *savepc;
	unsigned short inst;
	int            i, s, e;
	struct nlist  *sourcesym;
	char           buf[256];

	if (((unsigned)pc & 1) || nextreloc() == pc)
		return;

	branched = FALSE;
	lastrefsymbol = 0;

	while (pc < endpc)
	{
		savepc = pc;
		inst = *((unsigned short *)pc)++;

		s = 0;
		e = sizeof(thetable) / sizeof(struct instruction);

		while (i = (s + e) / 2, i > s)
		{
			if (thetable[i].code > inst)
				e = i;
			else
				s = i;
		}

		while ((inst ^ thetable[i].code) & ~thetable[i].mask)
		{
			i--;
			if (i < 0 || (inst & 0xf000) != (thetable[i].code & 0xf000))
			{
				pc = savepc;
				return;
			}
		}

		cptr = buf;
		sput(thetable[i].symbol);  /* put opcode mnemonic in buffer */
		illegal = FALSE;
		targettype = 0;
		sym = 0;

		if (thetable[i].extension)
			(thetable[i].extension)(inst);

		if (illegal || pc > endpc)
		{
			pc = savepc;
			break;
		}

		if (thetable[i].source)
		{
			cput('\t');
			(thetable[i].source)(inst);
		}

		if (illegal || pc > endpc)
		{
			pc = savepc;
			break;
		}

		sourcesym = sym;
		if (thetable[i].dest)
		{
			cput(',');
			(thetable[i].dest)(inst);
		}

		if (illegal || pc > endpc)
		{
			pc = savepc;
			break;
		}

		*cptr = 0;  /* null terminate the string concocted in the buffer */
		if (printit)
		{
			if (samelineflag)
				samelineflag = FALSE;	/* tab after label */
			else
				startline(savepc - reloc);
			cprint('\t');
			sprint(buf);
			printlabelcomment(sourcesym);
		}

		if (branched)
		{
			if (lastrefsymbol == NULL)
			{
				;
			/* detect new case tables: */
			} else if ((inst & 0xfff0) == 0x4ed0) {	/* jmp aN@ */
				settype(lastrefsymbol, PTR | CODE, 0);
			/* detect older Gnu case tables: */
			} else if (inst == 0x4efb && lastrefsymbol->n_desc == SHORT) {
				/* jmp pc+x.w, assumme table at pc */
				lastrefsymbol->n_desc = CASEWTABLE;
			}

			break;
		}
	}
}

/* this is not called unless we already know it is all zeros */
void 
disasmzeros(void)
{
	if (printit && endpc > pc)
	{
		tab();
		if (endpc == pc + 1 && (((unsigned)pc) & 1))
			fprint(".even");
		else
			fprint(".skip %d", endpc - pc);
	}
	pc = endpc;
}

/* find more than 1 trailing zero on block, return pointer to it or end */
void *
findtail(int size, void *e)
{
	void   *p;

	if (e <= pc)
		return (pc);

	for (p = e - 1; !*(char *)p && p >= pc; p--)
		;	/* find last non-zero byte */

	/* point at sized object after it */
	p = pc + ((p + size - pc) / size) * size;

	if (p > e)
		return (p - size);

	if (p + size > e)
		return (p);

	if (p + size + size > e)
		return (p + size);

	return (p);
}

/* Characters that don't look like strings are strongly discouraged,
 * as I only print one and then let it guesstype again.  Chiefly this
 * eats odd addresses.
 */
void 
disasmchar(int type)
{
	if (findtail(1, nextreloc()) > pc)
	{
		tab();
		fprint(".byte ");
		printoffset(*((signed char *)pc)++, FALSE);
	}
}

/* Shorts will continue until the next relocatable (assumed to be long) */
void 
disasmshort(int type)
{
	void   *e;

	if (((unsigned)pc) & 1)
		return;		/* odd address is n.g */

	e = nextreloc();

	if (((unsigned)e) & 1)
		e--;

	if (printit)
	{
		e = findtail(2, e);
		while (pc < e)
		{
			tab();
			fprint(".word ");
			printoffset(*((signed short *)pc)++, FALSE);
		}
	}

	pc = e;
}

/* Longs will continue until we hit non-multiple of 4:
   Pointers are very similar to longs, but checks are made to see
   if they are legitimate.  In particular they should all relocate.
   Even addresses are also insisted on unless pointed to type is CHAR
 */
void 
disasmlong(int type)
{
	void         *e, *f;
	unsigned long i;
	struct nlist *sym;

	if (((unsigned)pc) & 1)
		return;		/* odd address is n.g */

	e = findtail(4, endpc);

	while (pc < e)
	{
		f = nextreloc();
		if (f > pc && f < pc + 4)
			return;

		i = *((unsigned long *)pc);

		if ((i & 1)
			&& (type & PTR)
			&& (type & 15) != CHAR
			&& (type & 15) != UCHAR)
				return;

		sym = getlabel(&i, pc, FALSE);

		if (type & PTR)
		{
			if (!sym)
				return;
			settype(sym, type & 15, "C%x");
		}

		if (printit)
		{
			tab();
			fprint(".long ");
			if (sym)
				printlabel(sym);
			printoffset(i, sym != 0);
			printlabelcomment(sym);
		}

		((unsigned long *)pc)++;
	}
}

/* Floating numbers go until next relocatable.  It would probably be
   a good idea to quit on NaN's */
void 
disasmfloat(void)
{
	void   *e;

	if (((unsigned)pc) & 1)
		return;		/* odd address is n.g */

	e = nextreloc();
	e -= (e - pc) & 3;

	if (printit)
	{
		e = findtail(4, e);
		while (pc < e)
		{
			tab();
			fprint(".float %g", *((float *)pc)++);
		}
	}

	pc = e;
}

void 
disasmdouble(void)
{
	void   *e;

	if (((unsigned)pc) & 1)
		return;		/* odd address is n.g */

	e = nextreloc();

	e -= (e - pc) & 7;

	if (printit)
	{
		e = findtail(8, e);
		while (pc < e)
		{
			tab();
			fprint(".double %g", *((double *)pc)++);
		}
	}

	pc = e;
}

/* strings print thru the next null or to a relocatable or symbol:
 * this routine prints one line, cutting at \n or 60 characters: */
int 
printascii(void *e)
{
	signed char c;

	if (e - pc > 62)
	{
		e = pc + 60;
		if (*(signed char *)e == 0)
			e++;
	}

	cprint('\"');

	while (pc < e)
	{
		c = *((signed char *)pc)++;

		switch (c)
		{
		case 0:
			sprint("\\0\"");
			return (0);
		case '\t':
			sprint("\\t");
			break;
		case '\n':
			sprint("\\n");
			if (!*(signed char *)pc)
				break;
			cprint('\"');
			return (TRUE);
		case '\r':
			sprint("\\r");
			break;
		case '\\':
			sprint("\\\\");
			break;
		case '\"':
			sprint("\\\"");
			break;
		default:
			if (c >= ' ')
				cprint(c);
			else
				fprint("\\%03o", (int)c & 0xff);
			break;
		}
	}
	cprint('\"');
	return (TRUE);
}

void 
disasmstring(void)
{
	void   *e;
	e = nextreloc();
	if (printit)
	{
		while (pc < e && (tab(), sprint(".ascii "), printascii(e)))
			;
	} else
		while (pc < endpc && *((signed char *)pc)++)
			;
}

/*	Gnu cc produces tables of forward offsets relative to the start
	of the table for case statements.  This will print them.  It
	detects the end of the table by either running into some labelled
	code, or by an odd address
 */
void 
disasmcasewtable(void)
{
	long unsigned table, base;
	signed short *p;
	int     i;
	struct nlist *tablesym, *sym;

	table = pc - reloc;
	tablesym = findlabel(table);

	if (!tablesym)
		return;		/* no reference to table! */

	i = 0;
	while (pc < endpc)
	{
		base = table + *((signed short *)pc);

		if ( /* base<pc-reloc || */ base & 1)
			return;

		for (p = (signed short *)pc + 1;
			(void *)p < endpc && table + *p == base; p++)
			;

		sym = settype(getlabel(&base, pc, TRUE), CODE, "C%x");
		if (printit)
		{
			tab();
			sprint(".word ");
			printlabel(sym);
			cprint('-');
			printlabel(tablesym);
			((signed short *)pc)++;
			fprint("\t|case %d", i);
			i += (p - (signed short *)pc) + 1;

			if (pc < (void *)p)
				fprint("..%d", i - 1);

			while (pc < (void *)p)
			{
				tab();
				sprint(".word ");
				printlabel(sym);
				cprint('-');
				printlabel(tablesym);
				((signed short *)pc)++;
			}
		}
		pc = (void *)p;
	}
}

void
disasmblock(
	struct segment_command *p,
	struct section *q,
	int sectn, char *bpMap
	)
{
	struct nlist   *n;
	int             disasmtype;
	unsigned        a;
	unsigned        start, end;  /* address to start at, address to end at */
	extern unsigned symaddress;

	/* fill in some globals as appropriate for this section */

	curstart   = start = q->addr;    /* address to start at */
	end        = q->addr + q->size;  /* address to end at */

	/* where what address 0 would be, is memory mapped in this addr space */
	reloc      = (void *)((unsigned)bpMap + p->fileoff - p->vmaddr);

	/* sorted array of relocation info for this section */
	rtab       = (struct relocation_info *)(bpMap + q->reloff);

	/* address of last relocation info array entry */
	lastr      = rtab + q->nreloc;

	/* current relocation info entry (sorted in rev order?) */
	curr       = lastr - 1;

	cursection = sectn;

	/* first address memory mapped ??? */
	mapstart   = p->vmaddr;
	/* last address memory mapped ??? */
	mapend     = q->offset ? p->vmaddr + p->vmsize : 0;

	if (!printit)
	{
		if (start < mapstart)
			start = mapstart;
		if (end > mapend)
			end = mapend;
	}

	n = firstsymbol(start);
	pc = reloc + start;
	while ((a = pc - reloc) < end)
	{
		if (nextsymat)
			n = firstsymbol(nextsymat);
		nextsymat = 0;
		disasmtype = 0;

		/* handles all the symbols with the same "address". */
		while (n && symaddress == a)
		{
			if (printit)
				samelineflag = printsymbol(n);

			if (n->n_sect)
			{
				if (n->n_desc && !(n->n_type & N_STAB))
					disasmtype = n->n_desc;  /* not a debugging entry */
				else if (n->n_type == N_SLINE)
					disasmtype = CODE;
				else if (incode && globalscode && n->n_type == (N_SECT | 1))
					disasmtype = CODE;
			}

			n = nextsymbol();
		}

		a = (n && symaddress < end) ? symaddress : end;

		if (pc - reloc < mapstart && a > mapstart)
			a = mapstart;

		if (pc - reloc < mapstart || pc - reloc >= mapend)
		{
			tab();
			fprint(".skip %d", a - (pc - reloc));
			pc = reloc + a;
			continue;
		}

		if (a > mapend)
			a = mapend;
		endpc = reloc + a;

		pushsymbol();

		if (!disasmtype)
			disasmtype = guesstype();

		switch (disasmtype)
		{
		case CASEWTABLE:
			disasmcasewtable();
			break;
		case ZEROBLOCK:
			disasmzeros();
			break;
		case CODE:
			disasmcode();
			break;
		case CHAR:
		case UCHAR:
			disasmchar(disasmtype);
			break;
		case SHORT:
		case USHORT:
			disasmshort(disasmtype);
			break;
		case FLOAT:
			disasmfloat();
			break;
		case DOUBLE:
			disasmdouble();
			break;
		case STRTY:
			disasmstring();
			break;
		default:
			disasmlong(disasmtype);
			break;
		}
		popsymbol();
	}
}

/*==================== Label finding/creation ===================*/

/*	If something interesting can be said about a label, print it as
 *	a comment.  This version will print any constant strings it points
 *	at.
 */
void 
printlabelcomment(struct nlist * n)
{
	void   *savepc, *saveendpc;
	flag    saveincode;

	if (!printit || !n || !n->n_sect || n->n_value < mapstart ||
	    n->n_value >= mapend)
		return;

	savepc = pc;
	saveendpc = endpc;
	saveincode = incode;
	pc = reloc + n->n_value;
	endpc = pc + 100;
	incode = TRUE;
	if (n->n_desc == STRTY || (!n->n_desc && guesstype() == STRTY))
	{
		sprint("\t|");
		printascii(endpc);
	}
	pc = savepc;
	endpc = saveendpc;
	incode = saveincode;
}

struct nlist *
getlabel(unsigned long *address, void *pc, int pcrel)
{
	struct nlist *n;
	struct relocation_info *r;
	unsigned a;
	extern struct nlist *sourcesymtab;
	extern long unsigned symaddress;

	/* first see if there is an entry in the relocation table: */
	a = pc - reloc - curstart;
	for (r = 0; curr >= rtab; curr--)
	{
		if (curr->r_address == a)
		{
			if (pcrel ? curr->r_pcrel : !curr->r_pcrel)
				r = curr;
			break;
		}
		if (curr->r_address > a)
			break;
	}

	/* if relocated relative to an external symbol, use that: */
	if (r && r->r_extern)
	{
		/* note that address is relative already */
		return (lastrefsymbol = sourcesymtab + r->r_symbolnum);
	}

	/*
	 * If there is any relocation table, anything that is not relocated
	 * must be an absolute value: 
	 */
	if (lastr != rtab)
	{
		if (!r && !pcrel)
			return (0);
	} else {
		/* Otherwise, we guess that values near zero are absolute: */

		if (!pcrel && (*address <= 0x2000
			       || *address >= 0x100000
			       || (*address & 0xFFF) == 0xFFF
			       || (*address & 0xFFF) == 0))
			return (0);
	}

	/* otherwise it is a local label, find it: */
	if (n = findlabel(*address))
	{
		*address = 0;	/* modify address to relative value */
		return (lastrefsymbol = n);

	} else if (symaddress > *address - 4 && (n = findlabel(symaddress))) {

		/* fix use of bytes to test parts of long symbols: */
		if (n->n_desc == ULONG
			|| n->n_desc == LONG ||
			(symaddress > *address - 2 &&
				(n->n_desc == USHORT || n->n_desc == SHORT)))
		{
			*address -= symaddress;
			return (n);
		}
	}

	/* otherwise make a label.  Give it the type needed: */
	n = createlabel(*address, r ? r->r_symbolnum : cursection, 0, 0);

	if (*address <= pc - reloc)
	{
		newlabels++;	/* increment for earlier code */

	} else if (*address < endpc - reloc) {
		/* make it be the next symbol */
		endpc = reloc + *address;
		nextsymat = *address;
	}
	*address = 0;
	return (lastrefsymbol = n);
}

struct nlist *
settype(struct nlist *s, int type, char *name)
{
	if (s != NULL)
	{
		if (s->n_desc == 0)
			s->n_desc = type;
		if (s->n_un.n_name == NULL)
			s->n_un.n_name = name;
	}

	return s;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.