/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */
/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: dis.c 1.4 90/04/27 $@(#)bits.c	10.3"

#include	<stdio.h>
#include	"dislib.h"
#include	"tables.c"


#define		OPLEN	35	/* maximum length of a single operand	*/
				/* (will be used for printing)		*/

static	char	operand[3][OPLEN];	/* to store operands as they	*/
					/* are encountered		*/
static	char	symarr[3][OPLEN];
static char *overreg;	/* save the segment override register if any    */
static int data16;	/* 16- or 32-bit data */
static int addr16;	/* 16- or 32-bit addressing */

#define	WBIT(x)	(x & 0x1)		/* to get w bit	*/
#define DBIT(x) ( (x >> 1) & 0x1)       /* to get d bit */
#define	REGNO(x) (x & 0x7)		/* to get 3 bit register */
#define	VBIT(x)	((x)>>1 & 0x1)		/* to get 'v' bit */
#define	OPSIZE(data16,wbit) ((wbit) ? ((data16) ? 2:4) : 1 )

#define	REG_ONLY 3	/* mode indicates a single register with	*/
			/* no displacement is an operand		*/
#define	LONGOPERAND 1	/* value of the w-bit indicating a long		*/
			/* operand (2-bytes or 4-bytes)			*/

#define RM	0	/* in case RMMR, is the value of the dbit that	*/
			/* indicates RM rather than MR			*/

/*
 * The following are setup by dis386_init()
 */
unsigned char (*_386iread)(); /* function to read a byte from instr stream */
void (*_386_dolook)();      /* function to lookup&print labels */
void (*_386_do_lnums)();    /* function to print line numbers */
char *(*_386xlate_name)(); /* function to xlate name given absolute addr */
void (*_386xlate_local)(); /* function to xlate locals */

unsigned long _386i_addr;/* internal address of next instruction stream byte */
unsigned long _386s_addr;/* saved address of start of this instruction */
int _386und;		/* Undefined flag, if nonzero on return from format */
			/* then undefined instruction */
char _386i_str[132];    /* Array for holding disassembled instruction */

int _386_oflag = 0;
int _386_Rflag = 0;
int _386_sflag = 0;
unsigned short curbyte;
unsigned long cur4bytes, bytes_saved;
char object[NHEX];
char symrep[NLINE];
char *decode_1167();
void emit_386_or_1167();

int use_weitek;

#define NULL 0

/*
 *	xlocsympr()
 *
 *	Call the externally supplied local symbol translation function
 */
xlocsympr(val, regno, pos)
long val;
int regno;
char **pos;
{

	if(_386xlate_local != NULL) {
	   (*_386xlate_local)(val, regno, pos);
	} else {
	   *pos += sprintf(*pos, "%ld+%s",val,regname[regno]);
	}
}

/*
 *	xextsympr()
 *
 *	Call the externally supplied symbol translation function
 */
xextsympr(addr, pos)
	unsigned addr;
	char **pos;
{

	char *symn = NULL;

	if(_386xlate_name != NULL)
	   symn = (*_386xlate_name)(addr);
        if(symn != NULL) {
	   *pos += sprintf(*pos, "%s", symn);
	} else {
	   *pos += sprintf(*pos, _386_oflag?"0%o":"0x%x",addr);
	}
}

/*
 *	xprintline ()
 *
 *	Print the disassembled line, consisting of the object code
 *	and the mnemonics.  The breakpointable line number, if any,
 *	has already been printed, and 'object' contains the offset
 *	within the section for the instruction.
 */

xprintline()

{
	extern	int	_386_oflag;		/* in _extn.c */
	extern	int	_386_sflag;		/* in _extn.c */
	extern  char	object[];
	extern	char	symrep[];
#ifdef OLIST
	extern	int	nosource;
#endif

#ifdef IAPX
	(_386_oflag > 0) ?
		printf("%-36s%s\n",object,_386i_str):	/* to print octal */
		printf("%-30s%s\n",object,_386i_str);	/* to print hex */
#endif
#ifdef I386
	if (_386_oflag > 0)
		printf(_386_sflag?"%-36s%s\n%20c[%s]\n":"%-36s%s\n",
				object,_386i_str,' ',symrep); /* to print octal */
	else
		printf(_386_sflag?"%-30s%s\n%20c[%s]\n":"%-30s%s\n",
				object,_386i_str,' ',symrep); /* to print hex */
#endif
#ifdef MC68
	(_386_oflag > 0) ?
		printf("%-36s%s\n",object,_386i_str):	/* to print octal */
		printf("%-37s%s\n",object,_386i_str);	/* to print hex */
#endif
#if VAX || M32 || N3B
#ifdef OLIST
	if (nosource == TRUE) return;
	printf("%-46s%-8s%s\n", object, linebuf, _386i_str);
#else
	if (_386_oflag > 0)
		printf(_386_sflag?"%-36s%s\n%20c[%s]\n":"%-36s%s\n",
				object,_386i_str,' ',symrep); /* to print octal */
	else
		printf(_386_sflag?"%-37s%s\n%20c[%s]\n":"%-37s%s\n",
				object,_386i_str,' ',symrep); /* to print hex */
#endif
#endif
}

/*
 *	xprt_offset ()
 *
 *	Print the offset, right justified, followed by a ':'.
 */


xprt_offset()

{
	extern	int	_386_oflag;
	extern	char	object[];

#ifdef OLIST
	if (_386_oflag)
		sprintf(object,"0%.7lo  ", _386i_addr);
	else
		sprintf(object, "0x%.6lx  ", _386i_addr);
#else
	if (_386_oflag)
		sprintf(object,"%6lo:  ",_386i_addr);
	else
		sprintf(object,"%4lx:  ",_386i_addr);
#endif
}
/*
 *	compoff (lng, temp)
 *
 *	This routine will compute the location to which control is to be
 *	transferred.  'lng' is the number indicating the jump amount
 *	(already in proper form, meaning masked and negated if necessary)
 *	and 'temp' is a character array which already has the actual
 *	jump amount.  The result computed here will go at the end of 'temp'.
 *	(This is a great routine for people that don't like to compute in
 *	hex arithmetic.)
 */

compoff(lng, temp)
long	lng;
char	*temp;
{
	extern	int	_386_oflag;	/* from _extn.c */
	extern	long	loc;	/* from _extn.c */

	lng += _386i_addr;
	if (_386_oflag)
		sprintf(temp,"%s <%lo>",temp,lng);
	else
		sprintf(temp,"%s <%lx>",temp,lng);
}

/*
 * dis386_init() initializes some externally supplied
 * entities necessary for disassembly
 *
 *   This is where the interface from the disassembler back to the
 *   calling program is set up.  Here is the meaning of each parameter.
 *   All parameters may be 0 except for iread.
 *
 *   POINTERS TO FUNCTIONS:
 *
 *   iread:  Pointer to a function which is used to get the actual bytes
 *           to be disassembled.  Takes one arugment, type unsigned, which
 *           is the address of the byte to be read.  The first call will
 *           always be the address passed in to dis386, subsequent calls
 *           will increase the address by 1.
 *   dolook: Only used in disassembing jump tables.  Lets the calling
 *           program know that dis386 is disassembling at an address
 *           so the calling program can print the label if appropriate.
 *           One argument, type unsigned, address where label might be.
 *   do_lnums: Only used for jump tables.  Like dolook except for
 *           for line numbers.  No arguments.
 *   xlate_name: Used for symbolic disassembly.  The disassembler
 *           calls this routine to find the name of external/static
 *           symbols.  One argument, unsigned, the address of the
 *           symbol.
 *   xlate_local: Used for symbolic disassembly.  The disassembler
 *           calls this routine to find the name of local symbols,
 *           function arguments, and arrays.  Three arguments:
 *           the first is type long and is the offset from the base
 *           register, the second is type int, the register number,
 *           last is type "char **", (yes pointer to pointer), pointer
 *           to a pointer to a char.  Points to a pointer to the
 *           string where the variable name will be stored.  Useless
 *           complexity, but just used what the disassembler was using.
 *
 *   FLAGS
 *
 *   oflag:  If 0, produce hex, if 1, produce octal.
 *   Rflag:  If 0, 32bit mode, if 1, 16bit mode
 *   sflag:  If 0, no symbolic disassembly, if 1 do symbolic disassembly,
 *           provided that either xlate_name and/or xlate_local are
 *           non-null.
 *
 *   In addition to the initialization done by dis386_init, and the
 *   arguments passed in and returned from dis386 itself, there is an
 *   additional interface of external variables.
 *
 *   EXTERNAL VARIABLES
 *
 *   _386und:  0 if the last instruction was disassembled successfully,
 *             >0 if an undefined opcode was encountered.
 *   symrep:   String containing the symbolic representation of the
 *             addressing mode (ie. actual symbol name).
 *
 *   Note: look at bits.c in the program dis as an example of how 
 *         dis386_init and dis386 are used.
 */
dis386_init(iread, dolook, do_lnums, xlate_name, xlate_local, oflag, Rflag,
sflag)
	unsigned char (*iread)();
	void (*dolook)();
	void (*do_lnums)();
	char *(*xlate_name)();
	void (*xlate_local)();
	int oflag, Rflag, sflag;
{

	_386iread = iread;
	_386xlate_name = xlate_name;
	_386xlate_local = xlate_local;
	_386_dolook = dolook;
	_386_do_lnums = do_lnums;
	_386_oflag = (oflag != 0);
	_386_Rflag = (Rflag != 0);
	_386_sflag = (sflag != 0);
}

/*
 *	convert (num, temp, flag)
 *
 *	Convert the passed number to either hex or octal, depending on
 *	the _386_oflag, leaving the result in the supplied string array.
 *	If  LEAD  is specified, preceed the number with '0' or '0x' to
 *	indicate the base (used for information going to the mnemonic
 *	printout).  NOLEAD  will be used for all other printing (for
 *	printing the offset, object code, and the second byte in two
 *	byte immediates, displacements, etc.) and will assure that
 *	there are leading zeros.
 */

convert(num,temp,flag)
unsigned	num;
char	temp[];
int	flag;

{

#if UNIX < 4
#ifdef IAPX
	if (flag == NOLEAD) 
		(_386_oflag) ?	sprintf(temp,"%03o",num):
				sprintf(temp,"%02x",num);
#else
	if (flag == NOLEAD)
		(_386_oflag) ?	sprintf(temp,"%06o",num):
				sprintf(temp,"%04x",num);
#endif	/* IAPX */
#else
#ifdef IAPX
	if (flag == NOLEAD) 
		(_386_oflag) ?	sprintf(temp,"%.3o",num):
				sprintf(temp,"%.2x",num);
#else
	if (flag == NOLEAD)
		(_386_oflag) ?	sprintf(temp,"%.6o",num):
				sprintf(temp,"%.4x",num);
#endif	/* IAPX */
#endif	/* UNIX < 4 */
	if (flag == LEAD)
		(_386_oflag) ?	sprintf(temp,"0%o",num):
				sprintf(temp,"0x%x",num);
#ifdef	I386
#if UNIX < 4
	if (flag == NOLEADSHORT)
		(_386_oflag) ?	sprintf(temp,"%03o",num):
				sprintf(temp,"%02x",num);
#else
	if (flag == NOLEADSHORT)
		(_386_oflag) ?	sprintf(temp,"%.3o",num):
				sprintf(temp,"%.2x",num);
#endif	/* UNIX < 4 */
#endif
}

/*
 *	xlookbyte (ptr)
 *
 *	read a byte, mask it, then return the result in 'curbyte'.
 *	The byte is not immediately placed into the string object[].
 *	is incremented.
 */

int
xlookbyte()

{
	char	byte;

	byte = (*_386iread)(_386i_addr++);
	curbyte = byte & 0377;
}

/*
 *	xgtbyte ()
 *
 *	read a byte, mask it, then return the result in 'curbyte'.
 *	The getting of all single bytes is done here.
 */
void
xgtbyte()

{
	char	temp[NCPS+1];
	char	byte;

	byte = (*_386iread)(_386i_addr++);
	curbyte = byte & 0377;
#ifdef I386
	convert(curbyte, temp, NOLEADSHORT);
#else
	convert(curbyte, temp, NOLEAD);
#endif
	sprintf(object,"%s%s ",object,temp);
}

/*
 * dis386: main disassembly function, returns ptr to decoded
 *         instruction, sets length of instruction decoded
 */

char *
dis386(addr, length)
	unsigned addr;
	int *length;
{
	/* the following routines are in _utls.c	*/
	/* libc */
	extern	void	exit();
	extern	char 	*strcat();
	extern	char 	*strcpy();
	/* forward */
	void get_modrm_byte();
	void check_override();
	void imm_data();
	void get_opcode();
	void bad_opcode();
	void displacement();

	struct instable *dp;
	int dbit, wbit, vbit;
	unsigned mode, reg, r_m;
	/* nibbles of the opcode */
	unsigned opcode1, opcode2, opcode3, opcode4, opcode5;
	unsigned short tmpshort;
	long	lngval;
	char *reg_name;
	int got_modrm_byte;
	char mnemonic[132];
	char	temp[NCPS+1];

		_386i_addr = _386s_addr = addr;
		_386und = 0;
		mnemonic[0] = '\0';
		_386i_str[0] = '\0';
		symrep[0] = '\0';
		operand[0][0] = '\0';
		operand[1][0] = '\0';
		operand[2][0] = '\0';
		symarr[0][0] = '\0';
		symarr[1][0] = '\0';
		symarr[2][0] = '\0';
		addr16 = _386_Rflag;
		data16 = _386_Rflag;
		overreg = (char *) 0;
		use_weitek = 0;

		/*
		** As long as there is a prefix, the default segment register,
		** addressing-mode, or data-mode in the instruction will be overridden.
		** This may be more general than the chip actually is.
		*/
		for(;;) {
			get_opcode(&opcode1, &opcode2);
			dp = &distable[opcode1*16+opcode2];

			if ( dp->adr_mode == PREFIX ) strcat(mnemonic,dp->name);
			else if ( dp->adr_mode == AM ) addr16 = !addr16;
			else if ( dp->adr_mode == DM ) data16 = !data16;
			else if ( dp->adr_mode == OVERRIDE ) overreg = dp->name;
			else break;
		}

		/* some 386 instructions have 2 bytes of opcode before the mod_r/m */
		/* byte so we need to perform a table indirection.	      */
		if (dp->indirect == (struct instable *) op0F) {
			get_opcode(&opcode4,&opcode5);
			if (opcode4>12)  {
				bad_opcode();
				goto finish;
			}
			dp = &op0F[opcode4*16+opcode5];
			if(opcode4 == 0xc && opcode5 >= 0x8)
				opcode2 = opcode5;  /* Handle bswap */
		}

		got_modrm_byte = 0;
		if (dp->indirect != TERM) {
			/* This must have been an opcode for which several
			 * instructions exist.  The opcode3 field further decodes
			 * the instruction.
			 */
			got_modrm_byte = 1;
			get_modrm_byte(&mode, &opcode3, &r_m);
			/*
			 * decode 287 instructions (D8-DF) from opcodeN
			 */
			if (opcode1 == 0xD && opcode2 >= 0x8) {
				/* instruction form 5 */
				if (opcode2 == 0xB && mode == 0x3 && opcode3 == 4)
					dp = &opFP5[r_m];
				else if (opcode2 == 0xB && mode == 0x3 && opcode3 > 4) {
					bad_opcode();
					goto finish;
				}
				/* instruction form 4 */
				else if (opcode2==0x9 && mode==0x3 && opcode3 >= 4)
					dp = &opFP4[(opcode3-4)*8+r_m];
				/* instruction form 3 */
				else if (mode == 0x3)
					dp = &opFP3[(opcode2-8)*8+opcode3];
				/* instruction form 1 and 2 */
				else dp = &opFP1n2[(opcode2-8)*8+opcode3];
			}
			else
				dp = dp -> indirect + opcode3;
					/* now dp points the proper subdecode table entry */
		}

		if (dp->indirect != TERM) {
			bad_opcode();
			goto finish;
		}

		/* print the mnemonic */
		if ( dp->adr_mode != CBW  && dp->adr_mode != CWD ) {
			(void) strcat(mnemonic,dp -> name);  /* print the mnemonic */
			if (dp->suffix)
				(void) strcat(mnemonic, (data16? "w" : "l") );
			(void) sprintf(_386i_str, (strlen(mnemonic)<7 ? "%-7s" : "%-7s "),
			               mnemonic);
		}


		/*
		 * Each instruction has a particular instruction syntax format
		 * stored in the disassembly tables.  The assignment of formats
		 * to instructins was made by the author.  Individual formats
		 * are explained as they are encountered in the following
		 * switch construct.
		 */

		switch(dp -> adr_mode){

		/* register to or from a memory or register operand,	*/
		/* based on the 'd' bit					*/
		case RMMR:
			dbit = DBIT(opcode2);
			wbit = WBIT(opcode2);
			if (opcode1 == 0 && opcode2 == 0xF &&
				( ((opcode4<<4)|opcode5) == 0xa6 ||
				((opcode4<<4)|opcode5) == 0xc0)) {
				wbit = 0;
			}
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			opcode3 = curbyte;
			get_operand(mode, r_m, wbit, 0);
			if (opcode1 == 0 && opcode2 == 0xF) {
				switch((opcode4<<4)|opcode5) {
				case 0xa3:	/* bt   */
				case 0xa5:	/* shld */
				case 0xa7:	/* ibts */
				case 0xab:	/* bts  */
				case 0xad:	/* shrd */
				case 0xb3:	/* btr  */
				case 0xbb:	/* btc  */
				/* bsf and bsr are commented outr because
				 * Sys-V has the opposite ordering on these
				 * two as Dynix/BSD has
				 */
				/* case 0xbc: */	/* bsf  */
				/* case 0xbd: */	/* bsr  */
					dbit = RM;
					break;
				case 0xc0:	/* xaddb (486) */
				case 0xc1:	/* xadd (486) */
					if(mode != REG_ONLY)
						dbit = RM;
					break;
				}
			}
			reg_name = decode_1167(opcode1<<4|opcode2, opcode3, cur4bytes);
			if(reg_name == NULL)
			   reg_name = (data16 ? REG16 : REG32)[reg*2+wbit];
			emit_386_or_1167 (reg_name, reg, data16, dbit, wbit);
			if (_386_sflag && !use_weitek) {
			   if(dbit == RM)
			     (void) sprintf(symrep,"%s,%s",reg_name,symarr[0]);
			   else
			     (void) sprintf(symrep,"%s,%s",symarr[0],reg_name);
                        } else if (_386_sflag && use_weitek == 1) {
			   if(dbit == RM)
			     (void) sprintf(symrep,"%s,%s",reg_name,operand[0]);
			   else
			     (void) sprintf(symrep,"%s,%s",operand[0],reg_name);
                        } else if (_386_sflag && use_weitek == 2) {
			     (void) sprintf(symrep,"%s",reg_name);
                        }
			break;
		/* movsbl movsbw (0x0FBE) or movswl (0x0FBF) */
		/* movzbl movzbw (0x0FB6) or mobzwl (0x0FB7) */
		/* wbit lives in 2nd byte, note that operands are different sized */
		case MOVZ:
			/* Get second operand first so data16 can be destroyed */
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];

			wbit = WBIT(opcode5);
			data16 = 1;
			get_operand(mode, r_m, wbit, 0);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],reg_name);
			if (_386_sflag)
				(void) sprintf(symrep,"%s,%s",symarr[0],reg_name);
			break;

		/* imul instruction, with either 8-bit or longer immediate */
		case IMUL:
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 1);
			/* opcode 0x6B for byte, sign-extended displacement, 0x69 for word(s)*/
			imm_data( OPSIZE(data16,opcode2 == 0x9), 0);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];
			(void) sprintf(_386i_str,"%s%s,%s,%s",_386i_str,operand[0],operand[1],reg_name);
			if (_386_sflag)
				sprintf(symrep,"%s,%s,%s",symarr[0],symarr[1],reg_name);
			break;

		/* memory or register operand to register, with 'w' bit	*/
		case MRw:
			wbit = WBIT(opcode2);
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, wbit, 0);
			if (data16)
				reg_name = REG16[reg*2+wbit];
			else
				reg_name = REG32[reg*2+wbit];
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],reg_name);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],reg_name);
			break;

		/* register to memory or register operand, with 'w' bit	*/
		/* arpl happens to fit here also because it is odd */
		case RMw:
			wbit = WBIT(opcode2);
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, wbit, 0);
			if (data16)
				reg_name = REG16[reg*2+wbit];
			else
				reg_name = REG32[reg*2+wbit];
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,reg_name,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",reg_name,symarr[0]);
			break;

		/* Double shift. Has immediate operand specifying the shift. */
		case DSHIFT:
			get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 1);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];
			imm_data(1, 0);
			sprintf(_386i_str,"%s%s,%s,%s",_386i_str,operand[0],reg_name,operand[1]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s,%s",symarr[0],reg_name,symarr[1]);
			break;

		/* Double shift. With no immediate operand, specifies using %cl. */
		case DSHIFTcl:
			get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 0);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];
			sprintf(_386i_str,"%s%s,%s",_386i_str,reg_name,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",reg_name,symarr[0]);
			break;

		/* immediate to memory or register operand */
		case IMlw:
			wbit = WBIT(opcode2);
			get_operand(mode, r_m, wbit, 1);
			/* A long immediate is expected for opcode 0x81, not 0x80 nor 0x83 */
			imm_data(OPSIZE(data16,opcode2 == 1), 0);
			sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],operand[1]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],symarr[1]);
			break;

		/* immediate to memory or register operand with the	*/
		/* 'w' bit present					*/
		case IMw:
			wbit = WBIT(opcode2);
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, wbit, 1);
			decode_1167(opcode1<<4|opcode2,mode<<6|reg<<3|r_m,
			   cur4bytes);
			imm_data(OPSIZE(data16,wbit), 0);
			sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],operand[1]);
			if (_386_sflag && !use_weitek)
				sprintf(symrep,"%s,%s",symarr[0],symarr[1]);
			else if (_386_sflag && use_weitek)
				sprintf(symrep,"%s,%s",symarr[0],operand[1]);
			break;

		/* immediate to register with register in low 3 bits	*/
		/* of op code						*/
		case IR:
			wbit = opcode2 >>3 & 0x1; /* w-bit here (with regs) is bit 3 */
			reg = REGNO(opcode2);
			imm_data( OPSIZE(data16,wbit), 0);
			if (data16)
				reg_name = REG16[reg*2+wbit];
			else
				reg_name = REG32[reg*2+wbit];
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],reg_name);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],reg_name);
			break;

		/* memory operand to accumulator			*/
		case OA:
			dbit = MR;
			goto OAandAO;
			break;

		/* accumulator to memory operand			*/
		case AO:
			dbit = RM;
