ftp.nice.ch/pub/next/unix/hack/hackkit.2.N.bs.tar.gz#/freebies/source/dis/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

*/

#include "dis.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.
*/

typedef void parsefunc(unsigned short);

struct instruction {
    unsigned short code;
    unsigned short mask;	/* mask is "inverted" so zero means full match */
    char *symbol;
    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 symbol *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) {
    cptr += sprintf(cptr,fmt,x);
    }

/* eventually this will print aliases for the registers: */
static void printregister(n) {
    n &= 15;
    if (n==15) sput("sp");
    else if (n==14) sput("fp");
    else {
	cput(n>7 ? 'a' : 'd');
	cput((n&7)+'0');
	}
    }

/* 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)&3) {
    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)&15]);
    }

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

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

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

/* argument printing routines: */

putlabel(symbol *sym) {
    putf(sym->name ? sym->name : "D%x", sym->value);
    }

printlabel(symbol *sym) {
    fprint(sym->name ? sym->name : "D%x", sym->value);
    }

extern int decimalrange;	/* disasm switch, largest decimal value */
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);
    }

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);
    }

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;
	}
    }

static symbol *lastrefsymbol;	/* most recent symbol, for case tables */
symbol *getlabel(unsigned long *address,void *pc,int pcrel);
symbol *settype(symbol *s,int type,char *name);
void printlabelcomment(symbol *n);

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);
    }

void source(unsigned short inst) {
/*	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)}
*/
    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;

    signed char c;
    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));}

void disp(unsigned short inst) {
    /* 8,16,32 bit code displacement, as used by bra instruction */
    long n,disp;
    signed char c;
    short unsigned *rpc;
    symbol *sym;	/* local, because we don't want label comment */
    n = pc-reloc;
    c = inst;
    rpc = pc;
    if (c=='\377') {disp = *((long unsigned *)pc)++;}
    else if (c) {disp = c; rpc = (unsigned short *)pc-1;}
    else disp = *((signed short *)pc)++;
    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) putlabel(sym);
    putoffset(n,sym!=0);
    }

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

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

void dest6(unsigned short inst) {
    /* >>6 and swap 3-bit halves to get a dest ea in move instruction */
    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 */

void *nextreloc() {
/*	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 *ret;
    struct relocation_info *x;
    unsigned a = pc-reloc-curstart;
    for (x=curr; x>=rtab; x--) {
	if (x->address >= a) {
	    ret = x->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;
    symbol *sourcesym;
    char buf[100];
    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);
	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;
	if (printit) {
	    if (samelineflag) samelineflag = FALSE; /* tab after label */
	    else startline(savepc-reloc);
	    cprint('\t'); sprint(buf);
	    printlabelcomment(sourcesym);
	    }
	if (branched) {
	    if (!lastrefsymbol);
	    /* 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->desc==SHORT) {
		/* jmp pc+x.w, assumme table at pc */
		lastrefsymbol->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*/
    p = pc+((p+size-pc)/size)*size;	/* point at sized object after it */
    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 (assummed 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;
    symbol *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)++);
    }

void disasmcasewtable(void)
/*	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 */
{
    long unsigned table,base;
    int offset;
    signed short *p;
    int i;
    symbol *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;
	}
    }

disasmblock(
	    unsigned start,	/* address to start at */
	    unsigned end,	/* address to end at */
	    unsigned imapstart,	/* first address memory mapped (may be > or < start)*/
	    unsigned imapend,	/* after last address memory mapped */
	    void * ireloc,	/* where address 0 would be memory mapped */
	    struct relocation_info *irtable, /* relocation entries, SORTED hi->low */
	    int irnum,		/* number of entires */
	    int sect		/* segment for new symbols */
	    ) {
    symbol *n;
    int disasmtype;
    unsigned a;
    extern unsigned symaddress;

    curstart = start;
    reloc = ireloc;
    rtab = irtable;
    lastr = rtab+irnum;
    curr = lastr-1;
    cursection = sect;
    mapstart = imapstart; mapend = imapend;
    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;
	while (n && symaddress == a) {
	    if (printit) samelineflag = printsymbol(n);
	    if (n->section) {
		if (n->desc && !(n->type&0xe0)) {
		    disasmtype = n->desc;
		/*  if (disasmtype&FTN) disasmtype = CODE; */
		    }
		else if (n->type==N_SLINE) disasmtype = CODE;
		else if (incode && globalscode && 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;
	    }
    LNN: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(symbol *n)
{
    void *savepc,*saveendpc; flag saveincode;
    if (!printit || !n || !n->section || n->value<mapstart ||
	n->value>=mapend) return;
    savepc = pc; saveendpc = endpc; saveincode = incode;
    pc = reloc+n->value; endpc = pc+100; incode = TRUE;
    if (n->desc == STRTY || !n->desc && guesstype()==STRTY) {
	sprint("\t|"); printascii(endpc);
	}
    pc = savepc; endpc = saveendpc; incode = saveincode;
    }

symbol *getlabel(unsigned long *address,void *pc,int pcrel) {
    symbol *n;
    struct relocation_info *r;
    unsigned a;
    extern symbol *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->address == a) {
	    if (pcrel ? curr->pcrel : !curr->pcrel) r = curr;
	    break;
	    }
	if (curr->address > a) break;
	}
#if 0
    if (a==0x222||a==0x22b||a==0x28a||a==0xd94||a==0xede) {
	printf("Entry for address %x, data is %x\n",a,*address);
	if (r) {printf("relocation entry: address %x, symbolnum %d, pcrel %d, length %d, extern %d, reserved %d\n",
		       r->address, r->symbolnum, r->pcrel, r->length,
		       r->r_extern, r->reserved);
		n = sourcesymtab+r->symbolnum;
		printf("Symbol table: name %s, type %x, sect %d, desc %x, value %x\n",
		       n->name,n->type,n->desc,n->value);
		}
	else printf("No relocation entry.\n");
	}
#endif

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

    /* Otherwise, we guess that values near zero are absolute: */
    else {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->desc == ULONG || n->desc == LONG || symaddress>*address-2
	    && (n->desc == USHORT || n->desc == SHORT)) {
	    *address -= symaddress;
	    return(n);
	    }
	}
    /* otherwise make a label.  Give it the type needed: */
    n = createlabel(*address,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);
    }

symbol *settype(symbol *s,int type,char *name) {
    if (s) {
	if (!s->desc) s->desc = type;
	if (!s->name) s->name = name;
	}
    return(s);
    }

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