OAandAO:
			wbit = WBIT(opcode2);
			
			displacement(OPSIZE(addr16,LONGOPERAND), 0,&lngval);
			reg_name = decode_1167(opcode1<<4|opcode2, 0x05,
			   cur4bytes);
			if(reg_name == NULL) {
			 if(dbit == RM)
			   reg_name = ( addr16 ? REG16 : REG32 )[0*8+wbit];
			 else
			   reg_name = ( data16 ? REG16 : REG32 )[0*8+wbit];
			}
			emit_386_or_1167(reg_name, 0, addr16, dbit, wbit);
			
			if (_386_sflag && !use_weitek) {
				char *symstring = symarr[0];
				xextsympr(lngval,&symstring);
				if(dbit == RM)
				    sprintf(symrep,"%s,%s",reg_name,symarr[0]);
				else
				    sprintf(symrep,"%s,%s",symarr[0],reg_name);
			} else if(_386_sflag && use_weitek == 1) {
				if(dbit == RM)
				    sprintf(symrep,"%s,%s",reg_name,operand[0]);
				else
				    sprintf(symrep,"%s,%s",operand[0],reg_name);
			} else if(_386_sflag && use_weitek == 2) {
				    sprintf(symrep,"%s",reg_name);
			}
			break;

		/* memory or register operand to segment register	*/
		case MS:
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 0);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],SEGREG[reg]);
			break;

		/* segment register to memory or register operand	*/
		case SM:
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 0);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,SEGREG[reg],operand[0]);
			break;

		/* rotate or shift instrutions, which may shift by 1 or */
		/* consult the cl register, depending on the 'v' bit	*/
		case Mv:
			vbit = VBIT(opcode2);
			wbit = WBIT(opcode2);
			get_operand(mode, r_m, wbit, 0);
			/* When vbit is set, register is an operand, otherwise just $0x1 */
			reg_name = vbit ? "%cl," : "" ;
			(void) sprintf(_386i_str,"%s%s%s",_386i_str, reg_name, operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s%s", reg_name, symarr[0]);
			break;

		/* immediate rotate or shift instrutions, which may or */
		/* may not consult the cl register, depending on the 'v' bit	*/
		case MvI:
			vbit = VBIT(opcode2);
			wbit = WBIT(opcode2);
			get_operand(mode, r_m, wbit, 0);
			imm_data(1,1);
			/* When vbit is set, register is an operand, otherwise just $0x1 */
			reg_name = vbit ? "%cl," : "" ;
			(void) sprintf(_386i_str,"%s%s,%s%s",_386i_str,operand[1], reg_name, operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s%s",symarr[1],reg_name,symarr[0]);
			break;

		case MIb:
			get_operand(mode, r_m, LONGOPERAND, 0);
			imm_data(1,1);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[1], operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[1],symarr[0]);
			break;

		/* single memory or register operand with 'w' bit present*/
		case Mw:
			wbit = WBIT(opcode2);
			get_operand(mode, r_m, wbit, 0);
			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		/* single memory or register operand			*/
		case M:
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
                        /*
			 * The setcc instructions (op codes 0F90-0F9F),
			 * write 8-bit registers
			 */
			get_operand(mode, r_m, (opcode1==0 && opcode2==0xF
			   && opcode4==9) ? 0 : LONGOPERAND, 0);
		/*
		 * Decode pushl and popl weitek opcodes
		 */
		if ( (mode == 0 && r_m == 0x5)
		 &&  ( (opcode1 == 0xf && opcode2 == 0xf && opcode3 == 0x6)
		   ||  (opcode1 == 0x8 && opcode2 == 0xf && reg == 0x0)
		     )
		 &&  ( (cur4bytes & 0xffff0000) == 0xffc00000  ||
		       (cur4bytes & 0xffff0000) == 0xc0000000 ) )  {
			sprintf(operand[0], "%%fp%d", (cur4bytes >> 2) & 0x1f);
			sprintf(symarr[0],  "%%fp%d", (cur4bytes >> 2) & 0x1f);
                 }

			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		case SREG: /* special register */
			get_modrm_byte(&mode, &reg, &r_m);
			vbit = 0;
			switch (opcode5) {
			case 2:
				vbit = 1;
				/* fall thru */
			case 0: 
				reg_name = CONTROLREG[reg];
				break;
			case 3:
				vbit = 1;
				/* fall thru */
			case 1:
				reg_name = DEBUGREG[reg];
				break;
			case 6:
				vbit = 1;
				/* fall thru */
			case 4:
				reg_name = TESTREG[reg];
				break;
			}
			strcpy(operand[0], REG32[r_m*2+1]);

			if (vbit)
			{
				strcpy(operand[0], reg_name);
				reg_name = REG32[r_m*2+1];
			}
			
			(void) sprintf(_386i_str, "%s%s,%s",
				_386i_str, reg_name, operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",reg_name,operand[0]);
			break;

		/* single register operand with register in the low 3	*/
		/* bits of op code					*/
		case R:
			reg = REGNO(opcode2);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];
			(void) sprintf(_386i_str,"%s%s",_386i_str,reg_name);
			if (_386_sflag)
				strcpy(symrep,reg_name);
			break;

		/* register to accumulator with register in the low 3	*/
		/* bits of op code, xchg instructions                   */
		case RA: {
			char *eprefix;
			reg = REGNO(opcode2);
			if (data16) {
				eprefix = "";
				reg_name = REG16[reg*2+LONGOPERAND];
			}
			else {
				eprefix = "e";
				reg_name = REG32[reg*2+LONGOPERAND];
			}
			(void) sprintf(_386i_str,"%s%s,%%%sax",
				_386i_str,reg_name,eprefix);
			if (_386_sflag)
				(void) sprintf(symrep,"%s,%%%sax",
					reg_name,eprefix);
			break;
		}

		/* single segment register operand, with register in	*/
		/* bits 3-4 of op code					*/
		case SEG:
			reg = curbyte >> 3 & 0x3; /* segment register */
			(void) sprintf(_386i_str,"%s%s",_386i_str,SEGREG[reg]);
			if (_386_sflag)
				(void) strcpy(symrep,SEGREG[reg]);
			break;

		/* single segment register operand, with register in	*/
		/* bits 3-5 of op code					*/
		case LSEG:
			reg = curbyte >> 3 & 0x7; /* long seg reg from opcode */
			(void) sprintf(_386i_str,"%s%s",_386i_str,SEGREG[reg]);
			if (_386_sflag)
				(void) strcpy(symrep,SEGREG[reg]);
			break;

		/* memory or register operand to register		*/
		case MR:
			if (!got_modrm_byte)
				get_modrm_byte(&mode, &reg, &r_m);
			get_operand(mode, r_m, LONGOPERAND, 0);
			if (data16)
				reg_name = REG16[reg*2+LONGOPERAND];
			else
				reg_name = REG32[reg*2+LONGOPERAND];
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],reg_name);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],reg_name);
			break;

		/* immediate operand to accumulator			*/
		case IA: {
			int no_bytes = OPSIZE(data16,WBIT(opcode2));
			switch(no_bytes) {
				case 1: reg_name = "%al"; break;
				case 2: reg_name = "%ax"; break;
				case 4: reg_name = "%eax"; break;
			}
			imm_data(no_bytes, 0);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0], reg_name) ;
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],reg_name);
			break;
		}
		/* memory or register operand to accumulator		*/
		case MA:
			wbit = WBIT(opcode2);
			get_operand(mode, r_m, wbit, 0);
			(void) sprintf(_386i_str,"%s%s",_386i_str, operand[0] );
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		/* si register to di register				*/
		case SD:
			check_override(0);
			(void) sprintf(_386i_str,"%s%s(%%%ssi),(%%%sdi)",_386i_str,operand[0],
				addr16? "" : "e" , addr16? "" : "e");
			if (_386_sflag)
				(void) sprintf(symrep,"%s%%%ssi,%%%sdi",symarr[0],
					addr16? "" : "e" , addr16? "" : "e");
			break;

		/* accumulator to di register				*/
		case AD:
			wbit = WBIT(opcode2);
			check_override(0);
			reg_name = (data16 ? REG16 : REG32) [0*8+wbit] ;
			(void) sprintf(_386i_str,"%s%s,%s(%%%sdi)",_386i_str, reg_name, operand[0],
				addr16? "" : "e");
			if (_386_sflag)
				(void) sprintf(symrep,"%s,%s%%%sdi",reg_name,symarr[0],
					addr16? "" : "e");
			break;

		/* si register to accumulator				*/
		case SA:
			wbit = WBIT(opcode2);
			check_override(0);
			reg_name = (addr16 ? REG16 : REG32) [0*8+wbit] ;
			(void) sprintf(_386i_str,"%s%s(%%%ssi),%s",_386i_str,operand[0],
				addr16? "" : "e", reg_name);
			if (_386_sflag)
				(void) sprintf(symrep,"%s%%%ssi,%s",symarr[0],
				addr16? "" : "e", reg_name);
			break;

		/* single operand, a 16/32 bit displacement		*/
		/* added to current offset by 'compoff'			*/
		case D:
			displacement(OPSIZE(data16,LONGOPERAND), 0, &lngval);
			compoff(lngval, operand[1]);
			(void) sprintf(_386i_str,"%s%s%s",_386i_str,operand[0],
				(lngval == 0) ? "" : operand[1]);
			if (_386_sflag) {
				char *symstring = symarr[0];
				xextsympr(lngval+_386i_addr,&symstring);
				sprintf(symrep,"%s",symarr[0]);
			}
			break;

		/* indirect to memory or register operand		*/
		case INM:
			get_operand(mode, r_m, LONGOPERAND, 0);
			(void) sprintf(_386i_str,"%s*%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		/* for long jumps and long calls -- a new code segment   */
		/* register and an offset in IP -- stored in object      */
		/* code in reverse order                                 */
		case SO:
			displacement(OPSIZE(addr16,LONGOPERAND), 1,&lngval);
			/* will now get segment operand*/
			displacement(2, 0,&lngval);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],operand[1]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",operand[0],operand[1]);
			break;

		/* jmp/call. single operand, 8 bit displacement.	*/
		/* added to current EIP in 'compoff'			*/
		case BD:
			displacement(1, 0, &lngval);
			compoff(lngval, operand[1]);
			(void) sprintf(_386i_str,"%s%s%s",_386i_str, operand[0],
				(lngval == 0) ? "" : operand[1]);
			if (_386_sflag) {
				char *symstring = symarr[0];
				xextsympr(lngval+_386i_addr,&symstring);
				sprintf(symrep,"%s",symarr[0]);
			}
			break;

		/* single 32/16 bit immediate operand			*/
		case I:
			imm_data(OPSIZE(data16,LONGOPERAND), 0);
			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		/* single 8 bit immediate operand			*/
		case Ib:
			imm_data(1, 0);
			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		case ENTER:
			imm_data(2,0);
			imm_data(1,1);
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],operand[1]);
			if (_386_sflag)
				sprintf(symrep,"%s,%s",symarr[0],symarr[1]);
			break;

		/* 16-bit immediate operand */
		case RET:
			imm_data(2,0);
			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",symarr[0]);
			break;

		/* single 8 bit port operand				*/
		case P:
			check_override(0);
			imm_data(1, 0);
			(void) sprintf(_386i_str,"%s%s",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s",operand[0]);
			break;

		/* single operand, dx register (variable port instruction)*/
		case V:
			check_override(0);
			(void) sprintf(_386i_str,"%s%s(%%dx)",_386i_str,operand[0]);
			if (_386_sflag)
				sprintf(symrep,"%s(%%dx)",operand[0]);
			break;

		/* The int instruction, which has two forms: int 3 (breakpoint) or  */
		/* int n, where n is indicated in the subsequent byte (format Ib).  */
		/* The int 3 instruction (opcode 0xCC), where, although the 3 looks */
		/* like an operand, it is implied by the opcode. It must be converted */
		/* to the correct base and output. */
		case INT3:
			convert(3, temp, LEAD);
			(void) sprintf(_386i_str,"%s$%s",_386i_str,temp);
			if (_386_sflag)
				sprintf(symrep,"$%s",temp);
			break;

		/* an unused byte must be discarded			*/
		case U:
			xgtbyte();
			break;

		case CBW:
			if (data16)
				(void) strcat(_386i_str,"cbtw");
			else
				(void) strcat(_386i_str,"cwtl");
			break;

		case CWD:
			if (data16)
				(void) strcat(_386i_str,"cwtd");
			else
				(void) strcat(_386i_str,"cltd");
			break;

		/* no disassembly, the mnemonic was all there was	*/
		/* so go on						*/
		case GO_ON:
			break;

		/* Special byte indicating a the beginning of a 	*/
		/* jump table has been seen. The jump table addresses	*/
		/* will be printed until the address 0xffff which	*/
		/* indicates the end of the jump table is read.		*/
		case JTAB:
			(void) sprintf(_386i_str,"***JUMP TABLE BEGINNING***");
			xprintline();
			xprt_offset();
			xlookbyte();
			if (curbyte == FILL) {
			    if(_386_dolook != NULL)
	   			(*_386_dolook)(_386i_addr);
			    if(_386_do_lnums != NULL)
				(*_386_do_lnums)();
				(void) sprintf(_386i_str,"FILL BYTE FOR ALIGNMENT");
				(void) sprintf(object,"%s90",object);
				xprintline();
				xprt_offset();
				(void) printf("\t");
				xlookbyte();
				tmpshort = curbyte;
				xlookbyte();
				if (_386_oflag)
					(void) sprintf(object,"%s%03o %03o",
						object,curbyte,tmpshort);
				else
				         (void) sprintf(object,"%s%02x %02x",
						object,curbyte,tmpshort);
			}
			else {
				tmpshort = curbyte;
				xlookbyte();
				(void) printf("\t");
				if (_386_oflag)
					(void) sprintf(object,"%s%03o %03o",
						object,curbyte,tmpshort);
				else
				         (void) sprintf(object,"%s%02x %02x",
						object,curbyte,tmpshort);
			}
			(void) sprintf(_386i_str,"");
			while ((curbyte != 0x00ff) || (tmpshort != 0x00ff)) {
				xprintline();
				xprt_offset();
				(void) printf("\t");
				xlookbyte();
				tmpshort = curbyte;
				xlookbyte();
				if (_386_oflag)
					(void) sprintf(object,"%s%03o %03o",
						object,curbyte,tmpshort);
				else
				         (void) sprintf(object,"%s%02x %02x",
						object,curbyte,tmpshort);
			}
			(void) sprintf(_386i_str,"***JUMP TABLE END***");
			break;
		/* float reg */
		case F:
			(void) sprintf(_386i_str,"%s%%st(%1.1d)",_386i_str,r_m);
			if (_386_sflag)
				sprintf(symrep,"%%st(%1.1d)",r_m);
			break;

		/* float reg to float reg, with ret bit present */
		case FF:
			if ( opcode2 >> 2 & 0x1 ) {
				/* return result bit for 287 instructions	*/
				/* st -> st(i) */
				(void) sprintf(_386i_str,"%s%%st,%%st(%1.1d)",_386i_str,r_m);
				if (_386_sflag)
					sprintf(symrep,"%%st,%%st(%1.1d)",r_m);
			}
			else {
				/* st(i) -> st */
				(void) sprintf(_386i_str,"%s%%st(%1.1d),%%st",_386i_str,r_m);
				if (_386_sflag)
					sprintf(symrep,"%%st(%1.1d),%%st",r_m);
			}
			break;

		/* an invalid op code */
		case AM:
		case DM:
		case OVERRIDE:
		case PREFIX:
		case UNKNOWN:
			bad_opcode();
			break;

		default:
			(void) printf("dis bug: notify implementor:");
			(void) printf(" case from instruction table not found");
			exit(4);
			break;
		} /* end switch */
	finish:
		*length = _386i_addr - addr;
		return(_386i_str);

}

/*
 *	void get_modrm_byte (mode, reg, r_m)
 *
 *	Get the byte following the op code and separate it into the
 *	mode, register, and r/m fields.
 * Scale-Index-Bytes have a similar format.
 */

void
get_modrm_byte(mode, reg, r_m)
unsigned	*mode;
unsigned	*reg;
unsigned	*r_m;
{
	xgtbyte();

	*r_m = curbyte & 0x7; /* r/m field from curbyte */
	*reg = curbyte >> 3 & 0x7; /* register field from curbyte */
	*mode = curbyte >> 6 & 0x3; /* mode field from curbyte */
}

/*
 *	void check_override (opindex)
 *
 *	Check to see if there is a segment override prefix pending.
 *	If so, print it in the current 'operand' location and set
 *	the override flag back to false.
 */

void
check_override(opindex)
int opindex;
{
	if (overreg) {
		(void) sprintf(operand[opindex],"%s",overreg);
		(void) sprintf(symarr[opindex],"%s",overreg);
	}
	overreg = (char *) 0;
}

/*
 *	void displacement (no_bytes, opindex, value)
 *
 *	Get and print in the 'operand' array a one, two or four
 *	byte displacement from a register.
 */

void
displacement(no_bytes, opindex, value)
int no_bytes;
int opindex;
long *value;
{
	char	temp[(NCPS*2)+1];
	void check_override();
	getbytes(no_bytes, temp, value);
	if(no_bytes == 4)
		cur4bytes = bytes_saved;  /* For weitek stuff */
	check_override(opindex);
	(void) sprintf(operand[opindex],"%s%s",operand[opindex],temp);
}


get_operand(mode, r_m, wbit, opindex)
unsigned mode;
unsigned r_m;
int wbit;
int opindex;
{
	extern char **regname;
	int dispsize;   /* size of displacement in bytes */
	int dispvalue;  /* value of the displacement */
	char *resultreg; /* representation of index(es) */
	char *format;   /* output format of result */
	char *symstring; /* position in symbolic representation, if any */
	int s_i_b;      /* flag presence of scale-index-byte */
	unsigned ss;    /* scale-factor from opcode */
	unsigned index; /* index register number */
	unsigned base;  /* base register number */
	char indexbuffer[16]; /* char representation of index(es) */

	/* if symbolic representation, skip override prefix, if any */
	check_override(opindex);
	if (_386_sflag)
		symstring = symarr[opindex] + strlen(symarr[opindex]) ;

	/* check for the presence of the s-i-b byte */
	if (r_m==ESP && mode!=REG_ONLY && !addr16) {
		s_i_b = TRUE;
		get_modrm_byte(&ss, &index, &base);
	}
	else
		s_i_b = FALSE;

	if (addr16) {
		dispsize = dispsize16[r_m][mode];
		regname = regname16[mode]; /* Address of an array */
	}
	else {
		dispsize = dispsize32[r_m][mode];
		regname = regname32[mode]; /* Address of an array */
	}

	if (s_i_b && mode==0 && base==EBP) dispsize = 4;

	if (dispsize != 0)
		displacement(dispsize, opindex, &dispvalue);

	if (s_i_b) {
		register char *basereg = regname32[mode][base];
		(void) sprintf(indexbuffer, "%s%s,%s", basereg,
			indexname[index], scale_factor[ss]);
		resultreg = indexbuffer;
		format = "%s(%s)";
		if (_386_sflag) {
			regname = regname32[01]; /* Array address */
			if (base==EBP) {
				if (mode == 0) /* Displacement only */
					xextsympr(dispvalue,&symstring);
				else       /* Displacement off of frame */
					xlocsympr(dispvalue,base,&symstring);
				if (index != ESP) { /* No index */
					symstring += sprintf(symstring,"+%s",regname[index]);
					if (ss)
						symstring += sprintf(symstring,"*%s",scale_factor[ss]);
				}
			}
			else {
				if (dispsize) {
					xextsympr(dispvalue,&symstring);
					*symstring++ = '+';
				}
				symstring += sprintf(symstring,"%s", basereg);
				if (index != ESP) {  /* No index */
					symstring += sprintf(symstring,"+%s",regname[index]);
					if (ss)
						symstring += sprintf(symstring,"*%s",scale_factor[ss]);
				}
			}
		}
	}
	else { /* no s-i-b */
		if (mode == REG_ONLY) {
			format = "%s%s";
			if (data16)
				resultreg = REG16[r_m*2+wbit] ;
			else
				resultreg = REG32[r_m*2+wbit] ;
			if (_386_sflag)
				strcpy(symstring,resultreg);
		}
		else { /* Modes 00, 01, or 10 */
			if (addr16)
				resultreg = regname16[mode][r_m];
			else
				resultreg = regname32[mode][r_m];
			if (r_m ==EBP && mode == 0) { /* displacement only */
				format = "%s";
				if (_386_sflag)
					xextsympr(dispvalue,&symstring);
			}
			else { /* Modes 00, 01, or 10, not displacement only, and no s-i-b */
				format = "%s(%s)";
				if (_386_sflag) {
					if (dispsize == 0) /* Mode 00 */
						(void) strcpy(symstring,resultreg);
					else if (r_m == EBP) {
						regname = regname32[01]; /* Array address */
						xlocsympr(dispvalue,r_m,&symstring);
					}
					else {
						xextsympr(dispvalue,&symstring);
						(void) sprintf(symstring,"+%s",resultreg);
					}
				}
			}
		}
	}
	(void) sprintf(operand[opindex],format,operand[opindex], resultreg);
}

/*
** getbytes() reads no_bytes from a file and converts them into destbuf.
** A sign-extended value is placed into destvalue if it is non-null.
*/
getbytes(no_bytes, destbuf, destvalue)
int no_bytes;
char *destbuf;
long *destvalue;
{

	int i;
	unsigned long shiftbuf = 0;
	long value;

	for (i=0; i<no_bytes; i++) {
		xgtbyte();
#if I386        /* target is RBO */
#if i386 || vax /* host is RBO */
		shiftbuf |= (long) curbyte << (8*i);
#endif
#if m32        /* host is FBO  */
		shiftbuf = shiftbuf << 8 | curbyte ;
#endif
#endif
	}

	switch(no_bytes) {
		case 1:
			if (destvalue)
				*destvalue =
					(shiftbuf & 0x80) ?
					shiftbuf | ~0xffL : shiftbuf & 0xffL;
			break;
		case 2:
			if (destvalue) *destvalue = (short) shiftbuf;
			break;
		case 4:
			if (destvalue) *destvalue = shiftbuf;
			bytes_saved = shiftbuf;
			break;
	}
	convert(shiftbuf,destbuf,LEAD);
}


/*
 *	void imm_data (no_bytes, opindex)
 *
 *	Determine if 1, 2 or 4 bytes of immediate data are needed, then
 *	get and print them.
 */

void
imm_data(no_bytes, opindex)
int no_bytes;
int opindex;
{
	long value;
	int len = strlen(operand[opindex]);

	operand[opindex][len] = '$';
	getbytes(no_bytes, &operand[opindex][len+1], &value);
	if (_386_sflag) {
		char *symstring = symarr[opindex] + len;
		int len = strlen(symstring);
		symstring[len] = '$';
		symstring += len + 1;
		xextsympr(value,&symstring);
	}
}


/*
 *	get_opcode (high, low)
 *	Get the next byte and separate the op code into the high and
 *	low nibbles.
 */

void
get_opcode(high, low)
unsigned *high;
unsigned *low;		/* low 4 bits of op code   */
{

	xgtbyte();
	*low = curbyte & 0xf;  /* ----xxxx low 4 bits */
	*high = curbyte >> 4 & 0xf;  /* xxxx---- bits 7 to 4 */
}


/* 	bad_opcode	*/
/* 	print message and try to recover */

void
bad_opcode()
{

	(void) sprintf(_386i_str,"***ERROR--unknown op code***");
	_386und++;
}



/* 
 * Decode WEITEK instructions
 * Returns 2nd WEITEK register name or 
 * NULL if a 386 register is used.
 */
char *
decode_1167(op1, op2, addr)
	int op1;
	int op2;
	int addr;
{
	register char *s;
	register int src1, src2;
	int opcode;
	char *reg;

	/* Is this a reference to the WTL 1167? */
	if (
	     ( op1 != 0x88 && op1 != 0x89 && op1 != 0x8b &&
	       op1 != 0xa1 && op1 != 0xa2 && op1 != 0xa3 && op1 != 0xc7 )
	  || ( op2 & 0x07 ) != 0x5			/* r/m = 101 */
	  || ( op2 & 0xc0 ) != 0 			/* mod = 00  */
	  || ( (addr & 0xffff0000) != 0xffc00000 && 
		   (addr & 0xffff0000) != 0xc0000000 )
	   )
	{
		return (NULL);
	}
	/*
	 * Crack address into opcode, src1, and src2
	 */
	src1 = ((addr >> 5) & 0x1c) | (addr & 0x3);
	src2 = (addr >> 2) & 0x1f;
	opcode = (addr >> 10) & 0x3f;
	/* 
	 * Opcode name 
	 */
	switch ( opcode ) {
	case 0x00:	/* add.s */	s = "wadds"; break;
	case 0x20:	/* add.d */	s = "waddl"; break;
	case 0x01:	/* load  */	s = "wloads"; break;
	case 0x21:	/* load  */	s = "wloadl"; break;
	case 0x02:	/* mul.s */	s = "wmuls"; break;
	case 0x22:	/* mul.d */	s = "wmull"; break;
	case 0x03:	/* stor  */	s = "wstors"; break;
	case 0x23:	/* stor  */	s = "wstorl"; break;
	case 0x04:	/* sub.s */	s = "wsubs"; break;
	case 0x24:	/* sub.d */	s = "wsubl"; break;
	case 0x05:	/* div.s */	s = "wdivs"; break;
	case 0x25:	/* div.d */	s = "wdivl"; break;
	case 0x06:	/* muln.s */	s = "wmulns"; break;
	case 0x26:	/* muln.d */	s = "wmulnl"; break;
	case 0x07:	/* float.s */	s = "wfloats"; break;
	case 0x27:	/* float.d */	s = "wfloatl"; break;
	case 0x08:	/* cmpt.s */	s = "wcmpts"; break;
	case 0x28:	/* cmpt.d */	s = "wcmptl"; break;
	case 0x09:	/* tstt.s */	s = "wtstts"; break;
	case 0x29:	/* tstt.d */	s = "wtsttl"; break;
	case 0x0A:	/* neg.s */	s = "wnegs"; break;
	case 0x2A:	/* neg.d */	s = "wnegl"; break;
	case 0x0B:	/* abs.s */	s = "wabss"; break;
	case 0x2B:	/* abs.d */	s = "wabsl"; break;
	case 0x0C:	/* cmp.s */	s = "wcmps"; break;
	case 0x2C:	/* cmp.d */	s = "wcmpl"; break;
	case 0x0D:	/* tst.s */	s = "wtsts"; break;
	case 0x2D:	/* tst.d */	s = "wtstl"; break;
	case 0x0E:	/* amul.s */	s = "wamuls"; break;
	case 0x2E:	/* amul.d */	s = "wamull"; break;
	case 0x0F:	/* fix.s */	s = "wfixs"; break;
	case 0x2F:	/* fix.d */	s = "wfixl"; break;
	case 0x10:	/* cvts.d */	s = "wcvtsl"; break;
	case 0x11:	/* cvtd.s */	s = "wcvtls"; break;
	case 0x30:	/* ldctx */	s = "wldctx"; break;
	case 0x31:	/* stctx */	s = "wstctx"; break;
	case 0x12:	/* mac.s */	s = "wmacs"; break;
	case 0x32:	/* macd.s */	s = "wmacls"; break;
	default:
		return (NULL);
	}
	use_weitek = 1;  /* Use the weitek symboli representation */
	(void) sprintf(_386i_str, "%-8s", s);
	if ( src1 ) {
		/* weitek register to weitek register operation */
		static char regname[8];
		(void) sprintf(reg = regname, "%%fp%d", src1);
	} else
		reg = NULL;
	/*
	 * If called from IMw code, we need to put the reg name in the
	 * right slot.  The RMMR code, on the other hand, knows how to
	 * shuffle operands as needed.
	 */
	if (op1 == 0xc7)
		(void) sprintf(operand[1], "%%fp%d", src2);
	else
		(void) sprintf(operand[0], "%%fp%d", src2);
	return (reg);
}

/*
 * We have either a plain 386 instruction or mov instruction that
 * contains a WTL 1167 instruction.  The opcode and operands
 * have already been defined; we just stick them together in the
 * right order and check for certain 1167isms.
 */
void
emit_386_or_1167 (reg_name, reg, AD_mode, dbit, wbit)
	char		*reg_name;	/* null unless defined by decode_1167() */
	unsigned	reg;		/* i386 register number */
	int		AD_mode;	/* 16 or 32 -- select %ax or %eax */
	int		dbit;		/* RM or MR -- selects direction of mov */
	int		wbit;		/* Select byte or word/long operation. */
{
	if (reg_name == NULL) {
		if (AD_mode==1)
			reg_name = REG16[reg*2+wbit];
		else
			reg_name = REG32[reg*2+wbit];
	}
	if ( strncmp(_386i_str, "wtst", 4) == 0 
		||  strncmp(_386i_str, "wldctx", 6) == 0
		||  strncmp(_386i_str, "wstctx", 6) == 0 ) {
		use_weitek = 2;
		(void) sprintf(_386i_str,"%s%s",_386i_str,reg_name);
	} else {
		if (dbit == RM)
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,reg_name,operand[0]);
		else
			(void) sprintf(_386i_str,"%s%s,%s",_386i_str,operand[0],reg_name);
	}
}
